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 获取更新的数据

如何在flutter中从streambuilder导航到其他页面?

Flutter 应用程序后退按钮事件未重定向到后页