@@ -14,7 +14,6 @@ import 'constants.dart';
1414import 'ink_well.dart' ;
1515import 'material.dart' ;
1616import 'material_state.dart' ;
17- import 'material_state_mixin.dart' ;
1817import 'theme_data.dart' ;
1918
2019/// The base [StatefulWidget] class for buttons whose style is defined by a [ButtonStyle] object.
@@ -39,6 +38,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
3938 required this .focusNode,
4039 required this .autofocus,
4140 required this .clipBehavior,
41+ this .statesController,
4242 required this .child,
4343 }) : assert (autofocus != null ),
4444 assert (clipBehavior != null );
@@ -95,6 +95,9 @@ abstract class ButtonStyleButton extends StatefulWidget {
9595 /// {@macro flutter.widgets.Focus.autofocus}
9696 final bool autofocus;
9797
98+ /// {@macro flutter.material.inkwell.statesController}
99+ final MaterialStatesController ? statesController;
100+
98101 /// Typically the button's label.
99102 final Widget ? child;
100103
@@ -191,36 +194,61 @@ abstract class ButtonStyleButton extends StatefulWidget {
191194/// * [TextButton] , a simple button without a shadow.
192195/// * [ElevatedButton] , a filled button whose material elevates when pressed.
193196/// * [OutlinedButton] , similar to [TextButton] , but with an outline.
194- class _ButtonStyleState extends State <ButtonStyleButton > with MaterialStateMixin , TickerProviderStateMixin {
195- AnimationController ? _controller;
196- double ? _elevation;
197- Color ? _backgroundColor;
197+ class _ButtonStyleState extends State <ButtonStyleButton > with TickerProviderStateMixin {
198+ AnimationController ? controller;
199+ double ? elevation;
200+ Color ? backgroundColor;
201+ MaterialStatesController ? internalStatesController;
202+
203+ void handleStatesControllerChange () {
204+ // Force a rebuild to resolve MaterialStateProperty properties
205+ setState (() { });
206+ }
198207
199- @override
200- void initState () {
201- super .initState ();
202- setMaterialState (MaterialState .disabled, ! widget.enabled);
208+ MaterialStatesController get statesController => widget.statesController ?? internalStatesController! ;
209+
210+ void initStatesController () {
211+ if (widget.statesController == null ) {
212+ internalStatesController = MaterialStatesController ();
213+ }
214+ statesController.update (MaterialState .disabled, ! widget.enabled);
215+ statesController.addListener (handleStatesControllerChange);
203216 }
204217
205218 @override
206- void dispose () {
207- _controller ? . dispose ();
208- super . dispose ();
219+ void initState () {
220+ super . initState ();
221+ initStatesController ();
209222 }
210223
211224 @override
212225 void didUpdateWidget (ButtonStyleButton oldWidget) {
213226 super .didUpdateWidget (oldWidget);
214- setMaterialState (MaterialState .disabled, ! widget.enabled);
215- // If the button is disabled while a press gesture is currently ongoing,
216- // InkWell makes a call to handleHighlightChanged. This causes an exception
217- // because it calls setState in the middle of a build. To preempt this, we
218- // manually update pressed to false when this situation occurs.
219- if (isDisabled && isPressed) {
220- removeMaterialState (MaterialState .pressed);
227+ if (widget.statesController != oldWidget.statesController) {
228+ oldWidget.statesController? .removeListener (handleStatesControllerChange);
229+ if (widget.statesController != null ) {
230+ internalStatesController? .dispose ();
231+ internalStatesController = null ;
232+ }
233+ initStatesController ();
234+ }
235+ if (widget.enabled != oldWidget.enabled) {
236+ statesController.update (MaterialState .disabled, ! widget.enabled);
237+ if (! widget.enabled) {
238+ // The button may have been disabled while a press gesture is currently underway.
239+ statesController.update (MaterialState .pressed, false );
240+ }
221241 }
222242 }
223243
244+ @override
245+ void dispose () {
246+ statesController.removeListener (handleStatesControllerChange);
247+ internalStatesController? .dispose ();
248+ controller? .dispose ();
249+ super .dispose ();
250+ }
251+
224252 @override
225253 Widget build (BuildContext context) {
226254 final ButtonStyle ? widgetStyle = widget.style;
@@ -237,7 +265,9 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
237265
238266 T ? resolve <T >(MaterialStateProperty <T >? Function (ButtonStyle ? style) getProperty) {
239267 return effectiveValue (
240- (ButtonStyle ? style) => getProperty (style)? .resolve (materialStates),
268+ (ButtonStyle ? style) {
269+ return getProperty (style)? .resolve (statesController.value);
270+ },
241271 );
242272 }
243273
@@ -254,7 +284,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
254284 final BorderSide ? resolvedSide = resolve <BorderSide ?>((ButtonStyle ? style) => style? .side);
255285 final OutlinedBorder ? resolvedShape = resolve <OutlinedBorder ?>((ButtonStyle ? style) => style? .shape);
256286
257- final MaterialStateMouseCursor resolvedMouseCursor = _MouseCursor (
287+ final MaterialStateMouseCursor mouseCursor = _MouseCursor (
258288 (Set <MaterialState > states) => effectiveValue ((ButtonStyle ? style) => style? .mouseCursor? .resolve (states)),
259289 );
260290
@@ -309,16 +339,16 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
309339 // animates its elevation but not its color. SKIA renders non-zero
310340 // elevations as a shadow colored fill behind the Material's background.
311341 if (resolvedAnimationDuration! > Duration .zero
312- && _elevation != null
313- && _backgroundColor != null
314- && _elevation != resolvedElevation
315- && _backgroundColor ! .value != resolvedBackgroundColor! .value
316- && _backgroundColor ! .opacity == 1
342+ && elevation != null
343+ && backgroundColor != null
344+ && elevation != resolvedElevation
345+ && backgroundColor ! .value != resolvedBackgroundColor! .value
346+ && backgroundColor ! .opacity == 1
317347 && resolvedBackgroundColor.opacity < 1
318348 && resolvedElevation == 0 ) {
319- if (_controller ? .duration != resolvedAnimationDuration) {
320- _controller ? .dispose ();
321- _controller = AnimationController (
349+ if (controller ? .duration != resolvedAnimationDuration) {
350+ controller ? .dispose ();
351+ controller = AnimationController (
322352 duration: resolvedAnimationDuration,
323353 vsync: this ,
324354 )
@@ -328,12 +358,12 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
328358 }
329359 });
330360 }
331- resolvedBackgroundColor = _backgroundColor ; // Defer changing the background color.
332- _controller ! .value = 0 ;
333- _controller ! .forward ();
361+ resolvedBackgroundColor = backgroundColor ; // Defer changing the background color.
362+ controller ! .value = 0 ;
363+ controller ! .forward ();
334364 }
335- _elevation = resolvedElevation;
336- _backgroundColor = resolvedBackgroundColor;
365+ elevation = resolvedElevation;
366+ backgroundColor = resolvedBackgroundColor;
337367
338368 final Widget result = ConstrainedBox (
339369 constraints: effectiveConstraints,
@@ -350,24 +380,18 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
350380 child: InkWell (
351381 onTap: widget.onPressed,
352382 onLongPress: widget.onLongPress,
353- onHighlightChanged: updateMaterialState (MaterialState .pressed),
354- onHover: updateMaterialState (
355- MaterialState .hovered,
356- onChanged: widget.onHover,
357- ),
358- mouseCursor: resolvedMouseCursor,
383+ onHover: widget.onHover,
384+ mouseCursor: mouseCursor,
359385 enableFeedback: resolvedEnableFeedback,
360386 focusNode: widget.focusNode,
361387 canRequestFocus: widget.enabled,
362- onFocusChange: updateMaterialState (
363- MaterialState .focused,
364- onChanged: widget.onFocusChange,
365- ),
388+ onFocusChange: widget.onFocusChange,
366389 autofocus: widget.autofocus,
367390 splashFactory: resolvedSplashFactory,
368391 overlayColor: overlayColor,
369392 highlightColor: Colors .transparent,
370393 customBorder: resolvedShape.copyWith (side: resolvedSide),
394+ statesController: statesController,
371395 child: IconTheme .merge (
372396 data: IconThemeData (color: resolvedForegroundColor),
373397 child: Padding (
0 commit comments