Streambuilder не восстанавливается после события BLOC
Я пытаюсь реализовать разбиение на страницы в своем приложении, но мне это не удалось.
Я использую Firebase, в частности Firestore с шаблоном BLOC вместе с Built Value, который я начал использовать недавно, чтобы упростить разбиение на страницы.
Я был бы очень признателен за любую помощь или реферальные ссылки, как использовать эти технологии вместе.
Моя архитектура приложения выглядит следующим образом:
Я старался как можно больше придерживаться шаблона BLOC, но это, в свою очередь, сильно затруднило разбиение на страницы, поскольку использование встроенного значения в качестве встроенного значения действительно затрудняет использование потоков и фьючерсов. Я просмотрел весь Интернет, но не смог найти никаких руководств или документов по использованию встроенного значения с 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();
}))
Спасибо, что уделили время, и извините за такую многословность
- Мэтт
0 ответов
Мне удалось решить эту проблему,
Ниже ссылка на Github: https://github.com/felangel/bloc/issues/1707
Надеюсь, это кому-то поможет! -Матт