BLOC 事件后 Streambuilder 未重建
Posted
技术标签:
【中文标题】BLOC 事件后 Streambuilder 未重建【英文标题】:Streambuilder not rebuilding after BLOC event 【发布时间】:2020-12-24 12:21:25 【问题描述】:我正在尝试在我的应用程序中实现分页,但我没有成功。
我正在使用 Firebase,特别是带有 BLOC 模式和 Built Value 的 Firestore,我最近开始使用它来简化分页。
对于如何一起使用这些技术的任何帮助或推荐链接,我将不胜感激。
我的应用架构如下:
我试图尽可能地保持 BLOC 模式,但这反过来又使得在很大程度上分页变得非常困难,因为使用构建值作为构建值使得使用 Streams 和 Futures 变得非常困难。我浏览了整个 Internet,但找不到任何教程或文档来使用 Firestore 和 BLOC 的内置值来专门进行分页。
问题是,当我执行任何 CRUD 功能(例如从列表中删除类别)时,尽管分页和其他一切正常,但 Stream Builder 并未更新列表。
目前我尝试单独使用 Listview 构建器,但显然根本不起作用,所以我转移到 Stream Builder 并尝试了 Streams 和 Futures(.asStream) 但它没有更新。
下面是部分代码:
型号:
abstract class CategoryCard
implements Built<CategoryCard, CategoryCardBuilder>
String get category;
String get icon;
double get budget;
double get spent;
String get categoryRef;
DocumentSnapshot get document;
CategoryCard._();
factory CategoryCard([updates(CategoryCardBuilder b)]) = _$CategoryCard;
static Serializer<CategoryCard> get serializer => _$categoryCardSerializer;
查询:
Future<Stream<fs.QuerySnapshot>> getMoreCategoryAmounts(
fs.DocumentSnapshot documentSnapshot) async
var user = await getCurrentUser();
print(currentMonth);
fs.Query categoryAmountQuery = _instance
.collection('users')
.document(user.uid)
.collection('amounts')
.where('year', isEqualTo: currentYear)
.where('month', isEqualTo: currentMonth)
.orderBy('category', descending: false)
.limit(7);
return documentSnapshot != null
? categoryAmountQuery.startAfterDocument(documentSnapshot).snapshots()
: categoryAmountQuery.snapshots();
集团:
class CategoryCardBloc extends Bloc<CategoryCardEvents, CategoryCardState>
final BPipe bPipe;
final FirebaseRepository firebaseRepository;
CategoryCardBloc(@required this.bPipe, @required this.firebaseRepository)
: assert(bPipe != null),
assert(firebaseRepository != null);
@override
CategoryCardState get initialState => CategoryCardState.intial();
@override
Stream<CategoryCardState> mapEventToState(CategoryCardEvents event) async*
if (event is LoadCategoryCardEvent)
yield* _mapToEventLoadCategoryCard(event);
Stream<CategoryCardState> _mapToEventLoadCategoryCard(
LoadCategoryCardEvent event) async*
if (event.amountDocumentSnapshot == null)
yield CategoryCardState.loading();
try
Future<BuiltList<CategoryCard>> _newCategoryCards =
bPipe.getMoreCategoryCards(event.amountDocumentSnapshot);
yield CategoryCardState.loaded(
FutureMerger()
.merge<CategoryCard>(state.categoryCards, _newCategoryCards));
on NullException catch (err)
print('NULL_EXCEPTION');
yield CategoryCardState.failed(err.objectExceptionMessage,
state?.categoryCards ?? Stream<BuiltList<CategoryCard>>.empty());
on NoValueException catch (_)
print('NO VALUE EXCEPTION');
yield state.rebuild((b) => b..hasReachedEndOfDocuments = true);
catch (err)
print('UNKNOWN EXCEPTION');
yield CategoryCardState.failed(
err != null ? err.toString() : NullException.exceptionMessage,
state.categoryCards);
国家:
abstract class CategoryCardState
implements Built<CategoryCardState, CategoryCardStateBuilder>
Future<BuiltList<CategoryCard>> get categoryCards;
//*Reached end indicator
bool get hasReachedEndOfDocuments;
//*Error state
String get exception;
//*Loading state
@nullable
bool get isLoading;
//*Success state
@nullable
bool get isSuccessful;
//*Loaded state
@nullable
bool get isLoaded;
CategoryCardState._();
factory CategoryCardState([updates(CategoryCardStateBuilder b)]) =
_$CategoryCardState;
factory CategoryCardState.intial()
return CategoryCardState((b) => b
..exception = ''
..isSuccessful = false
..categoryCards =
Future<BuiltList<CategoryCard>>.value(BuiltList<CategoryCard>())
..hasReachedEndOfDocuments = false);
factory CategoryCardState.loading()
return CategoryCardState((b) => b
..exception = ''
..categoryCards =
Future<BuiltList<CategoryCard>>.value(BuiltList<CategoryCard>())
..hasReachedEndOfDocuments = false
..isLoading = true);
factory CategoryCardState.loaded(Future<BuiltList<CategoryCard>> cards)
return CategoryCardState((b) => b
..exception = ''
..categoryCards = cards
..hasReachedEndOfDocuments = false
..isLoading = false
..isLoaded = true);
factory CategoryCardState.success(Future<BuiltList<CategoryCard>> cards)
return CategoryCardState((b) => b
..exception = ''
..categoryCards =
Future<BuiltList<CategoryCard>>.value(BuiltList<CategoryCard>())
..hasReachedEndOfDocuments = false
..isSuccessful = true);
factory CategoryCardState.failed(
String exception, Future<BuiltList<CategoryCard>> cards)
return CategoryCardState((b) => b
..exception = exception
..categoryCards = cards
..hasReachedEndOfDocuments = false);
事件:
abstract class CategoryCardEvents extends Equatable
class LoadCategoryCardEvent extends CategoryCardEvents
final DocumentSnapshot amountDocumentSnapshot;
LoadCategoryCardEvent(@required this.amountDocumentSnapshot);
@override
List<Object> get props => [amountDocumentSnapshot];
分页屏幕(包含在有状态的小部件中):
//Notification Handler
bool _scrollNotificationHandler(
ScrollNotification notification,
DocumentSnapshot amountDocumentSnapshot,
bool hasReachedEndOfDocuments,
Future<BuiltList<CategoryCard>> cards)
if (notification is ScrollEndNotification &&
_scollControllerHomeScreen.position.extentAfter == 0 &&
!hasReachedEndOfDocuments)
setState(()
_hasReachedEnd = true;
);
_categoryCardBloc.add(LoadCategoryCardEvent(
amountDocumentSnapshot: amountDocumentSnapshot));
return false;
BlocListener<CategoryCardBloc, CategoryCardState>(
bloc: _categoryCardBloc,
listener: (context, state)
if (state.exception != null &&
state.exception.isNotEmpty)
if (state.exception == NullException.exceptionMessage)
print('Null Exception');
else
ErrorDialogs.customAlertDialog(
context,
'Failed to load',
'Please restart app or contact support');
print(state.exception);
,
child: BlocBuilder<CategoryCardBloc, CategoryCardState>(
bloc: _categoryCardBloc,
builder: (context, state)
if (state.isLoading != null && state.isLoading)
return Center(
child: CustomLoader(),
);
if (state.isLoaded != null && state.isLoaded)
return StreamBuilder<BuiltList<CategoryCard>>(
stream: state.categoryCards.asStream(),
builder: (context, snapshot)
if (!snapshot.hasData)
return Center(
child: CustomLoader(),
);
else
BuiltList<CategoryCard> categoryCards =
snapshot.data;
_hasReachedEnd = false;
print(state.hasReachedEndOfDocuments &&
state.hasReachedEndOfDocuments != null);
return Container(
height: Mquery.screenHeight(context),
width: Mquery.screenWidth(context),
child: NotificationListener<
ScrollNotification>(
onNotification: (notification) =>
_scrollNotificationHandler(
notification,
categoryCards.last.document,
state.hasReachedEndOfDocuments,
state.categoryCards),
child: SingleChildScrollView(
controller: _scollControllerHomeScreen,
child: Column(
children: [
CustomAppBar(),
Padding(
padding: EdgeInsets.all(
Mquery.padding(context, 2.0)),
child: Row(
children: [
Expanded(
flex: 5,
child: Padding(
padding: EdgeInsets.all(
Mquery.padding(
context, 1.0)),
child:Container(
width: Mquery.width(
context, 50.0),
height: Mquery.width(
context, 12.5),
decoration:
BoxDecoration(
color: middle_black,
borderRadius: BorderRadius
.circular(Constants
.CARD_BORDER_RADIUS),
boxShadow: [
BoxShadow(
color: Colors
.black54,
blurRadius:
4.0,
spreadRadius:
0.5)
],
),
child: Padding(
padding: EdgeInsets.fromLTRB(
Mquery.padding(
context,
4.0),
Mquery.padding(
context,
4.0),
Mquery.padding(
context,
2.0),
Mquery.padding(
context,
1.0)),
child: TextField(
textInputAction:
TextInputAction
.done,
style: TextStyle(
color: white,
fontSize: Mquery
.fontSize(
context,
4.25)),
controller:
searchController,
decoration:
InputDecoration(
border:
InputBorder
.none,
hintText: Constants
.SEARCH_MESSAGE,
hintStyle: TextStyle(
fontSize: Mquery
.fontSize(
context,
4.25),
color:
white),
),
),
),
),
),
Expanded(
flex: 1,
child: Padding(
padding: EdgeInsets.all(
Mquery.padding(
context, 1.0)),
child: Container(
decoration:
BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors
.black54,
blurRadius:
4.0,
spreadRadius:
0.5)
],
color: middle_black,
borderRadius: BorderRadius
.circular(Constants
.CARD_BORDER_RADIUS),
),
width: Mquery.width(
context, 12.5),
height: Mquery.width(
context, 12.5),
child: IconButton(
splashColor: Colors
.transparent,
highlightColor:
Colors
.transparent,
icon: Icon(
Icons.search,
color: white,
),
onPressed: ()
_onSearchButtonPressed();
,
),
),
))
],
),
),
ListView.builder(
shrinkWrap: true,
itemCount: categoryCards.length,
physics:
NeverScrollableScrollPhysics(),
itemBuilder: (context, index)
return GestureDetector(
onTap: ()
//Navigate
,
child:
CategoryCardWidget(
categoryCount:
categoryCards
.length,
categoryCard:
categoryCards[
index]));
,
),
_hasReachedEnd
? Padding(
padding: EdgeInsets.all(
Mquery.padding(
context, 4.0)),
child: CustomLoader(),
)
: Container()
],
),
),
),
);
,
);
return Container();
))
感谢您抽出宝贵时间,很抱歉如此冗长
马特【问题讨论】:
【参考方案1】:我设法解决了这个问题,
以下是 Github 链接: https://github.com/felangel/bloc/issues/1707
希望对某人有所帮助! -马特
【讨论】:
以上是关于BLOC 事件后 Streambuilder 未重建的主要内容,如果未能解决你的问题,请参考以下文章
使用 streamBuilder 实现 Flutter BLoC
Flutter Bloc A 在 Bloc B 中添加流,但此流不会通过 StreamBuilder 在 UI 中呈现
从 StreamBuilder BloC firestore Flutter 获取数据时出错
Flutter_bloc 从没有 UI 事件的 firestore 获取更新的数据