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