1141 lines
37 KiB
Dart
1141 lines
37 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/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../widgets/semantics_tester.dart';
|
|
|
|
void main() {
|
|
testWidgets('Material2 - Drawer control test', (WidgetTester tester) async {
|
|
const containerKey = Key('container');
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: Scaffold(
|
|
drawer: Drawer(
|
|
child: ListView(
|
|
children: <Widget>[
|
|
DrawerHeader(
|
|
child: Container(key: containerKey, child: const Text('header')),
|
|
),
|
|
const ListTile(leading: Icon(Icons.archive), title: Text('Archive')),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Archive'), findsNothing);
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
state.openDrawer();
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Archive'), findsOneWidget);
|
|
|
|
RenderBox box = tester.renderObject(find.byType(DrawerHeader));
|
|
expect(box.size.height, equals(160.0 + 8.0 + 1.0)); // height + bottom margin + bottom edge
|
|
|
|
final double drawerWidth = box.size.width;
|
|
final double drawerHeight = box.size.height;
|
|
|
|
box = tester.renderObject(find.byKey(containerKey));
|
|
expect(box.size.width, equals(drawerWidth - 2 * 16.0));
|
|
expect(box.size.height, equals(drawerHeight - 2 * 16.0));
|
|
|
|
expect(find.text('header'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Material3 - Drawer control test', (WidgetTester tester) async {
|
|
const containerKey = Key('container');
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
drawer: Drawer(
|
|
child: ListView(
|
|
children: <Widget>[
|
|
DrawerHeader(
|
|
child: Container(key: containerKey, child: const Text('header')),
|
|
),
|
|
const ListTile(leading: Icon(Icons.archive), title: Text('Archive')),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Archive'), findsNothing);
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
state.openDrawer();
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(find.text('Archive'), findsOneWidget);
|
|
|
|
RenderBox box = tester.renderObject(find.byType(DrawerHeader));
|
|
expect(box.size.height, equals(160.0 + 8.0 + 1.0)); // height + bottom margin + bottom edge
|
|
|
|
final double drawerWidth = box.size.width;
|
|
final double drawerHeight = box.size.height;
|
|
|
|
box = tester.renderObject(find.byKey(containerKey));
|
|
expect(box.size.width, equals(drawerWidth - 2 * 16.0));
|
|
expect(
|
|
box.size.height,
|
|
equals(drawerHeight - 2 * 16.0 - 1.0),
|
|
); // Header divider thickness is 1.0 in Material 3.
|
|
|
|
expect(find.text('header'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets(
|
|
'Drawer dismiss barrier has label',
|
|
(WidgetTester tester) async {
|
|
final semantics = SemanticsTester(tester);
|
|
await tester.pumpWidget(const MaterialApp(home: Scaffold(drawer: Drawer())));
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
state.openDrawer();
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
expect(
|
|
semantics,
|
|
includesNodeWith(
|
|
label: const DefaultMaterialLocalizations().modalBarrierDismissLabel,
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
),
|
|
);
|
|
|
|
semantics.dispose();
|
|
},
|
|
variant: const TargetPlatformVariant(<TargetPlatform>{
|
|
TargetPlatform.iOS,
|
|
TargetPlatform.macOS,
|
|
}),
|
|
);
|
|
|
|
testWidgets('Drawer dismiss barrier has no label', (WidgetTester tester) async {
|
|
final semantics = SemanticsTester(tester);
|
|
await tester.pumpWidget(const MaterialApp(home: Scaffold(drawer: Drawer())));
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
state.openDrawer();
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
expect(
|
|
semantics,
|
|
isNot(
|
|
includesNodeWith(
|
|
label: const DefaultMaterialLocalizations().modalBarrierDismissLabel,
|
|
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
|
|
),
|
|
),
|
|
);
|
|
|
|
semantics.dispose();
|
|
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
|
|
|
testWidgets('Scaffold drawerScrimColor', (WidgetTester tester) async {
|
|
// The scrim is a ColoredBox within a Semantics node labeled "Dismiss",
|
|
// within a DrawerController. Sorry.
|
|
Widget getScrim() {
|
|
return tester
|
|
.widget<Semantics>(
|
|
find.descendant(
|
|
of: find.byType(DrawerController),
|
|
matching: find.byWidgetPredicate((Widget widget) {
|
|
return widget is Semantics && widget.properties.label == 'Dismiss';
|
|
}),
|
|
),
|
|
)
|
|
.child!;
|
|
}
|
|
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
Widget buildFrame({Color? drawerScrimColor}) {
|
|
return MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
drawerScrimColor: drawerScrimColor,
|
|
drawer: Drawer(
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
Navigator.pop(context);
|
|
}, // close drawer
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> checkScrim(Color color) async {
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pump();
|
|
var scrim = getScrim() as ColoredBox;
|
|
expect(scrim.color, isSameColorAs(color.withValues(alpha: 0)));
|
|
|
|
await tester.pumpAndSettle();
|
|
scrim = getScrim() as ColoredBox;
|
|
expect(scrim.color, isSameColorAs(color));
|
|
|
|
await tester.tap(find.byType(Drawer));
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(Drawer), findsNothing);
|
|
}
|
|
|
|
// Default drawerScrimColor
|
|
await tester.pumpWidget(buildFrame());
|
|
await checkScrim(Colors.black54);
|
|
|
|
// Specific drawerScrimColor
|
|
await tester.pumpWidget(buildFrame(drawerScrimColor: const Color(0xFF323232)));
|
|
await checkScrim(const Color(0xFF323232));
|
|
});
|
|
|
|
testWidgets('Open/close drawers by flinging', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Scaffold(
|
|
drawer: Drawer(child: Text('start drawer')),
|
|
endDrawer: Drawer(child: Text('end drawer')),
|
|
),
|
|
),
|
|
);
|
|
|
|
// In the beginning, drawers are closed
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
expect(state.isDrawerOpen, equals(false));
|
|
expect(state.isEndDrawerOpen, equals(false));
|
|
final Size size = tester.getSize(find.byType(Scaffold));
|
|
|
|
// A fling from the left opens the start drawer
|
|
await tester.flingFrom(Offset(0, size.height / 2), const Offset(80, 0), 500);
|
|
await tester.pumpAndSettle();
|
|
expect(state.isDrawerOpen, equals(true));
|
|
expect(state.isEndDrawerOpen, equals(false));
|
|
|
|
// Now, a fling from the right closes the drawer
|
|
await tester.flingFrom(Offset(size.width - 1, size.height / 2), const Offset(-80, 0), 500);
|
|
await tester.pumpAndSettle();
|
|
expect(state.isDrawerOpen, equals(false));
|
|
expect(state.isEndDrawerOpen, equals(false));
|
|
|
|
// Another fling from the right opens the end drawer
|
|
await tester.flingFrom(Offset(size.width - 1, size.height / 2), const Offset(-80, 0), 500);
|
|
await tester.pumpAndSettle();
|
|
expect(state.isDrawerOpen, equals(false));
|
|
expect(state.isEndDrawerOpen, equals(true));
|
|
|
|
// And a fling from the left closes it
|
|
await tester.flingFrom(Offset(0, size.height / 2), const Offset(80, 0), 500);
|
|
await tester.pumpAndSettle();
|
|
expect(state.isDrawerOpen, equals(false));
|
|
expect(state.isEndDrawerOpen, equals(false));
|
|
});
|
|
|
|
testWidgets('Open/close drawer by dragging', (WidgetTester tester) async {
|
|
final draggable = ThemeData(platform: TargetPlatform.android);
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: draggable,
|
|
home: const Scaffold(drawer: Drawer()),
|
|
),
|
|
);
|
|
|
|
final TestGesture gesture = await tester.createGesture();
|
|
final Finder finder = find.byType(Drawer);
|
|
|
|
double drawerPosition() {
|
|
expect(finder, findsOneWidget);
|
|
final RenderBox renderBox = tester.renderObject(finder);
|
|
return renderBox.localToGlobal(Offset.zero).dx;
|
|
}
|
|
|
|
// Pointer down (drawer is closed).
|
|
await gesture.addPointer();
|
|
await gesture.down(const Offset(2, 2));
|
|
await tester.pump();
|
|
expect(finder, findsNothing);
|
|
|
|
// Open drawer slightly.
|
|
await gesture.moveBy(const Offset(20, 0));
|
|
await tester.pump();
|
|
expect(drawerPosition(), isNegative);
|
|
|
|
// Open drawer more than halfway.
|
|
await gesture.moveBy(const Offset(200, 0));
|
|
await tester.pump();
|
|
expect(drawerPosition(), isNegative);
|
|
|
|
// Drawer is fully open.
|
|
await gesture.moveBy(const Offset(200, 0));
|
|
await tester.pump();
|
|
expect(drawerPosition(), 0.0);
|
|
|
|
// Drawer is less than halfway closed.
|
|
await gesture.moveBy(const Offset(-100.0, 0));
|
|
await tester.pump();
|
|
expect(drawerPosition(), moreOrLessEquals(-100.0));
|
|
|
|
// Drawer is more than halfway closed.
|
|
await gesture.moveBy(const Offset(-100.0, 0));
|
|
await tester.pump();
|
|
expect(drawerPosition(), moreOrLessEquals(-200.0));
|
|
|
|
// Drawer is completely closed.
|
|
await gesture.moveTo(Offset.zero);
|
|
await tester.pump();
|
|
expect(finder, findsNothing);
|
|
});
|
|
|
|
testWidgets('Scaffold.drawer - null restorationId ', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
restorationScopeId: 'app',
|
|
home: Scaffold(key: scaffoldKey, drawer: const Text('drawer'), body: Container()),
|
|
),
|
|
);
|
|
await tester.pump(); // no effect
|
|
expect(find.text('drawer'), findsNothing);
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
|
|
await tester.restartAndRestore();
|
|
// Drawer state should not have been saved.
|
|
expect(find.text('drawer'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Scaffold.endDrawer - null restorationId ', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
restorationScopeId: 'app',
|
|
home: Scaffold(key: scaffoldKey, drawer: const Text('endDrawer'), body: Container()),
|
|
),
|
|
);
|
|
await tester.pump(); // no effect
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
await tester.restartAndRestore();
|
|
// Drawer state should not have been saved.
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Scaffold.drawer state restoration test', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
restorationScopeId: 'app',
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
restorationId: 'scaffold',
|
|
drawer: const Text('drawer'),
|
|
body: Container(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pump(); // no effect
|
|
expect(find.text('drawer'), findsNothing);
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
|
|
await tester.restartAndRestore();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
|
|
final TestRestorationData data = await tester.getRestorationData();
|
|
await tester.tapAt(const Offset(750.0, 100.0)); // on the mask
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsNothing);
|
|
|
|
await tester.restoreFrom(data);
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Scaffold.endDrawer state restoration test', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
restorationScopeId: 'app',
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
restorationId: 'scaffold',
|
|
endDrawer: const Text('endDrawer'),
|
|
body: Container(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pump(); // no effect
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
scaffoldKey.currentState!.openEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
await tester.restartAndRestore();
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
final TestRestorationData data = await tester.getRestorationData();
|
|
await tester.tapAt(const Offset(750.0, 100.0)); // on the mask
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
await tester.restoreFrom(data);
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Both drawer and endDrawer state restoration test', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
restorationScopeId: 'app',
|
|
home: Scaffold(
|
|
restorationId: 'scaffold',
|
|
key: scaffoldKey,
|
|
drawer: const Text('drawer'),
|
|
endDrawer: const Text('endDrawer'),
|
|
body: Container(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pump(); // no effect
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
await tester.restartAndRestore();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
TestRestorationData data = await tester.getRestorationData();
|
|
await tester.tapAt(const Offset(750.0, 100.0)); // on the mask
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
await tester.restoreFrom(data);
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
await tester.tapAt(const Offset(750.0, 100.0)); // on the mask
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
scaffoldKey.currentState!.openEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
await tester.restartAndRestore();
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
data = await tester.getRestorationData();
|
|
await tester.tapAt(const Offset(750.0, 100.0)); // on the mask
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
await tester.restoreFrom(data);
|
|
expect(find.text('drawer'), findsNothing);
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('ScaffoldState close drawer', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(key: scaffoldKey, drawer: const Text('Drawer'), body: Container()),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Drawer'), findsNothing);
|
|
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Drawer'), findsOneWidget);
|
|
|
|
scaffoldKey.currentState!.closeDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Drawer'), findsNothing);
|
|
});
|
|
|
|
testWidgets('ScaffoldState close drawer do not crash if drawer is already closed', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(key: scaffoldKey, drawer: const Text('Drawer'), body: Container()),
|
|
),
|
|
);
|
|
|
|
expect(find.text('Drawer'), findsNothing);
|
|
|
|
scaffoldKey.currentState!.closeDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Drawer'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Disposing drawer does not crash if drawer is open and framework is locked', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/34978
|
|
addTearDown(tester.view.reset);
|
|
tester.view.physicalSize = const Size(1800.0, 2400.0);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: OrientationBuilder(
|
|
builder: (BuildContext context, Orientation orientation) {
|
|
switch (orientation) {
|
|
case Orientation.portrait:
|
|
return Scaffold(drawer: const Text('drawer'), body: Container());
|
|
case Orientation.landscape:
|
|
return Scaffold(appBar: AppBar(), body: Container());
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('drawer'), findsNothing);
|
|
|
|
// Using a global key is a workaround for this issue.
|
|
final ScaffoldState portraitScaffoldState = tester.firstState(find.byType(Scaffold));
|
|
portraitScaffoldState.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
|
|
// Change the orientation and cause the drawer controller to be disposed
|
|
// while the framework is locked.
|
|
tester.view.physicalSize = const Size(2400.0, 1800.0);
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(BackButton), findsNothing);
|
|
});
|
|
|
|
testWidgets('Disposing endDrawer does not crash if endDrawer is open and framework is locked', (
|
|
WidgetTester tester,
|
|
) async {
|
|
// Regression test for https://github.com/flutter/flutter/issues/34978
|
|
addTearDown(tester.view.reset);
|
|
tester.view.physicalSize = const Size(1800.0, 2400.0);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: OrientationBuilder(
|
|
builder: (BuildContext context, Orientation orientation) {
|
|
switch (orientation) {
|
|
case Orientation.portrait:
|
|
return Scaffold(endDrawer: const Text('endDrawer'), body: Container());
|
|
case Orientation.landscape:
|
|
return Scaffold(appBar: AppBar(), body: Container());
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
// Using a global key is a workaround for this issue.
|
|
final ScaffoldState portraitScaffoldState = tester.firstState(find.byType(Scaffold));
|
|
portraitScaffoldState.openEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
// Change the orientation and cause the drawer controller to be disposed
|
|
// while the framework is locked.
|
|
tester.view.physicalSize = const Size(2400.0, 1800.0);
|
|
await tester.pumpAndSettle();
|
|
expect(find.byType(BackButton), findsNothing);
|
|
});
|
|
|
|
testWidgets('ScaffoldState close end drawer', (WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(key: scaffoldKey, endDrawer: const Text('endDrawer'), body: Container()),
|
|
),
|
|
);
|
|
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
|
|
scaffoldKey.currentState!.openEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('endDrawer'), findsOneWidget);
|
|
|
|
scaffoldKey.currentState!.closeEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('endDrawer'), findsNothing);
|
|
});
|
|
|
|
testWidgets('Drawer width defaults to Material spec', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const MaterialApp(home: Scaffold(drawer: Drawer())));
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
state.openDrawer();
|
|
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(Drawer));
|
|
expect(box.size.width, equals(304.0));
|
|
});
|
|
|
|
testWidgets('Drawer width can be customized by parameter', (WidgetTester tester) async {
|
|
const double smallWidth = 200;
|
|
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Scaffold(drawer: Drawer(width: smallWidth)),
|
|
),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
state.openDrawer();
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
final RenderBox box = tester.renderObject(find.byType(Drawer));
|
|
expect(box.size.width, equals(smallWidth));
|
|
});
|
|
|
|
testWidgets('Material3 - Drawer default shape (ltr)', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Scaffold(drawer: Drawer(), endDrawer: Drawer()),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder drawerMaterial = find.descendant(
|
|
of: find.byType(Drawer),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
|
|
// Open the drawer.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test the drawer shape.
|
|
Material material = tester.widget<Material>(drawerMaterial);
|
|
expect(
|
|
material.shape,
|
|
const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(16.0),
|
|
bottomRight: Radius.circular(16.0),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Close the opened drawer.
|
|
await tester.tapAt(const Offset(750, 300));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Open the end drawer.
|
|
state.openEndDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test the end drawer shape.
|
|
material = tester.widget<Material>(drawerMaterial);
|
|
expect(
|
|
material.shape,
|
|
const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(16.0),
|
|
bottomLeft: Radius.circular(16.0),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
|
|
testWidgets('Material3 - Drawer default shape (rtl)', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Scaffold(drawer: Drawer(), endDrawer: Drawer()),
|
|
),
|
|
),
|
|
);
|
|
|
|
final Finder drawerMaterial = find.descendant(
|
|
of: find.byType(Drawer),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
|
|
// Open the drawer.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test the drawer shape.
|
|
Material material = tester.widget<Material>(drawerMaterial);
|
|
expect(
|
|
material.shape,
|
|
const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(16.0),
|
|
bottomLeft: Radius.circular(16.0),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Close the opened drawer.
|
|
await tester.tapAt(const Offset(750, 300));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Open the end drawer.
|
|
state.openEndDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test the end drawer shape.
|
|
material = tester.widget<Material>(drawerMaterial);
|
|
expect(
|
|
material.shape,
|
|
const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.only(
|
|
topRight: Radius.circular(16.0),
|
|
bottomRight: Radius.circular(16.0),
|
|
),
|
|
),
|
|
);
|
|
});
|
|
|
|
testWidgets('Material3 - Drawer clip behavior', (WidgetTester tester) async {
|
|
await tester.pumpWidget(const MaterialApp(home: Scaffold(drawer: Drawer())));
|
|
|
|
final Finder drawerMaterial = find.descendant(
|
|
of: find.byType(Drawer),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
|
|
// Open the drawer.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test default clip behavior.
|
|
Material material = tester.widget<Material>(drawerMaterial);
|
|
expect(material.clipBehavior, Clip.hardEdge);
|
|
|
|
state.closeDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Provide a custom clip behavior.
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Scaffold(drawer: Drawer(clipBehavior: Clip.antiAlias)),
|
|
),
|
|
);
|
|
|
|
// Open the drawer again.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Clip behavior is now updated.
|
|
material = tester.widget<Material>(drawerMaterial);
|
|
expect(material.clipBehavior, Clip.antiAlias);
|
|
});
|
|
|
|
testWidgets('Drawer barrier is dismissible by default', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: Scaffold(
|
|
appBar: AppBar(
|
|
title: Semantics(headingLevel: 1, child: const Text('Drawer Dismissible')),
|
|
),
|
|
endDrawer: const Drawer(backgroundColor: Colors.white, width: 300, child: Text('Drawer')),
|
|
body: Container(
|
|
color: Colors.white,
|
|
width: 600,
|
|
height: 600,
|
|
child: const Center(child: Text('Drawer Dismissible')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Check the flag is set at the Scaffold level.
|
|
final Scaffold scaffold = tester.widget<Scaffold>(find.byType(Scaffold));
|
|
expect(scaffold.drawerBarrierDismissible, true);
|
|
|
|
// Verify whether the drawer barrier is dimissible by default via the state
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
expect(state.isDrawerBarrierDismissible, true);
|
|
|
|
// Open the drawer initially.
|
|
state.openEndDrawer();
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check that the drawer open.
|
|
expect(find.byType(Drawer), findsExactly(1));
|
|
|
|
// Close the drawer programmatically.
|
|
state.closeEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(Drawer), findsExactly(0));
|
|
|
|
// Open it again, and make sure the drawer is available.
|
|
state.openEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(Drawer), findsExactly(1));
|
|
|
|
// Find the ModalBarrier.
|
|
final Finder modalBarrierFinder = find.byType(ModalBarrier);
|
|
|
|
// Get the RenderBox of the ModalBarrier.
|
|
final modalBarrierRenderBox = tester.renderObject(modalBarrierFinder) as RenderBox;
|
|
|
|
// Calculate a point to tap outside the Drawer.
|
|
// This example taps on the ModalBarrier somewhere outside its boundaries.
|
|
const modalBarrierCenter = Offset(400, 300);
|
|
final Offset tapPosition = modalBarrierRenderBox.localToGlobal(modalBarrierCenter);
|
|
|
|
// Tap on the ModalBarrier.
|
|
await tester.tapAt(tapPosition);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Make sure the drawer is gone, since the drawerBarrierDismissible flag is set to true by default.
|
|
expect(find.byType(Drawer), findsExactly(0));
|
|
});
|
|
|
|
testWidgets('Drawer can be configured as not dismissible', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: Scaffold(
|
|
drawerBarrierDismissible: false,
|
|
appBar: AppBar(
|
|
title: Semantics(headingLevel: 1, child: const Text('Drawer Dismissible')),
|
|
),
|
|
endDrawer: const Drawer(backgroundColor: Colors.white, width: 300, child: Text('Drawer')),
|
|
body: Container(
|
|
color: Colors.white,
|
|
width: 600,
|
|
height: 600,
|
|
child: const Center(child: Text('Drawer Dismissible')),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Make sure the flag is set to false at the Scaffold level.
|
|
final Scaffold scaffold = tester.widget<Scaffold>(find.byType(Scaffold));
|
|
expect(scaffold.drawerBarrierDismissible, false);
|
|
|
|
// Verify the drawer barrier is not dimissible by checking the state's getter
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
expect(state.isDrawerBarrierDismissible, false);
|
|
|
|
// Open the drawer initially.
|
|
state.openEndDrawer();
|
|
|
|
await tester.pumpAndSettle();
|
|
|
|
// Check that the drawer is open.
|
|
expect(find.byType(Drawer), findsExactly(1));
|
|
|
|
// Close the drawer programmatically.
|
|
state.closeEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(Drawer), findsExactly(0));
|
|
|
|
// Open it again, and make sure the drawer is available.
|
|
state.openEndDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(find.byType(Drawer), findsExactly(1));
|
|
|
|
// Find the ModalBarrier.
|
|
final Finder modalBarrierFinder = find.byType(ModalBarrier);
|
|
|
|
// Get the RenderBox of the ModalBarrier.
|
|
final modalBarrierRenderBox = tester.renderObject(modalBarrierFinder) as RenderBox;
|
|
|
|
// Calculate a point to tap outside the Drawer.
|
|
// This example taps on the ModalBarrier somewhere outside its boundaries.
|
|
const modalBarrierCenter = Offset(400, 300);
|
|
final Offset tapPosition = modalBarrierRenderBox.localToGlobal(modalBarrierCenter);
|
|
|
|
// Tap on the ModalBarrier.
|
|
await tester.tapAt(tapPosition);
|
|
await tester.pumpAndSettle();
|
|
|
|
// Make sure the drawer is still present, and that tapping on the modal barrier
|
|
// didn't dismiss it, since the drawerBarrierDismissible property is set to false.
|
|
expect(find.byType(Drawer), findsExactly(1));
|
|
});
|
|
|
|
testWidgets('Drawer can be dismissed with the escape key by default', (
|
|
WidgetTester tester,
|
|
) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
|
|
// Test with drawerBarrierDismissible: true (default)
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
drawer: const Drawer(child: Text('drawer')),
|
|
),
|
|
),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
expect(state.isDrawerBarrierDismissible, isTrue);
|
|
|
|
// Open the drawer.
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
|
|
// Close the drawer with the escape key.
|
|
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsNothing);
|
|
});
|
|
|
|
testWidgets(
|
|
'Drawer cannot be dismissed with the escape key when drawerBarrierDismissible is false',
|
|
(WidgetTester tester) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
drawer: const Drawer(child: Text('drawer')),
|
|
drawerBarrierDismissible: false,
|
|
),
|
|
),
|
|
);
|
|
|
|
// Verify that the [Scaffold.drawerBarrierDismissible] flag is false
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
expect(state.isDrawerBarrierDismissible, isFalse);
|
|
|
|
// Open the drawer.
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
|
|
// Try to close the drawer with the escape key, and verify it does not close.
|
|
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('drawer'), findsOneWidget);
|
|
},
|
|
);
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/177005
|
|
testWidgets('Drawer semantics for mismatched platforms', (WidgetTester tester) async {
|
|
const localizations = DefaultMaterialLocalizations();
|
|
|
|
Future<void> pumpDrawerWithTheme(TargetPlatform themePlatform) async {
|
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(platform: themePlatform),
|
|
home: Scaffold(
|
|
key: scaffoldKey,
|
|
drawer: const Drawer(child: Text('Drawer')),
|
|
body: Container(),
|
|
),
|
|
),
|
|
);
|
|
|
|
scaffoldKey.currentState!.openDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Test label semantics.
|
|
final Finder drawerLabelFinder = find.bySemanticsLabel(localizations.drawerLabel);
|
|
if (defaultTargetPlatform == TargetPlatform.iOS ||
|
|
defaultTargetPlatform == TargetPlatform.macOS) {
|
|
expect(drawerLabelFinder, findsNothing); // Apple platforms don't show drawer label.
|
|
} else {
|
|
expect(drawerLabelFinder, findsOneWidget); // Non-Apple platforms show drawer label.
|
|
}
|
|
|
|
// Test barrier semantics.
|
|
final semantics = SemanticsTester(tester);
|
|
final expectBarrierExcluded = defaultTargetPlatform == TargetPlatform.android;
|
|
|
|
if (expectBarrierExcluded) {
|
|
expect(
|
|
semantics,
|
|
isNot(
|
|
includesNodeWith(
|
|
label: localizations.modalBarrierDismissLabel,
|
|
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
|
|
),
|
|
),
|
|
);
|
|
} else {
|
|
expect(
|
|
semantics,
|
|
includesNodeWith(
|
|
label: localizations.modalBarrierDismissLabel,
|
|
actions: <SemanticsAction>[SemanticsAction.tap],
|
|
),
|
|
);
|
|
}
|
|
|
|
semantics.dispose();
|
|
}
|
|
|
|
// Test with theme.platform = Android on different real platforms.
|
|
await pumpDrawerWithTheme(TargetPlatform.android);
|
|
|
|
// Test with theme.platform = iOS on different real platforms.
|
|
await pumpDrawerWithTheme(TargetPlatform.iOS);
|
|
}, variant: TargetPlatformVariant.all());
|
|
|
|
group('Material 2', () {
|
|
// These tests are only relevant for Material 2. Once Material 2
|
|
// support is deprecated and the APIs are removed, these tests
|
|
// can be deleted.
|
|
|
|
testWidgets('Material2 - Drawer default shape', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: const Scaffold(drawer: Drawer(), endDrawer: Drawer()),
|
|
),
|
|
);
|
|
|
|
final Finder drawerMaterial = find.descendant(
|
|
of: find.byType(Drawer),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
|
|
// Open the drawer.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test the drawer shape.
|
|
Material material = tester.widget<Material>(drawerMaterial);
|
|
expect(material.shape, null);
|
|
|
|
// Close the opened drawer.
|
|
await tester.tapAt(const Offset(750, 300));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Open the end drawer.
|
|
state.openEndDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test the end drawer shape.
|
|
material = tester.widget<Material>(drawerMaterial);
|
|
expect(material.shape, null);
|
|
});
|
|
|
|
testWidgets('Material2 - Drawer clip behavior', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: const Scaffold(drawer: Drawer()),
|
|
),
|
|
);
|
|
|
|
final Finder drawerMaterial = find.descendant(
|
|
of: find.byType(Drawer),
|
|
matching: find.byType(Material),
|
|
);
|
|
|
|
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
|
|
|
|
// Open the drawer.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Test default clip behavior.
|
|
Material material = tester.widget<Material>(drawerMaterial);
|
|
expect(material.clipBehavior, Clip.none);
|
|
|
|
state.closeDrawer();
|
|
await tester.pumpAndSettle();
|
|
|
|
// Provide a shape and custom clip behavior.
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
home: const Scaffold(
|
|
drawer: Drawer(clipBehavior: Clip.hardEdge, shape: RoundedRectangleBorder()),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Open the drawer again.
|
|
state.openDrawer();
|
|
await tester.pump();
|
|
await tester.pump(const Duration(seconds: 1));
|
|
|
|
// Clip behavior is now updated.
|
|
material = tester.widget<Material>(drawerMaterial);
|
|
expect(material.clipBehavior, Clip.hardEdge);
|
|
});
|
|
});
|
|
|
|
// Regression test for https://github.com/flutter/flutter/issues/6537
|
|
testWidgets('Drawer and DrawerHeader do not crash at zero area', (WidgetTester tester) async {
|
|
await tester.pumpWidget(
|
|
const MaterialApp(
|
|
home: Center(
|
|
child: SizedBox.shrink(
|
|
child: Drawer(child: DrawerHeader(child: Text('X'))),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
expect(tester.getSize(find.byType(Drawer)), Size.zero);
|
|
expect(tester.getSize(find.byType(DrawerHeader)), Size.zero);
|
|
});
|
|
}
|