Committed by
GitHub
Merge pull request #3050 from jonataslaw/snackbar-fix
Snackbar fix
Showing
8 changed files
with
71 additions
and
31 deletions
| @@ -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 | ), |
| @@ -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 |
-
Please register or login to post a comment