Test snack bar examples (#147774)
Adds tests to the last two Snack Bar examples as part of #130459. Makes the [last example](https://api.flutter.dev/flutter/material/SnackBar-class.html#material.SnackBar.3) more usable through the use of standard widgets and visual hierarchy. Constraints on options that are not required by the SnackBar contract have been removed (Overflow threshold works on fixed SnackBars).
This commit is contained in:
parent
1c1516c35e
commit
aaa4d336f6
@ -325,8 +325,6 @@ final Set<String> _knownMissingTests = <String>{
|
||||
'examples/api/test/material/flexible_space_bar/flexible_space_bar.0_test.dart',
|
||||
'examples/api/test/material/floating_action_button_location/standard_fab_location.0_test.dart',
|
||||
'examples/api/test/material/chip/deletable_chip_attributes.on_deleted.0_test.dart',
|
||||
'examples/api/test/material/snack_bar/snack_bar.2_test.dart',
|
||||
'examples/api/test/material/snack_bar/snack_bar.1_test.dart',
|
||||
'examples/api/test/material/icon_button/icon_button.3_test.dart',
|
||||
'examples/api/test/material/expansion_panel/expansion_panel_list.expansion_panel_list_radio.0_test.dart',
|
||||
'examples/api/test/material/input_decorator/input_decoration.1_test.dart',
|
||||
|
@ -17,12 +17,7 @@ class SnackBarExampleApp extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(useMaterial3: true),
|
||||
home: Scaffold(
|
||||
appBar: AppBar(title: const Text('SnackBar Sample')),
|
||||
body: const Center(
|
||||
child: SnackBarExample(),
|
||||
),
|
||||
),
|
||||
home: const SnackBarExample(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -42,24 +37,25 @@ class _SnackBarExampleState extends State<SnackBarExample> {
|
||||
bool _longActionLabel = false;
|
||||
double _sliderValue = 0.25;
|
||||
|
||||
Padding _padRow(List<Widget> children) => Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(children: children),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 50.0),
|
||||
child: Column(
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('SnackBar Sample')),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(_snackBar());
|
||||
},
|
||||
icon: const Icon(Icons.play_arrow),
|
||||
label: const Text('Show Snackbar'),
|
||||
),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
_padRow(<Widget>[
|
||||
Text('Snack Bar configuration', style: Theme.of(context).textTheme.bodyLarge),
|
||||
]),
|
||||
_padRow(
|
||||
<Widget>[
|
||||
const Text('Fixed'),
|
||||
Radio<SnackBarBehavior>(
|
||||
ExpansionTile(
|
||||
title: const Text('Behavior'),
|
||||
initiallyExpanded: true,
|
||||
children: <Widget>[
|
||||
RadioListTile<SnackBarBehavior>(
|
||||
title: const Text('Fixed'),
|
||||
value: SnackBarBehavior.fixed,
|
||||
groupValue: _snackBarBehavior,
|
||||
onChanged: (SnackBarBehavior? value) {
|
||||
@ -68,8 +64,8 @@ class _SnackBarExampleState extends State<SnackBarExample> {
|
||||
});
|
||||
},
|
||||
),
|
||||
const Text('Floating'),
|
||||
Radio<SnackBarBehavior>(
|
||||
RadioListTile<SnackBarBehavior>(
|
||||
title: const Text('Floating'),
|
||||
value: SnackBarBehavior.floating,
|
||||
groupValue: _snackBarBehavior,
|
||||
onChanged: (SnackBarBehavior? value) {
|
||||
@ -80,75 +76,65 @@ class _SnackBarExampleState extends State<SnackBarExample> {
|
||||
),
|
||||
],
|
||||
),
|
||||
_padRow(
|
||||
<Widget>[
|
||||
const Text('Include Icon '),
|
||||
Switch(
|
||||
ExpansionTile(
|
||||
title: const Text('Content'),
|
||||
initiallyExpanded: true,
|
||||
children: <Widget>[
|
||||
SwitchListTile(
|
||||
title: const Text('Include close Icon'),
|
||||
value: _withIcon,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
_withIcon = !_withIcon;
|
||||
_withIcon = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
_padRow(
|
||||
<Widget>[
|
||||
const Text('Include Action '),
|
||||
Switch(
|
||||
SwitchListTile(
|
||||
title: const Text('Multi Line Text'),
|
||||
value: _multiLine,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
_multiLine = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
SwitchListTile(
|
||||
title: const Text('Include Action'),
|
||||
value: _withAction,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
_withAction = !_withAction;
|
||||
_withAction = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 16.0),
|
||||
const Text('Long Action Label '),
|
||||
Switch(
|
||||
SwitchListTile(
|
||||
title: const Text('Long Action Label'),
|
||||
value: _longActionLabel,
|
||||
onChanged: !_withAction
|
||||
? null
|
||||
: (bool value) {
|
||||
setState(() {
|
||||
_longActionLabel = !_longActionLabel;
|
||||
});
|
||||
},
|
||||
: (bool value) => setState(() {
|
||||
_longActionLabel = value;
|
||||
}),
|
||||
),
|
||||
],
|
||||
|
||||
),
|
||||
ExpansionTile(
|
||||
title: const Text('Action new-line overflow threshold'),
|
||||
initiallyExpanded: true,
|
||||
children: <Widget>[
|
||||
Slider(
|
||||
value: _sliderValue,
|
||||
divisions: 20,
|
||||
label: _sliderValue.toStringAsFixed(2),
|
||||
onChanged: (double value) => setState(() {
|
||||
_sliderValue = value;
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
_padRow(
|
||||
<Widget>[
|
||||
const Text('Multi Line Text'),
|
||||
Switch(
|
||||
value: _multiLine,
|
||||
onChanged: switch (_snackBarBehavior) {
|
||||
SnackBarBehavior.fixed || null => null,
|
||||
SnackBarBehavior.floating => (bool value) => setState(() { _multiLine = !_multiLine; }),
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
_padRow(<Widget>[
|
||||
const Text('Action new-line overflow threshold'),
|
||||
Slider(
|
||||
value: _sliderValue,
|
||||
divisions: 20,
|
||||
label: _sliderValue.toStringAsFixed(2),
|
||||
onChanged: switch (_snackBarBehavior) {
|
||||
SnackBarBehavior.fixed || null => null,
|
||||
SnackBarBehavior.floating => (double value) => setState(() { _sliderValue = value; }),
|
||||
},
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16.0),
|
||||
ElevatedButton(
|
||||
child: const Text('Show Snackbar'),
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(_snackBar());
|
||||
},
|
||||
),
|
||||
// Avoid hiding content behind the floating action button
|
||||
const SizedBox(height: 100,),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -163,9 +149,14 @@ class _SnackBarExampleState extends State<SnackBarExample> {
|
||||
},
|
||||
)
|
||||
: null;
|
||||
final double? width = _snackBarBehavior == SnackBarBehavior.floating && _multiLine ? 400.0 : null;
|
||||
final String label =
|
||||
_multiLine ? 'A Snack Bar with quite a lot of text which spans across multiple lines' : 'Single Line Snack Bar';
|
||||
final double? width = _snackBarBehavior == SnackBarBehavior.floating
|
||||
? 400.0
|
||||
: null;
|
||||
final String label = _multiLine
|
||||
? 'A Snack Bar with quite a lot of text which spans across multiple '
|
||||
'lines. You can look at how the Action Label moves around when trying '
|
||||
'to layout this text.'
|
||||
: 'Single Line Snack Bar';
|
||||
return SnackBar(
|
||||
content: Text(label),
|
||||
showCloseIcon: _withIcon,
|
||||
|
@ -15,7 +15,7 @@ void main() {
|
||||
expect(find.widgetWithText(AppBar, 'SnackBar Sample'), findsOneWidget);
|
||||
expect(find.widgetWithText(ElevatedButton, 'Show Snackbar'), findsOneWidget);
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Show Snackbar'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump();
|
||||
expect(find.text('Awesome Snackbar!'), findsOneWidget);
|
||||
expect(find.text('Action'), findsOneWidget);
|
||||
});
|
||||
|
60
examples/api/test/material/snack_bar/snack_bar.1_test.dart
Normal file
60
examples/api/test/material/snack_bar/snack_bar.1_test.dart
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_api_samples/material/snack_bar/snack_bar.1.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Tapping on button shows snackbar', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
expect(find.widgetWithText(AppBar, 'SnackBar Sample'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.widgetWithText(ElevatedButton, 'Show Snackbar'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('Awesome SnackBar!'), findsOneWidget);
|
||||
expect(find.widgetWithText(SnackBarAction, 'Action'), findsOneWidget);
|
||||
|
||||
final SnackBar bar = tester.widget<SnackBar>(find.ancestor(
|
||||
of: find.text('Awesome SnackBar!'),
|
||||
matching: find.byType(SnackBar)));
|
||||
expect(bar.behavior, SnackBarBehavior.floating);
|
||||
});
|
||||
|
||||
testWidgets('Snackbar is styled correctly', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
await tester.tap(find.byType(ElevatedButton));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.byType(SnackBar), findsOneWidget);
|
||||
|
||||
final SnackBar bar = tester.widget<SnackBar>(find.byType(SnackBar));
|
||||
expect(bar.behavior, SnackBarBehavior.floating);
|
||||
expect(bar.width, 280.0);
|
||||
expect(bar.shape, isA<RoundedRectangleBorder>()
|
||||
.having((RoundedRectangleBorder b) => b.borderRadius, 'radius', BorderRadius.circular(10.0)));
|
||||
});
|
||||
|
||||
testWidgets('Snackbar should disappear after timeout', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
|
||||
await tester.tap(find.byType(ElevatedButton));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SnackBar), findsOneWidget);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 1500));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
});
|
||||
}
|
154
examples/api/test/material/snack_bar/snack_bar.2_test.dart
Normal file
154
examples/api/test/material/snack_bar/snack_bar.2_test.dart
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_api_samples/material/snack_bar/snack_bar.2.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Shows correct static elements', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
expect(find.widgetWithText(AppBar, 'SnackBar Sample'), findsOneWidget);
|
||||
expect(find.widgetWithText(FloatingActionButton, 'Show Snackbar'), findsOneWidget);
|
||||
expect(find.text('Behavior'), findsOneWidget);
|
||||
expect(find.text('Fixed'), findsOneWidget);
|
||||
expect(find.text('Floating'), findsOneWidget);
|
||||
expect(find.text('Content'), findsOneWidget);
|
||||
expect(find.widgetWithText(SwitchListTile, 'Include close Icon'), findsOneWidget);
|
||||
expect(find.widgetWithText(SwitchListTile, 'Multi Line Text'), findsOneWidget);
|
||||
expect(find.widgetWithText(SwitchListTile, 'Include Action'), findsOneWidget);
|
||||
expect(find.widgetWithText(SwitchListTile, 'Long Action Label'), findsOneWidget);
|
||||
|
||||
await tester.scrollUntilVisible(find.byType(Slider), 30);
|
||||
expect(find.text('Action new-line overflow threshold'), findsOneWidget);
|
||||
expect(find.byType(Slider), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Applies default configuration to snackbar', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
expect(find.text('Single Line Snack Bar'), findsNothing);
|
||||
expect(find.textContaining('spans across multiple lines'), findsNothing);
|
||||
expect(find.text('Long Action Text'), findsNothing);
|
||||
expect(find.text('Action'), findsNothing);
|
||||
expect(find.byIcon(Icons.close), findsNothing);
|
||||
|
||||
await tester.tap(find.text('Show Snackbar'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SnackBar), findsOneWidget);
|
||||
expect(find.text('Single Line Snack Bar'), findsOneWidget);
|
||||
expect(find.text('Action'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.close), findsOneWidget);
|
||||
expect(tester
|
||||
.widget<SnackBar>(find.byType(SnackBar))
|
||||
.behavior,
|
||||
SnackBarBehavior.floating);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.close));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Can configure fixed snack bar with long text', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Fixed'));
|
||||
await tester.tap(find.text('Multi Line Text'));
|
||||
await tester.tap(find.text('Long Action Label'));
|
||||
await tester.tap(find.text('Show Snackbar'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SnackBar), findsOneWidget);
|
||||
expect(find.textContaining('spans across multiple lines'), findsOneWidget);
|
||||
expect(find.text('Long Action Text'), findsOneWidget);
|
||||
expect(find.byIcon(Icons.close), findsOneWidget);
|
||||
expect(tester
|
||||
.widget<SnackBar>(find.byType(SnackBar))
|
||||
.behavior,
|
||||
SnackBarBehavior.fixed);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.close));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SnackBar), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Can configure to remove action and close icon', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Include close Icon'));
|
||||
await tester.tap(find.text('Include Action'));
|
||||
await tester.tap(find.text('Show Snackbar'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SnackBar), findsOneWidget);
|
||||
expect(find.byType(SnackBarAction), findsNothing);
|
||||
expect(find.byIcon(Icons.close), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Higher overflow threshold leads to smaller snack bars', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Fixed'));
|
||||
await tester.tap(find.text('Multi Line Text'));
|
||||
await tester.tap(find.text('Long Action Label'));
|
||||
|
||||
// Establish max size with low threshold (causes overflow)
|
||||
await tester.scrollUntilVisible(find.byType(Slider), 30);
|
||||
TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(Slider)));
|
||||
await gesture.moveTo(tester.getBottomLeft(find.byType(Slider)));
|
||||
await gesture.up();
|
||||
await tester.tapAt(tester.getBottomLeft(find.byType(Slider)));
|
||||
await tester.tap(find.text('Show Snackbar'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final double highSnackBar = tester.getSize(find.byType(SnackBar)).height;
|
||||
await tester.tap(find.byIcon(Icons.close));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Configure high threshold (everything is in one row)
|
||||
gesture = await tester.startGesture(tester.getCenter(find.byType(Slider)));
|
||||
await gesture.moveTo(tester.getTopRight(find.byType(Slider)));
|
||||
await gesture.up();
|
||||
await tester.tapAt(tester.getTopRight(find.byType(Slider)));
|
||||
await tester.tap(find.text('Show Snackbar'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.getSize(find.byType(SnackBar)).height,
|
||||
lessThan(highSnackBar));
|
||||
});
|
||||
|
||||
testWidgets('Disable unusable elements', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.SnackBarExampleApp(),
|
||||
);
|
||||
|
||||
expect(find.text('Long Action Label'), findsOneWidget);
|
||||
expect(tester.widget<SwitchListTile>(find.ancestor(
|
||||
of: find.text('Long Action Label'),
|
||||
matching: find.byType(SwitchListTile),
|
||||
)).onChanged, isNotNull);
|
||||
|
||||
await tester.tap(find.text('Include Action'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.widget<SwitchListTile>(find.ancestor(
|
||||
of: find.text('Long Action Label'),
|
||||
matching: find.byType(SwitchListTile),
|
||||
)).onChanged, isNull);
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user