global_state 1.0.1

Flutter Android iOS web

Global State automatically rebuilds Widgets on value changes. Includes an api for creating transition values to produce controllable animations.

Global State automatically rebuilds Widgets on value changes.
Includes an api for creating transition values to produce controllable animations.

How to use

import 'package:global_state/gs.dart';

-class Clicker extends StatelessWidget {
+class Clicker extends StatelessWidget with GS {
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Opacity(
          // Text displays the current value of gs[#clicks].
          child: Text('${gs[#clicks]}'),
          // opacity goes from 0 to 1 in two seconds
          opacity: transition(2000),
        )
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          gs[#clicks]++;
          // Restarts the opacity transition.
          TransitionGroup(context: context).restart();
        }),
      ),
    );
  }
}

gs is a [GSMap]
with StatefulGS for StatefulWidgets
[GSMap], [GSList] and transition are gs values providers
To set default gs values during the build phase, use [GS.initContext] or [GS.disposeContext]

[GSWidget] carries it's own [GSMap]:

import 'package:global_state/gs.dart';

class Clicker extends GSWidget {
  initGSMap() {
    gsmap[#clicks] = 0
  }

  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('${gsmap[#clicks]}'),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          gsmap[#clicks]++;
        }),
      ),
    );
  }
}

Suggested use cases #

Store data that affects many widgets (e.g. user data).

Asynchronous operations like data fetching (e.g gs['myData'] == null ? LoadingWidget() : MyWidget(gs['myData'])).

Animate widgets.

Install #

Add global_state dependency to the project's pubspec.yaml.

depedencies:
  global_state: ^1.0.0

Run flutter pub get in the root directory of the project.

Transitions and Animations #

[transition] provides a gs value that goes from 0 to 1:

@override
Widget build(BuildContext context) {
  return Opacity(
    opacity: transition(3000, key: #myKey), // transitions a number from 0 to 1
    child: Container(
      Text('Text will completely appear after 3 seconds')),
  );
}

Transitions of the same refresh periodicity are synchronized.

[TransitionGroup] instances can control transitions
[transitionOf] retrieves the value of a keyed transition
[transitionEval] receives an evaluate function as parameter
[Transition] provides patterns that wrap transition
[TransitionsConfig] can set default parameters
[TransitionGroup.refreshRateGS] returns the transitions refresh rate

[TransitionGroup]
Operations: resume, reverse, shift time, pause, restart or cancel.

// filter transitions with `key = #myKey` (there can be at most one).
var transitionGroup = TransitionGroup(key: #myKey);
// filter transitions with `bindContext = context` and `tag = #myTag`.
transitionGroup = TransitionGroup(context: context, tag: #myTag);

transitionGroup.reverse();  // reverses time.
transitionGroup.shiftTime(shiftMillis=1000);  // advances time 1 sec.

// Resumes transitions of the group that belong to the widget tree that starts
// at `rootContext`.
transitionGroup.resume(rootContext: context);

[transitionOf]
Retrieves the value of a keyed transition.

double t = transitionOf(#myKey);

[Transition]
Patterns that wrap the output of [transition].

// an int transitions from 5 to 20 over 2 seconds.
Transition.integer(5, 20, 2000);
// a string starts empty and finishes as 'My app' over 3 seconds.
Transition.string('My App', 3000);
// a value that oscillates between 0 and 1.
Transition.sin(2000, repeatAfterMillis=0);

// The patterns do not change the value of the transition, for example:
Transition.integer(5, 20, 2000, key: #myKey);
transitionOf(#myKey); // Returns number between 0 and 1.

[transitionEval]
A transition that stores an evaluated transition value.
It cannot be used inside build methods.

// The transition output is scaled by 10.
transitionEval(3000, (value) => 10*value, key: #myKey);
// reference it somewhere else (e.g. inside a build method):
transitionOf(#myKey);

[TransitionsConfig]

// sets the default refresh periodicity of transitions to 100ms.
TransitionsConfig.refreshPeriodicityMillis = 100;  
// Slows the rate of change of transitioned values by one half.
TransitionsConfig.timeDilationFactor = 0.5;

[TransitionGroup.refreshRateGS]

double refreshRate = TransitionGroup.refreshRateGS();

Special Considerations #

Builders #

GS values do not work inside [Builder] functions. A workaround is to read the gs values outside of the builder definition. Example:

Works fine:

  @override
  Widget build(BuildContext context) {
    final opacity = transition(3000);
    final text = gs[myText];
    return Builder(
      builder: (context) => Opacity(
        opacity: opacity,
        child: Container(
          Text(text),
      );
    );
  }

Should not do:

@override
Widget build(BuildContext context) {
  return Builder(
    builder: (context) => Opacity(
      // Error.
      opacity: transition(3000),
      child: Container(
        // The widget will not update if gs[myText] changes.
        Text(gs[myText])),
    );
  );
}

Transitions and Keys in Stateless Widgets #

Use keys on widgets that invoke [transition] when the widgets belong to the same list of children widgets ...children: [widget1, widget2, ...], and the list can change.

Details #

Maps and Lists #

[Map] and [List] values are not stored as they are when using []= operator, they are converted (copied) into new [GSMap]s or [GSList]s unless they have type [GSNotifier]. This behavior ensures that changes on maps or lists are detected in a deep sense.
Maps and lists can be stored without converting are by using the method [GSMap.setValue]. This method receives optional parameter to prevent triggering widget updates.

Initializing and Disposing a Context #

[GS.initContext] is invoked when the context is mounted (added to the element tree)
[GS.disposeContext] is invoked when the context is unmounted (removed permanently from tree)
These methods can be overriden to initialize or dispose gs values. They are the equivalent to [State.init] and [State.dispose].

Performance #

Including [GS] on a widget is less impactful than wrapping a widget with another widget.

Reading one value from a [GSMap] inside a build method is like reading five [Map] values.

GS only impacts widgets on their build time and it is less than x1.2 on minimal widgets (a container, a button and a text).

These build time increases can be considered rough references when comparing reading data from a [GSMap] in GS widgets, to reading the same data from a [LinkedHashMap] in widgets without GS. Only integer numbers were used as keys and values. It was also assumed that the same context would access the same keys on every invocation to [StatelessWidget.build]. It's more expensive when there are different keys read (that should be an uncommon case).

On small Widgets (less than 10 lines in the build method), including GS implies these build time performance hits:

  • x1.15 when 0 values are read.
  • x1.9 when up to 5 values are read.
  • x2.9 when up to 20 values are read.

On medium Widgets:

  • x1.1 when 0 values are read.
  • x1.6 when up to 5 values are read.
  • x2.5 when up to 20 values are read.

[GSMap] in comparison to a regular [LinkedHashMap]:

Reading:

  • x1.1 using the map like a regular map (outside build methods).
  • x3 while GS is on 'listening' mode (when a Widget is building).
  • x5 considering the whole preprocessing (start listening) and post processing (stop listening).

Writing:

  • x1.6.

Inspiration #

react-recollect.

Collaborate #

Write code, report bugs, give advice or ideas to improve the library.

3
likes
90
pub points
53%
popularity

Global State automatically rebuilds Widgets on value changes. Includes an api for creating transition values to produce controllable animations.

Repository (GitHub)
View/report issues

Documentation

API reference

Uploader

icata2016@gmail.com

License

MIT (LICENSE)

Dependencies

flutter

More

Packages that depend on global_state