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