provider

repository·master·Indexed 26 days ago

https://github.com/rrousselgit/provider

A wrapper around InheritedWidget for state management in Flutter. It simplifies resource allocation and supports lazy loading through a standardized API including Provider.of, Consumer, Selector, and BuildContext extensions like context.watch, context.read, and context.select. The library provides specialized providers such as ChangeNotifierProvider, StreamProvider, FutureProvider, and ProxyProvider for handling dependencies between providers.

Tokens
11.2K
Snippets
35
Records
64
Agent score
84%

What's inside provider

  1. Overview of the provider package

    master

    The provider package is a wrapper around InheritedWidget designed to make state management easier and more reusable in Flutter.

    Key benefits include:

    • Simplified resource allocation and disposal.
    • Lazy-loading support.
    • Drastic reduction in boilerplate code compared to manual InheritedWidget implementation.
    • Compatibility with Flutter DevTools (application state is visible).
    • Standardized ways to consume data via Provider.of, Consumer, and Selector.
    • Improved scalability for classes with complex notification mechanisms (like ChangeNotifier).
  2. Overview of provider

    master

    The provider package is a wrapper around InheritedWidget designed to make it easier to use and more usable in Flutter. It provides several advantages over manual InheritedWidget implementation:

    • Simplified resource allocation and disposal.
    • Lazy-loading support.
    • Reduced boilerplate compared to creating new classes for every widget.
    • Compatibility with Flutter DevTools.
    • Standardized ways to consume data via Provider.of, Consumer, and Selector.
    • Improved scalability for listener mechanisms (e.g., handling ChangeNotifier more efficiently than the O(N²) complexity of standard notification systems).
  3. Reuse an existing object instance with .value

    master

    If you already have an existing object instance (for example, a variable defined outside the widget tree) and want to reuse it, use the .value constructor.

    Warning: If you use the default constructor to pass an existing variable, the dispose method might be called on an object that is still in use elsewhere. Always use .value for existing instances, especially with ChangeNotifier.

    MyChangeNotifier variable;
    
    // Correct way to reuse an existing instance
    ChangeNotifierProvider.value(
      value: variable,
      child: ...
    )
  4. Migrate from v3.x.0 to v4.0.0

    master

    When upgrading from version 3.x.0 to 4.0.0, several breaking changes must be addressed:

    Parameter Changes

    • initialBuilder is replaced by create.
    • builder in proxy providers is replaced by update.
    • builder in classic providers is replaced by create.

    Lazy Loading

    New create and update callbacks are lazy-loaded by default, meaning they are only called when the value is first read. To disable this behavior, set lazy: false.

    FutureProvider(
      create: (_) async => doSomeHttpRequest(),
      lazy: false,
      child: ...
    )

    Renamed Classes and Interfaces

    • ProviderNotFoundError is now ProviderNotFoundException.
    • SingleChildCloneableWidget has been replaced by SingleChildWidget.
    • DelegateWidget and its family have been removed. For custom providers, use direct subclasses of InheritedProvider or an existing provider.

    Selector Behavior

    Selector now performs deeper comparisons for collections. To revert to a standard equality check, use the shouldRebuild parameter.

    Selector<Selected, Consumed>(
      shouldRebuild: (previous, next) => previous == next,
      builder: (context, next) => ...,
    )
  5. Expose a new object using Provider

    master

    To create and manage the lifecycle (creation, listening, and disposal) of a new object, use the default constructor of a provider.

    Important Rules:

    • CREATE the object inside the create callback.
    • DO NOT use .value constructors to create new objects; this can lead to unwanted side effects like premature disposal.
    • DO NOT pass variables that change over time directly into the create callback, as the object will not be updated when those variables change. Use ProxyProvider for this instead.
    • By default, create and update callbacks are lazy. They are only called when the value is requested at least once. Set lazy: false to compute the value immediately.
    // CORRECT: Create a new object
    Provider(
      create: (_) => MyModel(),
      child: ...
    )
    
    // INCORRECT: Using .value to create a new object
    ChangeNotifierProvider.value(
      value: MyModel(),
      child: ...
    )
    
    // Eager loading (disabling lazy behavior)
    MyProvider(
      create: (_) => Something(),
      lazy: false,
    )
  6. Use MultiProvider to reduce nesting

    master

    When injecting many values, use MultiProvider to avoid deeply nested widget trees. It is functionally identical to nesting multiple Provider widgets but improves code readability.

    MultiProvider(
      providers: [
        Provider<Something>(create: (_) => Something()),
        Provider<SomethingElse>(create: (_) => SomethingElse()),
        Provider<AnotherThing>(create: (_) => AnotherThing()),
      ],
      child: someWidget,
    )
  7. Expose a new object instance using Provider

    master

    To create and expose a new object, use the default constructor of a provider with the create callback.

    Important Guidelines:

    • DO create new objects inside the create callback.
    • DON'T use the .value constructor to create new objects; this can lead to unwanted side effects.
    • DON'T pass variables that change over time directly into the create callback, as the object will not be updated when those variables change. Use ProxyProvider instead.

    By default, create and update calls are lazy. The callback will not be executed until the value is requested at least once. You can disable this by setting lazy: false.

    // DO: Create a new object inside create
    Provider(
      create: (_) => MyModel(),
      child: ...
    )
    
    // DON'T: Use .value to create a new object
    ChangeNotifierProvider.value(
      value: MyModel(),
      child: ...
    )
    
    // Disable lazy loading
    MyProvider(
      create: (_) => Something(),
      lazy: false,
    )
  8. Use ProxyProvider to create a value based on another provider

    master

    ProxyProvider allows you to create a new object that depends on another provider. When the dependency changes, the ProxyProvider will update its own value.

    There are different variations:

    • ProxyProvider, ProxyProvider2, ProxyProvider3, etc.: The number indicates how many other providers the ProxyProvider depends on.
    • ProxyProvider vs ChangeNotifierProxyProvider vs ListenableProxyProvider: They function similarly, but instead of passing a result to a standard Provider, ChangeNotifierProxyProvider passes its value to a ChangeNotifierProvider.
    Widget build(BuildContext context) {
      return MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => Counter()),
          ProxyProvider<Counter, Translations>(
            update: (_, counter, __) => Translations(counter.value),
          ),
        ],
        child: Foo(),
      );
    }
    
    class Translations {
      const Translations(this._value);
    
      final int _value;
    
      String get title => 'You clicked $_value times';
    }
  9. Handle optional Providers with nullable types

    master

    If you want to support cases where a provider might not exist in the widget tree, use a nullable type in your watch or read calls.

    • context.watch<Model>() will throw a ProviderNotFoundException if no provider is found.
    • context.watch<Model?>() will return null if no provider is found instead of throwing an exception.
  10. Improve object visibility in DevTools by overriding toString

    master

    If you cannot use DiagnosticableTreeMixin (e.g., your class is in a non-Flutter package), you can override toString(). This is simpler but less powerful than DiagnosticableTreeMixin as you won't be able to expand/collapse details in DevTools.

    class MyClass with DiagnosticableTreeMixin {
      MyClass({this.a, this.b});
    
      final int a;
      final String b;
    
      @override
      String toString() {
        return '$runtimeType(a: $a, b: $b)';
      }
    }