759 lines
24 KiB
Dart
759 lines
24 KiB
Dart
// 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_test/flutter_test.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
|
|
import '../widgets/semantics_tester.dart';
|
|
|
|
void main() {
|
|
// Pumps and ensures that the BottomSheet animates non-linearly.
|
|
Future<void> _checkNonLinearAnimation(WidgetTester tester) async {
|
|
final Offset firstPosition = tester.getCenter(find.text('BottomSheet'));
|
|
await tester.pump(const Duration(milliseconds: 30));
|
|
final Offset secondPosition = tester.getCenter(find.text('BottomSheet'));
|
|
await tester.pump(const Duration(milliseconds: 30));
|
|
final Offset thirdPosition = tester.getCenter(find.text('BottomSheet'));
|
|
|
|
final double dyDelta1 = secondPosition.dy - firstPosition.dy;
|
|
final double dyDelta2 = thirdPosition.dy - secondPosition.dy;
|
|
|
|
// If the animation were linear, these two values would be the same.
|
|
expect(dyDelta1, isNot(moreOrLessEquals(dyDelta2, epsilon: 0.1)));
|
|
}
|
|
|
|
testWidgets('Tapping on a modal BottomSheet should not dismiss it', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Tap on the bottom sheet itself, it should not be dismissed
|
|
await tester.tap(find.text('BottomSheet'));
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
});
|
|
|
|
testWidgets('Tapping outside a modal BottomSheet should dismiss it by default', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Tap above the bottom sheet to dismiss it.
|
|
await tester.tapAt(const Offset(20.0, 20.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet dismiss animation.
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Tapping outside a modal BottomSheet should dismiss it when isDismissible=true', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
isDismissible: true,
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Tap above the bottom sheet to dismiss it.
|
|
await tester.tapAt(const Offset(20.0, 20.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet dismiss animation.
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Verify that the BottomSheet animates non-linearly', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
);
|
|
await tester.pump();
|
|
|
|
await _checkNonLinearAnimation(tester);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Tap above the bottom sheet to dismiss it.
|
|
await tester.tapAt(const Offset(20.0, 20.0));
|
|
await tester.pump();
|
|
await _checkNonLinearAnimation(tester);
|
|
await tester.pumpAndSettle(); // Bottom sheet dismiss animation.
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Tapping outside a modal BottomSheet should not dismiss it when isDismissible=false', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
isDismissible: false,
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Tap above the bottom sheet, attempting to dismiss it.
|
|
await tester.tapAt(const Offset(20.0, 20.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet should not dismiss.
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Swiping down a modal BottomSheet should dismiss it by default', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
isDismissible: false,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Swipe the bottom sheet to dismiss it.
|
|
await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet dismiss animation.
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Swiping down a modal BottomSheet should not dismiss it when enableDrag is false', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
isDismissible: false,
|
|
enableDrag: false,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Swipe the bottom sheet, attempting to dismiss it.
|
|
await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet should not dismiss.
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Swiping down a modal BottomSheet should dismiss it when enableDrag is true', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
await tester.pump();
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
bool showBottomSheetThenCalled = false;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
isDismissible: false,
|
|
enableDrag: true,
|
|
builder: (BuildContext context) => const Text('BottomSheet'),
|
|
).then<void>((void value) {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
|
|
// Swipe the bottom sheet to dismiss it.
|
|
await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet dismiss animation.
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Modal BottomSheet builder should only be called once', (WidgetTester tester) async {
|
|
late BuildContext savedContext;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Builder(
|
|
builder: (BuildContext context) {
|
|
savedContext = context;
|
|
return Container();
|
|
},
|
|
),
|
|
));
|
|
|
|
int numBuilderCalls = 0;
|
|
showModalBottomSheet<void>(
|
|
context: savedContext,
|
|
isDismissible: false,
|
|
enableDrag: true,
|
|
builder: (BuildContext context) {
|
|
numBuilderCalls++;
|
|
return const Text('BottomSheet');
|
|
},
|
|
);
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(numBuilderCalls, 1);
|
|
|
|
// Swipe the bottom sheet to dismiss it.
|
|
await tester.drag(find.text('BottomSheet'), const Offset(0.0, 150.0));
|
|
await tester.pumpAndSettle(); // Bottom sheet dismiss animation.
|
|
expect(numBuilderCalls, 1);
|
|
});
|
|
|
|
testWidgets('Verify that a downwards fling dismisses a persistent BottomSheet', (WidgetTester tester) async {
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
bool showBottomSheetThenCalled = false;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
body: const Center(child: Text('body')),
|
|
),
|
|
));
|
|
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
scaffoldKey.currentState!.showBottomSheet<void>((BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.all(40.0),
|
|
child: const Text('BottomSheet'),
|
|
);
|
|
}).closed.whenComplete(() {
|
|
showBottomSheetThenCalled = true;
|
|
});
|
|
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
|
|
await tester.pump(); // bottom sheet show animation starts
|
|
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
|
|
await tester.pump(const Duration(seconds: 1)); // animation done
|
|
|
|
expect(showBottomSheetThenCalled, isFalse);
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
|
|
// The fling below must be such that the velocity estimation examines an
|
|
// offset greater than the kTouchSlop. Too slow or too short a distance, and
|
|
// it won't trigger. Also, it must not be so much that it drags the bottom
|
|
// sheet off the screen, or we won't see it after we pump!
|
|
await tester.fling(find.text('BottomSheet'), const Offset(0.0, 50.0), 2000.0);
|
|
await tester.pump(); // drain the microtask queue (Future completion callback)
|
|
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
|
|
await tester.pump(); // bottom sheet dismiss animation starts
|
|
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
|
|
await tester.pump(const Duration(seconds: 1)); // animation done
|
|
|
|
expect(showBottomSheetThenCalled, isTrue);
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Verify that dragging past the bottom dismisses a persistent BottomSheet', (WidgetTester tester) async {
|
|
// This is a regression test for https://github.com/flutter/flutter/issues/5528
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
body: const Center(child: Text('body')),
|
|
),
|
|
));
|
|
|
|
scaffoldKey.currentState!.showBottomSheet<void>((BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.all(40.0),
|
|
child: const Text('BottomSheet'),
|
|
);
|
|
});
|
|
|
|
await tester.pump(); // bottom sheet show animation starts
|
|
await tester.pump(const Duration(seconds: 1)); // animation done
|
|
expect(find.text('BottomSheet'), findsOneWidget);
|
|
|
|
await tester.fling(find.text('BottomSheet'), const Offset(0.0, 400.0), 1000.0);
|
|
await tester.pump(); // drain the microtask queue (Future completion callback)
|
|
await tester.pump(); // bottom sheet dismiss animation starts
|
|
await tester.pump(const Duration(seconds: 1)); // animation done
|
|
|
|
expect(find.text('BottomSheet'), findsNothing);
|
|
});
|
|
|
|
testWidgets('modal BottomSheet has no top MediaQuery', (WidgetTester tester) async {
|
|
late BuildContext outerContext;
|
|
late BuildContext innerContext;
|
|
|
|
await tester.pumpWidget(Localizations(
|
|
locale: const Locale('en', 'US'),
|
|
delegates: const <LocalizationsDelegate<dynamic>>[
|
|
DefaultWidgetsLocalizations.delegate,
|
|
DefaultMaterialLocalizations.delegate,
|
|
],
|
|
child: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: MediaQuery(
|
|
data: const MediaQueryData(
|
|
padding: EdgeInsets.all(50.0),
|
|
size: Size(400.0, 600.0),
|
|
),
|
|
child: Navigator(
|
|
onGenerateRoute: (_) {
|
|
return PageRouteBuilder<void>(
|
|
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
|
outerContext = context;
|
|
return Container();
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
));
|
|
|
|
showModalBottomSheet<void>(
|
|
context: outerContext,
|
|
builder: (BuildContext context) {
|
|
innerContext = context;
|
|
return Container();
|
|
},
|
|
);
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
expect(
|
|
MediaQuery.of(outerContext)!.padding,
|
|
const EdgeInsets.all(50.0),
|
|
);
|
|
expect(
|
|
MediaQuery.of(innerContext)!.padding,
|
|
const EdgeInsets.only(left: 50.0, right: 50.0, bottom: 50.0),
|
|
);
|
|
});
|
|
|
|
testWidgets('modal BottomSheet has semantics', (WidgetTester tester) async {
|
|
final SemanticsTester semantics = SemanticsTester(tester);
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
body: const Center(child: Text('body')),
|
|
),
|
|
));
|
|
|
|
|
|
showModalBottomSheet<void>(context: scaffoldKey.currentContext!, builder: (BuildContext context) {
|
|
return Container(
|
|
child: const Text('BottomSheet'),
|
|
);
|
|
});
|
|
|
|
await tester.pump(); // bottom sheet show animation starts
|
|
await tester.pump(const Duration(seconds: 1)); // animation done
|
|
|
|
expect(semantics, hasSemantics(TestSemantics.root(
|
|
children: <TestSemantics>[
|
|
TestSemantics.rootChild(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
label: 'Dialog',
|
|
textDirection: TextDirection.ltr,
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.scopesRoute,
|
|
SemanticsFlag.namesRoute,
|
|
],
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
label: 'BottomSheet',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
TestSemantics(),
|
|
],
|
|
),
|
|
],
|
|
), ignoreTransform: true, ignoreRect: true, ignoreId: true));
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('Verify that visual properties are passed through', (WidgetTester tester) async {
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
const Color color = Colors.pink;
|
|
const double elevation = 9.0;
|
|
final ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.circular(12));
|
|
const Clip clipBehavior = Clip.antiAlias;
|
|
const Color barrierColor = Colors.red;
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
body: const Center(child: Text('body')),
|
|
),
|
|
));
|
|
|
|
showModalBottomSheet<void>(
|
|
context: scaffoldKey.currentContext!,
|
|
backgroundColor: color,
|
|
barrierColor: barrierColor,
|
|
elevation: elevation,
|
|
shape: shape,
|
|
clipBehavior: clipBehavior,
|
|
builder: (BuildContext context) {
|
|
return Container(
|
|
child: const Text('BottomSheet'),
|
|
);
|
|
},
|
|
);
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
final BottomSheet bottomSheet = tester.widget(find.byType(BottomSheet));
|
|
expect(bottomSheet.backgroundColor, color);
|
|
expect(bottomSheet.elevation, elevation);
|
|
expect(bottomSheet.shape, shape);
|
|
expect(bottomSheet.clipBehavior, clipBehavior);
|
|
|
|
final ModalBarrier modalBarrier = tester.widget(find.byType(ModalBarrier).last);
|
|
expect(modalBarrier.color, barrierColor);
|
|
});
|
|
|
|
testWidgets('modal BottomSheet with scrollController has semantics', (WidgetTester tester) async {
|
|
final SemanticsTester semantics = SemanticsTester(tester);
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
body: const Center(child: Text('body')),
|
|
),
|
|
));
|
|
|
|
|
|
showModalBottomSheet<void>(
|
|
context: scaffoldKey.currentContext!,
|
|
builder: (BuildContext context) {
|
|
return DraggableScrollableSheet(
|
|
expand: false,
|
|
builder: (_, ScrollController controller) {
|
|
return SingleChildScrollView(
|
|
controller: controller,
|
|
child: Container(
|
|
child: const Text('BottomSheet'),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
|
|
await tester.pump(); // bottom sheet show animation starts
|
|
await tester.pump(const Duration(seconds: 1)); // animation done
|
|
|
|
expect(semantics, hasSemantics(TestSemantics.root(
|
|
children: <TestSemantics>[
|
|
TestSemantics.rootChild(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
label: 'Dialog',
|
|
textDirection: TextDirection.ltr,
|
|
flags: <SemanticsFlag>[
|
|
SemanticsFlag.scopesRoute,
|
|
SemanticsFlag.namesRoute,
|
|
],
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
|
|
actions: <SemanticsAction>[SemanticsAction.scrollDown, SemanticsAction.scrollUp],
|
|
children: <TestSemantics>[
|
|
TestSemantics(
|
|
label: 'BottomSheet',
|
|
textDirection: TextDirection.ltr,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
TestSemantics(),
|
|
],
|
|
),
|
|
],
|
|
), ignoreTransform: true, ignoreRect: true, ignoreId: true));
|
|
semantics.dispose();
|
|
});
|
|
|
|
testWidgets('showModalBottomSheet does not use root Navigator by default', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
body: Navigator(onGenerateRoute: (RouteSettings settings) => MaterialPageRoute<void>(builder: (_) {
|
|
return const _TestPage();
|
|
})),
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
label: 'Item 1',
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.style),
|
|
label: 'Item 2',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.text('Show bottom sheet'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Bottom sheet is displayed in correct position within the inner navigator
|
|
// and above the BottomNavigationBar.
|
|
expect(tester.getBottomLeft(find.byType(BottomSheet)).dy, 544.0);
|
|
});
|
|
|
|
testWidgets('showModalBottomSheet uses root Navigator when specified', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
body: Navigator(onGenerateRoute: (RouteSettings settings) => MaterialPageRoute<void>(builder: (_) {
|
|
return const _TestPage(useRootNavigator: true);
|
|
})),
|
|
bottomNavigationBar: BottomNavigationBar(
|
|
items: const <BottomNavigationBarItem>[
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.ac_unit),
|
|
label: 'Item 1',
|
|
),
|
|
BottomNavigationBarItem(
|
|
icon: Icon(Icons.style),
|
|
label: 'Item 2',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
));
|
|
|
|
await tester.tap(find.text('Show bottom sheet'));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Bottom sheet is displayed in correct position above all content including
|
|
// the BottomNavigationBar.
|
|
expect(tester.getBottomLeft(find.byType(BottomSheet)).dy, 600.0);
|
|
});
|
|
|
|
testWidgets('Verify that route settings can be set in the showModalBottomSheet',
|
|
(WidgetTester tester) async {
|
|
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
|
const RouteSettings routeSettings =
|
|
RouteSettings(name: 'route_name', arguments: 'route_argument');
|
|
|
|
await tester.pumpWidget(MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
body: const Center(child: Text('body')),
|
|
),
|
|
));
|
|
|
|
late RouteSettings retrievedRouteSettings;
|
|
|
|
showModalBottomSheet<void>(
|
|
context: scaffoldKey.currentContext!,
|
|
routeSettings: routeSettings,
|
|
builder: (BuildContext context) {
|
|
retrievedRouteSettings = ModalRoute.of(context)!.settings;
|
|
return Container(
|
|
child: const Text('BottomSheet'),
|
|
);
|
|
},
|
|
);
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
expect(retrievedRouteSettings, routeSettings);
|
|
});
|
|
}
|
|
|
|
class _TestPage extends StatelessWidget {
|
|
const _TestPage({Key? key, this.useRootNavigator}) : super(key: key);
|
|
|
|
final bool? useRootNavigator;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Center(
|
|
child: TextButton(
|
|
child: const Text('Show bottom sheet'),
|
|
onPressed: () {
|
|
if (useRootNavigator != null) {
|
|
showModalBottomSheet<void>(
|
|
useRootNavigator: useRootNavigator!,
|
|
context: context,
|
|
builder: (_) => const Text('Modal bottom sheet'),
|
|
);
|
|
} else {
|
|
showModalBottomSheet<void>(
|
|
context: context,
|
|
builder: (_) => const Text('Modal bottom sheet'),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|