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

Generated by: LCOV version 1.15