observable_state 0.0.1+1 copy "observable_state: ^0.0.1+1" to clipboard
observable_state: ^0.0.1+1 copied to clipboard

outdated

Yet Another Flutter State Manager for a Reactive Application Architecture

Observable state https://pub.dartlang.org/packages/observable_state #

🔭 Yet Another Flutter State Manager for a Reactive Application Architecture.

But this time #

  • State mutations are handled by the OO Encapsulation Principle on Plain-old objects.
  • You can freely Unit Test your State and its Mutations, like pure Dart (because IT IS pure Dart).
  • Keep track of every State change using an Enum, simple like that.
  • Notify only the specific States that are Observing the triggered change, NOT the entire Widget tree.
  • Actually, observable_state does not mess with Widgets, it's all about the State. Stateless are kept Stateless.
  • No Streams or new Widgets, it is purely State and setState(), like Vanilla, but ⚡ Reactive!

How it works #

It's a sweet, sweet sugar on top of the battle-tested Observer pattern. Your State is stored in a List of Observables in a given Subject (Change) and it's setState() is called only when your Model explicit tells to notify about that Change.

  • It is not BLoC, but you still can maintain, test and visualize your Business Logic away from the UI.
  • It is not Flux (Redux/Rx/Stream), but you still can control data in an unidirectional flow.

Usage guide (Get started) #

The fact is: state is hard! Probably the hardest thing (after naming, of course). This is why things like BLoC, Flux/Redux and ScopedModel appears; to help you solve that.

And that is why observable_state is here too.

Talk is cheap. Show me the code — Torvalds, Linus

Installing #

dependencies:
  observable_state: ^0.0.1

State modeling #

class MyState {
  int counter;
}

From a simple model like that, to add observable_state super-powers you just need to:

class MyState extends Observable<MyState, Changes> {
  int counter;
}

Then make sure you declare what changes to your Model are:

enum Changes {
  increment,
}

Even better, remember that Mutations are handled by the OO Encapsulation Principle, so:

enum Changes {
  increment,
}

class MyState extends Observable<MyState, Changes> {
  int _counter;
  int get counter => _counter;

  void increment() {
    _counter++;
  }
}

Ok, cool, but how to notify about the increment Change? #

Well, how Flutter does it with local state? With the setState() method, right? observable_state borrows the same API, but with a little difference: the notify argument to notify States listening to this change!

enum Changes {
  increment,
}

class MyState extends Observable<MyState, Changes> {
  int _counter;
  int get counter => _counter;

  void increment() {
    setState(() => _counter++, notify: Changes.increment);
  }
}

That is it. Your Observable is notifying about MyState Changes.

How to listen to then? #

Here comes the StateObserver.

Remember: StatelessWidgets are state-less as the name suggests. We are not going to mess with them adding Streams or whatever. Actually, even the StatefulWidget remains the same. We are going to super-power the State!

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends StateObserver<MyStatefulWidget, MyState, Changes> {
  @override
  List<Changes> get subjects => [Changes.increment];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Observable state #${state.counter}')),
      body: Center(child: Text('Counter: ${state.counter}')),
      floatingActionButton: Builder(
        builder: (context) {
          return FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () {
              state.increment();
            },
          );
        },
      ),
    );
  }
}

Notice that we are extending StateObserver instead of State and we are given a list of Changes we are interest in, like Changes.increment. StateObserver already got the state so we can get counter and we can call increment() on it as well. Wherever increment() is called, since it notifies about Changes.increment, whoever StateObserver is observing this change, it will automatically calls its inner setState() method, then rebuilding it.

Where does the state comes from? #

The last piece: ObservableProvider.

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ObservableProvider(
      MyState(),
      child: MaterialApp(home: MyStatefulWidget()),
    );
  }
}

It's an InheritedWidget responsible to instantiate your initial State and handle it on its Context.

That is it! You're ready to Rock! 🎸

ObservableModel has no dependency at all with any Flutter APIs, so you can Unit test your Model using Plain-old test package. And you can test your Widgets using a Mock or a real state without any hassle.

Check out the example directory for a complete example with Asyncs, Services and Dependency Injection.

⚠️ For now, use with caution, API may change.


© 2019

4
likes
0
pub points
26%
popularity

Publisher

unverified uploader

Yet Another Flutter State Manager for a Reactive Application Architecture

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter

More

Packages that depend on observable_state