I have a Food object that contains properties like name, id, calories, etc. With a series of screens, the user populates the food object properties.
Once done, the user can press the submit button, that will call the addFood method in the store.
The problem is, after uploading the food to the server, i want to pop the screen or show error message in toast based on the response. I just don't know how to do this.
Following is my code (only the important bits): FoodDetailStore.dart
class FoodDetailStore = _FoodDetailStore with _$FoodDetailStore;
abstract class _FoodDetailStore with Store {
Repository _repository;
Food _food;
@observable
String msg = '';
// ... Other Observables and actions
@action
addFood(bool toAdd) {
if (toAdd) {
_repository.addFood(food).then((docId) {
if (docId != null) {
// need to pop the screen
}
}).catchError((e) {
// show error to the user.
// I tried this, but it didn't work
msg = 'there was an error with message ${e.toString()}. please try again.';
});
}
// .. other helper methods.
}
FoodDetailScreen.dart (Ignore the bloc references, I am currently refactoring code to mobx)
class FoodDataScreen extends StatefulWidget {
final String foodId;
final Serving prevSelectedServing;
final bool fromNewRecipe;
FoodDataScreen({@required this.foodId, this.prevSelectedServing, this.fromNewRecipe});
@override
_FoodDataScreenState createState() => _FoodDataScreenState(
this.foodId,
this.prevSelectedServing,
this.fromNewRecipe,
);
}
class _FoodDataScreenState extends State<FoodDataScreen> {
final String foodId;
final Serving prevSelectedServing;
final bool fromNewRecipe;
FoodDataBloc _foodDataBloc;
_FoodDataScreenState(
this.foodId,
this.prevSelectedServing,
this.fromNewRecipe,
);
FoodDetailStore store;
@override
void initState() {
store = FoodDetailStore();
store.initReactions();
store.initializeFood(foodId);
super.initState();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// I know this is silly, but this is what i tried. Didn't worked
Observer(
builder: (_) {
_showMsg(store.msg);
}
);
}
@override
Widget build(BuildContext context) {
return Container(
// ... UI
);
}
_popScreen() {
_showMsg('Food Added');
Majesty.router.pop(context);
}
_showMsg(String msg) {
Fluttertoast.showToast(msg: msg);
}
@override
void dispose() {
store.dispose();
super.dispose();
}
}
Constructing an Observer
instance inside the didChangeDependencies()
is indeed "silly" as you have rightly noted already :)
Observer
is a widget and widget needs to be inserted into the widgets tree in order to do something useful. In our case non-widget Mobx reactions come to the rescue.
I will show how I did it in my code for the case of showing a Snackbar
upon observable change so you will get an idea how to transform your code.
First of all, import import 'package:mobx/mobx.dart';
.
Then in the didChangeDependencies()
create a reaction which will use some of your observables. In my case these observables are _authStore.registrationError
and _authStore.loggedIn
:
final List<ReactionDisposer> _disposers = [];
@override
void dispose(){
_disposers.forEach((disposer) => disposer());
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_authStore = Provider.of<AuthStore>(context);
_disposers.add(
autorun(
(_) {
if (_authStore.registrationError != null)
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text(_authStore.registrationError),
backgroundColor: Colors.redAccent,
duration: Duration(seconds: 4),
),
);
},
),
);
_disposers.add(
reaction(
(_) => _authStore.loggedIn,
(_) => Navigator.of(context).pop(),
),
);
}
I use two types of Mobx reactions here: autorun
and reaction
. autorun
triggers the first time immediately after you crate it and then every time the observable changes its value. reaction
does not trigger the first time, only when the observable change.
Also pay attention to dispose the created reactions in the dispose()
method to avoid resources leak.
Here is a code of my Mobx store class with used observables to complete the picture:
import 'package:mobx/mobx.dart';
import 'dart:convert';
part "auth_store.g.dart";
class AuthStore = AuthStoreBase with _$AuthStore;
abstract class AuthStoreBase with Store{
@observable
String token;
@observable
String registrationError;
@observable
String loginError;
@action
void setToken(String newValue){
token = newValue;
}
@action
void setRegistrationError(String newValue){
registrationError = newValue;
}
@action
void setLoginError(String newValue){
loginError = newValue;
}
@action
void resetLoginError(){
loginError = null;
}
@computed
bool get loggedIn => token != null && token.length > 0;
@action
Future<void> logOut() async{
setToken(null);
}
}
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