context_plus 2.3.1 copy "context_plus: ^2.3.1" to clipboard
context_plus: ^2.3.1 copied to clipboard

Convenient value propagation and observing for Flutter

icon.png
context_plus #

showcase

context_plus context_plus context_plus context_plus

This package combines context_ref and context_watch into a single, more convenient package.

Visit context-plus.sonerik.dev for more information and interactive examples.

Table of Contents #

  1. Features
  2. Installation
  3. API

Features #

  • Provide any value to the descendant contexts with Ref.bind(context, ...) (or .bindLazy(), .bindValue())
  • Fetch ancestor-provided value with Ref.of(context)
  • Make widget dependent on any observable value with .watch(context) (or .watchOnly())
  • Make widget dependent on any observable value provided via a Ref with Ref.watch(context) (or Ref.watchOnly())

Supported observable types for Observable.watch() and Ref<Observable>.watch(): #

  • Listenable, ValueListenable:
    • ChangeNotifier
    • ValueNotifier
    • AnimationController
    • ScrollController
    • TabController
    • TextEditingController
    • FocusNode
    • PageController
    • RefreshController
    • ... and any other Listenable derivatives
  • Future, SynchronousFuture
  • Stream
  • ValueStream (from rxdart)
  • AsyncListenable (from async_listenable)

3rd party supported observable types for Observable.watch() via separate packages: #

Installation #

  1. Add context_plus to your pubspec.yaml:
    flutter pub add context_plus
    
  2. Wrap your app in ContextPlus.root:
    ContextPlus.root(
      child: MaterialApp(...),
    );
    
  3. (Optional, but recommended) Wrap default error handlers with ContextPlus.errorWidgetBuilder() and ContextPlus.onError() to get better hot reload related error messages:
    void main() {
      ErrorWidget.builder = ContextPlus.errorWidgetBuilder(ErrorWidget.builder);
      FlutterError.onError = ContextPlus.onError(FlutterError.onError);
    }
    
  4. (Optional) Remove context_ref and context_watch from your pubspec.yaml if you have them.

API #

Ref #

Ref<T> is a reference to a value of type T provided by a parent BuildContext.

It behaves similarly to InheritedWidget with a single value property and provides a conventional .of(context) method to access the value in descendant widgets.

Ref<AnyObservableType> also provides .watch() and .watchOnly() methods to observe the value conveniently.

Ref can be bound only to a single value per BuildContext. Child contexts can override their parents' Ref bindings.

Common places to declare Ref instances are:

  • As a global file-private variable.
    final _value = Ref<ValueType>();
    
    • Useful for sharing values across multiple closely-related widgets (e.g. per-screen values).
  • As a global public variable
    final appTheme = Ref<AppTheme>();
    
    • Useful for sharing values across the entire app.
  • As a static field in a widget class
    class SomeWidget extends StatelessWidget {
      static final _value = Ref<ValueType>();
      ...
    }
    
    • Useful for adding a state to a stateless widget without converting it to a stateful widget. The same applies to all previous examples, but this one is more localized, which improves readability for such use-case.

Ref.bind() #

T Ref<T>.bind(
  BuildContext context,
  T Function() create, {
  void Function(T value)? dispose,
  Object? key,
})
  • Binds a Ref<T> to the value initializer (create) for all descendants of context and context itself.
  • Value initialization happens immediately. Use .bindLazy() if you need it lazy.
  • Value is .dispose()'d automatically when the widget is disposed. Provide a dispose callback to customize the disposal if needed.
  • Similarly to widgets, key parameter allows for updating the value initializer when needed.

Ref.bindLazy() #

void Ref<T>.bindLazy(
  BuildContext context,
  T Function() create, {
  void Function(T value)? dispose,
  Object? key,
})

Same as Ref.bind(), but the value is created only when it's first accessed via Ref.of(context) or Ref.watch()/Ref.watchOnly(), thus not returned immediately.

Ref.bindValue() #

T Ref<T>.bindValue(
  BuildContext context,
  T value,
)
  • Binds a Ref<T> to the value for all descendants of context and context itself.
  • Whenever the value changes, the dependent widgets will be automatically rebuilt.
  • Values provided this way are not disposed automatically.

Ref.of(context) #

T Ref<T>.of(BuildContext context)

Ref<Observable>.watch() and Observable.watch() #

void Listenable.watch(BuildContext context)
void Ref<Listenable>.watch(BuildContext context)
  • Rebuilds the widget whenever the Listenable notifies about changes.
T ValueListenable<T>.watch(BuildContext context)
T Ref<ValueListenable<T>>.watch(BuildContext context)
  • Rebuilds the widget whenever the ValueListenable notifies about changes.
  • Returns the current value of the ValueListenable.
AsyncSnapshot<T> Future<T>.watch(BuildContext context)
AsyncSnapshot<T> Ref<Future<T>>.watch(BuildContext context)

AsyncSnapshot<T> Stream<T>.watch(BuildContext context)
AsyncSnapshot<T> Ref<Stream<T>>.watch(BuildContext context)

AsyncSnapshot<T> AsyncListenable<T>.watch(BuildContext context)
AsyncSnapshot<T> Ref<AsyncListenable<T>>.watch(BuildContext context)
  • Rebuilds the widget whenever the value notifies about changes.
  • Returns and AsyncSnapshot describing the current state of the value.
  • .watch()'ing a SynchronousFuture or ValueStream (from rxdart) will return a AsyncSnapshot with properly initialized data/error field, if initial value/error exists.
  • AsyncListenable can be used for dynamic swapping of the listened-to async value without losing the current state. See the live search example for a practical use-case.

Many popular observable types from 3rd party packages have their own .watch() methods provided by separate packages. See the 3rd party supported observable types for more information.

Ref<Observable>.watchOnly() and Observable.watchOnly() #

R TListenable.watchOnly<R>(
  BuildContext context,
  R Function(TListenable listenable) selector,
)
  • Invokes selector whenever the TListenable notifies about changes.
  • Rebuilds the widget whenever the selector returns a different value.
  • Returns the selected value.
R Future<T>.watchOnly<R>(
  BuildContext context,
  R Function(AsyncSnapshot<T> value) selector,
)

R Stream<T>.watchOnly<R>(
  BuildContext context,
  R Function(AsyncSnapshot<T> value) selector,
)
  • Invokes selector whenever the async value notifies about changes.
  • Rebuilds the widget whenever the selector returns a different value.
  • Returns the selected value.