Pull to refresh

Start using ReactiveX in dart and Flutter from beginning

Reading time3 min
Views4K


Yesterday my friend said something like "I’m writing simple offline app, I don’t need these streams and all that jazz". I was confused, but I thought, that there may be other coders in this delusion.


Below, literally in 50 lines I will show, on known example that reactivity is:


a) not about offline/online
b) very easy
c) very good for simplifying almost any code


To my hasty critics,
who rush into battle without looking back, supposing that BlocProvider — is a provider, I'd recommended to read first the basic article for general development. The reference to an article is on page of flutter_bloc, at first line in description.


A well-known to all example is “A counter”, which generated when the Flutter project is created, is the good start point for demonstration a lot of practices. So, it contains MyHomePage extends StatefulWidget, method _incrementCounter for increment’s command and setState for redraw entire hierarchy of widgets.


Let's introduce reactivity with the help of rxdart library and some easy steps:


Let’s add a library in pubspec.yaml


dependencies:
...
  rxdart: 0.22.2

Let’s change a counter’s architecture and add event


class _Counter {
  int _count;

  int get count => _count;

  _Counter(this._count)
      : this.onCounterUpd = BehaviorSubject<int>.seeded(_count);

  /// Here is the event.
  final BehaviorSubject<int> onCounterUpd;

  /// Increment now fires an event.
  Future incrementCounter() async {
    onCounterUpd.add(++_count);
  }
}

final _counter = _Counter(5);

Let’s make class StatelessWidget


class MyHomeRxPage extends StatelessWidget {
  final title;

  /// ! Look it can be constant one now.
  const MyHomeRxPage({Key key, this.title}) : super(key: key);
...

Let’s wrap up a widget in a StreamBuilder and change a call of increment’s 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. In full it looks like that:


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);

  /// event
  final BehaviorSubject<int> onCounterUpd;

  /// fire an event
  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(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          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:',
//            ),
            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),
      ), // 
    );
  }
}

Now the code is reactive, laconic, free from unnecessary redraws and easily extensible. For example, if it’s necessary to change text of other widget, when the counter is changing, it’s enough to do follow:


            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 this with InheritedWidget or with another pattern.


So, I hope, I showed that


  • The reactivity is very easy. Much easier than InheritedWidgets, BlocProvider, etc.
  • The reactivity isn’t about offline/online. It’s about architecture. In some simple case even mustn’t to enter additional classes to use it.
  • The reactivity is sympathetic UI, quick widening functional, elegant division of code on layers all type: MVC, MVP, MVI, MVVM, MVU — whatever you want.

Code: (branch iter_0004_rxdart)


Good luck in coding.

Tags:
Hubs:
+3
Comments0

Articles