Line data Source code
1 : import 'package:flutter/widgets.dart' 2 : show Widget, BuildContext, StatefulWidget, State, WidgetsBinding, Key; 3 : 4 : import 'package:meedu/meedu.dart'; 5 : 6 : import '../../utils/ambiguate.dart'; 7 : import '../widgets/watch_filter.dart'; 8 : 9 : /// a typedef for a common callback 10 : typedef _ProviderListenerCallback<T> = void Function( 11 : BuildContext _, 12 : T notifier, 13 : ); 14 : 15 : typedef _Builder<T> = Widget Function(BuildContext, T); 16 : 17 : /// A widget to listen events in a SimpleProvider or a StateProvider 18 : /// 19 : /// THis widget only listen the events, does not update the widget when a SimpleNotifier or a StateNotifier emit a new event 20 : class ProviderListener<T extends BaseNotifier> extends StatefulWidget { 21 : // ignore: public_member_api_docs 22 3 : const ProviderListener({ 23 : Key? key, 24 : this.onChange, 25 : required this.provider, 26 : required this.builder, 27 : this.onInitState, 28 : this.onDispose, 29 : this.onAfterFirstLayout, 30 3 : }) : super(key: key); 31 : 32 : /// callback that must return the widget child of the ProviderListener 33 : final _Builder<T> builder; 34 : 35 : /// provider to listen the changes 36 : final ListeneableProvider<T> provider; 37 : 38 : /// callback to listen the new events 39 : final _ProviderListenerCallback<T>? onChange; 40 : 41 : /// callback when initState is called 42 : final _ProviderListenerCallback<T>? onInitState; 43 : 44 : /// this callback will be called when the first frame was rendered 45 : /// use this callback if you want to show a dialog, snackbar or navigate 46 : /// after the first frame 47 : final _ProviderListenerCallback<T>? onAfterFirstLayout; 48 : 49 : /// callback when dispose is called 50 : final _ProviderListenerCallback<T>? onDispose; 51 : 52 3 : @override 53 3 : _ProviderListenerState createState() => _ProviderListenerState<T>(); 54 : } 55 : 56 : class _ProviderListenerState<T extends BaseNotifier> 57 : extends State<ProviderListener<T>> { 58 : /// the notifier attached to widget.provider 59 : late T _notifier; 60 : Target? _target; 61 : void Function(dynamic)? _listener; 62 : 63 3 : ListeneableNotifier get _listeneableNotifier => 64 3 : _notifier as ListeneableNotifier; 65 : 66 3 : @override 67 : void initState() { 68 3 : super.initState(); 69 : 70 16 : _target = widget.provider is Target ? widget.provider as Target : null; 71 : 72 3 : if (_target != null) { 73 : /// read and save the provider 74 6 : _notifier = _target!.notifier; 75 : } else { 76 4 : _notifier = (widget.provider as BaseProvider).read; 77 : } 78 : 79 : /// check if the onChange callback is defined 80 9 : if (widget.onChange != null && _target == null) { 81 : // add a listener for the current notifier 82 2 : _listener = _defaultListener; 83 3 : (_notifier as ListeneableNotifier).addListener(_listener!); 84 6 : } else if (widget.onChange != null && _target != null) { 85 2 : _buildTargetListener(); 86 : } 87 : 88 : // check if the onInitState callback needs to be called 89 6 : if (widget.onInitState != null) { 90 5 : widget.onInitState!(context, _notifier); 91 : } 92 : 93 : // check if the onAfterFirstLayout callback needs to be called 94 6 : if (widget.onAfterFirstLayout != null) { 95 : // wait after first frame 96 5 : ambiguate(WidgetsBinding.instance)?.endOfFrame.then((_) { 97 1 : if (mounted) { 98 5 : widget.onAfterFirstLayout!(context, _notifier); 99 : } 100 : }); 101 : } 102 : } 103 : 104 : /// listen when the widget is disposed 105 : /// and remove the listeners 106 3 : @override 107 : void dispose() { 108 9 : if (widget.onChange != null && _listener != null) { 109 9 : _listeneableNotifier.removeListener(_listener!); 110 : } 111 : 112 : // check if the onDispose callback 113 : // needs to be called 114 6 : if (widget.onDispose != null) { 115 3 : widget.onDispose!( 116 1 : context, 117 1 : _notifier, 118 : ); 119 : } 120 3 : super.dispose(); 121 : } 122 : 123 : /// listen when the widget is updated 124 : /// due to the properties has changes or for hot reaload 125 1 : @override 126 : void didUpdateWidget(covariant ProviderListener<T> oldWidget) { 127 1 : if (_listener != null) { 128 3 : if (oldWidget.onChange == null && widget.onChange != null) { 129 3 : _listeneableNotifier.addListener(_listener!); 130 3 : } else if (oldWidget.onChange != null && widget.onChange == null) { 131 3 : _listeneableNotifier.removeListener(_listener!); 132 : } 133 : } 134 1 : super.didUpdateWidget(oldWidget); 135 : } 136 : 137 : /// listen all notify events of one notifier 138 : /// and call to onChange callback 139 3 : void _defaultListener(_) { 140 6 : if (widget.onChange != null) { 141 : // before call onChange we need to check 142 : // if the widget is mounted 143 3 : if (mounted) { 144 15 : widget.onChange!(context, _notifier); 145 : } 146 : } 147 : } 148 : 149 2 : void _buildTargetListener() { 150 2 : assert(_target != null); 151 8 : _target!.rebuild = () => _defaultListener(null); 152 : 153 4 : if (_notifier is SimpleNotifier) { 154 3 : if (_target!.filter == Filter.select) { 155 2 : createSimpleSelectListener(_target!); 156 : } 157 : } else { 158 3 : if (_target!.filter == Filter.select) { 159 2 : createStateSelectListener(_target!); 160 : } else { 161 2 : createWhenListener(_target!); 162 : } 163 : } 164 6 : _listener = _target!.listener; 165 6 : _listeneableNotifier.addListener(_listener!); 166 : } 167 : 168 3 : @override 169 : Widget build(BuildContext context) { 170 12 : return widget.builder(context, _notifier); 171 : } 172 : }