@@ -14,7 +14,6 @@ import 'constants.dart';
14
14
import 'ink_well.dart' ;
15
15
import 'material.dart' ;
16
16
import 'material_state.dart' ;
17
- import 'material_state_mixin.dart' ;
18
17
import 'theme_data.dart' ;
19
18
20
19
/// The base [StatefulWidget] class for buttons whose style is defined by a [ButtonStyle] object.
@@ -39,6 +38,7 @@ abstract class ButtonStyleButton extends StatefulWidget {
39
38
required this .focusNode,
40
39
required this .autofocus,
41
40
required this .clipBehavior,
41
+ this .statesController,
42
42
required this .child,
43
43
}) : assert (autofocus != null ),
44
44
assert (clipBehavior != null );
@@ -95,6 +95,9 @@ abstract class ButtonStyleButton extends StatefulWidget {
95
95
/// {@macro flutter.widgets.Focus.autofocus}
96
96
final bool autofocus;
97
97
98
+ /// {@macro flutter.material.inkwell.statesController}
99
+ final MaterialStatesController ? statesController;
100
+
98
101
/// Typically the button's label.
99
102
final Widget ? child;
100
103
@@ -191,36 +194,61 @@ abstract class ButtonStyleButton extends StatefulWidget {
191
194
/// * [TextButton] , a simple button without a shadow.
192
195
/// * [ElevatedButton] , a filled button whose material elevates when pressed.
193
196
/// * [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
+ }
198
207
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);
203
216
}
204
217
205
218
@override
206
- void dispose () {
207
- _controller ? . dispose ();
208
- super . dispose ();
219
+ void initState () {
220
+ super . initState ();
221
+ initStatesController ();
209
222
}
210
223
211
224
@override
212
225
void didUpdateWidget (ButtonStyleButton oldWidget) {
213
226
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
+ }
221
241
}
222
242
}
223
243
244
+ @override
245
+ void dispose () {
246
+ statesController.removeListener (handleStatesControllerChange);
247
+ internalStatesController? .dispose ();
248
+ controller? .dispose ();
249
+ super .dispose ();
250
+ }
251
+
224
252
@override
225
253
Widget build (BuildContext context) {
226
254
final ButtonStyle ? widgetStyle = widget.style;
@@ -237,7 +265,9 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
237
265
238
266
T ? resolve <T >(MaterialStateProperty <T >? Function (ButtonStyle ? style) getProperty) {
239
267
return effectiveValue (
240
- (ButtonStyle ? style) => getProperty (style)? .resolve (materialStates),
268
+ (ButtonStyle ? style) {
269
+ return getProperty (style)? .resolve (statesController.value);
270
+ },
241
271
);
242
272
}
243
273
@@ -254,7 +284,7 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
254
284
final BorderSide ? resolvedSide = resolve <BorderSide ?>((ButtonStyle ? style) => style? .side);
255
285
final OutlinedBorder ? resolvedShape = resolve <OutlinedBorder ?>((ButtonStyle ? style) => style? .shape);
256
286
257
- final MaterialStateMouseCursor resolvedMouseCursor = _MouseCursor (
287
+ final MaterialStateMouseCursor mouseCursor = _MouseCursor (
258
288
(Set <MaterialState > states) => effectiveValue ((ButtonStyle ? style) => style? .mouseCursor? .resolve (states)),
259
289
);
260
290
@@ -309,16 +339,16 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
309
339
// animates its elevation but not its color. SKIA renders non-zero
310
340
// elevations as a shadow colored fill behind the Material's background.
311
341
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
317
347
&& resolvedBackgroundColor.opacity < 1
318
348
&& resolvedElevation == 0 ) {
319
- if (_controller ? .duration != resolvedAnimationDuration) {
320
- _controller ? .dispose ();
321
- _controller = AnimationController (
349
+ if (controller ? .duration != resolvedAnimationDuration) {
350
+ controller ? .dispose ();
351
+ controller = AnimationController (
322
352
duration: resolvedAnimationDuration,
323
353
vsync: this ,
324
354
)
@@ -328,12 +358,12 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
328
358
}
329
359
});
330
360
}
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 ();
334
364
}
335
- _elevation = resolvedElevation;
336
- _backgroundColor = resolvedBackgroundColor;
365
+ elevation = resolvedElevation;
366
+ backgroundColor = resolvedBackgroundColor;
337
367
338
368
final Widget result = ConstrainedBox (
339
369
constraints: effectiveConstraints,
@@ -350,24 +380,18 @@ class _ButtonStyleState extends State<ButtonStyleButton> with MaterialStateMixin
350
380
child: InkWell (
351
381
onTap: widget.onPressed,
352
382
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,
359
385
enableFeedback: resolvedEnableFeedback,
360
386
focusNode: widget.focusNode,
361
387
canRequestFocus: widget.enabled,
362
- onFocusChange: updateMaterialState (
363
- MaterialState .focused,
364
- onChanged: widget.onFocusChange,
365
- ),
388
+ onFocusChange: widget.onFocusChange,
366
389
autofocus: widget.autofocus,
367
390
splashFactory: resolvedSplashFactory,
368
391
overlayColor: overlayColor,
369
392
highlightColor: Colors .transparent,
370
393
customBorder: resolvedShape.copyWith (side: resolvedSide),
394
+ statesController: statesController,
371
395
child: IconTheme .merge (
372
396
data: IconThemeData (color: resolvedForegroundColor),
373
397
child: Padding (
0 commit comments