An open API service indexing awesome lists of open source software.

https://github.com/robsonsilv4/flutter_value_notifier

Flutter Widgets that make it easy to implement state management patterns.
https://github.com/robsonsilv4/flutter_value_notifier

dart dartlang flutter flutter-package library provider state state-management valuenotifier widgets

Last synced: about 1 year ago
JSON representation

Flutter Widgets that make it easy to implement state management patterns.

Awesome Lists containing this project

README

          


Flutter Value Notifier


Pub
Build
Stars on Github
License: MIT

---

Widgets that make it easy to use [ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) and helps to implement state management patterns in Flutter. Heavily inspired by [package:flutter_bloc](https://pub.dev/packages/flutter_bloc).

## Usage

Lets take a look at how to use `ValueNotifierProvider` to provide a `CounterNotifier` to a `CounterPage` and react to value changes with `ValueNotifierBuilder`.

### counter_notifier.dart

```dart
class CounterNotifier extends ValueNotifier {
CounterNotifier() : super(0);

void increment() => value = value + 1;
void decrement() => value = value - 1;
}
```

### main.dart

```dart
void main() => runApp(CounterApp());

class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ValueNotifierProvider(
create: (_) => CounterNotifier(),
child: CounterPage(),
),
);
}
}
```

### counter_page.dart

```dart
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: ValueNotifierBuilder(
builder: (_, count) => Center(child: Text('$count')),
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => context.read().increment(),
),
const SizedBox(height: 4),
FloatingActionButton(
child: const Icon(Icons.remove),
onPressed: () => context.read().decrement(),
),
],
),
);
}
}
```

At this point we have successfully separated our presentational layer from our business logic layer. Notice that the `CounterPage` widget knows nothing about what happens when a user taps the buttons. The widget simply notifies the `CounterNotifier` that the user has pressed either the increment or decrement button.

## ValueNotifier Widgets

### ValueNotifierProvider

**ValueNotifierProvider** is a Flutter widget which provides a notifier to its children via `ValueNotifierProvider.of(context)`. It is used as a dependency injection (DI) widget so that a single instance of a notifier can be provided to multiple widgets within a subtree.

In most cases, `ValueNotifierProvider` should be used to create new notifiers which will be made available to the rest of the subtree. In this case, since `ValueNotifierProvider` is responsible for creating the notifier, it will automatically handle closing it.

```dart
ValueNotifierProvider(
create: (_) => NotifierA(),
child: ChildA(),
);
```

By default, ValueNotifierProvider will create the notifier lazily, meaning `create` will get executed when the notifier is looked up via `ValueNotifierProvider.of(context)`.

To override this behavior and force `create` to be run immediately, `lazy` can be set to `false`.

```dart
ValueNotifierProvider(
lazy: false,
create: (_) => NotifierA(),
child: ChildA(),
);
```

In some cases, `ValueNotifierProvider` can be used to provide an existing notifier to a new portion of the widget tree. This will be most commonly used when an existing `notifier` needs to be made available to a new route. In this case, `ValueNotifierProvider` will not automatically close the notifier since it did not create it.

```dart
ValueNotifierProvider.value(
value: ValueNotifierProvider.of(context),
child: ScreenA(),
);
```

then from either `ChildA`, or `ScreenA` we can retrieve `NotifierA` with:

```dart
// with extensions
context.read();

// without extensions
ValueNotifierProvider.of(context);
```

The above snippets result in a one time lookup and the widget will not be notified of changes. To retrieve the instance and subscribe to subsequent value changes use:

```dart
// with extensions
context.watch();

// without extensions
ValueNotifierProvider.of(context, listen: true);
```

In addition, `context.select` can be used to retrieve part of a value and react to changes only when the selected part changes.

```dart
final isPositive = context.select((notifier) => notifier.value >= 0);
```

The snippet above will only rebuild if the value of the `CounterNotifier` changes from positive to negative or vice versa and is functionally identical to using a `ValueNotifierSelector`.

### MultiValueNotifierProvider

**MultiValueNotifierProvider** is a Flutter widget that merges multiple `ValueNotifierProvider` widgets into one.
`MultiValueNotifierProvider` improves the readability and eliminates the need to nest multiple `ValueNotifierProviders`.
By using `MultiValueNotifierProvider` we can go from:

```dart
ValueNotifierProvider(
create: (_) => NotifierA(),
child: ValueNotifierProvider(
create: (_) => NotifierB(),
child: ValueNotifierProvider(
create: (_) => NotifierC(),
child: ChildA(),
)
)
)
```

to:

```dart
MultiValueNotifierProvider(
providers: [
ValueNotifierProvider(
create: (_) => NotifierA(),
),
ValueNotifierProvider(
create: (_) => NotifierB(),
),
ValueNotifierProvider(
create: (_) => NotifierC(),
),
],
child: ChildA(),
)
```

### ValueNotifierBuilder

**ValueNotifierBuilder** is a Flutter widget which requires a `ValueNotifier` and a `builder` function. `ValueNotifierBuilder` handles building the widget in response to new values. `ValueNotifierBuilder` is very similar to `ValueListenableBuilder` but has a more simple API to reduce the amount of boilerplate code needed. The `builder` function will potentially be called many times and should be a [pure function](https://en.wikipedia.org/wiki/Pure_function) that returns a widget in response to the value.

See `ValueNotifierListener` if you want to "do" anything in response to value changes such as navigation, showing a dialog, etc...

If the `notifier` parameter is omitted, `ValueNotifierBuilder` will automatically perform a lookup using `ValueNotifierProvider` and the current `BuildContext`.

```dart
ValueNotifierBuilder(
builder: (_, value) {
// return widget here based on NotifierA's value
}
)
```

Only specify the notifier if you wish to provide a ValueNotifier that will be scoped to a single widget and isn't accessible via a parent `ValueNotifierProvider` and the current `BuildContext`.

```dart
ValueNotifierBuilder(
notifier: notifier, // provide the local ValueNotifier instance
builder: (_, value) {
// return widget here based on NotifierA's value
}
)
```

For fine-grained control over when the `builder` function is called an optional `buildWhen` can be provided. `buildWhen` takes the previous ValueNotifier value and current ValueNotifier value and returns a boolean. If `buildWhen` returns true, `builder` will be called with `value` and the widget will rebuild. If `buildWhen` returns false, `builder` will not be called with `value` and no rebuild will occur.

```dart
ValueNotifierBuilder(
buildWhen: (previousValue, value) {
// return true/false to determine whether or not
// to rebuild the widget with value
},
builder: (_, value) {
// return widget here based on NotifierA's value
}
)
```

### ValueNotifierSelector

**ValueNotifierSelector** is a Flutter widget which is analogous to `ValueNotifierBuilder` but allows developers to filter updates by selecting a new value based on the current notifier value. Unnecessary builds are prevented if the selected value does not change. The selected value must be immutable in order for `ValueNotifierSelector` to accurately determine whether `builder` should be called again.

If the `notifier` parameter is omitted, `ValueNotifierSelector` will automatically perform a lookup using `ValueNotifierProvider` and the current `BuildContext`.

```dart
ValueNotifierSelector(
selector: (value) {
// return selected value based on the provided value.
},
builder: (_, value) {
// return widget here based on the selected value.
},
)
```

### ValueNotifierListener

**ValueNotifierListener** is a Flutter widget which takes a `ValueNotifierWidgetListener` and an optional `notifier` and invokes the `listener` in response to value changes in the notifier. It should be used for functionality that needs to occur once per value change such as navigation, showing a `SnackBar`, showing a `Dialog`, etc...

`listener` is only called once for each value change (**NOT** including the initial value) unlike `builder` in `ValueNotifierBuilder` and is a `void` function.

If the notifier parameter is omitted, `ValueNotifierListener` will automatically perform a lookup using `ValueNotifierProvider` and the current `BuildContext`.

```dart
ValueNotifierListener(
listener: (context, value) {
// do stuff here based on NotifierA's value
},
child: Container(),
)
```

Only specify the notifier if you wish to provide a notifier that is otherwise not accessible via `ValueNotifierProvider` and the current `BuildContext`.

```dart
ValueNotifierListener(
notifier: notifier,
listener: (context, value) {
// do stuff here based on NotifierA's value
}
)
```

For fine-grained control over when the `listener` function is called an optional `listenWhen` can be provided. `listenWhen` takes the previous notifier value and current notifier value and returns a boolean. If `listenWhen` returns true, `listener` will be called with `value`. If `listenWhen` returns false, `listener` will not be called with `value`.

```dart
ValueNotifierListener(
listenWhen: (previousValue, value) {
// return true/false to determine whether or not
// to call listener with value
},
listener: (context, value) {
// do stuff here based on NotifierA's value
},
child: Container(),
)
```

### MultiValueNotifierListener

**MultiValueNotifierListener** is a Flutter widget that merges multiple `ValueNotifierListener` widgets into one.
`MultiValueNotifierListener` improves the readability and eliminates the need to nest multiple `ValueNotifierListeners`.
By using `MultiValueNotifierListener` we can go from:

```dart
ValueNotifierListener(
listener: (context, value) {},
child: ValueNotifierListener(
listener: (context, value) {},
child: ValueNotifierListener(
listener: (context, value) {},
child: ChildA(),
),
),
)
```

to:

```dart
MultiValueNotifierListener(
listeners: [
ValueNotifierListener(
listener: (context, value) {},
),
ValueNotifierListener(
listener: (context, value) {},
),
ValueNotifierListener(
listener: (context, value) {},
),
],
child: ChildA(),
)
```

### ValueNotifierConsumer

**ValueNotifierConsumer** exposes a `builder` and `listener` in order react to new values. `ValueNotifierConsumer` is analogous to a nested `ValueNotifierListener` and `ValueNotifierBuilder` but reduces the amount of boilerplate needed. `ValueNotifierConsumer` should only be used when it is necessary to both rebuild UI and execute other reactions to value changes in the `notifier`. `ValueNotifierConsumer` takes a required `ValueNotifierWidgetBuilder` and `ValueNotifierWidgetListener` and an optional `notifier`, `ValueNotifierBuilderCondition`, and `ValueNotifierListenerCondition`.

If the `notifier` parameter is omitted, `ValueNotifierConsumer` will automatically perform a lookup using
`ValueNotifierProvider` and the current `BuildContext`.

```dart
ValueNotifierConsumer(
listener: (context, value) {
// do stuff here based on NotifierA's value
},
builder: (_, value) {
// return widget here based on NotifierA's value
}
)
```

An optional `listenWhen` and `buildWhen` can be implemented for more granular control over when `listener` and `builder` are called. The `listenWhen` and `buildWhen` will be invoked on each `notifier` `value` change. They each take the previous `value` and current `value` and must return a `bool` which determines whether or not the `builder` and/or `listener` function will be invoked. The previous `value` will be initialized to the `value` of the `notifier` when the `ValueNotifierConsumer` is initialized. `listenWhen` and `buildWhen` are optional and if they aren't implemented, they will default to `true`.

```dart
ValueNotifierConsumer(
listenWhen: (previous, current) {
// return true/false to determine whether or not
// to invoke listener with value
},
listener: (context, value) {
// do stuff here based on NotifierA's value
},
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with value
},
builder: (_, value) {
// return widget here based on NotifierA's value
}
)
```

### DependencyProvider

**DependencyProvider** is a Flutter widget which provides a dependency to its children via `DependencyProvider.of(context)`. It is used as a dependency injection (DI) widget so that a single instance of a dependency can be provided to multiple widgets within a subtree. `ValueNotifierProvider` should be used to provide notifier whereas `DependencyProvider` should only be used for dependencies.

```dart
DependencyProvider(
create: (_) => DependencyA(),
child: ChildA(),
);
```

then from `ChildA` we can retrieve the `Dependency` instance with:

```dart
// with extensions
context.read();

// without extensions
DependencyProvider.of(context)
```

### MultiDependencyProvider

**MultiDependencyProvider** is a Flutter widget that merges multiple `DependencyProvider` widgets into one.
`MultiDependencyProvider` improves the readability and eliminates the need to nest multiple `DependencyProvider`.
By using `MultiDependencyProvider` we can go from:

```dart
DependencyProvider(
create: (_) => DependencyA(),
child: DependencyProvider(
create: (_) => DependencyB(),
child: DependencyProvider(
create: (_) => DependencyC(),
child: ChildA(),
)
)
)
```

to:

```dart
MultiDependencyProvider(
providers: [
DependencyProvider(
create: (_) => DependencyA(),
),
DependencyProvider(
create: (_) => DependencyB(),
),
DependencyProvider(
create: (_) => DependencyC(),
),
],
child: ChildA(),
)
```

## Dart Versions

- Dart 2: >=2.19.0
- Flutter 3: >=3.0.0

## Maintainers

- [@robsonsilv4 (Robson Silva)](https://github.com/robsonsilv4)

Thanks to [@felangel](https://github.com/felangel), [bloc library contributors](https://github.com/felangel/bloc/graphs/contributors) and [Icons8](https://icons8.com) by [Electricity](https://icons8.com/icon/0p9q9Fp5U2bz/electricity) icon.