Unverified Commit c3cd0166 authored by lsaudon's avatar lsaudon Committed by GitHub

Tap on button behind snack bar defined by margin (#127959)

If the margin is used, set the `HitTestBehavior` to `deferToChild`. 

*List which issues are fixed by this PR. You must list at least one issue.*
#78537 
#114810 

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
parent e90f980f
...@@ -283,6 +283,7 @@ class SnackBar extends StatefulWidget { ...@@ -283,6 +283,7 @@ class SnackBar extends StatefulWidget {
this.padding, this.padding,
this.width, this.width,
this.shape, this.shape,
this.hitTestBehavior,
this.behavior, this.behavior,
this.action, this.action,
this.actionOverflowThreshold, this.actionOverflowThreshold,
...@@ -331,6 +332,8 @@ class SnackBar extends StatefulWidget { ...@@ -331,6 +332,8 @@ class SnackBar extends StatefulWidget {
/// If this property is null, then [SnackBarThemeData.insetPadding] of /// If this property is null, then [SnackBarThemeData.insetPadding] of
/// [ThemeData.snackBarTheme] is used. If that is also null, then the default is /// [ThemeData.snackBarTheme] is used. If that is also null, then the default is
/// `EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0)`. /// `EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0)`.
///
/// If this property is not null and [hitTestBehavior] is null, then [hitTestBehavior] default is [HitTestBehavior.deferToChild].
final EdgeInsetsGeometry? margin; final EdgeInsetsGeometry? margin;
/// The amount of padding to apply to the snack bar's content and optional /// The amount of padding to apply to the snack bar's content and optional
...@@ -384,6 +387,13 @@ class SnackBar extends StatefulWidget { ...@@ -384,6 +387,13 @@ class SnackBar extends StatefulWidget {
/// circular corner radius of 4.0. /// circular corner radius of 4.0.
final ShapeBorder? shape; final ShapeBorder? shape;
/// 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;
/// This defines the behavior and location of the snack bar. /// This defines the behavior and location of the snack bar.
/// ///
/// Defines where a [SnackBar] should appear within a [Scaffold] and how its /// Defines where a [SnackBar] should appear within a [Scaffold] and how its
...@@ -489,6 +499,7 @@ class SnackBar extends StatefulWidget { ...@@ -489,6 +499,7 @@ class SnackBar extends StatefulWidget {
padding: padding, padding: padding,
width: width, width: width,
shape: shape, shape: shape,
hitTestBehavior: hitTestBehavior,
behavior: behavior, behavior: behavior,
action: action, action: action,
actionOverflowThreshold: actionOverflowThreshold, actionOverflowThreshold: actionOverflowThreshold,
...@@ -776,6 +787,7 @@ class _SnackBarState extends State<SnackBar> { ...@@ -776,6 +787,7 @@ class _SnackBarState extends State<SnackBar> {
key: const Key('dismissible'), key: const Key('dismissible'),
direction: widget.dismissDirection, direction: widget.dismissDirection,
resizeDuration: null, resizeDuration: null,
behavior: widget.hitTestBehavior ?? (widget.margin != null ? HitTestBehavior.deferToChild : HitTestBehavior.opaque),
onDismissed: (DismissDirection direction) { onDismissed: (DismissDirection direction) {
ScaffoldMessenger.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe); ScaffoldMessenger.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
}, },
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
@Tags(<String>['reduced-test-set']) @Tags(<String>['reduced-test-set'])
library; library;
import 'dart:async';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
...@@ -2915,6 +2916,175 @@ testWidgets('SnackBarAction backgroundColor works as a Color', (WidgetTester tes ...@@ -2915,6 +2916,175 @@ testWidgets('SnackBarAction backgroundColor works as a Color', (WidgetTester tes
expect(material.clipBehavior, Clip.antiAlias); expect(material.clipBehavior, Clip.antiAlias);
}); });
testWidgets('Tap on button behind snack bar defined by width', (WidgetTester tester) async {
tester.view.physicalSize = const Size.square(200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
const String buttonText = 'Show snackbar';
const String snackbarContent = 'Snackbar';
const String buttonText2 = 'Try press me';
final Completer<void> completer = Completer<void>();
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
behavior: SnackBarBehavior.floating,
width: 100,
content: Text(snackbarContent),
),
);
},
child: const Text(buttonText),
),
ElevatedButton(
onPressed: () {
completer.complete();
},
child: const Text(buttonText2),
),
],
);
},
),
),
));
await tester.tap(find.text(buttonText));
await tester.pumpAndSettle();
expect(find.text(snackbarContent), findsOneWidget);
await tester.tapAt(tester.getTopLeft(find.text(buttonText2)));
expect(find.text(snackbarContent), findsOneWidget);
expect(completer.isCompleted, true);
});
testWidgets('Tap on button behind snack bar defined by margin', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/78537.
tester.view.physicalSize = const Size.square(200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
const String buttonText = 'Show snackbar';
const String snackbarContent = 'Snackbar';
const String buttonText2 = 'Try press me';
final Completer<void> completer = Completer<void>();
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.only(left: 100),
content: Text(snackbarContent),
),
);
},
child: const Text(buttonText),
),
ElevatedButton(
onPressed: () {
completer.complete();
},
child: const Text(buttonText2),
),
],
);
},
),
),
));
await tester.tap(find.text(buttonText));
await tester.pumpAndSettle();
expect(find.text(snackbarContent), findsOneWidget);
await tester.tapAt(tester.getTopLeft(find.text(buttonText2)));
expect(find.text(snackbarContent), findsOneWidget);
expect(completer.isCompleted, true);
});
testWidgets("Can't tap on button behind snack bar defined by margin and HitTestBehavior.opaque", (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/78537.
tester.view.physicalSize = const Size.square(200);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
const String buttonText = 'Show snackbar';
const String snackbarContent = 'Snackbar';
const String buttonText2 = 'Try press me';
final Completer<void> completer = Completer<void>();
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
hitTestBehavior: HitTestBehavior.opaque,
behavior: SnackBarBehavior.floating,
margin: EdgeInsets.only(left: 100),
content: Text(snackbarContent),
),
);
},
child: const Text(buttonText),
),
ElevatedButton(
onPressed: () {
completer.complete();
},
child: const Text(buttonText2),
),
],
);
},
),
),
));
await tester.tap(find.text(buttonText));
await tester.pumpAndSettle();
expect(find.text(snackbarContent), findsOneWidget);
await tester.tapAt(tester.getTopLeft(find.text(buttonText2)));
expect(find.text(snackbarContent), findsOneWidget);
expect(completer.isCompleted, false);
});
} }
/// Start test for "SnackBar dismiss test". /// Start test for "SnackBar dismiss test".
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment