I have one question regarding how to reload a list after refresh indicator is called in Flutter, using Streams and RxDart.
Here is what I have , my model class:
class HomeState {
List<Event> result;
final bool hasError;
final bool isLoading;
HomeState({
this.result,
this.hasError = false,
this.isLoading = false,
});
factory HomeState.initial() =>
new HomeState(result: new List<Event>());
factory HomeState.loading() => new HomeState(isLoading: true);
factory HomeState.error() => new HomeState(hasError: true);
}
class HomeBloc {
Stream<HomeState> state;
final EventRepository repository;
HomeBloc(this.repository) {
state = new Observable.just(new HomeState.initial());
}
void loadEvents(){
state = new Observable.fromFuture(repository.getEventList(1)).map<HomeState>((List<Event> list){
return new HomeState(
result: list,
isLoading: false
);
}).onErrorReturn(new HomeState.error())
.startWith(new HomeState.loading());
}
}
My widget:
class HomePageRx extends StatefulWidget {
@override
_HomePageRxState createState() => _HomePageRxState();
}
class _HomePageRxState extends State<HomePageRx> {
HomeBloc bloc;
_HomePageRxState() {
bloc = new HomeBloc(new EventRest());
bloc.loadEvents();
}
Future<Null> _onRefresh() async {
bloc.loadEvents();
return null;
}
@override
Widget build(BuildContext context) {
return new StreamBuilder(
stream: bloc.state,
builder: (BuildContext context, AsyncSnapshot<HomeState> snapshot) {
var state = snapshot.data;
return new Scaffold(
body: new RefreshIndicator(
onRefresh: _onRefresh,
child: new LayoutBuilder(builder:
(BuildContext context, BoxConstraints boxConstraints) {
if (state.isLoading) {
return new Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.deepOrangeAccent,
strokeWidth: 5.0,
),
);
} else {
if (state.result.length > 0) {
return new ListView.builder(
itemCount: snapshot.data.result.length,
itemBuilder: (BuildContext context, int index) {
return new Text(snapshot.data.result[index].title);
});
} else {
return new Center(
child: new Text("Empty data"),
);
}
}
}),
),
);
});
}
}
The problem is when I do the pull refresh from list, the UI doesn't refresh (the server is called, the animation of the refreshindicator also), I know that the issue is related to the stream but I don't know how to solve it.
Expected result : Display the CircularProgressIndicator until the data is loaded
Any help? Thanks
You are not supposed to change the instance of state
.
You should instead submit a new value to the observable. So that StreamBuilder
, which is listening to state
will be notified of a new value.
Which means you can't just have an Observable
instance internally, as Observable
doesn't have any method for adding pushing new values. So you'll need a Subject
.
Basically this changes your Bloc to the following :
class HomeBloc {
final Stream<HomeState> state;
final EventRepository repository;
final Subject<HomeState> _stateSubject;
factory HomeBloc(EventRepository respository) {
final subject = new BehaviorSubject(seedValue: new HomeState.initial());
return new HomeBloc._(
repository: respository,
stateSubject: subject,
state: subject.asBroadcastStream());
}
HomeBloc._({this.state, Subject<HomeState> stateSubject, this.repository})
: _stateSubject = stateSubject;
Future<void> loadEvents() async {
_stateSubject.add(new HomeState.loading());
try {
final list = await repository.getEventList(1);
_stateSubject.add(new HomeState(result: list, isLoading: false));
} catch (err) {
_stateSubject.addError(err);
}
}
}
Also, notice how loadEvent
use addError
with the exception. Instead of pushing a HomeState
with a hasError: true
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With