👻
[Flutter] Riverpodのコードリーディング
flutter_riverpod-2.2.0/lib/src/consumer.dart
コメントを消すととてもシンプル
WidgetRef
ref.read
でお馴染みのクラス.
abstractクラスになっているのでインターフェースだけ.
abstract class WidgetRef {
BuildContext get context;
T watch<T>(ProviderListenable<T> provider);
bool exists(ProviderBase<Object?> provider);
void listen<T>(
ProviderListenable<T> provider,
void Function(T? previous, T next) listener, {
void Function(Object error, StackTrace stackTrace)? onError,
});
ProviderSubscription<T> listenManual<T>(
ProviderListenable<T> provider,
void Function(T? previous, T next) listener, {
void Function(Object error, StackTrace stackTrace)? onError,
bool fireImmediately,
});
T read<T>(ProviderListenable<T> provider);
State refresh<State>(Refreshable<State> provider);
void invalidate(ProviderOrFamily provider);
}
ConsumerWidget
abstract class ConsumerWidget extends ConsumerStatefulWidget {
const ConsumerWidget({super.key});
Widget build(BuildContext context, WidgetRef ref);
// ignore: library_private_types_in_public_api
_ConsumerState createState() => _ConsumerState();
}
class _ConsumerState extends ConsumerState<ConsumerWidget> {
WidgetRef get ref => context as WidgetRef;
Widget build(BuildContext context) {
return widget.build(context, ref);
}
}
typedef ConsumerBuilder = Widget Function(
BuildContext context,
WidgetRef ref,
Widget? child,
);
class Consumer extends ConsumerWidget {
/// {@template riverpod.consumer}
const Consumer({super.key, required ConsumerBuilder builder, Widget? child})
: _child = child,
_builder = builder;
final ConsumerBuilder _builder;
final Widget? _child;
Widget build(BuildContext context, WidgetRef ref) {
return _builder(context, ref, _child);
}
}
ConsumerStatefulWidget
abstract class ConsumerStatefulWidget extends StatefulWidget {
/// A [StatefulWidget] that can read providers.
const ConsumerStatefulWidget({super.key});
// ignore: no_logic_in_create_state
ConsumerState createState();
ConsumerStatefulElement createElement() {
return ConsumerStatefulElement(this);
}
}
abstract class ConsumerState<T extends ConsumerStatefulWidget>
extends State<T> {
/// An object that allows widgets to interact with providers.
late final WidgetRef ref = context as WidgetRef;
}
ConsumerStatefulElement
class ConsumerStatefulElement extends StatefulElement implements WidgetRef {
/// The [Element] for a [ConsumerStatefulWidget]
ConsumerStatefulElement(ConsumerStatefulWidget super.widget);
late ProviderContainer _container = ProviderScope.containerOf(this);
var _dependencies =
<ProviderListenable<Object?>, ProviderSubscription<Object?>>{};
Map<ProviderListenable<Object?>, ProviderSubscription<Object?>>?
_oldDependencies;
final _listeners = <ProviderSubscription<Object?>>[];
List<_ListenManual<Object?>>? _manualListeners;
void didChangeDependencies() {
super.didChangeDependencies();
final newContainer = ProviderScope.containerOf(this);
if (_container != newContainer) {
_container = newContainer;
for (final dependency in _dependencies.values) {
dependency.close();
}
_dependencies.clear();
}
}
Widget build() {
// TODO disallow didChangeDependencies
try {
_oldDependencies = _dependencies;
for (var i = 0; i < _listeners.length; i++) {
_listeners[i].close();
}
_listeners.clear();
_dependencies = {};
return super.build();
} finally {
for (final dep in _oldDependencies!.values) {
dep.close();
}
_oldDependencies = null;
}
}
Res watch<Res>(ProviderListenable<Res> target) {
return _dependencies.putIfAbsent(target, () {
final oldDependency = _oldDependencies?.remove(target);
if (oldDependency != null) {
return oldDependency;
}
return _container.listen<Res>(
target,
(_, __) => markNeedsBuild(),
);
}).read() as Res;
}
void unmount() {
/// Calling `super.unmount()` will call `dispose` on the state
/// And [ListenManual] subscriptions should be closed after `dispose`
super.unmount();
for (final dependency in _dependencies.values) {
dependency.close();
}
for (var i = 0; i < _listeners.length; i++) {
_listeners[i].close();
}
final manualListeners = _manualListeners?.toList();
if (manualListeners != null) {
for (final listener in manualListeners) {
listener.close();
}
_manualListeners = null;
}
}
void listen<T>(
ProviderListenable<T> provider,
void Function(T? previous, T value) listener, {
void Function(Object error, StackTrace stackTrace)? onError,
}) {
assert(
debugDoingBuild,
'ref.listen can only be used within the build method of a ConsumerWidget',
);
// We can't implement a fireImmediately flag because we wouldn't know
// which listen call was preserved between widget rebuild, and we wouldn't
// want to call the listener on every rebuild.
final sub = _container.listen<T>(provider, listener, onError: onError);
_listeners.add(sub);
}
bool exists(ProviderBase<Object?> provider) {
return ProviderScope.containerOf(this, listen: false).exists(provider);
}
T read<T>(ProviderListenable<T> provider) {
return ProviderScope.containerOf(this, listen: false).read(provider);
}
State refresh<State>(Refreshable<State> provider) {
return ProviderScope.containerOf(this, listen: false).refresh(provider);
}
void invalidate(ProviderOrFamily provider) {
_container.invalidate(provider);
}
ProviderSubscription<T> listenManual<T>(
ProviderListenable<T> provider,
void Function(T? previous, T next) listener, {
void Function(Object error, StackTrace stackTrace)? onError,
bool fireImmediately = false,
}) {
final listeners = _manualListeners ??= [];
final sub = _ListenManual(
ProviderScope.containerOf(this, listen: false).listen(
provider,
listener,
onError: onError,
fireImmediately: fireImmediately,
),
this,
);
listeners.add(sub);
return sub;
}
BuildContext get context => this;
}
class _ListenManual<T> implements ProviderSubscription<T> {
_ListenManual(this._subscription, this._element);
final ProviderSubscription<T> _subscription;
final ConsumerStatefulElement _element;
void close() {
_subscription.close();
_element._manualListeners?.remove(this);
}
T read() => _subscription.read();
}
Discussion