Refactor bottom sheet & related widgets (#159257)

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

This PR refactors Material's bottom sheet & related widgets. It also
replaces internal usage of deprecated types such as `MaterialState` with
`WidgetState`.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
Matthias Ngeo 2024-11-26 06:54:53 +08:00 committed by GitHub
parent 85632156c5
commit 0896292e35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -15,17 +15,16 @@ import 'bottom_sheet_theme.dart';
import 'color_scheme.dart'; import 'color_scheme.dart';
import 'colors.dart'; import 'colors.dart';
import 'constants.dart'; import 'constants.dart';
import 'curves.dart';
import 'debug.dart'; import 'debug.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'material_state.dart'; import 'motion.dart';
import 'scaffold.dart'; import 'scaffold.dart';
import 'theme.dart'; import 'theme.dart';
const Duration _bottomSheetEnterDuration = Duration(milliseconds: 250); const Duration _bottomSheetEnterDuration = Duration(milliseconds: 250);
const Duration _bottomSheetExitDuration = Duration(milliseconds: 200); const Duration _bottomSheetExitDuration = Duration(milliseconds: 200);
const Curve _modalBottomSheetCurve = decelerateEasing; const Curve _modalBottomSheetCurve = Easing.legacyDecelerate;
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; const double _defaultScrollControlDisabledMaxHeightRatio = 9.0 / 16.0;
@ -266,11 +265,11 @@ class _BottomSheetState extends State<BottomSheet> {
bool get _dismissUnderway => widget.animationController!.status == AnimationStatus.reverse; bool get _dismissUnderway => widget.animationController!.status == AnimationStatus.reverse;
Set<MaterialState> dragHandleMaterialState = <MaterialState>{}; Set<WidgetState> dragHandleStates = <WidgetState>{};
void _handleDragStart(DragStartDetails details) { void _handleDragStart(DragStartDetails details) {
setState(() { setState(() {
dragHandleMaterialState.add(MaterialState.dragged); dragHandleStates.add(WidgetState.dragged);
}); });
widget.onDragStart?.call(details); widget.onDragStart?.call(details);
} }
@ -297,7 +296,7 @@ class _BottomSheetState extends State<BottomSheet> {
return; return;
} }
setState(() { setState(() {
dragHandleMaterialState.remove(MaterialState.dragged); dragHandleStates.remove(WidgetState.dragged);
}); });
bool isClosing = false; bool isClosing = false;
if (details.velocity.pixelsPerSecond.dy > _minFlingVelocity) { if (details.velocity.pixelsPerSecond.dy > _minFlingVelocity) {
@ -335,12 +334,12 @@ class _BottomSheetState extends State<BottomSheet> {
} }
void _handleDragHandleHover(bool hovering) { void _handleDragHandleHover(bool hovering) {
if (hovering != dragHandleMaterialState.contains(MaterialState.hovered)) { if (hovering != dragHandleStates.contains(WidgetState.hovered)) {
setState(() { setState(() {
if (hovering){ if (hovering){
dragHandleMaterialState.add(MaterialState.hovered); dragHandleStates.add(WidgetState.hovered);
} else { } else {
dragHandleMaterialState.remove(MaterialState.hovered); dragHandleStates.remove(WidgetState.hovered);
} }
}); });
} }
@ -361,11 +360,11 @@ class _BottomSheetState extends State<BottomSheet> {
final bool showDragHandle = widget.showDragHandle ?? (widget.enableDrag && (bottomSheetTheme.showDragHandle ?? false)); final bool showDragHandle = widget.showDragHandle ?? (widget.enableDrag && (bottomSheetTheme.showDragHandle ?? false));
Widget? dragHandle; Widget? dragHandle;
if (showDragHandle){ if (showDragHandle) {
dragHandle = _DragHandle( dragHandle = _DragHandle(
onSemanticsTap: widget.onClosing, onSemanticsTap: widget.onClosing,
handleHover: _handleDragHandleHover, handleHover: _handleDragHandleHover,
materialState: dragHandleMaterialState, states: dragHandleStates,
dragHandleColor: widget.dragHandleColor, dragHandleColor: widget.dragHandleColor,
dragHandleSize: widget.dragHandleSize, dragHandleSize: widget.dragHandleSize,
); );
@ -431,20 +430,18 @@ class _BottomSheetState extends State<BottomSheet> {
// See scaffold.dart // See scaffold.dart
typedef _SizeChangeCallback<Size> = void Function(Size size);
class _DragHandle extends StatelessWidget { class _DragHandle extends StatelessWidget {
const _DragHandle({ const _DragHandle({
required this.onSemanticsTap, required this.onSemanticsTap,
required this.handleHover, required this.handleHover,
required this.materialState, required this.states,
this.dragHandleColor, this.dragHandleColor,
this.dragHandleSize, this.dragHandleSize,
}); });
final VoidCallback? onSemanticsTap; final VoidCallback? onSemanticsTap;
final ValueChanged<bool> handleHover; final ValueChanged<bool> handleHover;
final Set<MaterialState> materialState; final Set<WidgetState> states;
final Color? dragHandleColor; final Color? dragHandleColor;
final Size? dragHandleSize; final Size? dragHandleSize;
@ -470,8 +467,8 @@ class _DragHandle extends StatelessWidget {
width: handleSize.width, width: handleSize.width,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(handleSize.height/2), borderRadius: BorderRadius.circular(handleSize.height/2),
color: MaterialStateProperty.resolveAs<Color?>(dragHandleColor, materialState) color: WidgetStateProperty.resolveAs<Color?>(dragHandleColor, states)
?? MaterialStateProperty.resolveAs<Color?>(bottomSheetTheme.dragHandleColor, materialState) ?? WidgetStateProperty.resolveAs<Color?>(bottomSheetTheme.dragHandleColor, states)
?? m3Defaults.dragHandleColor, ?? m3Defaults.dragHandleColor,
), ),
), ),
@ -491,7 +488,7 @@ class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
super.child, super.child,
}); });
final _SizeChangeCallback<Size> onChildSizeChanged; final ValueChanged<Size> onChildSizeChanged;
final double animationValue; final double animationValue;
final bool isScrollControlled; final bool isScrollControlled;
final double scrollControlDisabledMaxHeightRatio; final double scrollControlDisabledMaxHeightRatio;
@ -518,7 +515,7 @@ class _BottomSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {
class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox { class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
_RenderBottomSheetLayoutWithSizeListener({ _RenderBottomSheetLayoutWithSizeListener({
RenderBox? child, RenderBox? child,
required _SizeChangeCallback<Size> onChildSizeChanged, required ValueChanged<Size> onChildSizeChanged,
required double animationValue, required double animationValue,
required bool isScrollControlled, required bool isScrollControlled,
required double scrollControlDisabledMaxHeightRatio, required double scrollControlDisabledMaxHeightRatio,
@ -530,9 +527,9 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
Size _lastSize = Size.zero; Size _lastSize = Size.zero;
_SizeChangeCallback<Size> get onChildSizeChanged => _onChildSizeChanged; ValueChanged<Size> get onChildSizeChanged => _onChildSizeChanged;
_SizeChangeCallback<Size> _onChildSizeChanged; ValueChanged<Size> _onChildSizeChanged;
set onChildSizeChanged(_SizeChangeCallback<Size> newCallback) { set onChildSizeChanged(ValueChanged<Size> newCallback) {
if (_onChildSizeChanged == newCallback) { if (_onChildSizeChanged == newCallback) {
return; return;
} }
@ -574,50 +571,20 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
markNeedsLayout(); markNeedsLayout();
} }
Size _getSize(BoxConstraints constraints) { @override
return constraints.constrain(constraints.biggest); double computeMinIntrinsicWidth(double height) => 0.0;
}
@override @override
double computeMinIntrinsicWidth(double height) { double computeMaxIntrinsicWidth(double height) => 0.0;
final double width = _getSize(BoxConstraints.tightForFinite(height: height)).width;
if (width.isFinite) {
return width;
}
return 0.0;
}
@override @override
double computeMaxIntrinsicWidth(double height) { double computeMinIntrinsicHeight(double width) => 0.0;
final double width = _getSize(BoxConstraints.tightForFinite(height: height)).width;
if (width.isFinite) {
return width;
}
return 0.0;
}
@override @override
double computeMinIntrinsicHeight(double width) { double computeMaxIntrinsicHeight(double width) => 0.0;
final double height = _getSize(BoxConstraints.tightForFinite(width: width)).height;
if (height.isFinite) {
return height;
}
return 0.0;
}
@override @override
double computeMaxIntrinsicHeight(double width) { Size computeDryLayout(BoxConstraints constraints) => constraints.biggest;
final double height = _getSize(BoxConstraints.tightForFinite(width: width)).height;
if (height.isFinite) {
return height;
}
return 0.0;
}
@override
Size computeDryLayout(BoxConstraints constraints) {
return _getSize(constraints);
}
@override @override
double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) { double? computeDryBaseline(covariant BoxConstraints constraints, TextBaseline baseline) {
@ -631,7 +598,7 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
return null; return null;
} }
final Size childSize = childConstraints.isTight ? childConstraints.smallest : child.getDryLayout(childConstraints); final Size childSize = childConstraints.isTight ? childConstraints.smallest : child.getDryLayout(childConstraints);
return result + _getPositionForChild(_getSize(constraints), childSize).dy; return result + _getPositionForChild(constraints.biggest, childSize).dy;
} }
BoxConstraints _getConstraintsForChild(BoxConstraints constraints) { BoxConstraints _getConstraintsForChild(BoxConstraints constraints) {
@ -650,7 +617,7 @@ class _RenderBottomSheetLayoutWithSizeListener extends RenderShiftedBox {
@override @override
void performLayout() { void performLayout() {
size = _getSize(constraints); size = constraints.biggest;
final RenderBox? child = this.child; final RenderBox? child = this.child;
if (child == null) { if (child == null) {
return; return;
@ -1137,11 +1104,11 @@ class ModalBottomSheetRoute<T> extends PopupRoute<T> {
@override @override
Widget buildModalBarrier() { Widget buildModalBarrier() {
if (barrierColor.alpha != 0 && !offstage) { // changedInternalState is called if barrierColor or offstage updates if (barrierColor.a != 0 && !offstage) { // changedInternalState is called if barrierColor or offstage updates
assert(barrierColor != barrierColor.withOpacity(0.0)); assert(barrierColor != barrierColor.withValues(alpha: 0.0));
final Animation<Color?> color = animation!.drive( final Animation<Color?> color = animation!.drive(
ColorTween( ColorTween(
begin: barrierColor.withOpacity(0.0), begin: barrierColor.withValues(alpha: 0.0),
end: barrierColor, // changedInternalState is called if barrierColor updates end: barrierColor, // changedInternalState is called if barrierColor updates
).chain(CurveTween(curve: barrierCurve)), // changedInternalState is called if barrierCurve updates ).chain(CurveTween(curve: barrierCurve)), // changedInternalState is called if barrierCurve updates
); );