Mobx.dart: show toast in Observer

Created on 30 Aug 2020  路  12Comments  路  Source: mobxjs/mobx.dart

how can i show simple toast or dialog in Observer widget?

for example:

Observer(builder: (context) {
 if (_registerViewModel.registeringState == RegisteringState.error) {
       showToast('message);
}

Most helpful comment

It is correct for it to be in didChangeDependencies. The _dashboardViewModel view-model can't be assigned in initState, since context is not yet available in initState.

All 12 comments

I'd use a reaction over an observer widget in this case. reactions

@tek08 could you paste simple code to know how can i use that in flutter? thanks

I highly recommend checking out the link in my message above. If it's still confusing after reading the section on reaction, gimme a holler.

as i'm using Provider i'm trying to know how can i do that

I highly recommend checking out the link in my message above. If it's still confusing after reading the section on reaction, gimme a holler.

@MahdiPishguy here is a short snippet with similar concept.

class _UpcomingDatePageState extends State<UpcomingDatePage> {
  GeoStore _geoStore;
  List<ReactionDisposer> _reactionDisposers;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();

  @override
  void initState() {
    super.initState();

    _geoStore = context.read<GeoStore>();

    _reactionDisposers = [
      reaction(
        (_) => _geoStore.hasGrantedLocationPermissions,
        (permissionsGranted) {
          if (!permissionsGranted) {
            _scaffoldKey.currentState.showSnackBar(SnackBar(
              content: const Text('Hey, give us permissions please!'),
              duration: const Duration(seconds: 2),
            ));
          }
        },
      ),
      reaction(
        (_) => _geoStore.userReachedDestination,
        (hasReachedDestination) {
          _scaffoldKey.currentState.showSnackBar(
            SnackBar(
              content: Text(hasReachedDestination
                  ? 'You\'ve reached destination'
                  : 'You are not here'),
              duration: const Duration(seconds: 2),
            ),
          );
        },
      ),
    ];
  }

  @override
  void dispose() {
    _reactionDisposers.forEach((disposer) => disposer.call());
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      body: Container(),
    );
  }
}
  • See official FAQ on how to use provider inside initState
  • If you do really want to use an Observer widget, you can try it together with a Builder widget, whereshowToast will be called and you can return Container, for example. However, I don't recommend such an approach, as showing Toaster is a side effect, for which you should better use reactions. Remember that Observer has to return some widget.

In short:

ReactionDisposer _disposer;

void initState() {
  super.initState();
  _disposer = reaction(
    (_) {
      // NOTE1: this will NOT be called when the MyViewModel provider instance changes!
      final _registerViewModel = Provider.of<MyViewModel>(context, listen: false);
      // NOTE2: this WILL be called ONLY if registeringState is marked as @observable or @computed AND changes
      return _registerViewModel.registeringState;
    }, (RegisteringState registeringState) {
      if (registeringState == RegisteringState.error) {
         showToast('message');
      }
    },
  );
}

void dispose() {
  _disposer();
  super.dispose();
}

I hope it helps.

As @tek08 and @bakeyevrus mention - please, read the docs.

@avioli Thanks a lot :) . i found this solution some hours ago

@avioli

  DashboardViewModel _dashboardViewModel;
  ReactionDisposer _reactionDisposer;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _dashboardViewModel ??= Provider.of<DashboardViewModel>(context, listen: false);

    _reactionDisposer = reaction((_)=>_dashboardViewModel .message, (String message)=>_showSnackBar(message));
  }

void dispose() {
  _reactionDisposer();
  super.dispose();
}

only there is one things, i'm not sure reaction should be inside didChangeDependencies or initState

It is correct for it to be in didChangeDependencies. The _dashboardViewModel view-model can't be assigned in initState, since context is not yet available in initState.

If you put it in didChangeDependencies and create the disposer there, then don鈥檛 forget to dispose the potential previous disposer, since that lifecycle method can be called more than once.

@Fleximex - context is available in initState. Why are you saying it isn鈥檛?

@avioli
About the first part:
You are correct. _reactionDisposer = reaction should also be called with the null check assignment, which makes sure it does not get assigned again when the variable is already assigned (not null): _reactionDisposer ??= reaction

About the second part:
Maybe it's better to clarify. Yes, you can reference the context variable in initState. But you can't call .of(context) yet.

Flutter docs:

initState:
You cannot use BuildContext.dependOnInheritedWidgetOfExactType from this method.
However, didChangeDependencies will be called immediately following this method,
and BuildContext.dependOnInheritedWidgetOfExactType can be used there.

@Fleximex well... that is why I use the listen: false argument.

Here鈥檚 my reasoning - when using MobX it makes sense to use a Provider just as a dependency injection. If the message is an observable, then there鈥檚 no need to use a ChangeNotifier and then update the listeners, so listening for a change is not so useful, but your mileage might vary and you can do anything you want.

Was this page helpful?
0 / 5 - 0 ratings