- A
Flutter
plugin for loading content asynchronously withDart Stream
andRxDart
. RxDart
loader bloc.- Reactive loader bloc.
- Simple reactive state management container.
Author: Petrus Nguyễn Thái Học
In your flutter project, add the dependency to your pubspec.yaml
dependencies:
...
stream_loader: <latest_version>
abstract class Comment implements Built<Comment, CommentBuilder> { ... }
class Api {
Stream<BuiltList<Comment>> getComments() { ... }
Stream<Comment> getCommentBy({@required int id}) { ... }
}
final api = Api();
import 'package:stream_loader/stream_loader.dart';
LoaderWidget<BuiltList<Comment>>(
blocProvider: () => LoaderBloc(
loaderFunction: api.getComments,
refresherFunction: api.getComments,
initialContent: <Comment>[].build(),
logger: print,
),
messageHandler: (context, message, bloc) {
message.fold(
onFetchFailure: (error, stackTrace) => context.snackBar('Fetch error'),
onFetchSuccess: (_) {},
onRefreshSuccess: (data) => context.snackBar('Refresh success'),
onRefreshFailure: (error, stackTrace) => context.snackBar('Refresh error'),
);
},
builder: (context, state, bloc) {
if (state.error != null) {
return ErrorWidget(error: state.error);
}
if (state.isLoading) {
return LoadingWidget();
}
return RefreshIndicator(
onRefresh: bloc.refresh,
child: CommentsListWidget(comments: state.content),
);
}
);
import 'package:stream_loader/stream_loader.dart';
final Comment comment;
final loadDetail = () => api.getCommentBy(id: comment.id);
LoaderWidget<Comment>(
blocProvider: () => LoaderBloc(
loaderFunction: loadDetail,
refresherFunction: loadDetail,
initialContent: comment,
logger: print,
),
messageHandler: (context, message, bloc) {
message.fold(
onFetchFailure: (_, __) {},
onFetchSuccess: (_) {},
onRefreshFailure: (_, __) {},
onRefreshSuccess: (_) => context.snackBar('Refresh success'),
);
},
builder: (context, state, bloc) {
return RefreshIndicator(
onRefresh: bloc.refresh,
child: CommentDetailWidget(comment: state.content),
);
},
);
class _CommentsState extends State<Comments> {
LoaderBloc<BuiltList<Comment>> bloc;
@override
void didChangeDependencies() {
super.didChangeDependencies();
bloc ??= LoaderBloc(
loaderFunction: api.getComments,
refresherFunction: api.getComments,
initialContent: <Comment>[].build(),
logger: print,
)..fetch();
}
@override
void dispose() {
bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StreamBuilder<LoaderState<BuiltList<Comment>>>(
stream: bloc.state$,
initialData: bloc.state$.value, // <- required because bloc.state$ does not replay the latest value
builder: (context, snapshot) {
final state = snapshot.data;
if (state.error != null) {
return ErrorWidget(error: state.error);
}
if (state.isLoading) {
return LoadingWidget();
}
return RefreshIndicator(
onRefresh: bloc.refresh,
child: CommentsListWidget(comments: state.content),
);
}
);
}
}
- Default behavior of
loaderFunction
isFlattenStrategy.latest
(usesswitchMap
). - Default behavior of
refreshFlatMapPolicy
isFlattenStrategy.first
, (usesexhaustMap
). - To change them, passing your value to
LoaderBloc
constructor
LoaderBloc(
...,
loaderFlattenStrategy: FlattenStrategy.concat, // asyncExpand
refreshFlattenStrategy: FlattenStrategy.latest, // switchMap
);
MIT License
Copyright (c) 2020-2022 Petrus Nguyễn Thái Học