states_rebuilder 1.7.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 93

states_rebuilder #

A Flutter state management combined with dependency injection solution that allows :

  • a 100% separation of User Interface (UI) representation from your logic classes
  • an easy control on how your widgets rebuild to reflect the actual state of your application. Model classes are simple vanilla dart classes without any need for inheritance, notification, streams or annotation and code generation.

example of the simple Counter app: #

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

//Pure dart class. No inheritance, no notification, no streams, and no code generation
class Counter {
  int count = 0;
  increment() => count++;
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      inject: [Inject<Counter>(() => Counter())],
      builder: (context, __) {
        ///`context` is optional. Here `getAsModel` is called without context because we do not want this widget to update
        final counter = Injector.getAsModel<Counter>();
        return Scaffold(
          appBar: AppBar(
            title: Text(" Counter App"),
          ),
          body: MyHome(),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            //To mutate the state, use `setState` method. This insures that the dependent widgets are updated after state mutation.
            onPressed: () => counter.setState((state) => state.increment()),
          ),
        );
      },
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ///Here the `getAsModel`is called with the `context` to automatically add this widget to the list of dependent widgets of the model.
    final counter = Injector.getAsModel<Counter>(context: context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text("You have pushed this many times"),
          //use the `state` getter to get the model state.
          Text("${counter.state.count}"),
        ],
      ),
    );
  }
}

Principal concepts of the states_rebuilder #

1- Register a model (or any kind of values) using the Injector widget.

  • The registered (injected) models are available for access within the Injector itself and any of its child widgets.
  • Registered (injected) models are automatically unregistered when the Injector is disposed (removed) from the widget tree.
  • Registered model can be disposed to release recourses. This is done by using the dispose parameter of Injector or set the disposeModels parameter to true.
  • Futures and Streams can be registered (injected) using the named constructor Inject.future and Inject.streamrespectively.
  • Streams are automatically disposed when the Injector is removed from the widget tree.
  • Models can be registered associated with custom names.
  • Models are lazily instantiated. Set the parameter isLazy of the Inject to false if you want to intentionally instantiate a registered model.

2- To get any of the registered models :

  • Use Injector.get<T>() to get a singleton of the T class.
  • No need for the context, hence you can get a registered model inside any class.
  • To get a model registered with a custom name, use Injector.get<T>(name: customName).
  • To get a new instance of a registered model, use Injector.getNew<T>().

3- To get any of the registered model and make it reactive :
(Reactive means that the model allows widgets to register as listeners notify them to update after state mutation).

  • Use Injector.getAsModel<T>() to get a singleton of type ModelStatesRebuilder<T>. (ModelStatesRebuilder extends StatesRebuilder).
  • Another way to make a model reactive is to extend it with StatesRebuilder class.
  • To register a widget as a listener in a particular reactive model :
  • Use Injector.getAsModel<T>(context:context). The context is optional and when it is provided, the widget is automatically registered in the model. For futures and streams, use Injector.getAsModel<T>(context:context).snapshot to get the AsyncSnapshot.
  • Use StateBuilder widget. StateBuilder make rebuild process filtrable. This is done by giving StateBuilder a tag. When a model sends notifications to its dependent with a particular tag, only those StateBuilder that have this tag will rebuild.

4- To notify listeners of a reactive model :

  • Use setState(Function(T) state,{List tag, dynamic Function(T) watch, bool catchError, void Function(BuildContext) onSetState}) method. This works with models obtained using Injector.getAsModel<T>(). See examples to see the use of catchError and onSetState.
  • Use rebuildStates([List tag]) method. This works inside a model that extends StatesRebuilder.
  • Use the getter state to get the state of a models obtained using Injector.getAsModel<T>().

5- states_rebuilder offers an easy facade to get:

The widget lifeState : use initState, dispose, didChangeDependencies, didUpdateWidget, afterInitialBuild and afterRebuild.
The app life cycle in Android (onCreate, onPause, ...) and in IOS (didFinishLaunchingWithOptions, applicationWillEnterForeground ..): use appLifeCycle.

6- states_rebuilder offers the widget StateWithMixinRebuilder to deal with the most common mixins. Available mixins are: singleTickerProviderStateMixin, tickerProviderStateMixin, AutomaticKeepAliveClientMixin and WidgetsBindingObserver.

List of article about states_rebuilder #

This Library offers the following classes and methods: #

Injector widget for dependency injection:

To register models and services use Injector. The registered model will be available to all child widgets of the Injector

  Injector<T>({
    List<Inject<D>> inject, // List of Model to register wrapped with `Inject` object.
                            // To inject future and stream use the named constructor `Inject<T>.future` and `Inject<T>.stream`.
    (BuildContext context, T model) → Widget builder, // The builder method.
    (T model) → void initState, // a custom method to call when Injector is first added to the widget tree.
    (T model) → void dispose, // a custom method to call when Injector is disposed.
    (T, AppLifecycleState) → dynamic appLifeCycle, // A closure to execute code depending on the life cycle of the app (in Android : onResume, onPause ...).
    (BuildContext, String,T) → void afterInitialBuild, // for code to be executed after the widget is inserted in the widget tree.
    (BuildContext, String) → void afterRebuild, // for code to be executed after each rebuild of the widget.
    bool disposeModels: false // Whether Injector will automatically call dispose method from the registered models.
  }) 

To get the same instance of the model inside any class use:

  Injector.get<T>({dynamic name, BuildContext context, bool silent = false}).

Where T is the type of the model and name is optional used if you want to call a named model.

To get the same instance of a registered model and make it reactive use:

  Injector.getAsModel<T>(({dynamic name, BuildContext context, bool silent = false})).

Where T is the type of the model and name is optionally used if you want to call a named model. If you provide the context parameter the widget will listen to the model and rebuild when the state of the model is changed.

To get a new instance of the model, you use:

  Injector.getNew<T>([String name]).

Models are automatically unregistered when the injector is disposed.

Injector.get or Injector.getAsModel throw if no registered model is found. This can be silent by setting the parameter silent to true

Prototype Example for dependency injection #

    Widget build(BuildContext context) {
    return Injector( 
      inject: [
        Inject(() => ModelA()),
        Inject(() => ModelB()),
        Inject(() => ModelC(Injector.get<ModelA>())),// Directly inject ModelA in ModelC constructor
        Inject<IModelD>(() => ModelD()),//To register with Interface type.
        Inject<bool>.future(() => Future(), initialValue:0),//To register a future.
        Inject<int>.stream(() => Stream()),//To register a stream.
        Inject(() => ModelD(),name:"customName"), // to use custom name
        ],
      builder: (context,model) => MyWidget(model), // model is of type `ModelA`. when `rebuildStates()` is called in `ModelA` this widget will rebuild
    );
  }

  // You can get your models from any class provided it is registered before calling it.
  class MyWidget extends StatelessWidget {

    final ModelA modelA = Injector.get<ModelA>(); // get the ModelA singleton
    final ModelA modelA1 = Injector.getNew<ModelA>(); // get new instance
    final modelD = Injector.getAsModel<ModelD>(context:context); // get ModelD as `StatesRebuilder` type and subscribe this widget
    final modelDNamed = Injector.get<ModelD>("costumeName");
    final futureSnapshot = Injector.getAsModel<bool>(context:context, name: "costumeName").snapshot; // get the snapshot of an injected future
    final streamSnapshot = Injector.getAsModel<int>(context:context).snapshot; // get the snapshot of an injected future

    @override
    Widget build(BuildContext context) {
      return Widget(
        child: ChildWidget(modelID.state.myVar ) // get the state of a reactive model
        onPressed:()=> modelD.setState((state) { state mutation }), // mutate the state of a reactive model
      )
    }
  }

The StateBuilder Widget.

You wrap any part of your widgets with it to add it to the listeners' list of your logic classes and hence can rebuild when the any of the logic classes send notifications.

notification can be sent by: 1- calling rebuildStates method inside a class that extends StatesRebuilder. 2- calling setState(Function(T)) of any reactive model obtained using Injector.getAsMode.

This is the constructor of the StateBuilder:

  StateBuilder( {
      Key key, 
      dynamic tag, // you define the tag of the state. This is the first way. You can provide a list of tags.
      List<StatesRebuilder> models, // You give a list of the logic classes (BloC) you want this widget to listen to.
      @required (BuildContext, String) → Widget builder,
      (BuildContext, String) → void initState, // for code to be executed in the initState of a StatefulWidget
      (BuildContext, String) → void dispose, // for code to be executed in the dispose of a StatefulWidget
      (BuildContext, String) → void didChangeDependencies, // for code to be executed in the didChangeDependencies of a StatefulWidget
      (BuildContext, String, StateBuilder) → void didUpdateWidget // for code to be executed in the didUpdateWidget of a StatefulWidget
      (BuildContext, String) → void afterInitialBuild, // for code to be executed after the widget is inserted in the widget tree.
      (BuildContext, String) → void afterRebuild, // for code to be executed after each rebuild of the widget.
    });

tag is of type dynamic. It can be String (for small projects) or enum members (enums are preferred for big projects). When a list of dynamic tags is provided, states_rebuilder considers it as many tags and will rebuild this widget if any of these tags are invoked by the rebuildStates method or by setState method.

The StatesRebuilder class.

Your logics classes (viewModels) will extend this class to create your own business logic BloC (equally can be called ViewModel or Model).

  • The rebuildStates method. You call it inside any of your logic classes that extend StatesRebuilder. It rebuilds all the mounted 'StateBuilder' widgets. It can filter the widgets to rebuild by tag. This is the signature of the rebuildState:
  rebuildStates([List<dynamic> tags])

You can use hasState to check whether the StatesRebuilder has state to rebuild or not before calling rebuildStates to avoid any error.

The StateWithMixinBuilder class.

  • To extends the state with mixin (practical case is animation), use StateWithMixinBuilder
StateWithMixinBuilder<T>( {
      Key key, 
      dynamic tag, // you define the tag of the state. This is the first way
      List<StatesRebuilder> models, // You give a list of the logic classes (BloC) you want this this widget to listen to.
      @required (BuildContext, String) → Widget builder, 
      @required (BuildContext, String,T) → void initState, // for code to be executed in the initState of a StatefulWidget
      @required (BuildContext, String,T) → void dispose, // for code to be executed in the dispose of a StatefulWidget
      (BuildContext, String,T) → void didChangeDependencies, // for code to be executed in the didChangeDependencies of a StatefulWidget
      (BuildContext, String,StateBuilder, T) → void didUpdateWidget // for code to be executed in the didUpdateWidget of a StatefulWidget,
      (String, AppLifecycleState) → void didChangeAppLifecycleState // for code to be executed depending on the life cycle of the app (in Android : onResume, onPause ...).
      (BuildContext, String,T) → void afterInitialBuild, // for code to be executed after the widget is inserted in the widget tree.
      (BuildContext, String) → void afterRebuild, // for code to be executed after each rebuild of the widget.
      @required MixinWith mixinWith
});

Available mixins are: singleTickerProviderStateMixin, tickerProviderStateMixin, AutomaticKeepAliveClientMixin and WidgetsBindingObserver.

1.7.0 (2019-11-14) #

1- Add onSetState parameter to the setState method to define a callback to be executed after state mutation. The callBack takes the context so you can push/pop routes, show dialogs or snackBar. (see example folder).

2- Add catchError parameter to the setState method to define whether to catch error while mutining the state or not.(see example folder). If an error is thrown, hasError getter is true and the error can be obtained via the error getter (see point 5 below).

3- Add the getter connectionState to the ModelStatesRebuilder<T>to get the asynchronous status of the state. it can be ConnectionState.none before executing the Future, ConnectionState.waiting while waiting for the Future and ConnectionState.done after resolving the Future.

4- Add the field stateStatus to the ModelStatesRebuilder<T> class. It allows defining a custom status of the state other than those defined by the connectionState getter.

5- add the getter hasError, hasData and error to the ModelStatesRebuilder<T> class.

6- Change the name blocs to models.

7- Refactor the code and fix bugs.

8- Update docs and examples.

1.6.1 (2019-10-22) #

  • Add watch parameter to setState method and Inject.stream constructor. watch allows to link the rebuild process to the variation of a set of variables.
  • Update docs

1.6.0+1 (2019-10-18) #

  • Add Injector.getAsModel method. When called with the context parameter, the calling widget is automatically registered as a listener.
  • Add setState(Function(state)) to mutate the state and update the dependent the views from the UI.
  • Model class have not to extend StatesRebuilder to get reactivity.
  • Add the named constructorInject.future which take a future and update dependents when future completes.
  • Add the named constructorInject.stream which take a steam and update dependents when stream emits a value.
  • Injector.get or Injector.getAsModel now throws if no registered model is found. This can be silent by setting the parameter silent to true
  • Injected model ara lazily instantiated. To do otherwise set the parameter isLazy of the Inject widget to false.

1.5.1 (2019-09-14) #

  • add afterInitialBuild and afterRebuild parameters to the StateBuilder, StateWithMixinBuilder and Injector widgets.afterInitialBuildandafterRebuild` are callBack to be executed after the widget is mounted and after each rebuild.

1.5.0+1 (2019-09-12) #

  • Use ObservableService and hasState instead of Observable and hasObserver, because the latters are widely used and can lead to conflict

1.5.0 (2019-09-06) #

  • Add hasStates getter to check if the StatesRebuilder object has listener.
  • Add inject parameter to the Injector widget as an alternative to the models parameter. With inject you can register models using interface Type.
  • Add observable interface. Any service class can implement it to notify any ViewModel to rebuild its corresponding view.
  • Refactor the library to make it design patterns wise and hence make it testable.
  • Test the library

1.3.2 (2019-06-24) #

  • Add appLifeCycle argument to Injector to track the life cycle of the app.
  • Refactor the code.

1.3.1 (2019-06-13) #

  • remove rebuildFromStreams.
  • Initial release of Streaming class
  • The builder closure of the Injector takes (BuildContext context, T model) where T is the generic type.
  • Fix typos

1.3.0 (2019-06-04) #

  • Initial release of rebuildFromStreams method.
  • Initial release of Injector for Dependency Injection.
  • deprecate blocs parameter and use viewModels instead
  • StateBuilder can have many tags.

1.2.0 (2019-05-23) #

  • Remove stateID and replace it by tag parameter. tag is optional and many widgets can have the same tag.
  • rebuildStates() when called without parameters, it rebuilds all widgets that are wrapped with StateBuilder and StateWithMixinBuilder.
  • Each StateBuilder has an automatically generated cached address. It is stored in the second parameter of the builder, initState, dispose, and other closures. You can call it inside the closures to rebuild that particular widget.
  • add StateWithMixinBuilder widget to account for some of the most used mixins.
  • Optimize the code and improve performance

1.1.0 (2019-05-13) #

  • Add withTickerProvider parameter to StateBuilder widget.

1.0.0 (2019-05-12) #

  • Add BlocProviderto provide your BloCs.
  • You can use enums to name your StateBuilder widgets.
  • rebuildStates now has only one positioned parameter of List
  • If rebuildStates is given without parameter, it will rebuild all widgets that have stateID.
  • improve performance.

0.1.4 #

  • improve performance

0.1.3 #

  • Add getter and setter for the stateMap.

0.1.2 #

  • Remove print statements

0.1.1 #

  • Change readme.md of the example

0.1.0 #

  • Initial version

example/README.md

1- Simple Counter App #

Simple counter with showing the Snackbar when the value of the counter reaches 10. This example shows the use of:

  • the getter state
  • The method setState
  • The parameter onSetState
import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

//Pure dart class. No inheritance, no notification, no streams, and no code generation
class Counter {
  int count = 0;
  increment() => count++;
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      inject: [Inject<Counter>(() => Counter())],
      builder: (context, __) {
        final counter = Injector.getAsModel<Counter>();
        return Scaffold(
          appBar: AppBar(
            title: Text(" Counter App"),
          ),
          body: MyHome(),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => counter.setState((state) => state.increment(),
            //with osSetState you can define a callback to be executed after mutating the state.
              onSetState: (context) {
                if (counter.state.count >= 10) {
                  Scaffold.of(context).showSnackBar(
                    SnackBar(
                      content: Text("You have reached 10 taps"),
                    ),
                  );
                }
              },
            ),
          ),
        );
      },
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Injector.getAsModel<Counter>(context: context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text("You have pushed this many times"),
          Text("${counter.state.count}"),
        ],
      ),
    );
  }
}

2- Counter App with Future #

asynchronous counter with showing CircularProgressIndicator while waiting for the Future to resolve. This example shows the use of:

  • the getter connectionState. It take the following values:
    ConnectionState.none : before executing async task.
    ConnectionState.waiting : while executing async task.
    ConnectionState.done : when async task resolves.
    Listener are notified to rebuild after each change of connectionState.
import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

class Counter {
  int count = 0;
  increment() async {
    await Future.delayed(Duration(seconds: 1));
    count++;
  }
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      inject: [Inject<Counter>(() => Counter())],
      builder: (context, __) {
        final counter = Injector.getAsModel<Counter>();
        return Scaffold(
          appBar: AppBar(
            title: Text(" Counter App"),
          ),
          body: MyHome(),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => counter.setState((state) => state.increment()),
          ),
        );
      },
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Injector.getAsModel<Counter>(context: context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text("You have pushed this many times"),
          //counter has connectionState getter.
          //`counter.connectionState` equals:
          // ConnectionState.none    : before executing async task.
          // ConnectionState.waiting : while executing async task.
          // ConnectionState.done    : when async task resolves.
          counter.connectionState == ConnectionState.waiting
              ? CircularProgressIndicator()
              : Text(counter.state.count.toString()),
        ],
      ),
    );
  }
}

3- Counter app : catching errors (show AlertDialog) #

asynchronous counter with possibility of throwing an error. An alert dialog is shown with the error message. This example shows the use of:

  • The parameter onSetState
  • The getter hasError
  • The getter error
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

class Counter {
  int _count = 0;
  int get count => _count;
  increment() async {
    //Simulating async task
    await Future.delayed(Duration(seconds: 1));
    //Simulating error (50% chance of error);
    bool isError = Random().nextBool();

    if (isError) {
      throw Exception("A fake network Error");
    }
    _count++;
  }
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      inject: [Inject<Counter>(() => Counter())],
      builder: (context, __) {
        final counterModel = Injector.getAsModel<Counter>();
        return Scaffold(
          appBar: AppBar(
            title: Text(" Counter App"),
          ),
          body: MyHome(),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => counterModel.setState(
              (state) => state.increment(),
              catchError: true, //catch the error
              onSetState: (context) {
                // onSetState will be executed after mutating the state.
                if (counterModel.hasError) {
                  showDialog(
                    context: context,
                    child: AlertDialog(
                      title: Text("Error!"),
                      content: Text("${counterModel.error}"),
                    ),
                  );
                }
              },
            ),
          ),
        );
      },
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterModel = Injector.getAsModel<Counter>(context: context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Builder(
            builder: (context) {
              if (counterModel.connectionState == ConnectionState.waiting) {
                return CircularProgressIndicator();
              }

              return Text(
                "${counterModel.state.count}",
                style: TextStyle(fontSize: 50),
              );
            },
          ),
        ],
      ),
    );
  }
}

4- Counter app : watching variable #

Simple counter with watching the change of the count variable. This example shows the use of:

  • The parameter watch
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

Color _color() {
  return Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);
}

class Counter {
  int _count1 = 0;
  int get count1 => _count1 <= 5 ? _count1 : 5;
  increment() {
    _count1++;
  }
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      inject: [Inject<Counter>(() => Counter())],
      builder: (context, __) {
        final counter = Injector.getAsModel<Counter>();
        return Scaffold(
          appBar: AppBar(
            title: Text(" Counter App"),
          ),
          body: MyHome(),
        );
      },
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Injector.getAsModel<Counter>(context: context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            width: double.infinity,
            height: 50,
            color: _color(),
            child: Center(
                child: Text("Random Color. It changes with each rebuild")),
          ),
          Text(
            "${counter.state.count1}",
            style: TextStyle(fontSize: 50),
          ),
          if (counter.state.count1 > 4)
            Column(
              children: <Widget>[
                Text("your have reached the maximum"),
                Divider(),
                Text(
                    "If you tap on the left button, nothing changes, and the rebuild process is stopped because the counter value is not changed.\nIf you tap on the right button, the random color changes because the counter value is not watched."),
              ],
            ),
          Divider(),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              RaisedButton(
                child: Text("increment and watch"),
                onPressed: () => counter.setState((state) => state.increment(),
                //watch variable `count1`, if it changes update the UI, else do not update the UI.
                    watch: (state) => state.count1),
                // you can watch many variables : ` watch : (state) => [state.count1, state.count2]`
              ),
              RaisedButton(
                child: Text("increment without watch"),
                onPressed: () => counter.setState((state) => state.increment()),
              )
            ],
          )
        ],
      ),
    );
  }
}

5- Injecting Futures and Streams #

This example shows the use of:

  • The parameter Inject.stream and Inject.future
import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      inject: [
        Inject.stream(
          () => Stream<int>.periodic(Duration(seconds: 1), (num) => num)
              .take(10),
        ),
        Inject<bool>.future(
            () => Future.delayed(Duration(seconds: 5), () => true),
            initialValue: false),
      ],
      builder: (_, __) {
        final futureSnap = Injector.getAsModel<bool>(context: context).snapshot;
        return Scaffold(
          appBar: futureSnap.data == false
              ? AppBar(
                  title: Text(" awaiting a Future"),
                  backgroundColor: Colors.red,
                )
              : AppBar(
                  title: Text("Future is completed"),
                  backgroundColor: Colors.blue,
                ),
          body: MyHome(),
        );
      },
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final streamSnap = Injector.getAsModel<int>(context: context).snapshot;
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text("Counter From stream"),
          streamSnap.hasData
              ? Text("${streamSnap.data}")
              : Text("Waiting for data ..."),
        ],
      ),
    );
  }
}

6- Animation with StateWithMixinBuilder. #

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

class CounterModel extends StatesRebuilder {
  int counter = 0;

  AnimationController controller;
  Animation animation;

  initAnimation(TickerProvider ticker) {
    controller =
        AnimationController(duration: Duration(seconds: 2), vsync: ticker);
    animation = Tween<double>(begin: 0, end: 2 * 3.14).animate(controller);
    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        controller.reset();
      }
    });
  }

  VoidCallback listener;
  triggerAnimation(tagID) {
    animation.removeListener(listener);
    listener = () {
      rebuildStates([tagID]);
    };
    animation.addListener(listener);
    controller.forward();
    counter++;
  }

  dispose() {
    controller.dispose();
  }
}

class AnimateSetExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector<CounterModel>(
      models:[()=> CounterModel()],
      builder:(context,model) => CounterGrid(),
    );
  }
}

class CounterGrid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final model = Injector.get<CounterModel>(context);
    return Padding(
      padding: EdgeInsets.all(10),
      child: Column(
        children: <Widget>[
          Text("Animate a set of boxes"),
          Expanded(
            child: StateWithMixinBuilder(
              mixinWith: MixinWith.singleTickerProviderStateMixin,
              initState: (_, __, ticker) => model.initAnimation(ticker),
              dispose: (_, __, ___) => model.dispose(),
              builder: (_, __) => GridView.count(
                    crossAxisCount: 3,
                    children: <Widget>[
                      for (var i = 0; i < 12; i++)
                        StateBuilder(
                          tag: i % 2,
                          viewModels: [model],
                          builder: (_, tagID) => Transform.rotate(
                                angle: model.animation.value,
                                child: GridItem(
                                  count: model.counter,
                                  onTap: () => model.triggerAnimation(i % 2),
                                ),
                              ),
                        ),
                    ],
                  ),
            ),
          ),
        ],
      ),
    );
  }
}

class GridItem extends StatelessWidget {
  final int count;
  final Function onTap;
  GridItem({this.count, this.onTap});
  @override
  Widget build(BuildContext context) {
    return InkWell(
      child: Container(
        margin: EdgeInsets.all(5),
        decoration: BoxDecoration(
          color: Colors.lightBlue,
          border:
              Border.all(color: Theme.of(context).primaryColorDark, width: 4),
          borderRadius: BorderRadius.circular(6),
        ),
        child: Center(
          child: Text(
            "$count",
            style: TextStyle(
              color: Colors.white,
              fontSize: 50,
            ),
          ),
        ),
      ),
      onTap: onTap,
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  states_rebuilder: ^1.7.0

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:states_rebuilder/states_rebuilder.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
87
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
93
Learn more about scoring.

We analyzed this package on Nov 21, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.6.1
  • pana: 0.12.21
  • Flutter: 1.9.1+hotfix.6

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.2.2 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.7 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test