Line data Source code
1 : import 'package:flutter/widgets.dart'; 2 : import 'package:flutter_bloc/flutter_bloc.dart'; 3 : import 'package:provider/provider.dart'; 4 : 5 : /// {@template bloc_consumer} 6 : /// [BlocConsumer] exposes a [builder] and [listener] in order react to new 7 : /// states. 8 : /// [BlocConsumer] is analogous to a nested `BlocListener` 9 : /// and `BlocBuilder` but reduces the amount of boilerplate needed. 10 : /// [BlocConsumer] should only be used when it is necessary to both rebuild UI 11 : /// and execute other reactions to state changes in the [bloc]. 12 : /// 13 : /// [BlocConsumer] takes a required `BlocWidgetBuilder` 14 : /// and `BlocWidgetListener` and an optional [bloc], 15 : /// `BlocBuilderCondition`, and `BlocListenerCondition`. 16 : /// 17 : /// If the [bloc] parameter is omitted, [BlocConsumer] will automatically 18 : /// perform a lookup using `BlocProvider` and the current `BuildContext`. 19 : /// 20 : /// ```dart 21 : /// BlocConsumer<BlocA, BlocAState>( 22 : /// listener: (context, state) { 23 : /// // do stuff here based on BlocA's state 24 : /// }, 25 : /// builder: (context, state) { 26 : /// // return widget here based on BlocA's state 27 : /// } 28 : /// ) 29 : /// ``` 30 : /// 31 : /// An optional [listenWhen] and [buildWhen] can be implemented for more 32 : /// granular control over when [listener] and [builder] are called. 33 : /// The [listenWhen] and [buildWhen] will be invoked on each [bloc] `state` 34 : /// change. 35 : /// They each take the previous `state` and current `state` and must return 36 : /// a [bool] which determines whether or not the [builder] and/or [listener] 37 : /// function will be invoked. 38 : /// The previous `state` will be initialized to the `state` of the [bloc] when 39 : /// the [BlocConsumer] is initialized. 40 : /// [listenWhen] and [buildWhen] are optional and if they aren't implemented, 41 : /// they will default to `true`. 42 : /// 43 : /// ```dart 44 : /// BlocConsumer<BlocA, BlocAState>( 45 : /// listenWhen: (previous, current) { 46 : /// // return true/false to determine whether or not 47 : /// // to invoke listener with state 48 : /// }, 49 : /// listener: (context, state) { 50 : /// // do stuff here based on BlocA's state 51 : /// }, 52 : /// buildWhen: (previous, current) { 53 : /// // return true/false to determine whether or not 54 : /// // to rebuild the widget with state 55 : /// }, 56 : /// builder: (context, state) { 57 : /// // return widget here based on BlocA's state 58 : /// } 59 : /// ) 60 : /// ``` 61 : /// {@endtemplate} 62 : class BlocConsumer<B extends BlocBase<S>, S> extends StatefulWidget { 63 : /// {@macro bloc_consumer} 64 1 : const BlocConsumer({ 65 : Key? key, 66 : required this.builder, 67 : required this.listener, 68 : this.bloc, 69 : this.buildWhen, 70 : this.listenWhen, 71 1 : }) : super(key: key); 72 : 73 : /// The [bloc] that the [BlocConsumer] will interact with. 74 : /// If omitted, [BlocConsumer] will automatically perform a lookup using 75 : /// `BlocProvider` and the current `BuildContext`. 76 : final B? bloc; 77 : 78 : /// The [builder] function which will be invoked on each widget build. 79 : /// The [builder] takes the `BuildContext` and current `state` and 80 : /// must return a widget. 81 : /// This is analogous to the [builder] function in [StreamBuilder]. 82 : final BlocWidgetBuilder<S> builder; 83 : 84 : /// Takes the `BuildContext` along with the [bloc] `state` 85 : /// and is responsible for executing in response to `state` changes. 86 : final BlocWidgetListener<S> listener; 87 : 88 : /// Takes the previous `state` and the current `state` and is responsible for 89 : /// returning a [bool] which determines whether or not to trigger 90 : /// [builder] with the current `state`. 91 : final BlocBuilderCondition<S>? buildWhen; 92 : 93 : /// Takes the previous `state` and the current `state` and is responsible for 94 : /// returning a [bool] which determines whether or not to call [listener] of 95 : /// [BlocConsumer] with the current `state`. 96 : final BlocListenerCondition<S>? listenWhen; 97 : 98 1 : @override 99 1 : State<BlocConsumer<B, S>> createState() => _BlocConsumerState<B, S>(); 100 : } 101 : 102 : class _BlocConsumerState<B extends BlocBase<S>, S> 103 : extends State<BlocConsumer<B, S>> { 104 : late B _bloc; 105 : 106 1 : @override 107 : void initState() { 108 1 : super.initState(); 109 5 : _bloc = widget.bloc ?? context.read<B>(); 110 : } 111 : 112 1 : @override 113 : void didUpdateWidget(BlocConsumer<B, S> oldWidget) { 114 1 : super.didUpdateWidget(oldWidget); 115 3 : final oldBloc = oldWidget.bloc ?? context.read<B>(); 116 2 : final currentBloc = widget.bloc ?? oldBloc; 117 2 : if (oldBloc != currentBloc) _bloc = currentBloc; 118 : } 119 : 120 1 : @override 121 : void didChangeDependencies() { 122 1 : super.didChangeDependencies(); 123 4 : final bloc = widget.bloc ?? context.read<B>(); 124 3 : if (_bloc != bloc) _bloc = bloc; 125 : } 126 : 127 1 : @override 128 : Widget build(BuildContext context) { 129 3 : if (widget.bloc == null) context.select<B, int>(identityHashCode); 130 1 : return BlocBuilder<B, S>( 131 1 : bloc: _bloc, 132 2 : builder: widget.builder, 133 1 : buildWhen: (previous, current) { 134 2 : if (widget.listenWhen?.call(previous, current) ?? true) { 135 2 : widget.listener(context, current); 136 : } 137 2 : return widget.buildWhen?.call(previous, current) ?? true; 138 : }, 139 : ); 140 : } 141 : }