global_state 1.0.0 copy "global_state: ^1.0.0" to clipboard
global_state: ^1.0.0 copied to clipboard

outdated

Global State automatically rebuilds Widgets on data changes. Create transition values to produce controllable animations.

Global State #

Global State automatically rebuilds Widgets on data changes. Create 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();
        }),
      ),
    );
  }
}

Other options:

  • ... extends StatelessWidget with GS is equivalent to ... extends StatelessWidgetGS.
  • ...extends StatefulWidget with StatefulGS or extend ... extends StatefulWidgetGS.
  • gs is a [GSMap].
  • [GSWidget] carries it's own [GSMap] that persists on rebuilds (an alternative to StatefulWidget).

dynamic values: values that cause GS enabled widgets to rebuild when they change. Any value in a GSMap is a dynamic value.

Writing dynamic values rule It's not allowed to set values from a [GSMap] or use [TransitionGroup] during the widgets build phase, except inside [GS.initContext].

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: ^3.0.0

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

Transitions and Animations #

GS enabled widgets can use [transition], a dynamic value that goes from 0 to 1 in the specified time:

@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] controls transitions.

Resume, reverse, shift time, pause, restart or cancel. They cannot be controlled from inside build methods.

// 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 current value

// retrieves the value of a keyed transition.
double t = transitionOf(#myKey);

[Transition] provide patterns over the output of [transition]

Patterns that wrap the output of [transition] (they do not change the internal value of it).

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

// It does not change the value of the transition
Transition.integer(5, 20, 2000, key: #myKey);
transitionOf(#myKey);   // still returns number between 0 and 1.

[transitionEval] receives an evaluate function as parameter

It cannot be used during the build phase. It can be useful to create from UI interactions and reference it with [transitionOf]. The evaluate function is invoked on every update cycle.

// 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] to set default parameters

TransitionsConfig.refreshPeriodicityMillis = 100;  // sets the default refresh periodicity of transitions to 100ms.
TransitionsConfig.timeDilationFactor = 0.5;  // the progress time of transitions advances at half the speed.

Refresh rate

double refreshRate = TransitionGroup.currentRefreshRateDynamic();

Special Considerations #

Builders #

Dynamic values do not work inside [Builder] functions. A workaround is to read the dynamic 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])),
    );
  );
}

The builder function is a callback that executes outside of the encompassing GS widget's [build] method and GS does not listen to these builds.

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 this list can change. These are the same conditions when keys should be used on Stateful widgets.

Details #

Maps and Lists #

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

Initializing and Disposing a Context #

[GS.initContext] is invoked by the [BuildContext] instance when it's added for the first time to the element tree.

[GS.disposeContext] is invoked by the [BuildContext] instance when it's unmounted (removed from tree to never be used again).

These methods are the equivalent to [State.init] and [State.dispose]. They can be overriden to initialize or dispose dynamic values.

Performance #

Performance rules of thumb:

  • 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 as 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.

2
likes
0
pub points
43%
popularity

Publisher

unverified uploader

Global State automatically rebuilds Widgets on data changes. Create transition values to produce controllable animations.

Repository (GitLab)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter

More

Packages that depend on global_state