From 096c4ace35d1b2da1c185911f75de11921aa22b5 Mon Sep 17 00:00:00 2001 From: Valentin Vignal <32538273+ValentinVignal@users.noreply.github.com> Date: Mon, 24 Mar 2025 17:55:06 +0100 Subject: [PATCH] Make `chip.dart` use `WidgetStatesController` (#161487) Fixes https://github.com/flutter/flutter/issues/128289 Follow up of https://github.com/flutter/flutter/pull/128507 and https://github.com/flutter/flutter/pull/159422 - Makes `RawChip` use `WidgetStatesController` instead of `MaterialStateMixin` - Remove references of `MaterialState` to replace them 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]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] 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. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [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 --------- Co-authored-by: Qun Cheng <36861262+QuncCccccc@users.noreply.github.com> --- packages/flutter/lib/src/material/chip.dart | 74 ++++++++++++--------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index b606e4721e..2bbe2bab48 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart @@ -28,7 +28,6 @@ import 'ink_well.dart'; import 'material.dart'; import 'material_localizations.dart'; import 'material_state.dart'; -import 'material_state_mixin.dart'; import 'text_theme.dart'; import 'theme.dart'; import 'theme_data.dart'; @@ -163,7 +162,7 @@ abstract interface class ChipAttributes { /// Resolves in the following states: /// * [WidgetState.selected]. /// * [WidgetState.disabled]. - MaterialStateProperty? get color; + WidgetStateProperty? get color; /// Color to be used for the unselected, enabled chip's background. /// @@ -739,7 +738,7 @@ class Chip extends StatelessWidget implements ChipAttributes, DeletableChipAttri @override final bool autofocus; @override - final MaterialStateProperty? color; + final WidgetStateProperty? color; @override final Color? backgroundColor; @override @@ -949,7 +948,7 @@ class RawChip extends StatefulWidget @override final bool autofocus; @override - final MaterialStateProperty? color; + final WidgetStateProperty? color; @override final Color? backgroundColor; @override @@ -997,8 +996,7 @@ class RawChip extends StatefulWidget State createState() => _RawChipState(); } -class _RawChipState extends State - with MaterialStateMixin, TickerProviderStateMixin { +class _RawChipState extends State with TickerProviderStateMixin { static const Duration pressedAnimationDuration = Duration(milliseconds: 75); late AnimationController selectController; @@ -1011,6 +1009,8 @@ class _RawChipState extends State late CurvedAnimation enableAnimation; late CurvedAnimation selectionFade; + final WidgetStatesController statesController = WidgetStatesController(); + bool get hasDeleteButton => widget.onDeleted != null; bool get hasAvatar => widget.avatar != null; @@ -1027,8 +1027,10 @@ class _RawChipState extends State void initState() { assert(widget.onSelected == null || widget.onPressed == null); super.initState(); - setMaterialState(MaterialState.disabled, !widget.isEnabled); - setMaterialState(MaterialState.selected, widget.selected); + statesController + ..update(WidgetState.disabled, !widget.isEnabled) + ..update(WidgetState.selected, widget.selected) + ..addListener(() => setState(() {})); selectController = AnimationController( duration: widget.chipAnimationStyle?.selectAnimation?.duration ?? _kSelectDuration, reverseDuration: widget.chipAnimationStyle?.selectAnimation?.reverseDuration, @@ -1091,6 +1093,7 @@ class _RawChipState extends State deleteDrawerAnimation.dispose(); enableAnimation.dispose(); selectionFade.dispose(); + statesController.dispose(); super.dispose(); } @@ -1098,7 +1101,7 @@ class _RawChipState extends State if (!canTap) { return; } - setMaterialState(MaterialState.pressed, true); + statesController.update(WidgetState.pressed, true); setState(() { _isTapping = true; }); @@ -1108,7 +1111,7 @@ class _RawChipState extends State if (!canTap) { return; } - setMaterialState(MaterialState.pressed, false); + statesController.update(WidgetState.pressed, false); setState(() { _isTapping = false; }); @@ -1118,7 +1121,7 @@ class _RawChipState extends State if (!canTap) { return; } - setMaterialState(MaterialState.pressed, false); + statesController.update(WidgetState.pressed, false); setState(() { _isTapping = false; }); @@ -1129,12 +1132,12 @@ class _RawChipState extends State OutlinedBorder _getShape(ThemeData theme, ChipThemeData chipTheme, ChipThemeData chipDefaults) { final BorderSide? resolvedSide = - MaterialStateProperty.resolveAs(widget.side, materialStates) ?? - MaterialStateProperty.resolveAs(chipTheme.side, materialStates); + WidgetStateProperty.resolveAs(widget.side, statesController.value) ?? + WidgetStateProperty.resolveAs(chipTheme.side, statesController.value); final OutlinedBorder resolvedShape = - MaterialStateProperty.resolveAs(widget.shape, materialStates) ?? - MaterialStateProperty.resolveAs(chipTheme.shape, materialStates) ?? - MaterialStateProperty.resolveAs(chipDefaults.shape, materialStates) + WidgetStateProperty.resolveAs(widget.shape, statesController.value) ?? + WidgetStateProperty.resolveAs(chipTheme.shape, statesController.value) ?? + WidgetStateProperty.resolveAs(chipDefaults.shape, statesController.value) // TODO(tahatesser): Remove this fallback when Material 2 is deprecated. ?? const StadiumBorder(); @@ -1150,19 +1153,19 @@ class _RawChipState extends State } Color? resolveColor({ - MaterialStateProperty? color, + WidgetStateProperty? color, Color? selectedColor, Color? backgroundColor, Color? disabledColor, - MaterialStateProperty? defaultColor, + WidgetStateProperty? defaultColor, }) { return _IndividualOverrides( color: color, selectedColor: selectedColor, backgroundColor: backgroundColor, disabledColor: disabledColor, - ).resolve(materialStates) ?? - defaultColor?.resolve(materialStates); + ).resolve(statesController.value) ?? + defaultColor?.resolve(statesController.value); } /// Picks between three different colors, depending upon the state of two @@ -1216,7 +1219,7 @@ class _RawChipState extends State super.didUpdateWidget(oldWidget); if (oldWidget.isEnabled != widget.isEnabled) { setState(() { - setMaterialState(MaterialState.disabled, !widget.isEnabled); + statesController.update(WidgetState.disabled, !widget.isEnabled); if (widget.isEnabled) { enableController.forward(); } else { @@ -1235,7 +1238,7 @@ class _RawChipState extends State } if (oldWidget.selected != widget.selected) { setState(() { - setMaterialState(MaterialState.selected, widget.selected); + statesController.update(WidgetState.selected, widget.selected); if (widget.selected) { selectController.forward(); } else { @@ -1360,9 +1363,9 @@ class _RawChipState extends State widget.deleteIconBoxConstraints ?? chipTheme.deleteIconBoxConstraints; final TextStyle effectiveLabelStyle = labelStyle.merge(widget.labelStyle); - final Color? resolvedLabelColor = MaterialStateProperty.resolveAs( + final Color? resolvedLabelColor = WidgetStateProperty.resolveAs( effectiveLabelStyle.color, - materialStates, + statesController.value, ); final TextStyle resolvedLabelStyle = effectiveLabelStyle.copyWith(color: resolvedLabelColor); final Widget? avatar = @@ -1398,14 +1401,21 @@ class _RawChipState extends State shape: resolvedShape, clipBehavior: widget.clipBehavior, child: InkWell( - onFocusChange: updateMaterialState(MaterialState.focused), + onFocusChange: (bool value) { + statesController.update(WidgetState.focused, value); + }, focusNode: widget.focusNode, autofocus: widget.autofocus, canRequestFocus: widget.isEnabled, onTap: canTap ? _handleTap : null, onTapDown: canTap ? _handleTapDown : null, onTapCancel: canTap ? _handleTapCancel : null, - onHover: canTap ? updateMaterialState(MaterialState.hovered) : null, + onHover: + canTap + ? (bool value) { + statesController.update(WidgetState.hovered, value); + } + : null, mouseCursor: widget.mouseCursor, hoverColor: (widget.color ?? chipTheme.color) == null ? null : Colors.transparent, customBorder: resolvedShape, @@ -1493,26 +1503,26 @@ class _RawChipState extends State } } -class _IndividualOverrides extends MaterialStateProperty { +class _IndividualOverrides extends WidgetStateProperty { _IndividualOverrides({this.color, this.backgroundColor, this.selectedColor, this.disabledColor}); - final MaterialStateProperty? color; + final WidgetStateProperty? color; final Color? backgroundColor; final Color? selectedColor; final Color? disabledColor; @override - Color? resolve(Set states) { + Color? resolve(Set states) { if (color != null) { return color!.resolve(states); } - if (states.contains(MaterialState.selected) && states.contains(MaterialState.disabled)) { + if (states.contains(WidgetState.selected) && states.contains(WidgetState.disabled)) { return selectedColor; } - if (states.contains(MaterialState.disabled)) { + if (states.contains(WidgetState.disabled)) { return disabledColor; } - if (states.contains(MaterialState.selected)) { + if (states.contains(WidgetState.selected)) { return selectedColor; } return backgroundColor;