Ausgabe
Ich versuche zu verstehen, wie das Flutter Consumer-Widget in Provider benachrichtigt wird, wenn NotifyListeners auf dem ChangeNotifier aufgerufen wird. Ich habe im Code nach dem Consumer-Widget gesucht und es gibt keine Anzeichen dafür, dass es sich als Listener für den ChangeNotifier registriert. Kann jemand erklären, wie es funktioniert. Auch, wie hört es auf zuzuhören.
// 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;
}
Lösung
Der Verbraucher verlässt sich auf Inheritedwidgets. Genauer gesagt verwendet es die context.dependOnInheritedWidgetOrExactType
Funktion.
Diese Funktion bindet ein Widget an ein Inheritedwidget. Wenn sich das Inheritedwidget ändert (gemäß seinem updateShouldNotify
), werden die zugehörigen Widgets neu erstellt.
Dieser Mechanismus erfordert kein Hinzufügen/Entfernen von Listenern. Zumindest nicht explizit. Flutter regelt das alleine
Beantwortet von – Rémi Rousselet
Antwort geprüft von – Dawn Plyler (FixError Volunteer)