Unverified Commit 9f374f12 authored by Alex Li's avatar Alex Li Committed by GitHub

🚀 Expose `scrollControlDisabledMaxHeightRatio` to the modal bottom sheet (#129688)

Adding the `scrollControlDisabledMaxHeightRatio` parameter for modal bottom sheet widgets, and using the default value `9.0 / 16.0` to avoid breaking.

Resolves #129690.
parent 300c5d82
...@@ -26,6 +26,7 @@ const Duration _bottomSheetExitDuration = Duration(milliseconds: 200); ...@@ -26,6 +26,7 @@ const Duration _bottomSheetExitDuration = Duration(milliseconds: 200);
const Curve _modalBottomSheetCurve = decelerateEasing; const Curve _modalBottomSheetCurve = decelerateEasing;
const double _minFlingVelocity = 700.0; const double _minFlingVelocity = 700.0;
const double _closeProgressThreshold = 0.5; const double _closeProgressThreshold = 0.5;
const double _defaultScrollControlDisabledMaxHeightRatio = 9.0 / 16.0;
/// A callback for when the user begins dragging the bottom sheet. /// A callback for when the user begins dragging the bottom sheet.
/// ///
...@@ -471,24 +472,26 @@ class _DragHandle extends StatelessWidget { ...@@ -471,24 +472,26 @@ class _DragHandle extends StatelessWidget {
} }
class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget { class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
const _BottomSheetLayoutWithSizeListener({ const _BottomSheetLayoutWithSizeListener({
required this.onChildSizeChanged,
required this.animationValue, required this.animationValue,
required this.isScrollControlled, required this.isScrollControlled,
required this.onChildSizeChanged, required this.scrollControlDisabledMaxHeightRatio,
super.child, super.child,
}); });
final _SizeChangeCallback<Size> onChildSizeChanged;
final double animationValue; final double animationValue;
final bool isScrollControlled; final bool isScrollControlled;
final _SizeChangeCallback<Size> onChildSizeChanged; final double scrollControlDisabledMaxHeightRatio;
@override @override
_RenderBottomSheetLayoutWithSizeListener createRenderObject(BuildContext context) { _RenderBottomSheetLayoutWithSizeListener createRenderObject(BuildContext context) {
return _RenderBottomSheetLayoutWithSizeListener( return _RenderBottomSheetLayoutWithSizeListener(
onChildSizeChanged: onChildSizeChanged,
animationValue: animationValue, animationValue: animationValue,
isScrollControlled: isScrollControlled, isScrollControlled: isScrollControlled,
onChildSizeChanged: onChildSizeChanged, scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
); );
} }
...@@ -497,6 +500,7 @@ class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget { ...@@ -497,6 +500,7 @@ class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
renderObject.onChildSizeChanged = onChildSizeChanged; renderObject.onChildSizeChanged = onChildSizeChanged;
renderObject.animationValue = animationValue; renderObject.animationValue = animationValue;
renderObject.isScrollControlled = isScrollControlled; renderObject.isScrollControlled = isScrollControlled;
renderObject.scrollControlDisabledMaxHeightRatio = scrollControlDisabledMaxHeightRatio;
} }
} }
...@@ -506,9 +510,11 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox { ...@@ -506,9 +510,11 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
required _SizeChangeCallback<Size> onChildSizeChanged, required _SizeChangeCallback<Size> onChildSizeChanged,
required double animationValue, required double animationValue,
required bool isScrollControlled, required bool isScrollControlled,
}) : _animationValue = animationValue, required double scrollControlDisabledMaxHeightRatio,
}) : _onChildSizeChanged = onChildSizeChanged,
_animationValue = animationValue,
_isScrollControlled = isScrollControlled, _isScrollControlled = isScrollControlled,
_onChildSizeChanged = onChildSizeChanged, _scrollControlDisabledMaxHeightRatio = scrollControlDisabledMaxHeightRatio,
super(child); super(child);
Size _lastSize = Size.zero; Size _lastSize = Size.zero;
...@@ -546,6 +552,17 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox { ...@@ -546,6 +552,17 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
markNeedsLayout(); markNeedsLayout();
} }
double get scrollControlDisabledMaxHeightRatio => _scrollControlDisabledMaxHeightRatio;
double _scrollControlDisabledMaxHeightRatio;
set scrollControlDisabledMaxHeightRatio(double newValue) {
if (_scrollControlDisabledMaxHeightRatio == newValue) {
return;
}
_scrollControlDisabledMaxHeightRatio = newValue;
markNeedsLayout();
}
Size _getSize(BoxConstraints constraints) { Size _getSize(BoxConstraints constraints) {
return constraints.constrain(constraints.biggest); return constraints.constrain(constraints.biggest);
} }
...@@ -591,13 +608,13 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox { ...@@ -591,13 +608,13 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
return _getSize(constraints); return _getSize(constraints);
} }
BoxConstraints _getConstraintsForChild(BoxConstraints constraints) { BoxConstraints _getConstraintsForChild(BoxConstraints constraints) {
return BoxConstraints( return BoxConstraints(
minWidth: constraints.maxWidth, minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth, maxWidth: constraints.maxWidth,
maxHeight: isScrollControlled maxHeight: isScrollControlled
? constraints.maxHeight ? constraints.maxHeight
: constraints.maxHeight * 9.0 / 16.0, : constraints.maxHeight * scrollControlDisabledMaxHeightRatio,
); );
} }
...@@ -634,12 +651,14 @@ class _ModalBottomSheet<T> extends StatefulWidget { ...@@ -634,12 +651,14 @@ class _ModalBottomSheet<T> extends StatefulWidget {
this.clipBehavior, this.clipBehavior,
this.constraints, this.constraints,
this.isScrollControlled = false, this.isScrollControlled = false,
this.scrollControlDisabledMaxHeightRatio = _defaultScrollControlDisabledMaxHeightRatio,
this.enableDrag = true, this.enableDrag = true,
this.showDragHandle = false, this.showDragHandle = false,
}); });
final ModalBottomSheetRoute<T> route; final ModalBottomSheetRoute<T> route;
final bool isScrollControlled; final bool isScrollControlled;
final double scrollControlDisabledMaxHeightRatio;
final Color? backgroundColor; final Color? backgroundColor;
final double? elevation; final double? elevation;
final ShapeBorder? shape; final ShapeBorder? shape;
...@@ -730,6 +749,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> { ...@@ -730,6 +749,7 @@ class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> {
}, },
animationValue: animationValue, animationValue: animationValue,
isScrollControlled: widget.isScrollControlled, isScrollControlled: widget.isScrollControlled,
scrollControlDisabledMaxHeightRatio: widget.scrollControlDisabledMaxHeightRatio,
child: child, child: child,
), ),
), ),
...@@ -815,6 +835,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -815,6 +835,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
this.enableDrag = true, this.enableDrag = true,
this.showDragHandle, this.showDragHandle,
required this.isScrollControlled, required this.isScrollControlled,
this.scrollControlDisabledMaxHeightRatio = _defaultScrollControlDisabledMaxHeightRatio,
super.settings, super.settings,
this.transitionAnimationController, this.transitionAnimationController,
this.anchorPoint, this.anchorPoint,
...@@ -842,6 +863,13 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -842,6 +863,13 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
/// to have the bottom sheet be draggable. /// to have the bottom sheet be draggable.
final bool isScrollControlled; final bool isScrollControlled;
/// The max height constraint ratio for the bottom sheet
/// when [isScrollControlled] set to false,
/// no ratio will be applied when [isScrollControlled] set to true.
///
/// Defaults to 9 / 16.
final double scrollControlDisabledMaxHeightRatio;
/// The bottom sheet's background color. /// The bottom sheet's background color.
/// ///
/// Defines the bottom sheet's [Material.color]. /// Defines the bottom sheet's [Material.color].
...@@ -1026,6 +1054,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> { ...@@ -1026,6 +1054,7 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
constraints: constraints, constraints: constraints,
isScrollControlled: isScrollControlled, isScrollControlled: isScrollControlled,
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
enableDrag: enableDrag, enableDrag: enableDrag,
showDragHandle: showDragHandle ?? (enableDrag && (sheetTheme.showDragHandle ?? false)), showDragHandle: showDragHandle ?? (enableDrag && (sheetTheme.showDragHandle ?? false)),
); );
...@@ -1192,6 +1221,7 @@ Future<T?> showModalBottomSheet<T>({ ...@@ -1192,6 +1221,7 @@ Future<T?> showModalBottomSheet<T>({
BoxConstraints? constraints, BoxConstraints? constraints,
Color? barrierColor, Color? barrierColor,
bool isScrollControlled = false, bool isScrollControlled = false,
double scrollControlDisabledMaxHeightRatio = _defaultScrollControlDisabledMaxHeightRatio,
bool useRootNavigator = false, bool useRootNavigator = false,
bool isDismissible = true, bool isDismissible = true,
bool enableDrag = true, bool enableDrag = true,
...@@ -1210,6 +1240,7 @@ Future<T?> showModalBottomSheet<T>({ ...@@ -1210,6 +1240,7 @@ Future<T?> showModalBottomSheet<T>({
builder: builder, builder: builder,
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context), capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
isScrollControlled: isScrollControlled, isScrollControlled: isScrollControlled,
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
barrierLabel: barrierLabel ?? localizations.scrimLabel, barrierLabel: barrierLabel ?? localizations.scrimLabel,
barrierOnTapHint: localizations.scrimOnTapHint(localizations.bottomSheetLabel), barrierOnTapHint: localizations.scrimOnTapHint(localizations.bottomSheetLabel),
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
......
...@@ -1789,7 +1789,7 @@ void main() { ...@@ -1789,7 +1789,7 @@ void main() {
}); });
group('constraints', () { group('constraints', () {
testWidgets('default constraints are max width 640 in material 3', (WidgetTester tester) async { testWidgets('default constraints are max width 640 in material 3', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData.light(useMaterial3: true), theme: ThemeData.light(useMaterial3: true),
...@@ -2045,6 +2045,58 @@ void main() { ...@@ -2045,6 +2045,58 @@ void main() {
); );
}); });
group('scrollControlDisabledMaxHeightRatio', () {
Future<void> test(
WidgetTester tester,
bool isScrollControlled,
double scrollControlDisabledMaxHeightRatio,
) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Builder(builder: (BuildContext context) {
return Center(
child: ElevatedButton(
child: const Text('Press me'),
onPressed: () {
showModalBottomSheet<void>(
context: context,
isScrollControlled: isScrollControlled,
scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,
builder: (BuildContext context) => const SizedBox.expand(
child: Text('BottomSheet'),
),
);
},
),
);
}),
),
),
);
await tester.tap(find.text('Press me'));
await tester.pumpAndSettle();
expect(
tester.getRect(find.text('BottomSheet')),
Rect.fromLTRB(
80,
600 * (isScrollControlled ? 0 : (1 - scrollControlDisabledMaxHeightRatio)),
720,
600,
),
);
}
testWidgets('works at 9 / 16', (WidgetTester tester) {
return test(tester, false, 9.0 / 16.0);
});
testWidgets('works at 8 / 16', (WidgetTester tester) {
return test(tester, false, 8.0 / 16.0);
});
testWidgets('works at isScrollControlled', (WidgetTester tester) {
return test(tester, true, 8.0 / 16.0);
});
});
}); });
group('showModalBottomSheet modalBarrierDismissLabel', () { group('showModalBottomSheet modalBarrierDismissLabel', () {
......
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