Showing
5 changed files
with
59 additions
and
151 deletions
1 | import 'package:flutter/material.dart'; | 1 | import 'package:flutter/material.dart'; |
2 | import 'package:get/get.dart'; | 2 | import 'package:get/get.dart'; |
3 | 3 | ||
4 | -// void main() { | ||
5 | -// runApp(const MyApp()); | ||
6 | -// } | ||
7 | - | ||
8 | -// class MyApp extends StatelessWidget { | ||
9 | -// const MyApp({Key? key}) : super(key: key); | ||
10 | - | ||
11 | -// @override | ||
12 | -// Widget build(BuildContext context) { | ||
13 | -// return GetMaterialApp( | ||
14 | -// theme: ThemeData(useMaterial3: true), | ||
15 | -// debugShowCheckedModeBanner: false, | ||
16 | -// enableLog: true, | ||
17 | -// logWriterCallback: Logger.write, | ||
18 | -// initialRoute: AppPages.INITIAL, | ||
19 | -// getPages: AppPages.routes, | ||
20 | -// locale: TranslationService.locale, | ||
21 | -// fallbackLocale: TranslationService.fallbackLocale, | ||
22 | -// translations: TranslationService(), | ||
23 | -// ); | ||
24 | -// } | ||
25 | -// } | ||
26 | - | ||
27 | -/// Nav 2 snippet | ||
28 | void main() { | 4 | void main() { |
29 | - runApp(const MyApp()); | 5 | + runApp(MyApp()); |
30 | } | 6 | } |
31 | 7 | ||
32 | class MyApp extends StatelessWidget { | 8 | class MyApp extends StatelessWidget { |
33 | - const MyApp({Key? key}) : super(key: key); | ||
34 | - | 9 | +// This widget is the root of your application. |
35 | @override | 10 | @override |
36 | Widget build(BuildContext context) { | 11 | Widget build(BuildContext context) { |
37 | return GetMaterialApp( | 12 | return GetMaterialApp( |
38 | - getPages: [ | ||
39 | - GetPage( | ||
40 | - participatesInRootNavigator: true, | ||
41 | - name: '/first', | ||
42 | - page: () => const First()), | ||
43 | - GetPage( | ||
44 | - name: '/second', | ||
45 | - page: () => const Second(), | ||
46 | - transition: Transition.downToUp, | 13 | + title: 'Scaffold demo', |
14 | + theme: ThemeData( | ||
15 | + primarySwatch: Colors.blue, | ||
47 | ), | 16 | ), |
48 | - GetPage( | ||
49 | - name: '/third', | ||
50 | - page: () => const Third(), | ||
51 | - ), | ||
52 | - ], | 17 | + home: MyHomePage(), |
53 | debugShowCheckedModeBanner: false, | 18 | debugShowCheckedModeBanner: false, |
54 | ); | 19 | ); |
55 | } | 20 | } |
56 | } | 21 | } |
57 | 22 | ||
58 | -class FirstController extends GetxController { | ||
59 | - @override | ||
60 | - void onClose() { | ||
61 | - print('on close first'); | ||
62 | - super.onClose(); | ||
63 | - } | ||
64 | -} | ||
65 | - | ||
66 | -class First extends StatelessWidget { | ||
67 | - const First({Key? key}) : super(key: key); | ||
68 | - | 23 | +class MyHomePage extends StatelessWidget { |
69 | @override | 24 | @override |
70 | Widget build(BuildContext context) { | 25 | Widget build(BuildContext context) { |
71 | - print('First rebuild'); | ||
72 | - Get.put(FirstController()); | ||
73 | return Scaffold( | 26 | return Scaffold( |
74 | appBar: AppBar( | 27 | appBar: AppBar( |
75 | - title: const Text('page one'), | ||
76 | - leading: IconButton( | ||
77 | - icon: const Icon(Icons.more), | ||
78 | - onPressed: () { | ||
79 | - Get.snackbar( | ||
80 | - 'title', | ||
81 | - "message", | ||
82 | - mainButton: | ||
83 | - TextButton(onPressed: () {}, child: const Text('button')), | ||
84 | - isDismissible: false, | ||
85 | - ); | ||
86 | - // print('THEME CHANGED'); | ||
87 | - // Get.changeTheme( | ||
88 | - // Get.isDarkMode ? ThemeData.light() : ThemeData.dark()); | ||
89 | - }, | 28 | + title: Text('Test'), |
29 | + centerTitle: true, | ||
30 | + backgroundColor: Colors.green, | ||
90 | ), | 31 | ), |
91 | - ), | ||
92 | - body: Center( | ||
93 | - child: SizedBox( | ||
94 | - height: 300, | ||
95 | - width: 300, | 32 | + bottomNavigationBar: SizedBox( |
33 | + width: double.infinity, | ||
96 | child: ElevatedButton( | 34 | child: ElevatedButton( |
35 | + child: Text('Tap me when Snackbar appears'), | ||
97 | onPressed: () { | 36 | onPressed: () { |
98 | - Get.toNamed('/second?id=123'); | 37 | + print('This should clicked'); |
99 | }, | 38 | }, |
100 | - child: const Text('next screen'), | ||
101 | ), | 39 | ), |
102 | ), | 40 | ), |
103 | - ), | ||
104 | - ); | ||
105 | - } | ||
106 | -} | ||
107 | - | ||
108 | -class SecondController extends GetxController { | ||
109 | - final textEdit = TextEditingController(); | ||
110 | - @override | ||
111 | - void onClose() { | ||
112 | - print('on close second'); | ||
113 | - textEdit.dispose(); | ||
114 | - super.onClose(); | ||
115 | - } | ||
116 | -} | ||
117 | - | ||
118 | -class Second extends StatelessWidget { | ||
119 | - const Second({Key? key}) : super(key: key); | ||
120 | - | ||
121 | - @override | ||
122 | - Widget build(BuildContext context) { | ||
123 | - final controller = Get.put(SecondController()); | ||
124 | - print('second rebuild'); | ||
125 | - return Scaffold( | ||
126 | - appBar: AppBar( | ||
127 | - title: Text('page two ${Get.parameters["id"]}'), | ||
128 | - ), | ||
129 | body: Center( | 41 | body: Center( |
130 | - child: Column( | ||
131 | - children: [ | ||
132 | - Expanded( | ||
133 | - child: TextField( | ||
134 | - controller: controller.textEdit, | ||
135 | - )), | ||
136 | - SizedBox( | ||
137 | - height: 300, | ||
138 | - width: 300, | ||
139 | child: ElevatedButton( | 42 | child: ElevatedButton( |
140 | - onPressed: () {}, | ||
141 | - child: const Text('next screen'), | ||
142 | - ), | ||
143 | - ), | ||
144 | - ], | ||
145 | - ), | ||
146 | - ), | 43 | + child: Text('Open Snackbar'), |
44 | + onPressed: () { | ||
45 | + Get.snackbar( | ||
46 | + "Snackbar Showed", | ||
47 | + "Please click the button on BottomNavigationBar", | ||
48 | + icon: Icon(Icons.check, color: Colors.green), | ||
49 | + backgroundColor: Colors.white, | ||
50 | + snackStyle: SnackStyle.floating, | ||
51 | + borderRadius: 20, | ||
52 | + isDismissible: false, | ||
53 | + snackPosition: SnackPosition.bottom, | ||
54 | + margin: EdgeInsets.fromLTRB(50, 15, 50, 15), | ||
147 | ); | 55 | ); |
148 | - } | ||
149 | -} | ||
150 | - | ||
151 | -class Third extends StatelessWidget { | ||
152 | - const Third({Key? key}) : super(key: key); | ||
153 | - | ||
154 | - @override | ||
155 | - Widget build(BuildContext context) { | ||
156 | - return Scaffold( | ||
157 | - backgroundColor: Colors.red, | ||
158 | - appBar: AppBar( | ||
159 | - title: const Text('page three'), | ||
160 | - ), | ||
161 | - body: Center( | ||
162 | - child: SizedBox( | ||
163 | - height: 300, | ||
164 | - width: 300, | ||
165 | - child: ElevatedButton( | ||
166 | - onPressed: () {}, | ||
167 | - child: const Text('go to first screen'), | ||
168 | - ), | 56 | + }, |
169 | ), | 57 | ), |
170 | ), | 58 | ), |
171 | ); | 59 | ); |
@@ -341,13 +341,12 @@ class GetRootState extends State<GetRoot> with WidgetsBindingObserver { | @@ -341,13 +341,12 @@ class GetRootState extends State<GetRoot> with WidgetsBindingObserver { | ||
341 | void onClose() { | 341 | void onClose() { |
342 | config.onDispose?.call(); | 342 | config.onDispose?.call(); |
343 | Get.clearTranslations(); | 343 | Get.clearTranslations(); |
344 | + config.snackBarQueue.disposeControllers(); | ||
344 | RouterReportManager.instance.clearRouteKeys(); | 345 | RouterReportManager.instance.clearRouteKeys(); |
345 | RouterReportManager.dispose(); | 346 | RouterReportManager.dispose(); |
346 | Get.resetInstance(clearRouteBindings: true); | 347 | Get.resetInstance(clearRouteBindings: true); |
347 | _controller = null; | 348 | _controller = null; |
348 | ambiguate(Engine.instance)!.removeObserver(this); | 349 | ambiguate(Engine.instance)!.removeObserver(this); |
349 | - config.snackBarQueue.cancelAllJobs(); | ||
350 | - config.snackBarQueue.disposeControllers(); | ||
351 | } | 350 | } |
352 | 351 | ||
353 | @override | 352 | @override |
@@ -19,6 +19,13 @@ class GetSnackBar extends StatefulWidget { | @@ -19,6 +19,13 @@ class GetSnackBar extends StatefulWidget { | ||
19 | /// The title displayed to the user | 19 | /// The title displayed to the user |
20 | final String? title; | 20 | final String? title; |
21 | 21 | ||
22 | + /// Defines how the snack bar area, including margin, will behave during hit testing. | ||
23 | + /// | ||
24 | + /// If this property is null and [margin] is not null, then [HitTestBehavior.deferToChild] is used by default. | ||
25 | + /// | ||
26 | + /// Please refer to [HitTestBehavior] for a detailed explanation of every behavior. | ||
27 | + final HitTestBehavior? hitTestBehavior; | ||
28 | + | ||
22 | /// The direction in which the SnackBar can be dismissed. | 29 | /// The direction in which the SnackBar can be dismissed. |
23 | /// | 30 | /// |
24 | /// Default is [DismissDirection.down] when | 31 | /// Default is [DismissDirection.down] when |
@@ -203,6 +210,7 @@ class GetSnackBar extends StatefulWidget { | @@ -203,6 +210,7 @@ class GetSnackBar extends StatefulWidget { | ||
203 | this.overlayColor = Colors.transparent, | 210 | this.overlayColor = Colors.transparent, |
204 | this.userInputForm, | 211 | this.userInputForm, |
205 | this.snackbarStatus, | 212 | this.snackbarStatus, |
213 | + this.hitTestBehavior, | ||
206 | }) : super(key: key); | 214 | }) : super(key: key); |
207 | 215 | ||
208 | @override | 216 | @override |
@@ -245,6 +245,7 @@ class SnackbarController { | @@ -245,6 +245,7 @@ class SnackbarController { | ||
245 | snackbar.onHover?.call(snackbar, SnackHoverState.entered), | 245 | snackbar.onHover?.call(snackbar, SnackHoverState.entered), |
246 | onExit: (_) => snackbar.onHover?.call(snackbar, SnackHoverState.exited), | 246 | onExit: (_) => snackbar.onHover?.call(snackbar, SnackHoverState.exited), |
247 | child: GestureDetector( | 247 | child: GestureDetector( |
248 | + behavior: snackbar.hitTestBehavior ?? HitTestBehavior.deferToChild, | ||
248 | onTap: snackbar.onTap != null | 249 | onTap: snackbar.onTap != null |
249 | ? () => snackbar.onTap?.call(snackbar) | 250 | ? () => snackbar.onTap?.call(snackbar) |
250 | : null, | 251 | : null, |
@@ -263,6 +264,7 @@ class SnackbarController { | @@ -263,6 +264,7 @@ class SnackbarController { | ||
263 | 264 | ||
264 | Widget _getDismissibleSnack(Widget child) { | 265 | Widget _getDismissibleSnack(Widget child) { |
265 | return Dismissible( | 266 | return Dismissible( |
267 | + behavior: snackbar.hitTestBehavior ?? HitTestBehavior.opaque, | ||
266 | direction: snackbar.dismissDirection ?? _getDefaultDismissDirection(), | 268 | direction: snackbar.dismissDirection ?? _getDefaultDismissDirection(), |
267 | resizeDuration: null, | 269 | resizeDuration: null, |
268 | confirmDismiss: (_) { | 270 | confirmDismiss: (_) { |
@@ -382,10 +384,19 @@ class SnackBarQueue { | @@ -382,10 +384,19 @@ class SnackBarQueue { | ||
382 | _snackbarList.clear(); | 384 | _snackbarList.clear(); |
383 | } | 385 | } |
384 | 386 | ||
385 | - Future<void> disposeControllers() async { | 387 | + void disposeControllers() { |
388 | + if (_currentSnackbar != null) { | ||
389 | + _currentSnackbar?._removeOverlay(); | ||
390 | + _currentSnackbar?._controller.dispose(); | ||
391 | + _snackbarList.remove(_currentSnackbar); | ||
392 | + } | ||
393 | + | ||
394 | + _queue.cancelAllJobs(); | ||
395 | + | ||
386 | for (var element in _snackbarList) { | 396 | for (var element in _snackbarList) { |
387 | element._controller.dispose(); | 397 | element._controller.dispose(); |
388 | } | 398 | } |
399 | + _snackbarList.clear(); | ||
389 | } | 400 | } |
390 | 401 | ||
391 | Future<void> closeCurrentJob() async { | 402 | Future<void> closeCurrentJob() async { |
@@ -107,13 +107,15 @@ void main() { | @@ -107,13 +107,15 @@ void main() { | ||
107 | }); | 107 | }); |
108 | 108 | ||
109 | testWidgets("test snackbar dismissible", (tester) async { | 109 | testWidgets("test snackbar dismissible", (tester) async { |
110 | - const dismissDirection = DismissDirection.vertical; | 110 | + const dismissDirection = DismissDirection.down; |
111 | const snackBarTapTarget = Key('snackbar-tap-target'); | 111 | const snackBarTapTarget = Key('snackbar-tap-target'); |
112 | 112 | ||
113 | const GetSnackBar getBar = GetSnackBar( | 113 | const GetSnackBar getBar = GetSnackBar( |
114 | + key: ValueKey('dismissible'), | ||
114 | message: 'bar1', | 115 | message: 'bar1', |
115 | duration: Duration(seconds: 2), | 116 | duration: Duration(seconds: 2), |
116 | isDismissible: true, | 117 | isDismissible: true, |
118 | + snackPosition: SnackPosition.bottom, | ||
117 | dismissDirection: dismissDirection, | 119 | dismissDirection: dismissDirection, |
118 | ); | 120 | ); |
119 | 121 | ||
@@ -149,14 +151,14 @@ void main() { | @@ -149,14 +151,14 @@ void main() { | ||
149 | await tester.tap(find.byKey(snackBarTapTarget)); | 151 | await tester.tap(find.byKey(snackBarTapTarget)); |
150 | await tester.pumpAndSettle(); | 152 | await tester.pumpAndSettle(); |
151 | 153 | ||
152 | - // expect(Get.isSnackbarOpen, true); | ||
153 | - // await tester.pump(const Duration(milliseconds: 500)); | ||
154 | - // expect(find.byWidget(getBar), findsOneWidget); | ||
155 | - // await tester.ensureVisible(find.byWidget(getBar)); | ||
156 | - // await tester.drag(find.byWidget(getBar), const Offset(0.0, 50.0)); | ||
157 | - // await tester.pump(const Duration(milliseconds: 500)); | ||
158 | - | ||
159 | - // expect(Get.isSnackbarOpen, false); | 154 | + expect(Get.isSnackbarOpen, true); |
155 | + await tester.pump(const Duration(milliseconds: 500)); | ||
156 | + expect(find.byWidget(getBar), findsOneWidget); | ||
157 | + await tester.ensureVisible(find.byWidget(getBar)); | ||
158 | + await tester.drag(find.byType(Dismissible), const Offset(0.0, 50.0)); | ||
159 | + await tester.pumpAndSettle(); | ||
160 | + await tester.pump(const Duration(milliseconds: 500)); | ||
161 | + expect(Get.isSnackbarOpen, false); | ||
160 | }); | 162 | }); |
161 | 163 | ||
162 | testWidgets("test snackbar onTap", (tester) async { | 164 | testWidgets("test snackbar onTap", (tester) async { |
-
Please register or login to post a comment