Drawer edge drag width improvements (#37492)
* Added customizable drawer edge drag width parameter to Drawer and Scaffold * Fix Drawer drag area width for notched devices * Update Drawer tests to reflect necessary LTR and RTL Drawer edge widths
This commit is contained in:
parent
232dce966b
commit
2003432cd8
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/gestures.dart' show DragStartBehavior;
|
||||
@ -182,6 +180,7 @@ class DrawerController extends StatefulWidget {
|
||||
this.drawerCallback,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.scrimColor,
|
||||
this.edgeDragWidth,
|
||||
}) : assert(child != null),
|
||||
assert(dragStartBehavior != null),
|
||||
assert(alignment != null),
|
||||
@ -228,6 +227,17 @@ class DrawerController extends StatefulWidget {
|
||||
/// By default, the color used is [Colors.black54]
|
||||
final Color scrimColor;
|
||||
|
||||
/// The width of the area within which a horizontal swipe will open the
|
||||
/// drawer.
|
||||
///
|
||||
/// By default, the value used is 20.0 added to the padding edge of
|
||||
/// `MediaQuery.of(context).padding` that corresponds to [alignment].
|
||||
/// This ensures that the drag area for notched devices is not obscured. For
|
||||
/// example, if [alignment] is set to [DrawerAlignment.start] and
|
||||
/// `TextDirection.of(context)` is set to [TextDirection.ltr],
|
||||
/// 20.0 will be added to `MediaQuery.of(context).padding.left`.
|
||||
final double edgeDragWidth;
|
||||
|
||||
@override
|
||||
DrawerControllerState createState() => DrawerControllerState();
|
||||
}
|
||||
@ -427,12 +437,24 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
|
||||
Widget _buildDrawer(BuildContext context) {
|
||||
final bool drawerIsStart = widget.alignment == DrawerAlignment.start;
|
||||
final EdgeInsets padding = MediaQuery.of(context).padding;
|
||||
double dragAreaWidth = drawerIsStart ? padding.left : padding.right;
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
|
||||
if (Directionality.of(context) == TextDirection.rtl)
|
||||
dragAreaWidth = drawerIsStart ? padding.right : padding.left;
|
||||
double dragAreaWidth = widget.edgeDragWidth;
|
||||
if (widget.edgeDragWidth == null) {
|
||||
switch (textDirection) {
|
||||
case TextDirection.ltr: {
|
||||
dragAreaWidth = _kEdgeDragWidth +
|
||||
(drawerIsStart ? padding.left : padding.right);
|
||||
}
|
||||
break;
|
||||
case TextDirection.rtl: {
|
||||
dragAreaWidth = _kEdgeDragWidth +
|
||||
(drawerIsStart ? padding.right : padding.left);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dragAreaWidth = max(dragAreaWidth, _kEdgeDragWidth);
|
||||
if (_controller.status == AnimationStatus.dismissed) {
|
||||
return Align(
|
||||
alignment: _drawerOuterAlignment,
|
||||
|
@ -968,6 +968,7 @@ class Scaffold extends StatefulWidget {
|
||||
this.drawerDragStartBehavior = DragStartBehavior.start,
|
||||
this.extendBody = false,
|
||||
this.drawerScrimColor,
|
||||
this.drawerEdgeDragWidth,
|
||||
}) : assert(primary != null),
|
||||
assert(extendBody != null),
|
||||
assert(drawerDragStartBehavior != null),
|
||||
@ -1140,6 +1141,16 @@ class Scaffold extends StatefulWidget {
|
||||
/// {@macro flutter.material.drawer.dragStartBehavior}
|
||||
final DragStartBehavior drawerDragStartBehavior;
|
||||
|
||||
/// The width of the area within which a horizontal swipe will open the
|
||||
/// drawer.
|
||||
///
|
||||
/// By default, the value used is 20.0 added to the padding edge of
|
||||
/// `MediaQuery.of(context).padding` that corresponds to [alignment].
|
||||
/// This ensures that the drag area for notched devices is not obscured. For
|
||||
/// example, if `TextDirection.of(context)` is set to [TextDirection.ltr],
|
||||
/// 20.0 will be added to `MediaQuery.of(context).padding.left`.
|
||||
final double drawerEdgeDragWidth;
|
||||
|
||||
/// The state from the closest instance of this class that encloses the given context.
|
||||
///
|
||||
/// {@tool snippet --template=freeform}
|
||||
@ -1984,6 +1995,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
|
||||
drawerCallback: _endDrawerOpenedCallback,
|
||||
dragStartBehavior: widget.drawerDragStartBehavior,
|
||||
scrimColor: widget.drawerScrimColor,
|
||||
edgeDragWidth: widget.drawerEdgeDragWidth,
|
||||
),
|
||||
_ScaffoldSlot.endDrawer,
|
||||
// remove the side padding from the side we're not touching
|
||||
@ -2007,6 +2019,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
|
||||
drawerCallback: _drawerOpenedCallback,
|
||||
dragStartBehavior: widget.drawerDragStartBehavior,
|
||||
scrimColor: widget.drawerScrimColor,
|
||||
edgeDragWidth: widget.drawerEdgeDragWidth,
|
||||
),
|
||||
_ScaffoldSlot.drawer,
|
||||
// remove the side padding from the side we're not touching
|
||||
|
@ -1345,16 +1345,15 @@ void main() {
|
||||
expect(find.text('drawer'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Drawer opens correctly with padding from MediaQuery', (WidgetTester tester) async {
|
||||
// The padding described by MediaQuery is larger than the default
|
||||
// drawer drag zone width which is 20.
|
||||
testWidgets('Drawer opens correctly with padding from MediaQuery (LTR)', (WidgetTester tester) async {
|
||||
const double simulatedNotchSize = 40.0;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
drawer: const Drawer(
|
||||
child: Text('drawer'),
|
||||
child: Text('Drawer'),
|
||||
),
|
||||
body: const Text('scaffold body'),
|
||||
body: const Text('Scaffold Body'),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Title'),
|
||||
@ -1364,25 +1363,23 @@ void main() {
|
||||
);
|
||||
|
||||
ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
|
||||
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.dragFrom(const Offset(35, 100), const Offset(300, 0));
|
||||
await tester.dragFrom(const Offset(simulatedNotchSize + 15.0, 100), const Offset(300, 0));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.fromLTRB(40, 0, 0, 0)
|
||||
padding: EdgeInsets.fromLTRB(simulatedNotchSize, 0, 0, 0),
|
||||
),
|
||||
child: Scaffold(
|
||||
drawer: const Drawer(
|
||||
child: Text('drawer'),
|
||||
child: Text('Drawer'),
|
||||
),
|
||||
body: const Text('scaffold body'),
|
||||
body: const Text('Scaffold Body'),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Title'),
|
||||
@ -1391,33 +1388,60 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
scaffoldState = tester.state(find.byType(Scaffold));
|
||||
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.dragFrom(const Offset(35, 100), const Offset(300, 0));
|
||||
await tester.dragFrom(
|
||||
const Offset(simulatedNotchSize + 15.0, 100),
|
||||
const Offset(300, 0),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(scaffoldState.isDrawerOpen, true);
|
||||
});
|
||||
|
||||
testWidgets('Drawer opens correctly with padding from MediaQuer (RTL)', (WidgetTester tester) async {
|
||||
// The padding described by MediaQuery is larger than the default
|
||||
// drawer drag zone width which is 20.
|
||||
testWidgets('Drawer opens correctly with padding from MediaQuery (RTL)', (WidgetTester tester) async {
|
||||
const double simulatedNotchSize = 40.0;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
drawer: const Drawer(
|
||||
child: Text('Drawer'),
|
||||
),
|
||||
body: const Text('Scaffold Body'),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Title'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final double scaffoldWidth = tester.renderObject<RenderBox>(
|
||||
find.byType(Scaffold),
|
||||
).size.width;
|
||||
ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.dragFrom(
|
||||
Offset(scaffoldWidth - simulatedNotchSize - 15.0, 100),
|
||||
const Offset(-300, 0),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 40, 0)
|
||||
padding: EdgeInsets.fromLTRB(0, 0, simulatedNotchSize, 0),
|
||||
),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
drawer: const Drawer(
|
||||
child: Text('drawer'),
|
||||
child: Text('Drawer'),
|
||||
),
|
||||
body: const Text('scaffold body'),
|
||||
body: const Text('Scaffold body'),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Title'),
|
||||
@ -1427,21 +1451,66 @@ void main() {
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
|
||||
|
||||
scaffoldState = tester.state(find.byType(Scaffold));
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.dragFrom(const Offset(765, 100), const Offset(-300, 0));
|
||||
await tester.dragFrom(
|
||||
Offset(scaffoldWidth - simulatedNotchSize - 15.0, 100),
|
||||
const Offset(-300, 0),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(scaffoldState.isDrawerOpen, true);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('Drawer opens correctly with custom edgeDragWidth', (WidgetTester tester) async {
|
||||
// The default edge drag width is 20.0.
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
drawer: const Drawer(
|
||||
child: Text('Drawer'),
|
||||
),
|
||||
body: const Text('Scaffold body'),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Title'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.dragFrom(const Offset(35, 100), const Offset(300, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
drawer: const Drawer(
|
||||
child: Text('Drawer'),
|
||||
),
|
||||
drawerEdgeDragWidth: 40.0,
|
||||
body: const Text('Scaffold Body'),
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text('Title'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
scaffoldState = tester.state(find.byType(Scaffold));
|
||||
expect(scaffoldState.isDrawerOpen, false);
|
||||
|
||||
await tester.dragFrom(const Offset(35, 100), const Offset(300, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(scaffoldState.isDrawerOpen, true);
|
||||
});
|
||||
|
||||
testWidgets('Nested scaffold body insets', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/20295
|
||||
|
||||
final Key bodyKey = UniqueKey();
|
||||
|
||||
Widget buildFrame(bool innerResizeToAvoidBottomInset, bool outerResizeToAvoidBottomInset) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user