Streambuilder 在 BLOC 事件后不重建

Streambuilder not rebuilding after BLOC event

我正在尝试在我的应用程序中实现分页,但没有成功。

我正在使用 Firebase,特别是带有 BLOC 模式的 Firestore 以及我最近开始使用的 Built Value 来简化分页。

如果能提供有关如何一起使用这些技术的帮助或推荐链接,我将不胜感激。

我的应用架构如下:

我试图尽可能地保持 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();
                        }))

感谢您的宝贵时间,很抱歉这么冗长

我设法解决了这个问题,

下面是Githublink: https://github.com/felangel/bloc/issues/1707

希望对某人有所帮助! -马特