provide_it 0.34.0 copy "provide_it: ^0.34.0" to clipboard
provide_it: ^0.34.0 copied to clipboard

Provider-like state binding, management, and injection using only context extensions.

Provide It #

A minimalist state sharing library.

Setup #

// Wrap your app with ProvideIt.
void main() => runApp(ProvideIt(child: MyApp()));

Usage #

For handling local state quickly.

Local State (Hooks) #

// Hook & use locally.
final counter = context.use(() => Counter());
final (count, setCount) = context.useState(0);

Hook Example #

class CounterExample extends StatelessWidget {
  const CounterExample({super.key});

  @override
  Widget build(BuildContext context) {
    final (count, setCount) = context.useState(0);

    return GestureDetector(
      onTap: () => setCount(count + 1),
      child: Text('Increment: $count'),
    );
  }
}

Shared State (DI) #

For sharing state across screens and routes.

// Provide it...
context.provide(() => Counter()); // classic
context.provide(Counter.new); // auto-injects dependencies
context.provideValue(0);

// ... and use it anywhere
final counter = context.watch<Counter>();
final count = context.select((Counter c) => c.count);

// ... or just listen for side-effects!
context.listen<Counter>((it) => print(it.count));
context.listenSelected((Counter it) => it.count, (prev, next) {
  print('Count changed $prev -> $next');
});

Inherited Example #

class InheritedExample extends StatelessWidget {
  const InheritedExample({super.key});

  @override
  Widget build(BuildContext context) {
    // handle local state with HookProvider
    final snapshot = context.useFuture(() async => fetchData());

    // provide shared state with InheritedProvider
    context.provideValue(snapshot.data);

    return switch (snapshot) {
      AsyncSnapshot(:final data?) => Text('data: $data'),
      AsyncSnapshot(:final error?) => Text('error: $error'),
      _ => Text('loading'),
    };
  }
}

Available Providers #

Below is a list of all context providers currently available.

Extension method Provider type Description
context.provide InheritedProvider Provides a value with auto-dependency injection
context.provideAsync InheritedProvider Provides a Future value asynchronously
context.provideValue InheritedProvider Provides an existing value with optional update callback
context.use HookProvider Creates a local value tied to the context
context.useState HookProvider Returns a mutable value record
context.useStream HookProvider Subscribes to a Stream and returns AsyncSnapshot
context.useFuture HookProvider Subscribes to a Future and returns AsyncSnapshot
context.useValueNotifier HookProvider Creates a ValueNotifier that is automatically disposed
context.useAppLifecycleState HookProvider Rebuilds when app lifecycle state changes
context.useAppLifecycleListener HookProvider Listens to app lifecycle events without rebuilding
context.useFocusNode HookProvider Creates a FocusNode that is automatically disposed
context.useScrollController HookProvider Creates a ScrollController that is automatically disposed
context.usePageController HookProvider Creates a PageController that is automatically disposed
context.useTextEditingController HookProvider Creates a TextEditingController that is automatically disposed
context.useTextEditingControllerFromValue HookProvider Creates a TextEditingController from a TextEditingValue
context.useSingleTickerProvider HookProvider Provides a TickerProvider for animations
context.useAnimationController HookProvider Creates an AnimationController tied to context
context.useAutomaticKeepAlive HookProvider Enables/disables automatic keep-alive for the subtree

For existing Future, Stream, and Listenable values, prefer Future.watch(context), Stream.watch(context), and Listenable.watch(context).


The "What Ifs" behind the Magic #

ProvideIt was born from a few questions:

  • What if I could context.listen with provider?
  • What if I could scope and auto-dispose with get_it?
  • What if I could provide/hook state without custom widgets?

Inspirations #

This project took cues from several existing packages and ideas:

  • provider – syntax and scoping.
  • flutter_hooks & context_watch – binding and reactivity engine.
  • auto_injector – automatic injection without code generation.

And of course flutter, by using native tools like Listenable, ValueNotifier and AsyncSnapshot. This package depends only on flutter/widgets.

How it Works: The Scoped Container #

ProvideIt doesn't behave exactly like a Service Locator or a traditional Provider chain. It's a scoped container designed to be Flutter-native.

  • Not a Service Locator: Unlike get_it, ProvideIt is lifecycle-aware. It knows when a widget dies and cleans up the mess.

  • Not a Scoped Wrapper: Unlike provider, it isn't strictly chained to the parent-child hierarchy. You can access states in sibling routes or dialogs without complex nesting.

State Binding #

At its core, ProvideIt is a single InheritedWidget acting as a container. When you use an extension like context.useState or context.provide, you are performing State Binding.

There are two types of bindings:

  1. HookProvider (Local): Private state. It lives and dies with that specific widget.
  2. InheritedProvider (Shared): Global-ish state. It's registered in the container and becomes available to other contexts.

Note: For stability and predictability, you must never use conditional logic (if/else) when calling providers. Both InheritedProvider and HookProvider rely on a consistent execution order to maintain internal state correctly.

The "Use" Mindset #

While the syntax may remind you of React Hooks, ProvideIt is built from the ground up for Flutter. The "use" prefix signifies Creation and Lifecycle Management.

  • Creation & Lifecycle: context.use works like a persistent initState + dispose. It's responsible for creating the object and ensuring it's cleaned up (auto-dispose). For example, context.useAnimationController creates the controller and disposes it when the widget is unmounted.
  • Reactivity vs. Creation: We separate creation from observation to avoid confusion:
    • context.useFuture(...) / context.useStream(...): Creates and manages the lifecycle of a new Future or Stream.
    • future.watch(context) / stream.watch(context): Listens to an already existing Future or Stream. Under the hood both ways use the same HookProvider engine.
  • Explicit Reactivity: For existing values, use the clear and familiar .watch, .listen, and .select extensions.

Scoping & Disambiguation #

Wait, if it's a single container, how does it handle multiple states of the same type? ProvideIt is smart about context:

  • Closest Wins: If you have two providers of the same type, ProvideIt looks for the one closest to your current context.
  • Sibling Safety: If you try to access a state that exists in two different sibling branches (like two different routes) at the same time, ProvideIt throws an exception to prevent bugs.

This makes ProvideIt "retro-compatible" with the provider mental model, but with the freedom to reach across the app tree.

2
likes
160
points
516
downloads

Documentation

API reference

Publisher

verified publisherbranvier.com

Weekly Downloads

Provider-like state binding, management, and injection using only context extensions.

Homepage
Repository (GitHub)
View/report issues

Topics

#provider #context #state #management #injection

License

MIT (license)

Dependencies

flutter

More

Packages that depend on provide_it