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