flutter_simple_state 0.7.0 flutter_simple_state: ^0.7.0 copied to clipboard
An easy to understand and simple state management solution for Flutter.
RENAMED package! flutter_simple_state is now reactive_state #
ATTENTION! This package has been renamed to reactive_state.
Old intro #
Easy to understand state management for Flutter apps and for writing reusable Flutter components:
- Events are handled using callbacks/functions.
- Nothing to learn: Simple concept that everybody knows.
- Easy to debug/understand: Use "jump to definition" to see what the code does.
- Good for reusable components: Callbacks work well with any app architecture (BLoC, Stream, etc.).
- State is held in one or multiple instances of
ValueNotifier
/ValueListenable
.- Nothing to learn: Standard Flutter classes that are widely in use and that everybody knows.
- You always have access to the current value (better than working with streams).
AutoRebuild
automatically rebuilds your widgets when aValueNotifier
(or actually, anyListenable
) triggers a notification.- Tracks all
ValueListenable
/Listenable
objects for you, so you don't have to calladdListener
/removeListener
. - Provides fine-grained control for minimizing amount of redraws (more fine-grained than
InheritedWidget
). - Standard Flutter classes like
TextEditingController
andAnimation
implementValueListenable
and thus work nicely withAutoRebuild
.
- Tracks all
- No indirection and no boilerplate (e.g. compared to BLoC or Redux).
- No custom event objects.
- No event handlers with long
switch()
statements. - No streams, no ugly
StreamBuilder
. - Only small, trivial code that everyone can understand and debug with ease.
Usage #
Note: Also see reference for details.
A simple AutoRebuild
example:
import 'package:flutter/material.dart';
import 'package:flutter_simple_state/flutter_simple_state.dart';
class MyPage extends StatelessWidget {
MyPage({Key key, @required this.counter}) : super(key: key);
final ValueNotifier<int> counter;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Column(
children: <Widget>[
AutoRebuild(builder: (context, get, track) {
return Text('Counter: ${get(counter)}');
}),
MaterialButton(
onPressed: () => counter.value++,
child: Text('Increment'),
),
],
),
);
}
}
Also, take a look at the example in the repo. It shows everything you need to know.
As a rule of thumb, try to avoid global state because that can make your code too tightly coupled and at some point turn into a monolithic mess.
Imagine your app as a tree of pages (and each page as a tree of widgets) and think about which page depends on which state/action:
Put the state/action as deeply nested (close to the leafs) in the tree as possible.
Use arguments instead of Provider
if that's convenient enough.
Only use global state if every part of your app depends on it (e.g. the currently logged-in user).
autorun and AutoRunner #
Outside of widgets you might still want to react to state changes.
You can do that with autorun()
and AutoRunner
(see reference for details).
Value vs ValueNotifier #
As an alternative to ValueNotifier
you can also use flutter_simple_state
's Value
class which provides an update()
method for modifying more complex objects:
class User {
String name = '';
String email = '';
// ...
}
var userValue = Value(User());
userValue.update((user) {
user.name = 'Adam';
user.email = 'adam@adam.com';
});
This is similar to calling setState()
with StatefulWidget
.
With update()
you can change multiple attributes and Value
will trigger a single notification once finished - even if nothing was changed (so you don't need to implement comparison operators for complex objects).
DerivedValue #
DerivedValue
is a dynamically calculated ValueListenable
that updates its value whenever its dependencies change:
var user = Value(User());
var emailLink = DerivedValue((get, track) => 'mailto:${get(user).email}');
Here, emailLink
can be observed on its own and is updated whenever user
is modified.