Jonny Borges
Committed by GitHub

Merge pull request #3050 from jonataslaw/snackbar-fix

Snackbar fix
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 # This file should be version controlled and should not be manually edited. 4 # This file should be version controlled and should not be manually edited.
5 5
6 version: 6 version:
7 - revision: "e1e47221e86272429674bec4f1bd36acc4fc7b77" 7 + revision: "ba393198430278b6595976de84fe170f553cc728"
8 channel: "stable" 8 channel: "stable"
9 9
10 project_type: app 10 project_type: app
@@ -13,11 +13,11 @@ project_type: app @@ -13,11 +13,11 @@ project_type: app
13 migration: 13 migration:
14 platforms: 14 platforms:
15 - platform: root 15 - platform: root
16 - create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77  
17 - base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 16 + create_revision: ba393198430278b6595976de84fe170f553cc728
  17 + base_revision: ba393198430278b6595976de84fe170f553cc728
18 - platform: ios 18 - platform: ios
19 - create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77  
20 - base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 19 + create_revision: ba393198430278b6595976de84fe170f553cc728
  20 + base_revision: ba393198430278b6595976de84fe170f553cc728
21 21
22 # User provided section 22 # User provided section
23 23
@@ -76,9 +76,18 @@ class First extends StatelessWidget { @@ -76,9 +76,18 @@ class First extends StatelessWidget {
76 leading: IconButton( 76 leading: IconButton(
77 icon: const Icon(Icons.more), 77 icon: const Icon(Icons.more),
78 onPressed: () { 78 onPressed: () {
79 - print('THEME CHANGED');  
80 - Get.changeTheme(  
81 - Get.isDarkMode ? ThemeData.light() : ThemeData.dark()); 79 + Get.snackbar(
  80 + 'title',
  81 + "message",
  82 + mainButton:
  83 + TextButton(onPressed: () {}, child: const Text('button')),
  84 + isDismissible: true,
  85 + duration: Duration(seconds: 5),
  86 + snackbarStatus: (status) => print(status),
  87 + );
  88 + // print('THEME CHANGED');
  89 + // Get.changeTheme(
  90 + // Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
82 }, 91 },
83 ), 92 ),
84 ), 93 ),
1 -sdk.dir=C:\\Users\\anike\\AppData\\Local\\Android\\sdk  
2 -flutter.sdk=C:\\flutter  
  1 +sdk.dir=/Users/jonatasborges/Library/Android/sdk
  2 +flutter.sdk=/Users/jonatasborges/flutter
@@ -45,6 +45,7 @@ class ConfigData { @@ -45,6 +45,7 @@ class ConfigData {
45 final Duration defaultDialogTransitionDuration; 45 final Duration defaultDialogTransitionDuration;
46 final Routing routing; 46 final Routing routing;
47 final Map<String, String?> parameters; 47 final Map<String, String?> parameters;
  48 + final SnackBarQueue snackBarQueue = SnackBarQueue();
48 49
49 ConfigData({ 50 ConfigData({
50 required this.routingCallback, 51 required this.routingCallback,
@@ -340,6 +341,7 @@ class GetRootState extends State<GetRoot> with WidgetsBindingObserver { @@ -340,6 +341,7 @@ class GetRootState extends State<GetRoot> with WidgetsBindingObserver {
340 void onClose() { 341 void onClose() {
341 config.onDispose?.call(); 342 config.onDispose?.call();
342 Get.clearTranslations(); 343 Get.clearTranslations();
  344 + config.snackBarQueue.disposeControllers();
343 RouterReportManager.instance.clearRouteKeys(); 345 RouterReportManager.instance.clearRouteKeys();
344 RouterReportManager.dispose(); 346 RouterReportManager.dispose();
345 Get.resetInstance(clearRouteBindings: true); 347 Get.resetInstance(clearRouteBindings: true);
@@ -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
@@ -5,12 +5,14 @@ import 'dart:ui'; @@ -5,12 +5,14 @@ import 'dart:ui';
5 import 'package:flutter/material.dart'; 5 import 'package:flutter/material.dart';
6 6
7 import '../../../get.dart'; 7 import '../../../get.dart';
  8 +import '../root/get_root.dart';
8 9
9 class SnackbarController { 10 class SnackbarController {
10 - static final _snackBarQueue = _SnackBarQueue();  
11 - static bool get isSnackbarBeingShown => _snackBarQueue._isJobInProgress;  
12 final key = GlobalKey<GetSnackBarState>(); 11 final key = GlobalKey<GetSnackBarState>();
13 12
  13 + static bool get isSnackbarBeingShown =>
  14 + GetRootState.controller.config.snackBarQueue.isJobInProgress;
  15 +
14 late Animation<double> _filterBlurAnimation; 16 late Animation<double> _filterBlurAnimation;
15 late Animation<Color?> _filterColorAnimation; 17 late Animation<Color?> _filterColorAnimation;
16 18
@@ -60,7 +62,7 @@ class SnackbarController { @@ -60,7 +62,7 @@ class SnackbarController {
60 /// Only one GetSnackbar will be displayed at a time, and this method returns 62 /// Only one GetSnackbar will be displayed at a time, and this method returns
61 /// a future to when the snackbar disappears. 63 /// a future to when the snackbar disappears.
62 Future<void> show() { 64 Future<void> show() {
63 - return _snackBarQueue._addJob(this); 65 + return GetRootState.controller.config.snackBarQueue.addJob(this);
64 } 66 }
65 67
66 void _cancelTimer() { 68 void _cancelTimer() {
@@ -243,6 +245,7 @@ class SnackbarController { @@ -243,6 +245,7 @@ class SnackbarController {
243 snackbar.onHover?.call(snackbar, SnackHoverState.entered), 245 snackbar.onHover?.call(snackbar, SnackHoverState.entered),
244 onExit: (_) => snackbar.onHover?.call(snackbar, SnackHoverState.exited), 246 onExit: (_) => snackbar.onHover?.call(snackbar, SnackHoverState.exited),
245 child: GestureDetector( 247 child: GestureDetector(
  248 + behavior: snackbar.hitTestBehavior ?? HitTestBehavior.deferToChild,
246 onTap: snackbar.onTap != null 249 onTap: snackbar.onTap != null
247 ? () => snackbar.onTap?.call(snackbar) 250 ? () => snackbar.onTap?.call(snackbar)
248 : null, 251 : null,
@@ -261,6 +264,7 @@ class SnackbarController { @@ -261,6 +264,7 @@ class SnackbarController {
261 264
262 Widget _getDismissibleSnack(Widget child) { 265 Widget _getDismissibleSnack(Widget child) {
263 return Dismissible( 266 return Dismissible(
  267 + behavior: snackbar.hitTestBehavior ?? HitTestBehavior.opaque,
264 direction: snackbar.dismissDirection ?? _getDefaultDismissDirection(), 268 direction: snackbar.dismissDirection ?? _getDefaultDismissDirection(),
265 resizeDuration: null, 269 resizeDuration: null,
266 confirmDismiss: (_) { 270 confirmDismiss: (_) {
@@ -348,15 +352,15 @@ class SnackbarController { @@ -348,15 +352,15 @@ class SnackbarController {
348 } 352 }
349 353
350 static Future<void> cancelAllSnackbars() async { 354 static Future<void> cancelAllSnackbars() async {
351 - await _snackBarQueue._cancelAllJobs(); 355 + await GetRootState.controller.config.snackBarQueue.cancelAllJobs();
352 } 356 }
353 357
354 static Future<void> closeCurrentSnackbar() async { 358 static Future<void> closeCurrentSnackbar() async {
355 - await _snackBarQueue._closeCurrentJob(); 359 + await GetRootState.controller.config.snackBarQueue.closeCurrentJob();
356 } 360 }
357 } 361 }
358 362
359 -class _SnackBarQueue { 363 +class SnackBarQueue {
360 final _queue = GetQueue(); 364 final _queue = GetQueue();
361 final _snackbarList = <SnackbarController>[]; 365 final _snackbarList = <SnackbarController>[];
362 366
@@ -365,22 +369,37 @@ class _SnackBarQueue { @@ -365,22 +369,37 @@ class _SnackBarQueue {
365 return _snackbarList.first; 369 return _snackbarList.first;
366 } 370 }
367 371
368 - bool get _isJobInProgress => _snackbarList.isNotEmpty; 372 + bool get isJobInProgress => _snackbarList.isNotEmpty;
369 373
370 - Future<void> _addJob(SnackbarController job) async { 374 + Future<void> addJob(SnackbarController job) async {
371 _snackbarList.add(job); 375 _snackbarList.add(job);
372 final data = await _queue.add(job._show); 376 final data = await _queue.add(job._show);
373 _snackbarList.remove(job); 377 _snackbarList.remove(job);
374 return data; 378 return data;
375 } 379 }
376 380
377 - Future<void> _cancelAllJobs() async { 381 + Future<void> cancelAllJobs() async {
378 await _currentSnackbar?.close(); 382 await _currentSnackbar?.close();
379 _queue.cancelAllJobs(); 383 _queue.cancelAllJobs();
380 _snackbarList.clear(); 384 _snackbarList.clear();
381 } 385 }
382 386
383 - Future<void> _closeCurrentJob() 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 +
  396 + for (var element in _snackbarList) {
  397 + element._controller.dispose();
  398 + }
  399 + _snackbarList.clear();
  400 + }
  401 +
  402 + Future<void> closeCurrentJob() async {
384 if (_currentSnackbar == null) return; 403 if (_currentSnackbar == null) return;
385 await _currentSnackbar!.close(); 404 await _currentSnackbar!.close();
386 } 405 }
@@ -6,9 +6,10 @@ mixin Equality { @@ -6,9 +6,10 @@ mixin Equality {
6 List get props; 6 List get props;
7 7
8 @override 8 @override
9 - bool operator ==(dynamic other) { 9 + bool operator ==(Object other) {
10 return identical(this, other) || 10 return identical(this, other) ||
11 runtimeType == other.runtimeType && 11 runtimeType == other.runtimeType &&
  12 + other is Equality &&
12 const DeepCollectionEquality().equals(props, other.props); 13 const DeepCollectionEquality().equals(props, other.props);
13 } 14 }
14 15
@@ -107,10 +107,17 @@ void main() { @@ -107,10 +107,17 @@ 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 - late final GetSnackBar getBar; 113 + const GetSnackBar getBar = GetSnackBar(
  114 + key: ValueKey('dismissible'),
  115 + message: 'bar1',
  116 + duration: Duration(seconds: 2),
  117 + isDismissible: true,
  118 + snackPosition: SnackPosition.bottom,
  119 + dismissDirection: dismissDirection,
  120 + );
114 121
115 await tester.pumpWidget(GetMaterialApp( 122 await tester.pumpWidget(GetMaterialApp(
116 home: Scaffold( 123 home: Scaffold(
@@ -121,12 +128,6 @@ void main() { @@ -121,12 +128,6 @@ void main() {
121 GestureDetector( 128 GestureDetector(
122 key: snackBarTapTarget, 129 key: snackBarTapTarget,
123 onTap: () { 130 onTap: () {
124 - getBar = const GetSnackBar(  
125 - message: 'bar1',  
126 - duration: Duration(seconds: 2),  
127 - isDismissible: true,  
128 - dismissDirection: dismissDirection,  
129 - );  
130 Get.showSnackbar(getBar); 131 Get.showSnackbar(getBar);
131 }, 132 },
132 behavior: HitTestBehavior.opaque, 133 behavior: HitTestBehavior.opaque,
@@ -154,9 +155,9 @@ void main() { @@ -154,9 +155,9 @@ void main() {
154 await tester.pump(const Duration(milliseconds: 500)); 155 await tester.pump(const Duration(milliseconds: 500));
155 expect(find.byWidget(getBar), findsOneWidget); 156 expect(find.byWidget(getBar), findsOneWidget);
156 await tester.ensureVisible(find.byWidget(getBar)); 157 await tester.ensureVisible(find.byWidget(getBar));
157 - await tester.drag(find.byWidget(getBar), const Offset(0.0, 50.0)); 158 + await tester.drag(find.byType(Dismissible), const Offset(0.0, 50.0));
  159 + await tester.pumpAndSettle();
158 await tester.pump(const Duration(milliseconds: 500)); 160 await tester.pump(const Duration(milliseconds: 500));
159 -  
160 expect(Get.isSnackbarOpen, false); 161 expect(Get.isSnackbarOpen, false);
161 }); 162 });
162 163