问题描述
我正在尝试在我的应用程序中实现分页,但是没有成功。
我正在使用Firebase,特别是具有BLOC模式的Firestore,以及我最近开始使用的“内置价值”来简化分页。
我非常感谢任何帮助或推荐链接如何一起使用这些技术。
我的应用程序体系结构如下:
我试图尽可能地保持BLOC模式,但这反过来又使得很难进行大量分页,因为将内置值用作内置值使得使用流和期货真的很困难。我在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();
}
BLOC:
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(),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,2.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,child: Container(
decoration:
Boxdecoration(
BoxShadow: [
BoxShadow(
color: Colors
.black54,color: middle_black,width: Mquery.width(
context,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();
}))
感谢您的时间,并很抱歉如此冗长
- 马特