LCOV - code coverage report
Current view: top level - src - bloc_hook.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 37 37 100.0 %
Date: 2021-07-04 09:18:57 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : 
       3             : import 'package:flutter/foundation.dart';
       4             : import 'package:flutter/widgets.dart' show BuildContext;
       5             : import 'package:flutter_hooks/flutter_hooks.dart';
       6             : 
       7             : import 'flutter_bloc.dart' show Cubit, Bloc, BlocBase, ReadContext;
       8             : 
       9             : /// Signature for the `listener` function which takes the `BuildContext` along
      10             : /// with the `current` and `previous` state and is responsible for executing in
      11             : /// response to `state` changes.
      12             : typedef BlocHookListener<S> = bool Function(
      13             :   BuildContext context,
      14             :   S previous,
      15             :   S current,
      16             : );
      17             : 
      18             : /// {@template BlocWidget}
      19             : /// The [BlocWidget] is the base for every reimplementation of `flutter_bloc`'s
      20             : /// widgets based on `Hook`s.
      21             : /// {@endtemplate}
      22             : abstract class BlocWidget<B extends BlocBase<S>, S extends Object>
      23             :     extends HookWidget {
      24             :   /// {@macro BlocWidget}
      25           4 :   const BlocWidget({
      26             :     Key? key,
      27             :     this.bloc,
      28           4 :   }) : super(key: key);
      29             : 
      30             :   /// `bloc` that has the state. If it's null, it will be infered from
      31             :   /// [BuildContext]
      32             :   final B? bloc;
      33             : 
      34             :   /// The `$use` method is a sugar syntax for the `useBloc`.
      35           4 :   @protected
      36          12 :   B $use() => useBloc<B, S>(bloc: bloc, onEmitted: onStateEmitted);
      37             : 
      38             :   /// The `onStateEmitted` method allows to customize the behavior
      39             :   /// of the implementation of the [BlocWidget]
      40             :   @protected
      41             :   bool onStateEmitted(BuildContext context, S previous, S state);
      42             : }
      43             : 
      44             : /// Subscribes to a Cubit and handles a listener or a rebuild.
      45             : ///
      46             : /// Whenever [BlocBase.state] updates, it will mark the caller [HookWidget]
      47             : /// as needing build if either `allowRebuild` is `true` or `buildWhen`
      48             : /// invocation returns `true`.
      49             : ///
      50             : /// if [bloc] is null, it will be inherited with `context.bloc()`
      51             : ///
      52             : /// The following example showcase a basic counter application.
      53             : ///
      54             : /// ```dart
      55             : /// class CounterCubit extends Cubit<int> {
      56             : ///   CounterCubit() : super(0);
      57             : ///
      58             : ///   void increment() => emit(state + 1);
      59             : /// }
      60             : ///
      61             : /// class Counter extends HookWidget {
      62             : ///   @override
      63             : ///   Widget build(BuildContext context) {
      64             : ///     // automatically triggers a rebuild of Counter widget
      65             : ///     final counterCubit = useBloc<CounterCubit, int>();
      66             : ///
      67             : ///     return GestureDetector(
      68             : ///       onTap: () => counterCubit.increment(),
      69             : ///       child: Text('${counter.state}'),
      70             : ///     );
      71             : ///   }
      72             : /// }
      73             : /// ```
      74             : ///
      75             : /// See also:
      76             : ///
      77             : ///  * [Cubit]
      78             : ///  * [Bloc]
      79             : ///  * [BlocBase]
      80           4 : B useBloc<B extends BlocBase<S>, S extends Object>({
      81             :   B? bloc,
      82             :   BlocHookListener<S>? onEmitted,
      83             : }) {
      84           4 :   final _bloc = bloc ?? useContext().read<B>();
      85           8 :   return use(_BlocHook<S>(_bloc, onEmitted)) as B;
      86             : }
      87             : 
      88             : class _BlocHook<S> extends Hook<BlocBase<S>> {
      89           4 :   const _BlocHook(this.bloc, this.onEmitted);
      90             : 
      91             :   final BlocBase<S> bloc;
      92             :   final BlocHookListener<S>? onEmitted;
      93             : 
      94           4 :   @override
      95           4 :   HookState<BlocBase<S>, _BlocHook<S>> createState() => _BlocHookState<S>();
      96             : }
      97             : 
      98             : class _BlocHookState<S> extends HookState<BlocBase<S>, _BlocHook<S>> {
      99             :   // ignore: cancel_subscriptions
     100             :   StreamSubscription<S>? _subscription;
     101             : 
     102             :   /// Previous state from cubit
     103             :   late S _previous;
     104             : 
     105           4 :   @override
     106           8 :   BlocBase<S> build(BuildContext context) => hook.bloc;
     107             : 
     108           4 :   @override
     109             :   void initHook() {
     110           4 :     super.initHook();
     111          16 :     _previous = hook.bloc.state;
     112           4 :     _subscribe();
     113             :   }
     114             : 
     115           3 :   @override
     116             :   void didUpdateHook(_BlocHook<S> oldHook) {
     117           3 :     super.didUpdateHook(oldHook);
     118           3 :     final oldCubit = oldHook.bloc;
     119           6 :     final currentCubit = hook.bloc;
     120           3 :     if (oldCubit != currentCubit) {
     121           2 :       if (_subscription != null) {
     122           2 :         _unsubscribe();
     123           8 :         _previous = hook.bloc.state;
     124             :       }
     125           2 :       _subscribe();
     126             :     }
     127             :   }
     128             : 
     129           4 :   @override
     130             :   void dispose() {
     131           4 :     _unsubscribe();
     132           4 :     super.dispose();
     133             :   }
     134             : 
     135           4 :   void _subscribe() {
     136          24 :     _subscription = hook.bloc.stream.listen((state) {
     137          20 :       if (hook.onEmitted?.call(context, _previous, state) ?? true) {
     138           4 :         setState(() {});
     139             :       }
     140           4 :       _previous = state;
     141             :     });
     142             :   }
     143             : 
     144           4 :   void _unsubscribe() {
     145           4 :     if (_subscription != null) {
     146           8 :       _subscription!.cancel();
     147           4 :       _subscription = null;
     148             :     }
     149             :   }
     150             : }

Generated by: LCOV version 1.15