0

I'm trying to figure out a good way of architecturing a Flutter app. I have several pages, which can navigate to other pages. Data modified by one page, might be used by other pages and needs to be updated. I recently started using the Provider package. I let each page have a model which implements ChangeNotifier and set a ChangeNotifierProvider above the MaterialApp. Now to update the data for other pages, i have to get the other page's model and call an update method on it. It's quite messy. I use Firestore, so i could listen to database changes, but i want to avoid too much magic. Also, sometimes it makes sense to just send data from one page to another, without using a network call, or pass data back in the Navigator.pop method. I find it hard to make good choices in how to solve this. I should probably look at a more refined architecture like redux or mobx. Or just go for a vanilla solution and use lot's of callbacks for all the various events. Those big'ol models per page, seems a bit bit bloated and messy after a while. Do anyone have a similar experience in Flutter how to architect these things? Confused

Erlend
  • 1,711
  • 1
  • 22
  • 25

2 Answers2

3

Using provider to apply the BLoC pattern and combining it with RxDart is a way of solving this.

You could, for example, create an AppBloc:

class AppBloc {
  StreamController<UserInputState> _userInputController =
    BehaviorSubject<UserInputState>.seeded(UserInputState('NOTHING'));
  StreamSink<UserInputState> get userInputSink => _userInputController.sink;
  Observable<UserInputState> get userInputObs => Observable(_userInputController.stream);

  AppBloc();
}

class UserInputState {
  final String input;

  const UserInputState(this.input);
}

On a page you could pass data through it:

final appBloc = Provider.of<AppBloc>(context);
appBloc.userInputSink.add(UserInputState("input"));

and another page can listen to data:

final appBloc = Provider.of<AppBloc>(context);
appBloc.userInputObs.
    .where((state) => state.input == "input")
    .listen((state) => /* do something */);

I've simplified it here, but this can be a very robust way of handling data in a reactive way.

And the beauty is that you can bind the Observable to a StreamBuilder (part of Flutter) to make the UI reactive too!

voracious
  • 890
  • 4
  • 7
  • Why do you need an Observable? Can't you just get a broadcast stream that other can listen to? – Erlend Nov 22 '19 at 01:00
  • @Erlend An `Observable` is a `Stream` on steroids. It simply extends the `Stream` and adds a lot of useful functionality. More details can be found [here](https://pub.dev/documentation/rxdart/latest/rx/Observable-class.html#dart-streams-vs-observables). – voracious Nov 22 '19 at 12:42
  • Thanks. I think i will look into using streams to solve my state problem, and maybe come up with a custom solution. I will accept your answer, since my question was kind of all over the place. I also discovered the following [repo](https://github.com/christianalfoni/flutter_observable_state) and [video](https://www.youtube.com/watch?v=A7gK-VT5YsA) which i find interesting. – Erlend Nov 22 '19 at 14:43
  • 1
    @Erlend yes, the github repo shows an interesting concept! I would normally recommend to _at least_ play around with vanilla streams/rxdart before using abstractions like the `ObservableState`. I have also wrapped around the `StreamController`, `StreamSink` and `Observable` into my own custom classes just so they become much easier to use for my own use-cases. – voracious Nov 22 '19 at 15:36
  • 1
    Just starting using streams, dartrx and BehaviorSubject, in combination with StreamBuilder and it's so nice !! – Erlend Nov 23 '19 at 01:57
1

Using Provider (or any library that implements BloC pattern) and a Repository Pattern should work to move/store data locally. Basically each Bloc/Provider gets an instance of the repository and read/write values to it. Because the repository instance is the same always, you will have all yout data updated.

Also, you can use a local database like SQLite (with the sqflite package) or Hive that is a pure NoSQL database.

I personally don't like Redux, but mobx is a really good option.