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: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> * Update packages/flutter/lib/src/material/bottom_sheet.dart Co-authored-by: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> * Update packages/flutter/lib/src/material/bottom_sheet.dart Co-authored-by: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> * Update packages/flutter/lib/src/material/bottom_sheet.dart Co-authored-by: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> * Update packages/flutter/lib/src/material/bottom_sheet.dart Co-authored-by: Pierre-Louis <6655696+guidezpl@users.noreply.github.com> * Update packages/flutter/lib/src/material/bottom_sheet.dart Co-authored-by: Pierre-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: Pierre-Louis <6655696+guidezpl@users.noreply.github.com>
This commit is contained in:
parent
d58be8c7c8
commit
9fc1fd15f6
@ -17,6 +17,7 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData {
|
||||
elevation: ${elevation("md.comp.sheet.bottom.docked.standard.container")},
|
||||
modalElevation: ${elevation("md.comp.sheet.bottom.docked.modal.container")},
|
||||
shape: ${shape("md.comp.sheet.bottom.docked.container")},
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
);
|
||||
|
||||
final BuildContext context;
|
||||
@ -30,6 +31,12 @@ class _${blockName}DefaultsM3 extends BottomSheetThemeData {
|
||||
|
||||
@override
|
||||
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")};
|
||||
}
|
||||
''';
|
||||
}
|
||||
|
@ -5,16 +5,19 @@
|
||||
import 'dart:ui' show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'bottom_sheet_theme.dart';
|
||||
import 'color_scheme.dart';
|
||||
import 'colors.dart';
|
||||
import 'constants.dart';
|
||||
import 'curves.dart';
|
||||
import 'debug.dart';
|
||||
import 'material.dart';
|
||||
import 'material_localizations.dart';
|
||||
import 'material_state.dart';
|
||||
import 'scaffold.dart';
|
||||
import 'theme.dart';
|
||||
|
||||
@ -65,7 +68,8 @@ typedef BottomSheetDragEndHandler = void Function(
|
||||
/// sheet.
|
||||
/// * [BottomSheetThemeData], which can be used to customize the default
|
||||
/// bottom sheet property values.
|
||||
/// * <https://material.io/design/components/sheets-bottom.html>
|
||||
/// * 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>.
|
||||
class BottomSheet extends StatefulWidget {
|
||||
/// Creates a bottom sheet.
|
||||
///
|
||||
@ -76,6 +80,9 @@ class BottomSheet extends StatefulWidget {
|
||||
super.key,
|
||||
this.animationController,
|
||||
this.enableDrag = true,
|
||||
this.showDragHandle,
|
||||
this.dragHandleColor,
|
||||
this.dragHandleSize,
|
||||
this.onDragStart,
|
||||
this.onDragEnd,
|
||||
this.backgroundColor,
|
||||
@ -111,9 +118,34 @@ class BottomSheet extends StatefulWidget {
|
||||
/// If true, the bottom sheet can be dragged up and down and dismissed by
|
||||
/// swiping downwards.
|
||||
///
|
||||
/// If [showDragHandle] is true, this only applies to the content below the drag handle,
|
||||
/// because the drag handle is always draggable.
|
||||
///
|
||||
/// Default is true.
|
||||
final bool enableDrag;
|
||||
|
||||
/// Specifies whether a drag handle is shown.
|
||||
///
|
||||
/// The drag handle appears at the top of the bottom sheet. The default color is
|
||||
/// [ColorScheme.onSurfaceVariant] with an opacity of 0.4 and can be customized
|
||||
/// using [dragHandleColor]. The default size is `Size(32,4)` and can be customized
|
||||
/// with [dragHandleSize].
|
||||
///
|
||||
/// If null, then the value of [BottomSheetThemeData.showDragHandle] is used. If
|
||||
/// that is also null, defaults to false.
|
||||
final bool? showDragHandle;
|
||||
|
||||
/// The bottom sheet drag handle's color.
|
||||
///
|
||||
/// Defaults to [BottomSheetThemeData.dragHandleColor].
|
||||
/// If that is also null, defaults to [ColorScheme.onSurfaceVariant]
|
||||
/// with an opacity of 0.4.
|
||||
final Color? dragHandleColor;
|
||||
|
||||
/// Defaults to [BottomSheetThemeData.dragHandleSize].
|
||||
/// If that is also null, defaults to Size(32, 4).
|
||||
final Size? dragHandleSize;
|
||||
|
||||
/// Called when the user begins dragging the bottom sheet vertically, if
|
||||
/// [enableDrag] is true.
|
||||
///
|
||||
@ -222,14 +254,19 @@ class _BottomSheetState extends State<BottomSheet> {
|
||||
|
||||
bool get _dismissUnderway => widget.animationController!.status == AnimationStatus.reverse;
|
||||
|
||||
Set<MaterialState> dragHandleMaterialState = <MaterialState>{};
|
||||
|
||||
void _handleDragStart(DragStartDetails details) {
|
||||
setState(() {
|
||||
dragHandleMaterialState.add(MaterialState.dragged);
|
||||
});
|
||||
widget.onDragStart?.call(details);
|
||||
}
|
||||
|
||||
void _handleDragUpdate(DragUpdateDetails details) {
|
||||
assert(
|
||||
widget.enableDrag && widget.animationController != null,
|
||||
"'BottomSheet.animationController' can not be null when 'BottomSheet.enableDrag' is true. "
|
||||
(widget.enableDrag || (widget.showDragHandle?? false)) && widget.animationController != null,
|
||||
"'BottomSheet.animationController' cannot be null when 'BottomSheet.enableDrag' or 'BottomSheet.showDragHandle' is true. "
|
||||
"Use 'BottomSheet.createAnimationController' to create one, or provide another AnimationController.",
|
||||
);
|
||||
if (_dismissUnderway) {
|
||||
@ -240,13 +277,16 @@ class _BottomSheetState extends State<BottomSheet> {
|
||||
|
||||
void _handleDragEnd(DragEndDetails details) {
|
||||
assert(
|
||||
widget.enableDrag && widget.animationController != null,
|
||||
"'BottomSheet.animationController' can not be null when 'BottomSheet.enableDrag' is true. "
|
||||
(widget.enableDrag || (widget.showDragHandle?? false)) && widget.animationController != null,
|
||||
"'BottomSheet.animationController' cannot be null when 'BottomSheet.enableDrag' or 'BottomSheet.showDragHandle' is true. "
|
||||
"Use 'BottomSheet.createAnimationController' to create one, or provide another AnimationController.",
|
||||
);
|
||||
if (_dismissUnderway) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
dragHandleMaterialState.remove(MaterialState.dragged);
|
||||
});
|
||||
bool isClosing = false;
|
||||
if (details.velocity.pixelsPerSecond.dy > _minFlingVelocity) {
|
||||
final double flingVelocity = -details.velocity.pixelsPerSecond.dy / _childHeight;
|
||||
@ -282,17 +322,55 @@ class _BottomSheetState extends State<BottomSheet> {
|
||||
return false;
|
||||
}
|
||||
|
||||
void _handleDragHandleHover(bool hovering) {
|
||||
if (hovering != dragHandleMaterialState.contains(MaterialState.hovered)) {
|
||||
setState(() {
|
||||
if(hovering){
|
||||
dragHandleMaterialState.add(MaterialState.hovered);
|
||||
}
|
||||
else{
|
||||
dragHandleMaterialState.remove(MaterialState.hovered);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final BottomSheetThemeData bottomSheetTheme = Theme.of(context).bottomSheetTheme;
|
||||
final BottomSheetThemeData defaults = Theme.of(context).useMaterial3 ? _BottomSheetDefaultsM3(context) : const BottomSheetThemeData();
|
||||
final BoxConstraints? constraints = widget.constraints ?? bottomSheetTheme.constraints;
|
||||
final bool useMaterial3 = Theme.of(context).useMaterial3;
|
||||
final BottomSheetThemeData defaults = useMaterial3 ? _BottomSheetDefaultsM3(context) : const BottomSheetThemeData();
|
||||
final BoxConstraints? constraints = widget.constraints ?? bottomSheetTheme.constraints ?? defaults.constraints;
|
||||
final Color? color = widget.backgroundColor ?? bottomSheetTheme.backgroundColor ?? defaults.backgroundColor;
|
||||
final Color? surfaceTintColor = bottomSheetTheme.surfaceTintColor ?? defaults.surfaceTintColor;
|
||||
final Color? shadowColor = widget.shadowColor ?? bottomSheetTheme.shadowColor ?? defaults.shadowColor;
|
||||
final double elevation = widget.elevation ?? bottomSheetTheme.elevation ?? defaults.elevation ?? 0;
|
||||
final ShapeBorder? shape = widget.shape ?? bottomSheetTheme.shape ?? defaults.shape;
|
||||
final Clip clipBehavior = widget.clipBehavior ?? bottomSheetTheme.clipBehavior ?? Clip.none;
|
||||
final bool showDragHandle = widget.showDragHandle ?? (widget.enableDrag && (bottomSheetTheme.showDragHandle ?? false));
|
||||
|
||||
Widget? dragHandle;
|
||||
if (showDragHandle){
|
||||
dragHandle = _DragHandle(
|
||||
onSemanticsTap: widget.onClosing,
|
||||
handleHover: _handleDragHandleHover,
|
||||
materialState: dragHandleMaterialState,
|
||||
dragHandleColor: widget.dragHandleColor,
|
||||
dragHandleSize: widget.dragHandleSize,
|
||||
);
|
||||
// Only add [GestureDetector] to the drag handle when the rest of the
|
||||
// bottom sheet is not draggable. If the whole bottom sheet is draggable,
|
||||
// no need to add it.
|
||||
if(!widget.enableDrag) {
|
||||
dragHandle = GestureDetector(
|
||||
onVerticalDragStart: _handleDragStart,
|
||||
onVerticalDragUpdate: _handleDragUpdate,
|
||||
onVerticalDragEnd: _handleDragEnd,
|
||||
excludeFromSemantics: true,
|
||||
child: dragHandle,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget bottomSheet = Material(
|
||||
key: _childKey,
|
||||
@ -304,7 +382,18 @@ class _BottomSheetState extends State<BottomSheet> {
|
||||
clipBehavior: clipBehavior,
|
||||
child: NotificationListener<DraggableScrollableNotification>(
|
||||
onNotification: extentChanged,
|
||||
child: widget.builder(context),
|
||||
child: !showDragHandle
|
||||
? widget.builder(context)
|
||||
: Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: <Widget>[
|
||||
dragHandle!,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: kMinInteractiveDimension),
|
||||
child: widget.builder(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -335,6 +424,55 @@ class _BottomSheetState extends State<BottomSheet> {
|
||||
|
||||
typedef _SizeChangeCallback<Size> = void Function(Size);
|
||||
|
||||
class _DragHandle extends StatelessWidget {
|
||||
const _DragHandle({
|
||||
required this.onSemanticsTap,
|
||||
required this.handleHover,
|
||||
required this.materialState,
|
||||
this.dragHandleColor,
|
||||
this.dragHandleSize,
|
||||
});
|
||||
|
||||
final VoidCallback? onSemanticsTap;
|
||||
final Function(bool) handleHover;
|
||||
final Set<MaterialState> materialState;
|
||||
final Color? dragHandleColor;
|
||||
final Size? dragHandleSize;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final BottomSheetThemeData bottomSheetTheme = Theme.of(context).bottomSheetTheme;
|
||||
final BottomSheetThemeData m3Defaults = _BottomSheetDefaultsM3(context);
|
||||
final Size handleSize = dragHandleSize ?? bottomSheetTheme.dragHandleSize ?? m3Defaults.dragHandleSize!;
|
||||
|
||||
return MouseRegion(
|
||||
onEnter: (PointerEnterEvent event) => handleHover(true),
|
||||
onExit: (PointerExitEvent event) => handleHover(false),
|
||||
child: Semantics(
|
||||
label: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
container: true,
|
||||
onTap: onSemanticsTap,
|
||||
child: SizedBox(
|
||||
height: kMinInteractiveDimension,
|
||||
width: kMinInteractiveDimension,
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: handleSize.height,
|
||||
width: handleSize.width,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(handleSize.height/2),
|
||||
color: MaterialStateProperty.resolveAs<Color?>(dragHandleColor, materialState)
|
||||
?? MaterialStateProperty.resolveAs<Color?>(bottomSheetTheme.dragHandleColor, materialState)
|
||||
?? m3Defaults.dragHandleColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
|
||||
|
||||
const _BottomSheetLayoutWithSizeListener({
|
||||
@ -500,6 +638,7 @@ class _ModalBottomSheet<T> extends StatefulWidget {
|
||||
this.constraints,
|
||||
this.isScrollControlled = false,
|
||||
this.enableDrag = true,
|
||||
this.showDragHandle = false,
|
||||
});
|
||||
|
||||
final ModalBottomSheetRoute<T> route;
|
||||
@ -510,6 +649,7 @@ class _ModalBottomSheet<T> extends StatefulWidget {
|
||||
final Clip? clipBehavior;
|
||||
final BoxConstraints? constraints;
|
||||
final bool enableDrag;
|
||||
final bool showDragHandle;
|
||||
|
||||
@override
|
||||
_ModalBottomSheetState<T> createState() => _ModalBottomSheetState<T>();
|
||||
@ -571,6 +711,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
|
||||
clipBehavior: widget.clipBehavior,
|
||||
constraints: widget.constraints,
|
||||
enableDrag: widget.enableDrag,
|
||||
showDragHandle: widget.showDragHandle,
|
||||
onDragStart: handleDragStart,
|
||||
onDragEnd: handleDragEnd,
|
||||
),
|
||||
@ -658,7 +799,8 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
|
||||
/// and then becomes scrollable once it reaches its maximum size.
|
||||
/// * [DisplayFeatureSubScreen], which documents the specifics of how
|
||||
/// [DisplayFeature]s can split the screen into sub-screens.
|
||||
/// * <https://material.io/design/components/sheets-bottom.html#modal-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>.
|
||||
class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
/// A modal bottom sheet route.
|
||||
ModalBottomSheetRoute({
|
||||
@ -674,6 +816,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
this.modalBarrierColor,
|
||||
this.isDismissible = true,
|
||||
this.enableDrag = true,
|
||||
this.showDragHandle,
|
||||
required this.isScrollControlled,
|
||||
super.settings,
|
||||
this.transitionAnimationController,
|
||||
@ -773,9 +916,22 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
/// If true, the bottom sheet can be dragged up and down and dismissed by
|
||||
/// swiping downwards.
|
||||
///
|
||||
/// This applies to the content below the drag handle, if showDragHandle is true.
|
||||
///
|
||||
/// Defaults is true.
|
||||
final bool enableDrag;
|
||||
|
||||
/// Specifies whether a drag handle is shown.
|
||||
///
|
||||
/// The drag handle appears at the top of the bottom sheet. The default color is
|
||||
/// [ColorScheme.onSurfaceVariant] with an opacity of 0.4 and can be customized
|
||||
/// using dragHandleColor. The default size is `Size(32,4)` and can be customized
|
||||
/// with dragHandleSize.
|
||||
///
|
||||
/// If null, then the value of [BottomSheetThemeData.showDragHandle] is used. If
|
||||
/// that is also null, defaults to false.
|
||||
final bool? showDragHandle;
|
||||
|
||||
/// The animation controller that controls the bottom sheet's entrance and
|
||||
/// exit animations.
|
||||
///
|
||||
@ -875,6 +1031,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
|
||||
constraints: constraints,
|
||||
isScrollControlled: isScrollControlled,
|
||||
enableDrag: enableDrag,
|
||||
showDragHandle: showDragHandle ?? (enableDrag && (sheetTheme.showDragHandle ?? false)),
|
||||
);
|
||||
},
|
||||
),
|
||||
@ -1023,7 +1180,8 @@ class _BottomSheetSuspendedCurve extends ParametricCurve<double> {
|
||||
/// and then becomes scrollable once it reaches its maximum size.
|
||||
/// * [DisplayFeatureSubScreen], which documents the specifics of how
|
||||
/// [DisplayFeature]s can split the screen into sub-screens.
|
||||
/// * <https://material.io/design/components/sheets-bottom.html#modal-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>.
|
||||
Future<T?> showModalBottomSheet<T>({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
@ -1037,6 +1195,7 @@ Future<T?> showModalBottomSheet<T>({
|
||||
bool useRootNavigator = false,
|
||||
bool isDismissible = true,
|
||||
bool enableDrag = true,
|
||||
bool? showDragHandle,
|
||||
bool useSafeArea = false,
|
||||
RouteSettings? routeSettings,
|
||||
AnimationController? transitionAnimationController,
|
||||
@ -1061,6 +1220,7 @@ Future<T?> showModalBottomSheet<T>({
|
||||
isDismissible: isDismissible,
|
||||
modalBarrierColor: barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,
|
||||
enableDrag: enableDrag,
|
||||
showDragHandle: showDragHandle,
|
||||
settings: routeSettings,
|
||||
transitionAnimationController: transitionAnimationController,
|
||||
anchorPoint: anchorPoint,
|
||||
@ -1111,7 +1271,8 @@ Future<T?> showModalBottomSheet<T>({
|
||||
/// * [showModalBottomSheet], which can be used to display a modal bottom
|
||||
/// sheet.
|
||||
/// * [Scaffold.of], for information about how to obtain the [BuildContext].
|
||||
/// * <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>({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
@ -1154,19 +1315,26 @@ class _BottomSheetDefaultsM3 extends BottomSheetThemeData {
|
||||
elevation: 1.0,
|
||||
modalElevation: 1.0,
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(28.0))),
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
);
|
||||
|
||||
final BuildContext context;
|
||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
Color? get backgroundColor => _colors.surface;
|
||||
Color get backgroundColor => _colors.surface;
|
||||
|
||||
@override
|
||||
Color? get surfaceTintColor => _colors.surfaceTint;
|
||||
Color get surfaceTintColor => _colors.surfaceTint;
|
||||
|
||||
@override
|
||||
Color? get shadowColor => Colors.transparent;
|
||||
Color get shadowColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Color get dragHandleColor => Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.4);
|
||||
|
||||
@override
|
||||
Size get dragHandleSize => const Size(32, 4);
|
||||
}
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES - BottomSheet
|
||||
|
@ -36,6 +36,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
this.shadowColor,
|
||||
this.modalElevation,
|
||||
this.shape,
|
||||
this.showDragHandle,
|
||||
this.dragHandleColor,
|
||||
this.dragHandleSize,
|
||||
this.clipBehavior,
|
||||
this.constraints,
|
||||
});
|
||||
@ -80,6 +83,15 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
/// [BottomSheet] is rectangular.
|
||||
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].
|
||||
///
|
||||
/// If null, [BottomSheet] uses [Clip.none].
|
||||
@ -101,6 +113,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
Color? shadowColor,
|
||||
double? modalElevation,
|
||||
ShapeBorder? shape,
|
||||
bool? showDragHandle,
|
||||
Color? dragHandleColor,
|
||||
Size? dragHandleSize,
|
||||
Clip? clipBehavior,
|
||||
BoxConstraints? constraints,
|
||||
}) {
|
||||
@ -113,6 +128,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
shadowColor: shadowColor ?? this.shadowColor,
|
||||
modalElevation: modalElevation ?? this.modalElevation,
|
||||
shape: shape ?? this.shape,
|
||||
showDragHandle: showDragHandle ?? this.showDragHandle,
|
||||
dragHandleColor: dragHandleColor ?? this.dragHandleColor,
|
||||
dragHandleSize: dragHandleSize ?? this.dragHandleSize,
|
||||
clipBehavior: clipBehavior ?? this.clipBehavior,
|
||||
constraints: constraints ?? this.constraints,
|
||||
);
|
||||
@ -136,6 +154,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
|
||||
modalElevation: lerpDouble(a?.modalElevation, b?.modalElevation, 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,
|
||||
constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t),
|
||||
);
|
||||
@ -151,6 +172,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
shadowColor,
|
||||
modalElevation,
|
||||
shape,
|
||||
showDragHandle,
|
||||
dragHandleColor,
|
||||
dragHandleSize,
|
||||
clipBehavior,
|
||||
constraints,
|
||||
);
|
||||
@ -172,6 +196,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
&& other.modalBarrierColor == modalBarrierColor
|
||||
&& other.modalElevation == modalElevation
|
||||
&& other.shape == shape
|
||||
&& other.showDragHandle == showDragHandle
|
||||
&& other.dragHandleColor == dragHandleColor
|
||||
&& other.dragHandleSize == dragHandleSize
|
||||
&& other.clipBehavior == clipBehavior
|
||||
&& other.constraints == constraints;
|
||||
}
|
||||
@ -187,6 +214,9 @@ class BottomSheetThemeData with Diagnosticable {
|
||||
properties.add(ColorProperty('modalBarrierColor', modalBarrierColor, defaultValue: null));
|
||||
properties.add(DoubleProperty('modalElevation', modalElevation, 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<BoxConstraints>('constraints', constraints, defaultValue: null));
|
||||
}
|
||||
|
@ -2425,7 +2425,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
||||
/// * [showModalBottomSheet], which can be used to display a modal bottom
|
||||
/// sheet.
|
||||
/// * [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>(
|
||||
WidgetBuilder builder, {
|
||||
Color? backgroundColor,
|
||||
|
@ -1008,6 +1008,138 @@ void main() {
|
||||
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 {
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
@ -1617,6 +1749,21 @@ void main() {
|
||||
});
|
||||
|
||||
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 {
|
||||
await tester.pumpWidget(const MaterialApp(
|
||||
|
@ -32,6 +32,8 @@ void main() {
|
||||
expect(bottomSheetTheme.shape, null);
|
||||
expect(bottomSheetTheme.clipBehavior, null);
|
||||
expect(bottomSheetTheme.constraints, null);
|
||||
expect(bottomSheetTheme.dragHandleColor, null);
|
||||
expect(bottomSheetTheme.dragHandleSize, null);
|
||||
});
|
||||
|
||||
testWidgets('Default BottomSheetThemeData debugFillProperties', (WidgetTester tester) async {
|
||||
@ -55,6 +57,8 @@ void main() {
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
constraints: BoxConstraints(minWidth: 200, maxWidth: 640),
|
||||
dragHandleColor: Color(0xFFFFFFFF),
|
||||
dragHandleSize: Size(20, 20)
|
||||
).debugFillProperties(builder);
|
||||
|
||||
final List<String> description = builder.properties
|
||||
@ -67,6 +71,8 @@ void main() {
|
||||
'elevation: 2.0',
|
||||
'shadowColor: Color(0xff00ffff)',
|
||||
'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))',
|
||||
'dragHandleColor: Color(0xffffffff)',
|
||||
'dragHandleSize: Size(20.0, 20.0)',
|
||||
'clipBehavior: Clip.antiAlias',
|
||||
'constraints: BoxConstraints(200.0<=w<=640.0, 0.0<=h<=Infinity)',
|
||||
]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user