Home > Software design >  How does Flutter Provider Consumer widget listen to notifications
How does Flutter Provider Consumer widget listen to notifications

Time:10-14

I'm trying to understand how the Flutter Consumer widget in Provider gets notified when notifyListeners is called on the ChangeNotifier. I've looked in the code for the Consumer widget and there's no sign of it registering as a listener to the ChangeNotifier. Can anyone explain how it works. Also, how does it stop listening.

// the state class
//
class ApplicationState extends ChangeNotifier {
  ApplicationState() {
    init();
  }
// .....
// 
// the ChangeNotifierProvider at the base of the widget tree
void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => ApplicationState(),
      child: const App(),
    ),
  );
}
//
// the consumer widget halfway up the widget tree
//
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          const SizedBox(height: 8),
          const IconAndDetail(Icons.calendar_today, 'October 30'),
          const IconAndDetail(Icons.location_city, 'San Francisco'),
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          const Divider(
// ....
//
// The Consumer class from the Provider package
//
class Consumer<T> extends SingleChildStatelessWidget {
  /// {@template provider.consumer.constructor}
  /// Consumes a [Provider<T>]
  /// {@endtemplate}
  Consumer({
    Key? key,
    required this.builder,
    Widget? child,
  }) : super(key: key, child: child);

  /// {@template provider.consumer.builder}
  /// Build a widget tree based on the value from a [Provider<T>].
  ///
  /// Must not be `null`.
  /// {@endtemplate}
  final Widget Function(
    BuildContext context,
    T value,
    Widget? child,
  ) builder;

  @override
  Widget buildWithChild(BuildContext context, Widget? child) {
    return builder(
      context,
      Provider.of<T>(context),
      child,
    );
  }
}
//
//
// This is Provider.of
//
  static T of<T>(BuildContext context, {bool listen = true}) {
    assert(
      context.owner!.debugBuilding ||
          listen == false ||
          debugIsInInheritedProviderUpdate,
      '''
Tried to listen to a value exposed with provider, from outside of the widget tree.

This is likely caused by an event handler (like a button's onPressed) that called
Provider.of without passing `listen: false`.

To fix, write:
Provider.of<$T>(context, listen: false);

It is unsupported because may pointlessly rebuild the widget associated to the
event handler, when the widget tree doesn't care about the value.

The context used was: $context
''',
    );

    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {
      // bind context with the element
      // We have to use this method instead of dependOnInheritedElement, because
      // dependOnInheritedElement does not support relocating using GlobalKey
      // if no provider were found previously.
      context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
    }

    final value = inheritedElement?.value;

    if (_isSoundMode) {
      if (value is! T) {
        throw ProviderNullException(T, context.widget.runtimeType);
      }
      return value;
    }

    return value as T;
  }

 

CodePudding user response:

Consumer relies on Inheritedwidgets. More specifically, it uses the context.dependOnInheritedWidgetOrExactType function.

This function binds a widget with an Inheritedwidget. Such that when the Inheritedwidget changes (as per to its updateShouldNotify), the associated widgets will rebuild.

This mechanism does not require adding/removing listeners. At least not explicitly. Flutter handles that on its own

  • Related