Line data Source code
1 : import 'package:flutter/foundation.dart'; 2 : import 'package:flutter/widgets.dart'; 3 : import 'package:flutter_hooks/flutter_hooks.dart'; 4 : 5 : import 'package:flutter_hooks_bloc/src/bloc_listener.dart'; 6 : import 'package:flutter_hooks_bloc/src/flutter_bloc.dart'; 7 : 8 : /// {@template multi_bloc_listener} 9 : /// Merges multiple [BlocListener] widgets into one widget tree. 10 : /// 11 : /// [MultiBlocListener] improves the readability and eliminates the need 12 : /// to nest multiple [BlocListener]s. 13 : /// 14 : /// By using [MultiBlocListener] we can go from: 15 : /// 16 : /// ```dart 17 : /// BlocListener<BlocA, BlocAState>( 18 : /// listener: (context, state) {}, 19 : /// child: BlocListener<BlocB, BlocBState>( 20 : /// listener: (context, state) {}, 21 : /// child: BlocListener<BlocC, BlocCState>( 22 : /// listener: (context, state) {}, 23 : /// child: ChildA(), 24 : /// ), 25 : /// ), 26 : /// ) 27 : /// ``` 28 : /// 29 : /// to: 30 : /// 31 : /// ```dart 32 : /// MultiBlocListener( 33 : /// listeners: [ 34 : /// BlocListener<BlocA, BlocAState>( 35 : /// listener: (context, state) {}, 36 : /// ), 37 : /// BlocListener<BlocB, BlocBState>( 38 : /// listener: (context, state) {}, 39 : /// ), 40 : /// BlocListener<BlocC, BlocCState>( 41 : /// listener: (context, state) {}, 42 : /// ), 43 : /// ], 44 : /// child: ChildA(), 45 : /// ) 46 : /// ``` 47 : /// 48 : /// [MultiBlocListener] avoids converts a tree of nested [BlocListener] and 49 : /// only add a widget to the widget tree. 50 : /// As a result, the only advantages of using [MultiBlocListener] are both the 51 : /// reduce of widgets in the widget tree and better readability due to the 52 : /// reduction in nesting and boilerplate. 53 : /// {@endtemplate} 54 : class MultiBlocListener extends HookWidget { 55 : /// {@macro multi_bloc_listener} 56 2 : MultiBlocListener({Key? key, required this.listeners, required this.child}) 57 3 : : assert(listeners.isNotEmpty), 58 3 : assert(listeners._debugBlocListenerWithNoChild()), 59 2 : super(key: key); 60 : 61 : /// List of [BlocListener] and/or [NestableBlocListener]. 62 : /// Must have at least one element 63 : final List<NestableBlocListener> listeners; 64 : 65 : /// `child` [Widget] for [MultiBlocListener] 66 : final Widget child; 67 : 68 1 : @override 69 : Widget build(BuildContext context) { 70 2 : for (final it in listeners) { 71 1 : it.listen(); 72 : } 73 1 : return child; 74 : } 75 : 76 1 : @override 77 : void debugFillProperties(DiagnosticPropertiesBuilder properties) { 78 1 : super.debugFillProperties(properties); 79 1 : properties.add( 80 3 : BlocListenerTree(listeners: listeners).toDiagnosticsNode( 81 : name: 'listeners', 82 : style: DiagnosticsTreeStyle.dense, 83 : ), 84 : ); 85 : } 86 : 87 1 : @override 88 : List<DiagnosticsNode> debugDescribeChildren() => 89 3 : [for (final listener in listeners) listener.asDiagnosticsNode()]; 90 : } 91 : 92 : /// The [NestableBlocListener] is the base for every item in a 93 : /// [MultiBlocProvider]. Thus, the bloc listener is not limited to be a 94 : /// [BlocListener], but can another type. 95 : /// 96 : /// see also 97 : /// - [MultiBlocListener] 98 : abstract class NestableBlocListener { 99 : /// The `listen` method describe how a widget must listen a [BlocBase] 100 : void listen(); 101 : 102 : /// Given that the [NestableBlocListener] are used in a [MultiBlocProvider] 103 : /// The must not have a child each one. Then, the `hasNoChild` allows 104 : /// the [MultiBlocProvider] to check that they don't have them. 105 : bool get hasNoChild; 106 : 107 : /// Generates a [DiagnosticsNode] for show the `bloc` states in the debugger. 108 : DiagnosticsNode asDiagnosticsNode(); 109 : } 110 : 111 : extension _DebugBlocListenerWithNoChildX on List<NestableBlocListener> { 112 8 : bool _debugBlocListenerWithNoChild() => every((it) => it.hasNoChild); 113 : } 114 : 115 : /// {@template bloc_listener_tree} 116 : /// The [BlocListenerTree] is a [DiagnosticableTree] for showwing 117 : /// the `bloc` providers in the devtools. 118 : /// {@endtemplate} 119 : @visibleForTesting 120 : class BlocListenerTree extends DiagnosticableTree { 121 : /// {@macro bloc_listener_tree} 122 1 : const BlocListenerTree({required this.listeners}); 123 : 124 : /// List of [NestableBlocListener] ([BlocListener] for example) that 125 : /// will be used in the [MultiBlocListener]. 126 : final List<NestableBlocListener> listeners; 127 : 128 1 : @override 129 : List<DiagnosticsNode> debugDescribeChildren() => 130 3 : [for (final listener in listeners) listener.asDiagnosticsNode()]; 131 : }