Yesterday, my good friend said something like "Iβm writing a simple offline application, I donβt need all these streams and streams." I was even confused, and then I thought that other encoders could share this error.
Below, literally in 50 lines, I will show by a well-known example that reactivity
a) this is not about offline / online
b) it is very simple
c) very good for simplifying almost any code
To my hasty critics
who rush into battle without thinking, considering that BlocProvider
is a provider , I recommend for general development to read the basic article first, the link to which is on the flutter_bloc page , on the first line of the description.
The well-known example "Counter" that is generated when creating a Flutter project is whipping boy A good starting point to demonstrate many practices.
So, it contains MyHomePage extends StatefulWidget
, the _incrementCounter
method for the increment command and setState
to redraw the entire widget hierarchy.
Add reactivity with the rxdart
library and a few simple steps:
Add the library to pubspec.yaml
dependencies: ... rxdart: 0.22.2
Change the counter architecture and add event
class _Counter { int _count; int get count => _count; _Counter(this._count) : this.onCounterUpd = BehaviorSubject<int>.seeded(_count); /// . final BehaviorSubject<int> onCounterUpd; /// , . Future incrementCounter() async { onCounterUpd.add(++_count); } } final _counter = _Counter(5);
Let's make the class StatelessWidget
/// " " class MyHomeRxPage extends StatelessWidget { final title; /// ! - const MyHomeRxPage({Key key, this.title}) : super(key: key); ...
Wrap the display widget in StreamBuilder and change the call to the increment method
StreamBuilder<int>( stream: _counter.onCounterUpd, builder: (context, snapshot) { return Text( '${snapshot.data}', style: Theme.of(context).textTheme.display1, ); }), ... floatingActionButton: FloatingActionButton( onPressed: _counter.incrementCounter, ...
That's all. It totally looks like this
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:rxdart/rxdart.dart'; class _Counter { int _count; int get count => _count; _Counter(this._count) : this.onCounterUpd = BehaviorSubject<int>.seeded(_count); /// . final BehaviorSubject<int> onCounterUpd; /// , . Future incrementCounter() async { onCounterUpd.add(++_count); } } final _counter = _Counter(5); /// class MyHomeRxPage extends StatelessWidget { final title; /// ! - const MyHomeRxPage({Key key, this.title}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text(title), ), body: Center( // Center is a layout widget. It takes a single child and positions it // in the middle of the parent. child: Column( // Column is also a layout widget. It takes a list of children and // arranges them vertically. By default, it sizes itself to fit its // children horizontally, and tries to be as tall as its parent. // // Invoke "debug painting" (press "p" in the console, choose the // "Toggle Debug Paint" action from the Flutter Inspector in Android // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) // to see the wireframe for each widget. // // Column has various properties to control how it sizes itself and // how it positions its children. Here we use mainAxisAlignment to // center the children vertically; the main axis here is the vertical // axis because Columns are vertical (the cross axis would be // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ StreamBuilder<int>( stream: _counter.onCounterUpd, builder: (context, snapshot) { return Text( 'You have pushed the button ${snapshot.data} times:', ); }), // Text( // 'You have pushed the button this many times:', // ), /// 6. StreamBuilder<int>( stream: _counter.onCounterUpd, builder: (context, snapshot) { return Text( '${snapshot.data}', style: Theme.of(context).textTheme.display1, ); }), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _counter.incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } }
Now the code is reactive, concise, saved from unnecessary redrawings, and easily extensible.
For example, if at the time of changing the counter you need to change the text of another widget, just do this:
StreamBuilder<int>( stream: onCounterUpd, builder: (context, snapshot) { return Text( 'You have pushed the button ${snapshot.data} times:', ); }), // Text( // 'You have pushed the button this many times:', // ),
and voila!
For comparison, try to do the same with InheritedWidget, or another pattern.
So, hopefully, I showed that
- Reactivity is very simple. Much easier
InheritedWidgets
,BlocProvider
, etc. - Reactivity is not about offline / online. She's about architecture. As I have shown, in the simplest cases you donβt even need to add additional classes to apply it.
- Reactivity is responsive UIs, fast extension of functionality, graceful separation of code into layers of any type: MVC, MVP, MVI, MVVM, whatever you want.
Example code (branch iter_0004_rxdart
)
Edited an hour later
In vain did it too simple, got clicks for "global variables" and a wrong understanding of BehaviorSubject
initialization, fixed