diff --git a/example/.metadata b/example/.metadata index b665bf909..b7bae1617 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "e1e47221e86272429674bec4f1bd36acc4fc7b77" + revision: "ba393198430278b6595976de84fe170f553cc728" channel: "stable" project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 - base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 - platform: ios - create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 - base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 + create_revision: ba393198430278b6595976de84fe170f553cc728 + base_revision: ba393198430278b6595976de84fe170f553cc728 # User provided section diff --git a/example/lib/main.dart b/example/lib/main.dart index 06af54cde..5f1d73d42 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -76,9 +76,18 @@ class First extends StatelessWidget { leading: IconButton( icon: const Icon(Icons.more), onPressed: () { - print('THEME CHANGED'); - Get.changeTheme( - Get.isDarkMode ? ThemeData.light() : ThemeData.dark()); + Get.snackbar( + 'title', + "message", + mainButton: + TextButton(onPressed: () {}, child: const Text('button')), + isDismissible: true, + duration: Duration(seconds: 5), + snackbarStatus: (status) => print(status), + ); + // print('THEME CHANGED'); + // Get.changeTheme( + // Get.isDarkMode ? ThemeData.light() : ThemeData.dark()); }, ), ), diff --git a/example_nav2/android/local.properties b/example_nav2/android/local.properties index cebbb54d3..79defc581 100644 --- a/example_nav2/android/local.properties +++ b/example_nav2/android/local.properties @@ -1,2 +1,2 @@ -sdk.dir=C:\\Users\\anike\\AppData\\Local\\Android\\sdk -flutter.sdk=C:\\flutter \ No newline at end of file +sdk.dir=/Users/jonatasborges/Library/Android/sdk +flutter.sdk=/Users/jonatasborges/flutter \ No newline at end of file diff --git a/lib/get_navigation/src/root/get_root.dart b/lib/get_navigation/src/root/get_root.dart index c8c7489a8..eab7a6983 100644 --- a/lib/get_navigation/src/root/get_root.dart +++ b/lib/get_navigation/src/root/get_root.dart @@ -45,6 +45,7 @@ class ConfigData { final Duration defaultDialogTransitionDuration; final Routing routing; final Map parameters; + final SnackBarQueue snackBarQueue = SnackBarQueue(); ConfigData({ required this.routingCallback, @@ -340,6 +341,7 @@ class GetRootState extends State with WidgetsBindingObserver { void onClose() { config.onDispose?.call(); Get.clearTranslations(); + config.snackBarQueue.disposeControllers(); RouterReportManager.instance.clearRouteKeys(); RouterReportManager.dispose(); Get.resetInstance(clearRouteBindings: true); diff --git a/lib/get_navigation/src/snackbar/snackbar.dart b/lib/get_navigation/src/snackbar/snackbar.dart index d51c775e9..29973476f 100644 --- a/lib/get_navigation/src/snackbar/snackbar.dart +++ b/lib/get_navigation/src/snackbar/snackbar.dart @@ -19,6 +19,13 @@ class GetSnackBar extends StatefulWidget { /// The title displayed to the user final String? title; + /// Defines how the snack bar area, including margin, will behave during hit testing. + /// + /// If this property is null and [margin] is not null, then [HitTestBehavior.deferToChild] is used by default. + /// + /// Please refer to [HitTestBehavior] for a detailed explanation of every behavior. + final HitTestBehavior? hitTestBehavior; + /// The direction in which the SnackBar can be dismissed. /// /// Default is [DismissDirection.down] when @@ -203,6 +210,7 @@ class GetSnackBar extends StatefulWidget { this.overlayColor = Colors.transparent, this.userInputForm, this.snackbarStatus, + this.hitTestBehavior, }) : super(key: key); @override diff --git a/lib/get_navigation/src/snackbar/snackbar_controller.dart b/lib/get_navigation/src/snackbar/snackbar_controller.dart index 54a4c54ac..4d408b8f9 100644 --- a/lib/get_navigation/src/snackbar/snackbar_controller.dart +++ b/lib/get_navigation/src/snackbar/snackbar_controller.dart @@ -5,12 +5,14 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import '../../../get.dart'; +import '../root/get_root.dart'; class SnackbarController { - static final _snackBarQueue = _SnackBarQueue(); - static bool get isSnackbarBeingShown => _snackBarQueue._isJobInProgress; final key = GlobalKey(); + static bool get isSnackbarBeingShown => + GetRootState.controller.config.snackBarQueue.isJobInProgress; + late Animation _filterBlurAnimation; late Animation _filterColorAnimation; @@ -60,7 +62,7 @@ class SnackbarController { /// Only one GetSnackbar will be displayed at a time, and this method returns /// a future to when the snackbar disappears. Future show() { - return _snackBarQueue._addJob(this); + return GetRootState.controller.config.snackBarQueue.addJob(this); } void _cancelTimer() { @@ -243,6 +245,7 @@ class SnackbarController { snackbar.onHover?.call(snackbar, SnackHoverState.entered), onExit: (_) => snackbar.onHover?.call(snackbar, SnackHoverState.exited), child: GestureDetector( + behavior: snackbar.hitTestBehavior ?? HitTestBehavior.deferToChild, onTap: snackbar.onTap != null ? () => snackbar.onTap?.call(snackbar) : null, @@ -261,6 +264,7 @@ class SnackbarController { Widget _getDismissibleSnack(Widget child) { return Dismissible( + behavior: snackbar.hitTestBehavior ?? HitTestBehavior.opaque, direction: snackbar.dismissDirection ?? _getDefaultDismissDirection(), resizeDuration: null, confirmDismiss: (_) { @@ -348,15 +352,15 @@ class SnackbarController { } static Future cancelAllSnackbars() async { - await _snackBarQueue._cancelAllJobs(); + await GetRootState.controller.config.snackBarQueue.cancelAllJobs(); } static Future closeCurrentSnackbar() async { - await _snackBarQueue._closeCurrentJob(); + await GetRootState.controller.config.snackBarQueue.closeCurrentJob(); } } -class _SnackBarQueue { +class SnackBarQueue { final _queue = GetQueue(); final _snackbarList = []; @@ -365,22 +369,37 @@ class _SnackBarQueue { return _snackbarList.first; } - bool get _isJobInProgress => _snackbarList.isNotEmpty; + bool get isJobInProgress => _snackbarList.isNotEmpty; - Future _addJob(SnackbarController job) async { + Future addJob(SnackbarController job) async { _snackbarList.add(job); final data = await _queue.add(job._show); _snackbarList.remove(job); return data; } - Future _cancelAllJobs() async { + Future cancelAllJobs() async { await _currentSnackbar?.close(); _queue.cancelAllJobs(); _snackbarList.clear(); } - Future _closeCurrentJob() async { + void disposeControllers() { + if (_currentSnackbar != null) { + _currentSnackbar?._removeOverlay(); + _currentSnackbar?._controller.dispose(); + _snackbarList.remove(_currentSnackbar); + } + + _queue.cancelAllJobs(); + + for (var element in _snackbarList) { + element._controller.dispose(); + } + _snackbarList.clear(); + } + + Future closeCurrentJob() async { if (_currentSnackbar == null) return; await _currentSnackbar!.close(); } diff --git a/lib/get_utils/src/equality/equality.dart b/lib/get_utils/src/equality/equality.dart index e54c1b103..3c478d22f 100644 --- a/lib/get_utils/src/equality/equality.dart +++ b/lib/get_utils/src/equality/equality.dart @@ -6,9 +6,10 @@ mixin Equality { List get props; @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || runtimeType == other.runtimeType && + other is Equality && const DeepCollectionEquality().equals(props, other.props); } diff --git a/test/navigation/snackbar_test.dart b/test/navigation/snackbar_test.dart index f561b6dbd..42e270cd9 100644 --- a/test/navigation/snackbar_test.dart +++ b/test/navigation/snackbar_test.dart @@ -107,10 +107,17 @@ void main() { }); testWidgets("test snackbar dismissible", (tester) async { - const dismissDirection = DismissDirection.vertical; + const dismissDirection = DismissDirection.down; const snackBarTapTarget = Key('snackbar-tap-target'); - late final GetSnackBar getBar; + const GetSnackBar getBar = GetSnackBar( + key: ValueKey('dismissible'), + message: 'bar1', + duration: Duration(seconds: 2), + isDismissible: true, + snackPosition: SnackPosition.bottom, + dismissDirection: dismissDirection, + ); await tester.pumpWidget(GetMaterialApp( home: Scaffold( @@ -121,12 +128,6 @@ void main() { GestureDetector( key: snackBarTapTarget, onTap: () { - getBar = const GetSnackBar( - message: 'bar1', - duration: Duration(seconds: 2), - isDismissible: true, - dismissDirection: dismissDirection, - ); Get.showSnackbar(getBar); }, behavior: HitTestBehavior.opaque, @@ -154,9 +155,9 @@ void main() { await tester.pump(const Duration(milliseconds: 500)); expect(find.byWidget(getBar), findsOneWidget); await tester.ensureVisible(find.byWidget(getBar)); - await tester.drag(find.byWidget(getBar), const Offset(0.0, 50.0)); + await tester.drag(find.byType(Dismissible), const Offset(0.0, 50.0)); + await tester.pumpAndSettle(); await tester.pump(const Duration(milliseconds: 500)); - expect(Get.isSnackbarOpen, false); });