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 : /// Signature for the `builder` function which takes the `BuildContext` and 6 : /// [state] and is responsible for returning a widget which is to be rendered. 7 : /// This is analogous to the `builder` function in [StreamBuilder]. 8 : typedef BlocWidgetBuilder<S> = Widget Function(BuildContext context, S state); 9 : 10 : /// Signature for the `buildWhen` function which takes the previous `state` and 11 : /// the current `state` and is responsible for returning a [bool] which 12 : /// determines whether to rebuild [BlocBuilder] with the current `state`. 13 : typedef BlocBuilderCondition<S> = bool Function(S previous, S current); 14 : 15 : /// {@template bloc_builder} 16 : /// [BlocBuilder] handles building a widget in response to new `states`. 17 : /// [BlocBuilder] is analogous to [StreamBuilder] but has simplified API to 18 : /// reduce the amount of boilerplate code needed as well as [bloc]-specific 19 : /// performance improvements. 20 : 21 : /// Please refer to [BlocListener] if you want to "do" anything in response to 22 : /// `state` changes such as navigation, showing a dialog, etc... 23 : /// 24 : /// If the [bloc] parameter is omitted, [BlocBuilder] will automatically 25 : /// perform a lookup using [BlocProvider] and the current [BuildContext]. 26 : /// 27 : /// ```dart 28 : /// BlocBuilder<BlocA, BlocAState>( 29 : /// builder: (context, state) { 30 : /// // return widget here based on BlocA's state 31 : /// } 32 : /// ) 33 : /// ``` 34 : /// 35 : /// Only specify the [bloc] if you wish to provide a [bloc] that is otherwise 36 : /// not accessible via [BlocProvider] and the current [BuildContext]. 37 : /// 38 : /// ```dart 39 : /// BlocBuilder<BlocA, BlocAState>( 40 : /// bloc: blocA, 41 : /// builder: (context, state) { 42 : /// // return widget here based on BlocA's state 43 : /// } 44 : /// ) 45 : /// ``` 46 : /// {@endtemplate} 47 : /// 48 : /// {@template bloc_builder_build_when} 49 : /// An optional [buildWhen] can be implemented for more granular control over 50 : /// how often [BlocBuilder] rebuilds. 51 : /// [buildWhen] should only be used for performance optimizations as it 52 : /// provides no security about the state passed to the [builder] function. 53 : /// [buildWhen] will be invoked on each [bloc] `state` change. 54 : /// [buildWhen] takes the previous `state` and current `state` and must 55 : /// return a [bool] which determines whether or not the [builder] function will 56 : /// be invoked. 57 : /// The previous `state` will be initialized to the `state` of the [bloc] when 58 : /// the [BlocBuilder] is initialized. 59 : /// [buildWhen] is optional and if omitted, it will default to `true`. 60 : /// 61 : /// ```dart 62 : /// BlocBuilder<BlocA, BlocAState>( 63 : /// buildWhen: (previous, current) { 64 : /// // return true/false to determine whether or not 65 : /// // to rebuild the widget with state 66 : /// }, 67 : /// builder: (context, state) { 68 : /// // return widget here based on BlocA's state 69 : /// } 70 : /// ) 71 : /// ``` 72 : /// {@endtemplate} 73 : class BlocBuilder<B extends BlocBase<S>, S> extends BlocBuilderBase<B, S> { 74 : /// {@macro bloc_builder} 75 : /// {@macro bloc_builder_build_when} 76 4 : const BlocBuilder({ 77 : Key? key, 78 : required this.builder, 79 : B? bloc, 80 : BlocBuilderCondition<S>? buildWhen, 81 4 : }) : super(key: key, bloc: bloc, buildWhen: buildWhen); 82 : 83 : /// The [builder] function which will be invoked on each widget build. 84 : /// The [builder] takes the `BuildContext` and current `state` and 85 : /// must return a widget. 86 : /// This is analogous to the [builder] function in [StreamBuilder]. 87 : final BlocWidgetBuilder<S> builder; 88 : 89 4 : @override 90 4 : Widget build(BuildContext context, S state) => builder(context, state); 91 : } 92 : 93 : /// {@template bloc_builder_base} 94 : /// Base class for widgets that build themselves based on interaction with 95 : /// a specified [bloc]. 96 : /// 97 : /// A [BlocBuilderBase] is stateful and maintains the state of the interaction 98 : /// so far. The type of the state and how it is updated with each interaction 99 : /// is defined by sub-classes. 100 : /// {@endtemplate} 101 : abstract class BlocBuilderBase<B extends BlocBase<S>, S> 102 : extends StatefulWidget { 103 : /// {@macro bloc_builder_base} 104 4 : const BlocBuilderBase({Key? key, this.bloc, this.buildWhen}) 105 4 : : super(key: key); 106 : 107 : /// The [bloc] that the [BlocBuilderBase] will interact with. 108 : /// If omitted, [BlocBuilderBase] will automatically perform a lookup using 109 : /// [BlocProvider] and the current `BuildContext`. 110 : final B? bloc; 111 : 112 : /// {@macro bloc_builder_build_when} 113 : final BlocBuilderCondition<S>? buildWhen; 114 : 115 : /// Returns a widget based on the `BuildContext` and current [state]. 116 : Widget build(BuildContext context, S state); 117 : 118 4 : @override 119 4 : State<BlocBuilderBase<B, S>> createState() => _BlocBuilderBaseState<B, S>(); 120 : } 121 : 122 : class _BlocBuilderBaseState<B extends BlocBase<S>, S> 123 : extends State<BlocBuilderBase<B, S>> { 124 : late B _bloc; 125 : late S _state; 126 : 127 4 : @override 128 : void initState() { 129 4 : super.initState(); 130 16 : _bloc = widget.bloc ?? context.read<B>(); 131 12 : _state = _bloc.state; 132 : } 133 : 134 2 : @override 135 : void didUpdateWidget(BlocBuilderBase<B, S> oldWidget) { 136 2 : super.didUpdateWidget(oldWidget); 137 4 : final oldBloc = oldWidget.bloc ?? context.read<B>(); 138 4 : final currentBloc = widget.bloc ?? oldBloc; 139 2 : if (oldBloc != currentBloc) { 140 2 : _bloc = currentBloc; 141 6 : _state = _bloc.state; 142 : } 143 : } 144 : 145 4 : @override 146 : void didChangeDependencies() { 147 4 : super.didChangeDependencies(); 148 12 : final bloc = widget.bloc ?? context.read<B>(); 149 8 : if (_bloc != bloc) { 150 1 : _bloc = bloc; 151 3 : _state = _bloc.state; 152 : } 153 : } 154 : 155 4 : @override 156 : Widget build(BuildContext context) { 157 10 : if (widget.bloc == null) context.select<B, int>(identityHashCode); 158 4 : return BlocListener<B, S>( 159 4 : bloc: _bloc, 160 8 : listenWhen: widget.buildWhen, 161 12 : listener: (context, state) => setState(() => _state = state), 162 12 : child: widget.build(context, _state), 163 : ); 164 : } 165 : }