Jonny Borges
Committed by GitHub

Merge pull request #3050 from jonataslaw/snackbar-fix

Snackbar fix
... ... @@ -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
... ...
... ... @@ -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());
},
),
),
... ...
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
... ...
... ... @@ -45,6 +45,7 @@ class ConfigData {
final Duration defaultDialogTransitionDuration;
final Routing routing;
final Map<String, String?> parameters;
final SnackBarQueue snackBarQueue = SnackBarQueue();
ConfigData({
required this.routingCallback,
... ... @@ -340,6 +341,7 @@ class GetRootState extends State<GetRoot> with WidgetsBindingObserver {
void onClose() {
config.onDispose?.call();
Get.clearTranslations();
config.snackBarQueue.disposeControllers();
RouterReportManager.instance.clearRouteKeys();
RouterReportManager.dispose();
Get.resetInstance(clearRouteBindings: true);
... ...
... ... @@ -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
... ...
... ... @@ -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<GetSnackBarState>();
static bool get isSnackbarBeingShown =>
GetRootState.controller.config.snackBarQueue.isJobInProgress;
late Animation<double> _filterBlurAnimation;
late Animation<Color?> _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<void> 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<void> cancelAllSnackbars() async {
await _snackBarQueue._cancelAllJobs();
await GetRootState.controller.config.snackBarQueue.cancelAllJobs();
}
static Future<void> closeCurrentSnackbar() async {
await _snackBarQueue._closeCurrentJob();
await GetRootState.controller.config.snackBarQueue.closeCurrentJob();
}
}
class _SnackBarQueue {
class SnackBarQueue {
final _queue = GetQueue();
final _snackbarList = <SnackbarController>[];
... ... @@ -365,22 +369,37 @@ class _SnackBarQueue {
return _snackbarList.first;
}
bool get _isJobInProgress => _snackbarList.isNotEmpty;
bool get isJobInProgress => _snackbarList.isNotEmpty;
Future<void> _addJob(SnackbarController job) async {
Future<void> addJob(SnackbarController job) async {
_snackbarList.add(job);
final data = await _queue.add(job._show);
_snackbarList.remove(job);
return data;
}
Future<void> _cancelAllJobs() async {
Future<void> cancelAllJobs() async {
await _currentSnackbar?.close();
_queue.cancelAllJobs();
_snackbarList.clear();
}
Future<void> _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<void> closeCurrentJob() async {
if (_currentSnackbar == null) return;
await _currentSnackbar!.close();
}
... ...
... ... @@ -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);
}
... ...
... ... @@ -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);
});
... ...