👻

[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