Unverified Commit 1f3d423f authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

Step 1: SnackBarBehavior.floating offset fix - Soft breaking change (#50597)

* Adds an opt-in flag to fix floating snackbar's offset when no floating action button is present. This flag will be removed once the migration for the fix is complete.
Co-authored-by: 's avatarfilaps <filip1997.28@mail.ru>
parent 550c82d5
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// TODO(shihaohong): remove ignoring deprecated member use analysis
// when Scaffold.shouldSnackBarIgnoreFABRect parameter is removed.
// ignore_for_file: deprecated_member_use_from_same_package
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:math' as math; import 'dart:math' as math;
...@@ -547,9 +551,19 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate { ...@@ -547,9 +551,19 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
if (snackBarSize == Size.zero) { if (snackBarSize == Size.zero) {
snackBarSize = layoutChild(_ScaffoldSlot.snackBar, fullWidthConstraints); snackBarSize = layoutChild(_ScaffoldSlot.snackBar, fullWidthConstraints);
} }
final double snackBarYOffsetBase = floatingActionButtonRect != null && isSnackBarFloating
? floatingActionButtonRect.top double snackBarYOffsetBase;
: contentBottom; if (Scaffold.shouldSnackBarIgnoreFABRect) {
if (floatingActionButtonRect.size != Size.zero && isSnackBarFloating)
snackBarYOffsetBase = floatingActionButtonRect.top;
else
snackBarYOffsetBase = contentBottom;
} else {
snackBarYOffsetBase = floatingActionButtonRect != null && isSnackBarFloating
? floatingActionButtonRect.top
: contentBottom;
}
positionChild(_ScaffoldSlot.snackBar, Offset(0.0, snackBarYOffsetBase - snackBarSize.height)); positionChild(_ScaffoldSlot.snackBar, Offset(0.0, snackBarYOffsetBase - snackBarSize.height));
} }
...@@ -1299,6 +1313,17 @@ class Scaffold extends StatefulWidget { ...@@ -1299,6 +1313,17 @@ class Scaffold extends StatefulWidget {
/// 20.0 will be added to `MediaQuery.of(context).padding.left`. /// 20.0 will be added to `MediaQuery.of(context).padding.left`.
final double drawerEdgeDragWidth; final double drawerEdgeDragWidth;
/// This flag is deprecated and fixes and issue with incorrect clipping
/// and positioning of the [SnackBar] set to [SnackBarBehavior.floating].
@Deprecated(
'This property controls whether to clip and position the snackbar as '
'if there is always a floating action button, even if one is not present. '
'It exists to provide backwards compatibility to ease migrations, and will '
'eventually be removed. '
'This feature was deprecated after v1.15.3.'
)
static bool shouldSnackBarIgnoreFABRect = false;
/// The state from the closest instance of this class that encloses the given context. /// The state from the closest instance of this class that encloses the given context.
/// ///
/// {@tool dartpad --template=freeform} /// {@tool dartpad --template=freeform}
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// TODO(shihaohong): remove ignoring deprecated member use analysis
// when Scaffold.shouldSnackBarIgnoreFABRect parameter is removed.
// ignore_for_file: deprecated_member_use_from_same_package
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -470,58 +474,62 @@ void main() { ...@@ -470,58 +474,62 @@ void main() {
expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 17.0 + 40.0); // margin + bottom padding expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 17.0 + 40.0); // margin + bottom padding
}, skip: isBrowser); }, skip: isBrowser);
testWidgets('SnackBar is positioned above BottomNavigationBar', (WidgetTester tester) async { testWidgets(
await tester.pumpWidget(MaterialApp( 'Custom padding between SnackBar and its contents when set to SnackBarBehavior.fixed',
home: MediaQuery( (WidgetTester tester) async {
data: const MediaQueryData( await tester.pumpWidget(MaterialApp(
padding: EdgeInsets.only( home: MediaQuery(
left: 10.0, data: const MediaQueryData(
top: 20.0, padding: EdgeInsets.only(
right: 30.0, left: 10.0,
bottom: 40.0, top: 20.0,
), right: 30.0,
), bottom: 40.0,
child: Scaffold( ),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.favorite), title: Text('Animutation')),
BottomNavigationBarItem(icon: Icon(Icons.block), title: Text('Zombo.com')),
],
), ),
body: Builder( child: Scaffold(
builder: (BuildContext context) { bottomNavigationBar: BottomNavigationBar(
return GestureDetector( items: const <BottomNavigationBarItem>[
onTap: () { BottomNavigationBarItem(icon: Icon(Icons.favorite), title: Text('Animutation')),
Scaffold.of(context).showSnackBar(SnackBar( BottomNavigationBarItem(icon: Icon(Icons.block), title: Text('Zombo.com')),
content: const Text('I am a snack bar.'), ],
duration: const Duration(seconds: 2), ),
action: SnackBarAction(label: 'ACTION', onPressed: () { }), body: Builder(
)); builder: (BuildContext context) {
}, return GestureDetector(
child: const Text('X'), onTap: () {
); Scaffold.of(context).showSnackBar(SnackBar(
} content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
));
},
child: const Text('X'),
);
}
),
), ),
), ),
), ));
)); await tester.tap(find.text('X'));
await tester.tap(find.text('X')); await tester.pump(); // start animation
await tester.pump(); // start animation await tester.pump(const Duration(milliseconds: 750)); // Animation last frame.
await tester.pump(const Duration(milliseconds: 750)); // Animation last frame.
final Offset textBottomLeft = tester.getBottomLeft(find.text('I am a snack bar.'));
final Offset textBottomLeft = tester.getBottomLeft(find.text('I am a snack bar.')); final Offset textBottomRight = tester.getBottomRight(find.text('I am a snack bar.'));
final Offset textBottomRight = tester.getBottomRight(find.text('I am a snack bar.')); final Offset actionTextBottomLeft = tester.getBottomLeft(find.text('ACTION'));
final Offset actionTextBottomLeft = tester.getBottomLeft(find.text('ACTION')); final Offset actionTextBottomRight = tester.getBottomRight(find.text('ACTION'));
final Offset actionTextBottomRight = tester.getBottomRight(find.text('ACTION')); final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar)); final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
expect(textBottomLeft.dx - snackBarBottomLeft.dx, 24.0 + 10.0); // margin + left padding
expect(textBottomLeft.dx - snackBarBottomLeft.dx, 24.0 + 10.0); // margin + left padding expect(snackBarBottomLeft.dy - textBottomLeft.dy, 17.0); // margin (with no bottom padding)
expect(snackBarBottomLeft.dy - textBottomLeft.dy, 17.0); // margin (with no bottom padding) expect(actionTextBottomLeft.dx - textBottomRight.dx, 24.0);
expect(actionTextBottomLeft.dx - textBottomRight.dx, 24.0); expect(snackBarBottomRight.dx - actionTextBottomRight.dx, 24.0 + 30.0); // margin + right padding
expect(snackBarBottomRight.dx - actionTextBottomRight.dx, 24.0 + 30.0); // margin + right padding expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 17.0); // margin (with no bottom padding)
expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 17.0); // margin (with no bottom padding) },
}, skip: isBrowser); skip: isBrowser,
);
testWidgets('SnackBar should push FloatingActionButton above', (WidgetTester tester) async { testWidgets('SnackBar should push FloatingActionButton above', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
...@@ -557,70 +565,28 @@ void main() { ...@@ -557,70 +565,28 @@ void main() {
), ),
)); ));
final Offset floatingActionButtonOriginBottomCenter = tester.getCenter(find.byType(FloatingActionButton)); // Get the Rect of the FAB to compare after the SnackBar appears.
final Rect originalFabRect = tester.getRect(find.byType(FloatingActionButton));
await tester.tap(find.text('X')); await tester.tap(find.text('X'));
await tester.pump(); // start animation await tester.pump(); // start animation
await tester.pump(const Duration(milliseconds: 750)); // Animation last frame. await tester.pump(const Duration(milliseconds: 750)); // Animation last frame.
final Offset snackBarTopCenter = tester.getCenter(find.byType(SnackBar)); final Rect fabRect = tester.getRect(find.byType(FloatingActionButton));
final Offset floatingActionButtonBottomCenter = tester.getCenter(find.byType(FloatingActionButton));
expect(floatingActionButtonOriginBottomCenter.dy > floatingActionButtonBottomCenter.dy, true); // FAB should shift upwards after SnackBar appears.
expect(snackBarTopCenter.dy > floatingActionButtonBottomCenter.dy, true); expect(fabRect.center.dy, lessThan(originalFabRect.center.dy));
});
testWidgets('Floating SnackBar button text alignment', (WidgetTester tester) async { final Offset snackBarTopRight = tester.getTopRight(find.byType(SnackBar));
await tester.pumpWidget(MaterialApp(
theme: ThemeData(
snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating,)
),
home: MediaQuery(
data: const MediaQueryData(
padding: EdgeInsets.only(
left: 10.0,
top: 20.0,
right: 30.0,
bottom: 40.0,
),
),
child: Scaffold(
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
));
},
child: const Text('X'),
);
}
),
),
),
));
await tester.tap(find.text('X'));
await tester.pump(); // start animation
await tester.pump(const Duration(milliseconds: 750)); // Animation last frame.
final Offset textBottomLeft = tester.getBottomLeft(find.text('I am a snack bar.')); // FAB's surrounding padding is set to [kFloatingActionButtonMargin] in floating_action_button_location.dart by default.
final Offset textBottomRight = tester.getBottomRight(find.text('I am a snack bar.')); const int defaultFabPadding = 16;
final Offset actionTextBottomLeft = tester.getBottomLeft(find.text('ACTION'));
final Offset actionTextBottomRight = tester.getBottomRight(find.text('ACTION'));
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
expect(textBottomLeft.dx - snackBarBottomLeft.dx, 31.0 + 10.0); // margin + left padding // FAB should be positioned above the SnackBar by the default padding.
expect(snackBarBottomLeft.dy - textBottomLeft.dy, 27.0); // margin (with no bottom padding) expect(fabRect.bottomRight.dy, snackBarTopRight.dy - defaultFabPadding);
expect(actionTextBottomLeft.dx - textBottomRight.dx, 16.0); });
expect(snackBarBottomRight.dx - actionTextBottomRight.dx, 31.0 + 30.0); // margin + right padding
expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 27.0); // margin (with no bottom padding)
}, skip: isBrowser);
testWidgets('Floating SnackBar is positioned above BottomNavigationBar', (WidgetTester tester) async { testWidgets('Floating SnackBar button text alignment', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
theme: ThemeData( theme: ThemeData(
snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating,) snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating,)
...@@ -635,12 +601,6 @@ void main() { ...@@ -635,12 +601,6 @@ void main() {
), ),
), ),
child: Scaffold( child: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.favorite), title: Text('Animutation')),
BottomNavigationBarItem(icon: Icon(Icons.block), title: Text('Zombo.com')),
],
),
body: Builder( body: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
...@@ -676,108 +636,65 @@ void main() { ...@@ -676,108 +636,65 @@ void main() {
expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 27.0); // margin (with no bottom padding) expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 27.0); // margin (with no bottom padding)
}, skip: isBrowser); }, skip: isBrowser);
testWidgets('Floating SnackBar is positioned above FloatingActionButton', (WidgetTester tester) async { testWidgets(
await tester.pumpWidget(MaterialApp( 'Custom padding between SnackBar and its contents when set to SnackBarBehavior.floating',
theme: ThemeData( (WidgetTester tester) async {
snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating,) await tester.pumpWidget(MaterialApp(
), theme: ThemeData(
home: MediaQuery( snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating)
data: const MediaQueryData(
padding: EdgeInsets.only(
left: 10.0,
top: 20.0,
right: 30.0,
bottom: 40.0,
),
), ),
child: Scaffold( home: MediaQuery(
floatingActionButton: FloatingActionButton( data: const MediaQueryData(
child: const Icon(Icons.send), padding: EdgeInsets.only(
onPressed: () {}, left: 10.0,
top: 20.0,
right: 30.0,
bottom: 40.0,
),
), ),
body: Builder( child: Scaffold(
builder: (BuildContext context) { bottomNavigationBar: BottomNavigationBar(
return GestureDetector( items: const <BottomNavigationBarItem>[
onTap: () { BottomNavigationBarItem(icon: Icon(Icons.favorite), title: Text('Animutation')),
Scaffold.of(context).showSnackBar(SnackBar( BottomNavigationBarItem(icon: Icon(Icons.block), title: Text('Zombo.com')),
content: const Text('I am a snack bar.'), ],
duration: const Duration(seconds: 2), ),
action: SnackBarAction(label: 'ACTION', onPressed: () {}), body: Builder(
)); builder: (BuildContext context) {
}, return GestureDetector(
child: const Text('X'), onTap: () {
); Scaffold.of(context).showSnackBar(SnackBar(
} content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
));
},
child: const Text('X'),
);
}
),
), ),
), ),
), ));
)); await tester.tap(find.text('X'));
await tester.tap(find.text('X')); await tester.pump(); // start animation
await tester.pump(); // start animation await tester.pump(const Duration(milliseconds: 750)); // Animation last frame.
await tester.pump(const Duration(milliseconds: 750)); // Animation last frame.
final Offset textBottomLeft = tester.getBottomLeft(find.text('I am a snack bar.'));
final Offset snackBarBottomCenter = tester.getBottomLeft(find.byType(SnackBar)); final Offset textBottomRight = tester.getBottomRight(find.text('I am a snack bar.'));
final Offset floatingActionButtonTopCenter = tester.getTopLeft(find.byType(FloatingActionButton)); final Offset actionTextBottomLeft = tester.getBottomLeft(find.text('ACTION'));
final Offset actionTextBottomRight = tester.getBottomRight(find.text('ACTION'));
// Since padding and margin is handled inside snackBarBox, final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
// the bottom offset of snackbar should equal with top offset of FAB final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
expect(snackBarBottomCenter.dy == floatingActionButtonTopCenter.dy, true);
}); expect(textBottomLeft.dx - snackBarBottomLeft.dx, 31.0 + 10.0); // margin + left padding
expect(snackBarBottomLeft.dy - textBottomLeft.dy, 27.0); // margin (with no bottom padding)
testWidgets('SnackBar bottom padding is not consumed by viewInsets', (WidgetTester tester) async { expect(actionTextBottomLeft.dx - textBottomRight.dx, 16.0);
final Widget child = Directionality( expect(snackBarBottomRight.dx - actionTextBottomRight.dx, 31.0 + 30.0); // margin + right padding
textDirection: TextDirection.ltr, expect(snackBarBottomRight.dy - actionTextBottomRight.dy, 27.0); // margin (with no bottom padding)
child: Scaffold( },
resizeToAvoidBottomInset: false, skip: isBrowser,
floatingActionButton: FloatingActionButton( );
child: const Icon(Icons.send),
onPressed: () {},
),
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(
SnackBar(
content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
),
);
},
child: const Text('X'),
);
}
),
));
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(
padding: EdgeInsets.only(bottom: 20.0),
),
child: child,
),
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle(); // Show snackbar
final Offset initialPoint = tester.getCenter(find.byType(SnackBar));
// Consume bottom padding - as if by the keyboard opening
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(
padding: EdgeInsets.zero,
viewPadding: EdgeInsets.only(bottom: 20),
viewInsets: EdgeInsets.only(bottom: 300),
),
child: child,
),
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
final Offset finalPoint = tester.getCenter(find.byType(SnackBar));
expect(initialPoint, finalPoint);
});
testWidgets('SnackBarClosedReason', (WidgetTester tester) async { testWidgets('SnackBarClosedReason', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
...@@ -1146,4 +1063,296 @@ void main() { ...@@ -1146,4 +1063,296 @@ void main() {
expect(find.text('hello'), findsOneWidget); expect(find.text('hello'), findsOneWidget);
expect(called, 1); expect(called, 1);
}); });
group('SnackBar position', () {
for (final SnackBarBehavior behavior in SnackBarBehavior.values) {
final SnackBar snackBar = SnackBar(
content: const Text('SnackBar text'),
behavior: behavior,
);
testWidgets(
'$behavior should align SnackBar with the bottom of Scaffold '
'when Scaffold has no other elements',
(WidgetTester tester) async {
// TODO(shihaohong): Remove this flag once the migration to fix
// SnackBarBehavior.floating is complete.
Scaffold.shouldSnackBarIgnoreFABRect = true;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(),
),
),
);
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
scaffoldState.showSnackBar(snackBar);
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
final Offset scaffoldBottomRight = tester.getBottomRight(find.byType(Scaffold));
expect(snackBarBottomRight, equals(scaffoldBottomRight));
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset scaffoldBottomLeft = tester.getBottomLeft(find.byType(Scaffold));
expect(snackBarBottomLeft, equals(scaffoldBottomLeft));
// TODO(shihaohong): Remove this flag once the migration to fix
// SnackBarBehavior.floating is complete.
Scaffold.shouldSnackBarIgnoreFABRect = false;
},
);
testWidgets(
'$behavior should align SnackBar with the top of BottomNavigationBar '
'when Scaffold has no FloatingActionButton',
(WidgetTester tester) async {
// TODO(shihaohong): Remove this flag once the migration to fix
// SnackBarBehavior.floating is complete.
Scaffold.shouldSnackBarIgnoreFABRect = true;
final UniqueKey boxKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(),
bottomNavigationBar: SizedBox(key: boxKey, width: 800, height: 60),
),
),
);
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
scaffoldState.showSnackBar(snackBar);
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
final Offset bottomNavigationBarTopRight = tester.getTopRight(find.byKey(boxKey));
expect(snackBarBottomRight, equals(bottomNavigationBarTopRight));
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset bottomNavigationBarTopLeft = tester.getTopLeft(find.byKey(boxKey));
expect(snackBarBottomLeft, equals(bottomNavigationBarTopLeft));
// TODO(shihaohong): Remove this flag once the migration to fix
// SnackBarBehavior.floating is complete.
Scaffold.shouldSnackBarIgnoreFABRect = false;
},
);
testWidgets(
'Padding of $behavior is not consumed by viewInsets',
(WidgetTester tester) async {
final Widget child = Directionality(
textDirection: TextDirection.ltr,
child: Scaffold(
resizeToAvoidBottomInset: false,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.send),
onPressed: () {},
),
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(
SnackBar(
content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
behavior: behavior,
),
);
},
child: const Text('X'),
);
},
),
),
);
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(
padding: EdgeInsets.only(bottom: 20.0),
),
child: child,
),
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle(); // Show snackbar
final Offset initialBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset initialBottomRight = tester.getBottomRight(find.byType(SnackBar));
// Consume bottom padding - as if by the keyboard opening
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(
padding: EdgeInsets.zero,
viewPadding: EdgeInsets.all(20),
viewInsets: EdgeInsets.all(100),
),
child: child,
),
);
await tester.tap(find.text('X'));
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset finalBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset finalBottomRight = tester.getBottomRight(find.byType(SnackBar));
expect(initialBottomLeft, finalBottomLeft);
expect(initialBottomRight, finalBottomRight);
},
);
}
testWidgets(
'${SnackBarBehavior.fixed} should align SnackBar with the bottom of Scaffold '
'when Scaffold has a FloatingActionButton',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(),
floatingActionButton: FloatingActionButton(onPressed: () {}),
),
),
);
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
scaffoldState.showSnackBar(
const SnackBar(
content: Text('Snackbar text'),
behavior: SnackBarBehavior.fixed,
),
);
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
final Offset scaffoldBottomRight = tester.getBottomRight(find.byType(Scaffold));
expect(snackBarBottomRight, equals(scaffoldBottomRight));
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset scaffoldBottomLeft = tester.getBottomLeft(find.byType(Scaffold));
expect(snackBarBottomLeft, equals(scaffoldBottomLeft));
},
);
testWidgets(
'${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton'
'when Scaffold has a FloatingActionButton',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.send),
onPressed: () {},
),
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
Scaffold.of(context).showSnackBar(SnackBar(
content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
behavior: SnackBarBehavior.floating,
));
},
child: const Text('X'),
);
},
),
),
));
await tester.tap(find.text('X'));
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset floatingActionButtonTopLeft = tester.getTopLeft(
find.byType(FloatingActionButton),
);
// Since padding between the SnackBar and the FAB is created by the SnackBar,
// the bottom offset of the SnackBar should be equal to the top offset of the FAB
expect(snackBarBottomLeft.dy, floatingActionButtonTopLeft.dy);
},
);
testWidgets(
'${SnackBarBehavior.fixed} should align SnackBar with the top of BottomNavigationBar '
'when Scaffold has a BottomNavigationBar and FloatingActionButton',
(WidgetTester tester) async {
final UniqueKey boxKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(),
bottomNavigationBar: SizedBox(key: boxKey, width: 800, height: 60),
floatingActionButton: FloatingActionButton(onPressed: () {}),
),
),
);
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
scaffoldState.showSnackBar(
const SnackBar(
content: Text('SnackBar text'),
behavior: SnackBarBehavior.fixed,
),
);
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
final Offset bottomNavigationBarTopRight = tester.getTopRight(find.byKey(boxKey));
expect(snackBarBottomRight, equals(bottomNavigationBarTopRight));
final Offset snackBarBottomLeft = tester.getBottomLeft(find.byType(SnackBar));
final Offset bottomNavigationBarTopLeft = tester.getTopLeft(find.byKey(boxKey));
expect(snackBarBottomLeft, equals(bottomNavigationBarTopLeft));
},
);
testWidgets(
'${SnackBarBehavior.floating} should align SnackBar with the top of FloatingActionButton '
'when Scaffold has BottomNavigationBar and FloatingActionButton',
(WidgetTester tester) async {
final UniqueKey boxKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Container(),
bottomNavigationBar: SizedBox(key: boxKey, width: 800, height: 60),
floatingActionButton: FloatingActionButton(onPressed: () {}),
),
),
);
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
scaffoldState.showSnackBar(
const SnackBar(
content: Text('SnackBar text'),
behavior: SnackBarBehavior.floating,
),
);
await tester.pumpAndSettle(); // Have the SnackBar fully animate out.
final Offset snackBarBottomRight = tester.getBottomRight(find.byType(SnackBar));
final Offset fabTopRight = tester.getTopRight(find.byType(FloatingActionButton));
expect(snackBarBottomRight.dy, equals(fabTopRight.dy));
},
);
});
} }
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