We are always looking for a powerful way of state management. We knows flutter itself has provided us with state management Stateful widget
Flutter has different ways of estate management topics
Stateful Widget
InheritedWidget
Provider
BLoC
Widgets which will change its behaviour/state dynamically called stateful widgets
Stateful widgets are useful when the part of the user interface you are describing can change dynamically. User interfaces need to respond to a variety of things: The user doing something in the user interface.
Receiving data from another computer.
This is what Stateful Widgets are for. They store data (state) in an associated State class and they can respond when that data (state) changes as the result of the user doing something
Let's create a Counter app with Stateful widget
class MyStatefulwidget extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return MyState(); } } class MyState extends State<MyStatefulwidget>{ int count; @override void initState() { // TODO: implement initState super.initState(); count=0; } @override Widget build(BuildContext context) { // TODO: implement build return Scaffold( appBar: AppBar(title: Text("Statefulwidget"),backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton(onPressed: (){ setState(() { count++; }); },child: Icon(Icons.add,color: Colors.white,),backgroundColor: Colors.pink,), body: Container( child: Center( child: Text("Count : $count",style: TextStyle(color: Colors.pink,fontSize: 20),), ), ), ); } } |
here on tap on add button every time we are updating the state by calling the setState() method, so this will entirely rebuild the widget
This will be useful when a single widget needs to update the State.
Flutter provides an InheritedWidget that can define provide context to every widget below it in the tree.
InheritedWidget is a special widget that can store and retrieve data , and subcomponents can obtain stored data. Commonly used MediaQuery and Theme are inherited from InheritedWidget
While this is nice in theory, we can see that it takes quite a lot of code to get a basic example wired up. Fortunately, there are libraries like Bloc, Redux, and Scoped Model abstract this complexity away
Let's check the counter app with InheritedWidget
Create InheritedWidget
class CounterInherited extends InheritedWidget{ final Map _counter={'count':0}; Widget child; CounterInherited ({@required Widget this. child}):super(child:child); get counter=>_counter['count']; increment() { _counter['count']++; } @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } static CounterInherited of(BuildContext ctx)=> ctx.inheritFromWidgetOfExactType(CounterInherited); } |
Here we are extending the class with InheritedWidget, which have override method will tell the widget to need to update state or not
@override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } |
Create Child widget
Child widget will have the counter increment UI
class Counter extends StatefulWidget{ @override State<StatefulWidget> createState() { // TODO: implement createState return CounterState(); } } class CounterState extends State<Counter>{ @override Widget build(BuildContext context) { int count=CounterInherited.of(context).counter; return Scaffold( appBar: AppBar(title: Text("InheritedWidget"),centerTitle: true,backgroundColor: Colors.pink,), floatingActionButton: FloatingActionButton(onPressed: (){ setState((){}); CounterInherited.of(context).increment(); },child: Icon(Icons.add,color: Colors.white,),backgroundColor: Colors.pink,), body: Container( child: Center( child: Text("Count : $count",style: TextStyle(color: Colors.pink,fontSize: 20),), ), ), ); } } |
This Child widget need parent widget instance to update counter value,
This will done by static method of CounterInherited widget
CounterInherited.of(context)
Main Widget
Now create a widget which contains parent widget as Inherited widget and pass child widget of our counter widget
class MyInherited extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build return CounterInherited( child: Counter(), ); } } void main() => runApp(MyInherited ()); |
Flutter, however, brings a new reactive style that is not entirely compatible with MVC. Its design idea is to separate data from views, and render views by data mapping
A variation of this classical pattern has emerged from the Flutter community – BLoC
BLoC stands for Business Logic Components, BLoC is a method of building applications using reactive programming. This is a completely asynchronous world composed of streams
Wrap stateful components with StreamBuilder, streambuilder will listen for a stream
This stream comes from BLoC
The data in the stateful widget comes from the listening stream.
User interaction gestures are detected and events are generated. For example, press the button.
Call the function of bloc to handle this event
After processing in bloc, the latest data will be added to the sink of the stream.
StreamBuilder listens to new data, generates a new snapshot, and calls the build method again
Widget is rebuilt
Here we coding a simple counter application with BLoC
This Example show the Number counts in the first page, in the second page we are increase the counter number, this will reflect in the fistpage
For this we are going to create app with below steps
Create bloc model
CountBLoC
class CountBLoC{ int _count = 0; var _countController = StreamController<int>.broadcast(); Stream<int> get stream => _countController.stream; int get value => _count; addCount() { _countController.sink.add(++_count); } dispose() { _countController.close(); } } |
Create Provider
class BlocProvider extends InheritedWidget{ CountBLoC bLoC = CountBLoC(); BlocProvider({Key key, Widget child}) : super(key: key, child: child); @override bool updateShouldNotify(InheritedWidget oldWidget) { // TODO: implement updateShouldNotify return true; } static CountBLoC of(BuildContext context) => (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).bLoC; } |
Create First Page
This Page will show the number counts, for this we are accessing the data with streambuilder
class CountPage extends StatelessWidget{ @override Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Counter Page',), backgroundColor: Colors.pink, ), body: Center( child: StreamBuilder<int>( stream: bloc.stream, initialData: bloc.value, builder: (BuildContext context, AsyncSnapshot<int> snapshot) { return Text( 'Number Counts : ${snapshot.data}',style: TextStyle(color: Colors.pink,fontSize: 20), ); }), ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.pink, child: Icon(Icons.add,color: Colors.white,), onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => DisplayPage()))), ); } } |
Create Second page
This Page will increase the count by tap on button. With calling the addCount function will increase the count value
class DisplayPage extends StatelessWidget{ @override Widget build(BuildContext context) { // TODO: implement build final bloc = BlocProvider.of(context); print('build'); return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Update Count'), backgroundColor: Colors.pink, ), body: Center( child: StreamBuilder( stream: bloc.stream, initialData: bloc.value, builder: (context, snapshot) => Center( child: Text( " Counter : ${snapshot.data} \nIncreate Count by Button tap", style: TextStyle(color: Colors.pink,fontSize: 20),textAlign: TextAlign.center,), )), ), floatingActionButton: FloatingActionButton( onPressed: () => bloc.addCount(), backgroundColor: Colors.pink, child: Icon(Icons.add,color: Colors.white,), ), ); } } |
Now It’s time to check out Main Page
Here we are using the Provider to load the child widgets to Inherited widget.
class MyBloc extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( child: MaterialApp( title: 'BLoC', theme: ThemeData.dark(), home: CountPage(), ), ); } } |