Unverified Commit 9fc1fd15 authored by hangyu's avatar hangyu Committed by GitHub

Update Material 3 bottom sheet (#122445)

* M3 bottomsheet

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update bottom_sheet.dart

* Update packages/flutter/lib/src/material/bottom_sheet.dart
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>

* Update packages/flutter/lib/src/material/bottom_sheet.dart
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>

* Update packages/flutter/lib/src/material/bottom_sheet.dart
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>

* Update packages/flutter/lib/src/material/bottom_sheet.dart
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>

* Update packages/flutter/lib/src/material/bottom_sheet.dart
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>

* Update packages/flutter/lib/src/material/bottom_sheet.dart
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>

* Update bottom_sheet.dart

Update bottom_sheet_test.dart

Update bottom_sheet.dart

* showDragHandle defaults to false

* fix test

---------
Co-authored-by: 's avatarPierre-Louis <6655696+guidezpl@users.noreply.github.com>
parent d58be8c7
...@@ -17,6 +17,7 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData { ...@@ -17,6 +17,7 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData {
elevation: ${elevation("md.comp.sheet.bottom.docked.standard.container")}, elevation: ${elevation("md.comp.sheet.bottom.docked.standard.container")},
modalElevation: ${elevation("md.comp.sheet.bottom.docked.modal.container")}, modalElevation: ${elevation("md.comp.sheet.bottom.docked.modal.container")},
shape: ${shape("md.comp.sheet.bottom.docked.container")}, shape: ${shape("md.comp.sheet.bottom.docked.container")},
constraints: const BoxConstraints(maxWidth: 640),
); );
final BuildContext context; final BuildContext context;
...@@ -30,6 +31,12 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData { ...@@ -30,6 +31,12 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData {
@override @override
Color? get shadowColor => Colors.transparent; Color? get shadowColor => Colors.transparent;
@override
Color? get dragHandleColor => ${componentColor("md.comp.sheet.bottom.docked.drag-handle")};
@override
Size? get dragHandleSize => ${size("md.comp.sheet.bottom.docked.drag-handle")};
} }
'''; ''';
} }
...@@ -36,6 +36,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -36,6 +36,9 @@ class BottomSheetThemeData with Diagnosticable {
this.shadowColor, this.shadowColor,
this.modalElevation, this.modalElevation,
this.shape, this.shape,
this.showDragHandle,
this.dragHandleColor,
this.dragHandleSize,
this.clipBehavior, this.clipBehavior,
this.constraints, this.constraints,
}); });
...@@ -80,6 +83,15 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -80,6 +83,15 @@ class BottomSheetThemeData with Diagnosticable {
/// [BottomSheet] is rectangular. /// [BottomSheet] is rectangular.
final ShapeBorder? shape; final ShapeBorder? shape;
/// Overrides the default value for [BottomSheet.showDragHandle].
final bool? showDragHandle;
/// Overrides the default value for [BottomSheet.dragHandleColor].
final Color? dragHandleColor;
/// Overrides the default value for [BottomSheet.dragHandleSize].
final Size? dragHandleSize;
/// Overrides the default value for [BottomSheet.clipBehavior]. /// Overrides the default value for [BottomSheet.clipBehavior].
/// ///
/// If null, [BottomSheet] uses [Clip.none]. /// If null, [BottomSheet] uses [Clip.none].
...@@ -101,6 +113,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -101,6 +113,9 @@ class BottomSheetThemeData with Diagnosticable {
Color? shadowColor, Color? shadowColor,
double? modalElevation, double? modalElevation,
ShapeBorder? shape, ShapeBorder? shape,
bool? showDragHandle,
Color? dragHandleColor,
Size? dragHandleSize,
Clip? clipBehavior, Clip? clipBehavior,
BoxConstraints? constraints, BoxConstraints? constraints,
}) { }) {
...@@ -113,6 +128,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -113,6 +128,9 @@ class BottomSheetThemeData with Diagnosticable {
shadowColor: shadowColor ?? this.shadowColor, shadowColor: shadowColor ?? this.shadowColor,
modalElevation: modalElevation ?? this.modalElevation, modalElevation: modalElevation ?? this.modalElevation,
shape: shape ?? this.shape, shape: shape ?? this.shape,
showDragHandle: showDragHandle ?? this.showDragHandle,
dragHandleColor: dragHandleColor ?? this.dragHandleColor,
dragHandleSize: dragHandleSize ?? this.dragHandleSize,
clipBehavior: clipBehavior ?? this.clipBehavior, clipBehavior: clipBehavior ?? this.clipBehavior,
constraints: constraints ?? this.constraints, constraints: constraints ?? this.constraints,
); );
...@@ -136,6 +154,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -136,6 +154,9 @@ class BottomSheetThemeData with Diagnosticable {
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t), shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
modalElevation: lerpDouble(a?.modalElevation, b?.modalElevation, t), modalElevation: lerpDouble(a?.modalElevation, b?.modalElevation, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t), shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
showDragHandle: t < 0.5 ? a?.showDragHandle : b?.showDragHandle,
dragHandleColor: Color.lerp(a?.dragHandleColor, b?.dragHandleColor, t),
dragHandleSize: Size.lerp(a?.dragHandleSize, b?.dragHandleSize, t),
clipBehavior: t < 0.5 ? a?.clipBehavior : b?.clipBehavior, clipBehavior: t < 0.5 ? a?.clipBehavior : b?.clipBehavior,
constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t), constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t),
); );
...@@ -151,6 +172,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -151,6 +172,9 @@ class BottomSheetThemeData with Diagnosticable {
shadowColor, shadowColor,
modalElevation, modalElevation,
shape, shape,
showDragHandle,
dragHandleColor,
dragHandleSize,
clipBehavior, clipBehavior,
constraints, constraints,
); );
...@@ -172,6 +196,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -172,6 +196,9 @@ class BottomSheetThemeData with Diagnosticable {
&& other.modalBarrierColor == modalBarrierColor && other.modalBarrierColor == modalBarrierColor
&& other.modalElevation == modalElevation && other.modalElevation == modalElevation
&& other.shape == shape && other.shape == shape
&& other.showDragHandle == showDragHandle
&& other.dragHandleColor == dragHandleColor
&& other.dragHandleSize == dragHandleSize
&& other.clipBehavior == clipBehavior && other.clipBehavior == clipBehavior
&& other.constraints == constraints; && other.constraints == constraints;
} }
...@@ -187,6 +214,9 @@ class BottomSheetThemeData with Diagnosticable { ...@@ -187,6 +214,9 @@ class BottomSheetThemeData with Diagnosticable {
properties.add(ColorProperty('modalBarrierColor', modalBarrierColor, defaultValue: null)); properties.add(ColorProperty('modalBarrierColor', modalBarrierColor, defaultValue: null));
properties.add(DoubleProperty('modalElevation', modalElevation, defaultValue: null)); properties.add(DoubleProperty('modalElevation', modalElevation, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null)); properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('showDragHandle', showDragHandle, defaultValue: null));
properties.add(ColorProperty('dragHandleColor', dragHandleColor, defaultValue: null));
properties.add(DiagnosticsProperty<Size>('dragHandleSize', dragHandleSize, defaultValue: null));
properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: null)); properties.add(DiagnosticsProperty<Clip>('clipBehavior', clipBehavior, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null)); properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null));
} }
......
...@@ -2425,7 +2425,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto ...@@ -2425,7 +2425,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
/// * [showModalBottomSheet], which can be used to display a modal bottom /// * [showModalBottomSheet], which can be used to display a modal bottom
/// sheet. /// sheet.
/// * [Scaffold.of], for information about how to obtain the [ScaffoldState]. /// * [Scaffold.of], for information about how to obtain the [ScaffoldState].
/// * <https://material.io/design/components/sheets-bottom.html#standard-bottom-sheet> /// * The Material 2 spec at <https://m2.material.io/components/sheets-bottom>.
/// * The Material 3 spec at <https://m3.material.io/components/bottom-sheets/overview>.
PersistentBottomSheetController<T> showBottomSheet<T>( PersistentBottomSheetController<T> showBottomSheet<T>(
WidgetBuilder builder, { WidgetBuilder builder, {
Color? backgroundColor, Color? backgroundColor,
......
...@@ -1008,6 +1008,138 @@ void main() { ...@@ -1008,6 +1008,138 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('modal BottomSheet with drag handle has semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
await tester.pumpWidget(MaterialApp(
theme: ThemeData.light(useMaterial3: true),
home: Scaffold(
key: scaffoldKey,
body: const Center(child: Text('body')),
),
));
showModalBottomSheet<void>(
context: scaffoldKey.currentContext!,
showDragHandle: true,
builder: (BuildContext context) {
return 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,
children: <TestSemantics>[
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Dismiss',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
TestSemantics(
children: <TestSemantics>[
TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.dismiss],
label: 'Scrim',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
), ignoreTransform: true, ignoreRect: true, ignoreId: true));
semantics.dispose();
});
testWidgets('Drag handle color can take MaterialStateProperty', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
const Color defaultColor=Colors.blue;
const Color hoveringColor=Colors.green;
await tester.pumpWidget(MaterialApp(
theme: ThemeData.light(useMaterial3: true).copyWith(
bottomSheetTheme: BottomSheetThemeData(
dragHandleColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return hoveringColor;
}
return defaultColor;
}),
),
),
home: Scaffold(
key: scaffoldKey,
body: const Center(child: Text('body')),
),
));
showModalBottomSheet<void>(
context: scaffoldKey.currentContext!,
showDragHandle: true,
builder: (BuildContext context) {
return const Text('BottomSheet');
},
);
await tester.pump(); // bottom sheet show animation starts
await tester.pump(const Duration(seconds: 1)); // animation done
final Finder dragHandle = find.bySemanticsLabel('Dismiss');
expect(
tester.getSize(dragHandle),
const Size(48, 48),
);
final Offset center = tester.getCenter(dragHandle);
final Offset edge = tester.getTopLeft(dragHandle) - const Offset(1, 1);
// Shows default drag handle color
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1);
await gesture.addPointer(location: edge);
await tester.pump();
BoxDecoration boxDecoration=tester.widget<Container>(find.descendant(
of: dragHandle,
matching: find.byWidgetPredicate((Widget widget) => widget is Container && widget.decoration != null),
)).decoration! as BoxDecoration;
expect(boxDecoration.color, defaultColor);
// Shows hovering drag handle color
await gesture.moveTo(center);
await tester.pump();
boxDecoration = tester.widget<Container>(find.descendant(
of: dragHandle,
matching: find.byWidgetPredicate((Widget widget) => widget is Container && widget.decoration != null),
)).decoration! as BoxDecoration;
expect(boxDecoration.color, hoveringColor);
});
testWidgets('showModalBottomSheet does not use root Navigator by default', (WidgetTester tester) async { testWidgets('showModalBottomSheet does not use root Navigator by default', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
...@@ -1617,6 +1749,21 @@ void main() { ...@@ -1617,6 +1749,21 @@ void main() {
}); });
group('constraints', () { group('constraints', () {
testWidgets('default constraints are max width 640 in material 3', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light(useMaterial3: true),
home: const MediaQuery(
data: MediaQueryData(size: Size(1000, 1000)),
child: Scaffold(
body: Center(child: Text('body')),
bottomSheet: Placeholder(fallbackWidth: 800),
),
),
),
);
expect(tester.getSize(find.byType(Placeholder)).width, 640);
});
testWidgets('No constraints by default for bottomSheet property', (WidgetTester tester) async { testWidgets('No constraints by default for bottomSheet property', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp( await tester.pumpWidget(const MaterialApp(
......
...@@ -32,6 +32,8 @@ void main() { ...@@ -32,6 +32,8 @@ void main() {
expect(bottomSheetTheme.shape, null); expect(bottomSheetTheme.shape, null);
expect(bottomSheetTheme.clipBehavior, null); expect(bottomSheetTheme.clipBehavior, null);
expect(bottomSheetTheme.constraints, null); expect(bottomSheetTheme.constraints, null);
expect(bottomSheetTheme.dragHandleColor, null);
expect(bottomSheetTheme.dragHandleSize, null);
}); });
testWidgets('Default BottomSheetThemeData debugFillProperties', (WidgetTester tester) async { testWidgets('Default BottomSheetThemeData debugFillProperties', (WidgetTester tester) async {
...@@ -55,6 +57,8 @@ void main() { ...@@ -55,6 +57,8 @@ void main() {
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))), shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))),
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
constraints: BoxConstraints(minWidth: 200, maxWidth: 640), constraints: BoxConstraints(minWidth: 200, maxWidth: 640),
dragHandleColor: Color(0xFFFFFFFF),
dragHandleSize: Size(20, 20)
).debugFillProperties(builder); ).debugFillProperties(builder);
final List<String> description = builder.properties final List<String> description = builder.properties
...@@ -67,6 +71,8 @@ void main() { ...@@ -67,6 +71,8 @@ void main() {
'elevation: 2.0', 'elevation: 2.0',
'shadowColor: Color(0xff00ffff)', 'shadowColor: Color(0xff00ffff)',
'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))', 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))',
'dragHandleColor: Color(0xffffffff)',
'dragHandleSize: Size(20.0, 20.0)',
'clipBehavior: Clip.antiAlias', 'clipBehavior: Clip.antiAlias',
'constraints: BoxConstraints(200.0<=w<=640.0, 0.0<=h<=Infinity)', 'constraints: BoxConstraints(200.0<=w<=640.0, 0.0<=h<=Infinity)',
]); ]);
......
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