|
| 1 | +import 'package:bot_toast/src/toast_widget/toast_widget.dart'; |
1 | 2 | import 'package:flutter/material.dart';
|
| 3 | +import '../bot_toast.dart'; |
| 4 | +import 'bot_toast_init.dart'; |
| 5 | +import 'toast.dart'; |
2 | 6 |
|
3 |
| -class BotToastManager extends StatefulWidget { |
4 |
| - const BotToastManager({Key key}) : super(key: key); |
5 | 7 |
|
6 |
| - @override |
7 |
| - BotToastManagerState createState() => BotToastManagerState(); |
8 |
| -} |
| 8 | +class BotToastManager { |
| 9 | + final Map<String, Map<UniqueKey, OverlayEntry>> _map = {}; |
| 10 | + NavigatorState _navigatorState; |
| 11 | + BotToastNavigatorObserverProxy _observerProxy; |
| 12 | + BotToastInitState botToastInitState; |
| 13 | + |
| 14 | + BotToastManager(this.botToastInitState); |
9 | 15 |
|
10 |
| -class BotToastManagerState extends State<BotToastManager> { |
11 |
| - final Map<String, Map<UniqueKey, IndexedSemantics>> _map = {}; |
12 |
| - int index = 0; |
13 | 16 |
|
14 |
| - @override |
15 |
| - Widget build(BuildContext context) { |
16 |
| - List<IndexedSemantics> children = _map.values.fold([], (value, items) { |
17 |
| - return value..addAll(items.values); |
| 17 | + void dispose() { |
| 18 | + if (_observerProxy != null) { |
| 19 | + BotToastNavigatorObserver.unregister(_observerProxy); |
| 20 | + } |
| 21 | + _children.forEach((item) { |
| 22 | + item.remove(); |
18 | 23 | });
|
19 |
| - children.sort((a, b) => a.index.compareTo(b.index)); |
20 |
| - return Stack(children: children); |
| 24 | + _children.clear(); |
21 | 25 | }
|
22 | 26 |
|
23 |
| - ///这里不使用传进来的key,是因为该key在这里担当的是一个UUid的职责, |
24 |
| - ///不应该用在widget里面, |
25 |
| - ///那为什么有需要自己new 一个UniqueKey() |
26 |
| - ///这表明这个Widget在这里面是独一无二的, |
27 |
| - ///这会防止一些隐晦的bug, |
28 |
| - ///例如:如果不加入该UniqueKey将会导致 |
29 |
| - ///[BotToast.showEnhancedWidget]onlyOne功能失效, |
30 |
| - ///主要是因为flutter,只是根据runtimeType来进行判断的话 |
31 |
| - ///会导致调错了各个Toast的dispose |
32 |
| - void insert(String groupKey, UniqueKey key, Widget widget) { |
33 |
| - setState(() { |
34 |
| - _map[groupKey] ??= {}; |
35 |
| - _map[groupKey][key] = IndexedSemantics( |
36 |
| - index: index++, |
37 |
| - child: widget, |
38 |
| - key: UniqueKey(), |
| 27 | + |
| 28 | + ///需要监听didPush是因为,当Navigator的Route集合为空再推一个Route会导致这个页面覆盖_BotToastManager上面,挡住了Toast,因此要手动移动到最后 |
| 29 | + ///在1.7.8版本以下,一般是在使用[Navigator.pushAndRemoveUntil]才可能发生这种情况 |
| 30 | + void _checkNavigatorState(BuildContext context) { |
| 31 | + assert(BotToastNavigatorObserver.debugInitialization, """ |
| 32 | + Please initialize properly! |
| 33 | + Example: |
| 34 | + BotToastInit( |
| 35 | + child: MaterialApp( |
| 36 | + title: 'BotToast Demo', |
| 37 | + navigatorObservers: [BotToastNavigatorObserver()], |
| 38 | + home: XxxPage() |
| 39 | + ), |
| 40 | + ); |
| 41 | + """); |
| 42 | + |
| 43 | + void visitor(Element element) { |
| 44 | + assert(() { |
| 45 | + if (element.widget is Localizations && |
| 46 | + ((element as StatefulElement).state as dynamic).locale == null) { |
| 47 | + return false; |
| 48 | + } |
| 49 | + return true; |
| 50 | + }(), 'Initialization error : locale==null'); |
| 51 | + if (element.widget is Navigator) { |
| 52 | + if (_navigatorState == null || |
| 53 | + _navigatorState != (element as StatefulElement).state) { |
| 54 | + _navigatorState = (element as StatefulElement).state; |
| 55 | + } |
| 56 | + } else { |
| 57 | + element.visitChildElements(visitor); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + context.visitChildElements(visitor); |
| 62 | + |
| 63 | + assert(_navigatorState != null, ''' |
| 64 | + Initialization error. |
| 65 | + The initialization method has been modified in version 2.0. |
| 66 | + do you wrapped you app widget like this? |
| 67 | + |
| 68 | + BotToastInit( |
| 69 | + child: MaterialApp( |
| 70 | + navigatorObservers: [BotToastNavigatorObserver()], |
| 71 | + home: XxxPage(), |
| 72 | + ), |
| 73 | + ); |
| 74 | + '''); |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | + if (_observerProxy == null) { |
| 79 | + _observerProxy = BotToastNavigatorObserverProxy( |
| 80 | + didPush: (route, _) { |
| 81 | + if (route.isFirst && _children.isNotEmpty) { |
| 82 | + _navigatorState.overlay |
| 83 | + .rearrange(_children, below: _children.first); |
| 84 | + } |
| 85 | + }, |
39 | 86 | );
|
| 87 | + BotToastNavigatorObserver.register(_observerProxy); |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + List<OverlayEntry> get _children => |
| 92 | + _map.values.fold([], (value, items) { |
| 93 | + return value..addAll(items.values); |
| 94 | + }); |
| 95 | + |
| 96 | + |
| 97 | + void insert(String groupKey, UniqueKey key, Widget widget) { |
| 98 | + _map[groupKey] ??= {}; |
| 99 | + final uniqueKey = UniqueKey(); |
| 100 | + final overlayEntry = OverlayEntry(builder: (_) => |
| 101 | + ProxyDispose(key: uniqueKey, child: widget, disposeCallback: () { |
| 102 | + _map[groupKey]?.remove(key); |
| 103 | + },)); |
| 104 | + _map[groupKey][key] = overlayEntry; |
| 105 | + safeRun(() { |
| 106 | + assert(botToastInitState != null); |
| 107 | + if (botToastInitState.needInit) { |
| 108 | + botToastInitState.reset(); |
| 109 | + _checkNavigatorState(botToastInitState.context); |
| 110 | + } |
| 111 | + _navigatorState.overlay.insert(overlayEntry); |
40 | 112 | });
|
41 | 113 | }
|
42 | 114 |
|
43 | 115 | void remove(String groupKey, UniqueKey key) {
|
44 |
| - setState(() { |
45 |
| - _map[groupKey]?.remove(key); |
| 116 | + safeRun(() { |
| 117 | + final result = _map[groupKey]?.remove(key); |
| 118 | + if (result != null) { |
| 119 | + result.remove(); |
| 120 | + } |
46 | 121 | });
|
47 | 122 | }
|
48 | 123 |
|
49 | 124 | void removeAll(String groupKey) {
|
50 |
| - setState(() { |
| 125 | + safeRun(() { |
| 126 | + _map[groupKey]?.forEach((key, value) { |
| 127 | + assert(value != null); |
| 128 | + value.remove(); |
| 129 | + }); |
51 | 130 | _map[groupKey]?.clear();
|
52 | 131 | });
|
53 | 132 | }
|
54 | 133 |
|
55 | 134 | void cleanAll() {
|
56 |
| - setState(() { |
| 135 | + safeRun(() { |
| 136 | + List<OverlayEntry> children = _children; |
| 137 | + assert(children.every((test) => test != null)); |
| 138 | + children.forEach((item) { |
| 139 | + item.remove(); |
| 140 | + }); |
57 | 141 | _map.clear();
|
58 | 142 | });
|
59 | 143 | }
|
60 | 144 | }
|
| 145 | + |
| 146 | + |
0 commit comments