Line data Source code
1 : import 'package:flutter/widgets.dart'; 2 : import 'package:flutter_bloc/flutter_bloc.dart'; 3 : 4 : /// Signature for the `selector` function which 5 : /// is responsible for returning a selected value, [T], based on [state]. 6 : typedef BlocWidgetSelector<S, T> = T Function(S state); 7 : 8 : /// {@template bloc_selector} 9 : /// [BlocSelector] is analogous to [BlocBuilder] but allows developers to 10 : /// filter updates by selecting a new value based on the bloc state. 11 : /// Unnecessary builds are prevented if the selected value does not change. 12 : /// 13 : /// **Note**: the selected value must be immutable in order for [BlocSelector] 14 : /// to accurately determine whether [builder] should be called again. 15 : /// 16 : /// ```dart 17 : /// BlocSelector<BlocA, BlocAState, SelectedState>( 18 : /// selector: (state) { 19 : /// // return selected state based on the provided state. 20 : /// }, 21 : /// builder: (context, state) { 22 : /// // return widget here based on the selected state. 23 : /// }, 24 : /// ) 25 : /// ``` 26 : /// {@endtemplate} 27 : class BlocSelector<B extends BlocBase<S>, S, T> extends StatefulWidget { 28 : /// {@macro bloc_selector} 29 1 : const BlocSelector({ 30 : Key? key, 31 : required this.selector, 32 : required this.builder, 33 : this.bloc, 34 1 : }) : super(key: key); 35 : 36 : /// The [bloc] that the [BlocSelector] will interact with. 37 : /// If omitted, [BlocSelector] will automatically perform a lookup using 38 : /// [BlocProvider] and the current [BuildContext]. 39 : final B? bloc; 40 : 41 : /// The [builder] function which will be invoked 42 : /// when the selected state changes. 43 : /// The [builder] takes the [BuildContext] and selected `state` and 44 : /// must return a widget. 45 : /// This is analogous to the [builder] function in [BlocBuilder]. 46 : final BlocWidgetBuilder<T> builder; 47 : 48 : /// The [selector] function which will be invoked on each widget build 49 : /// and is responsible for returning a selected value of type [T] based on 50 : /// the current state. 51 : final BlocWidgetSelector<S, T> selector; 52 : 53 1 : @override 54 1 : State<BlocSelector<B, S, T>> createState() => _BlocSelectorState<B, S, T>(); 55 : } 56 : 57 : class _BlocSelectorState<B extends BlocBase<S>, S, T> 58 : extends State<BlocSelector<B, S, T>> { 59 : late B _bloc; 60 : late T _state; 61 : 62 1 : @override 63 : void initState() { 64 1 : super.initState(); 65 5 : _bloc = widget.bloc ?? context.read<B>(); 66 5 : _state = widget.selector(_bloc.state); 67 : } 68 : 69 1 : @override 70 : void didUpdateWidget(BlocSelector<B, S, T> oldWidget) { 71 1 : super.didUpdateWidget(oldWidget); 72 3 : final oldBloc = oldWidget.bloc ?? context.read<B>(); 73 2 : final currentBloc = widget.bloc ?? oldBloc; 74 1 : if (oldBloc != currentBloc) { 75 1 : _bloc = currentBloc; 76 5 : _state = widget.selector(_bloc.state); 77 : } 78 : } 79 : 80 1 : @override 81 : void didChangeDependencies() { 82 1 : super.didChangeDependencies(); 83 4 : final bloc = widget.bloc ?? context.read<B>(); 84 2 : if (_bloc != bloc) { 85 1 : _bloc = bloc; 86 5 : _state = widget.selector(_bloc.state); 87 : } 88 : } 89 : 90 1 : @override 91 : Widget build(BuildContext context) { 92 3 : if (widget.bloc == null) context.select<B, int>(identityHashCode); 93 1 : return BlocListener<B, S>( 94 1 : bloc: _bloc, 95 1 : listener: (context, state) { 96 2 : final selectedState = widget.selector(state); 97 5 : if (_state != selectedState) setState(() => _state = selectedState); 98 : }, 99 3 : child: widget.builder(context, _state), 100 : ); 101 : } 102 : }