Widget state properties (#142151)

Fixes #138270.

Moves the majority of the logic of MaterialStateProperties down to the widgets layer, then has the existing Material classes pull from the widgets versions.
This commit is contained in:
Mitchell Goodwin 2024-03-19 10:58:13 -07:00 committed by GitHub
parent 3236957f02
commit 6190c5eea1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 1435 additions and 545 deletions

View file

@ -358,13 +358,13 @@ class _TextButtonExampleState extends State<TextButtonExample> {
},
style: TextButton.styleFrom(
overlayColor: Colors.transparent,
foregroundBuilder: (BuildContext context, Set<MaterialState> states, Widget? child) {
foregroundBuilder: (BuildContext context, Set<WidgetState> states, Widget? child) {
late final ImageProvider image;
if (currentAction != null) {
image = runningImage;
} else if (states.contains(MaterialState.pressed)) {
} else if (states.contains(WidgetState.pressed)) {
image = pressedImage;
} else if (states.contains(MaterialState.hovered)) {
} else if (states.contains(WidgetState.hovered)) {
image = hoveredImage;
} else {
image = defaultImage;

View file

@ -23,6 +23,7 @@
# * SliverAppBar: fix_sliver_app_bar.yaml
# * TextTheme: fix_text_theme.yaml
# * ThemeData: fix_theme_data.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/86198

View file

@ -23,6 +23,7 @@
# * SliverAppBar: fix_sliver_app_bar.yaml
# * TextTheme: fix_text_theme.yaml
# * ThemeData: fix_theme_data.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/86198

View file

@ -23,6 +23,7 @@
# * SliverAppBar: fix_sliver_app_bar.yaml
# * TextTheme: fix_text_theme.yaml
# * ThemeData: fix_theme_data.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:

View file

@ -23,6 +23,7 @@
# * SliverAppBar: fix_sliver_app_bar.yaml
# * TextTheme: fix_text_theme.yaml
# * ThemeData: fix_theme_data.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/15303

View file

@ -23,6 +23,7 @@
# * Material (general): fix_material.yaml
# * TextTheme: fix_text_theme.yaml
# * ThemeData: fix_theme_data.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/86198

View file

@ -23,6 +23,7 @@
# * Material (general): fix_material.yaml
# * SliverAppBar: fix_sliver_app_bar.yaml
# * ThemeData: fix_theme_data.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/48547

View file

@ -23,6 +23,7 @@
# * Material (general): fix_material.yaml
# * SliverAppBar: fix_sliver_app_bar.yaml
# * TextTheme: fix_text_theme.yaml
# * WidgetState: fix_widget_state.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/87281

View file

@ -0,0 +1,129 @@
# Copyright 2014 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# For details regarding the *Flutter Fix* feature, see
# https://flutter.dev/docs/development/tools/flutter-fix
# Please add new fixes to the top of the file, separated by one blank line
# from other fixes. In a comment, include a link to the PR where the change
# requiring the fix was made.
# Every fix must be tested. See the flutter/packages/flutter/test_fixes/README.md
# file for instructions on testing these data driven fixes.
# For documentation about this file format, see
# https://dart.dev/go/data-driven-fixes.
# * Fixes in this file are for the MaterialState enum and classes from the Material library. *
# For fixes to
# * AppBar: fix_app_bar.yaml
# * AppBarTheme: fix_app_bar_theme.yaml
# * ColorScheme: fix_color_scheme.yaml
# * Material (general): fix_material.yaml
# * SliverAppBar: fix_sliver_app_bar.yaml
# * TextTheme: fix_text_theme.yaml
# * ThemeDate: fix_theme_data.yaml
version: 1
transforms:
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetState'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
enum: 'MaterialState'
changes:
- kind: 'rename'
newName: 'WidgetState'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetPropertyResolver'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
typedef: 'MaterialPropertyResolver'
changes:
- kind: 'rename'
newName: 'WidgetPropertyResolver'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStateColor'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStateColor'
changes:
- kind: 'rename'
newName: 'WidgetStateColor'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStateMouseCursor'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStateMouseCursor'
changes:
- kind: 'rename'
newName: 'WidgetStateMouseCursor'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStateBorderSide'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStateBorderSide'
changes:
- kind: 'rename'
newName: 'WidgetStateBorderSide'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStateOutlinedBorder'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStateOutlinedBorder'
changes:
- kind: 'rename'
newName: 'WidgetStateOutlinedBorder'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStateTextStyle'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStateTextStyle'
changes:
- kind: 'rename'
newName: 'WidgetStateTextStyle'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStateProperty'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStateProperty'
changes:
- kind: 'rename'
newName: 'WidgetStateProperty'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStatePropertyAll'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStatePropertyAll'
changes:
- kind: 'rename'
newName: 'WidgetStatePropertyAll'
# Changes made in https://github.com/flutter/flutter/pull/142151
- title: "Replace with 'WidgetStatesController'"
date: 2024-02-01
element:
uris: [ 'material.dart', 'widgets.dart' ]
class: 'MaterialStatesController'
changes:
- kind: 'rename'
newName: 'WidgetStatesController'
# Before adding a new fix: read instructions at the top of this file.

View file

@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'input_border.dart';
@ -20,6 +18,9 @@ import 'input_border.dart';
///
/// See also:
///
/// * [WidgetState], a general non-Material version that can be used
/// interchangebly with `MaterialState`. They functionally work the same,
/// except [WidgetState] can be used outside of Material.
/// * [MaterialStateProperty], an interface for objects that "resolve" to
/// different values depending on a widget's material state.
/// {@template flutter.material.MaterialStateProperty.implementations}
@ -45,62 +46,26 @@ import 'input_border.dart';
/// `MaterialStateProperty` which is used in APIs that need to accept either
/// a [TextStyle] or a [MaterialStateProperty<TextStyle>].
/// {@endtemplate}
enum MaterialState {
/// The state when the user drags their mouse cursor over the given widget.
///
/// See: https://material.io/design/interaction/states.html#hover.
hovered,
/// The state when the user navigates with the keyboard to a given widget.
///
/// This can also sometimes be triggered when a widget is tapped. For example,
/// when a [TextField] is tapped, it becomes [focused].
///
/// See: https://material.io/design/interaction/states.html#focus.
focused,
/// The state when the user is actively pressing down on the given widget.
///
/// See: https://material.io/design/interaction/states.html#pressed.
pressed,
/// The state when this widget is being dragged from one place to another by
/// the user.
///
/// https://material.io/design/interaction/states.html#dragged.
dragged,
/// The state when this item has been selected.
///
/// This applies to things that can be toggled (such as chips and checkboxes)
/// and things that are selected from a set of options (such as tabs and radio buttons).
///
/// See: https://material.io/design/interaction/states.html#selected.
selected,
/// The state when this widget overlaps the content of a scrollable below.
///
/// Used by [AppBar] to indicate that the primary scrollable's
/// content has scrolled up and behind the app bar.
scrolledUnder,
/// The state when this widget is disabled and cannot be interacted with.
///
/// Disabled widgets should not respond to hover, focus, press, or drag
/// interactions.
///
/// See: https://material.io/design/interaction/states.html#disabled.
disabled,
/// The state when the widget has entered some form of invalid state.
///
/// See https://material.io/design/interaction/states.html#usage.
error,
}
@Deprecated(
'Use WidgetState instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialState = WidgetState;
/// Signature for the function that returns a value of type `T` based on a given
/// set of states.
typedef MaterialPropertyResolver<T> = T Function(Set<MaterialState> states);
///
/// See also:
///
/// * [WidgetPropertyResolver], the non-Material form of `MaterialPropertyResolver`
/// that can be used interchangably with `MaterialPropertyResolver.
@Deprecated(
'Use WidgetPropertyResolver instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialPropertyResolver<T> = WidgetPropertyResolver<T>;
/// Defines a [Color] that is also a [MaterialStateProperty].
///
@ -149,55 +114,17 @@ typedef MaterialPropertyResolver<T> = T Function(Set<MaterialState> states);
/// }
/// ```
/// {@end-tool}
abstract class MaterialStateColor extends Color implements MaterialStateProperty<Color> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateColor(super.defaultValue);
/// Creates a [MaterialStateColor] from a [MaterialPropertyResolver<Color>]
/// callback function.
///
/// If used as a regular color, the color resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null color in the default
/// state.
static MaterialStateColor resolveWith(MaterialPropertyResolver<Color> callback) => _MaterialStateColor(callback);
/// Returns a [Color] that's to be used when a Material component is in the
/// specified state.
@override
Color resolve(Set<MaterialState> states);
/// A constant whose value is [Colors.transparent] for all states.
static const MaterialStateColor transparent = _MaterialStateColorTransparent();
}
/// A [MaterialStateColor] created from a [MaterialPropertyResolver<Color>]
/// callback alone.
///
/// If used as a regular color, the color resolved in the default state will
/// be used.
/// See also
///
/// Used by [MaterialStateColor.resolveWith].
class _MaterialStateColor extends MaterialStateColor {
_MaterialStateColor(this._resolve) : super(_resolve(_defaultStates).value);
final MaterialPropertyResolver<Color> _resolve;
/// The default state for a Material component, the empty set of interaction states.
static const Set<MaterialState> _defaultStates = <MaterialState>{};
@override
Color resolve(Set<MaterialState> states) => _resolve(states);
}
class _MaterialStateColorTransparent extends MaterialStateColor {
const _MaterialStateColorTransparent() : super(0x00000000);
@override
Color resolve(Set<MaterialState> states) => const Color(0x00000000);
}
/// * [WidgetStateColor], the non-Material version that can be used
/// interchangably with `MaterialStateColor`.
@Deprecated(
'Use WidgetStateColor instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStateColor = WidgetStateColor;
/// Defines a [MouseCursor] whose value depends on a set of [MaterialState]s which
/// represent the interactive state of a component.
@ -225,76 +152,17 @@ class _MaterialStateColorTransparent extends MaterialStateColor {
///
/// See also:
///
/// * [WidgetStateMouseCursor], the non-Material version that can be used
/// interchangeably with `MaterialStateMouseCursor`.
/// * [MouseCursor] for introduction on the mouse cursor system.
/// * [SystemMouseCursors], which defines cursors that are supported by
/// native platforms.
abstract class MaterialStateMouseCursor extends MouseCursor implements MaterialStateProperty<MouseCursor> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateMouseCursor();
@protected
@override
MouseCursorSession createSession(int device) {
return resolve(<MaterialState>{}).createSession(device);
}
/// Returns a [MouseCursor] that's to be used when a Material component is in
/// the specified state.
///
/// This method should never return null.
@override
MouseCursor resolve(Set<MaterialState> states);
/// A mouse cursor for clickable material widgets, which resolves differently
/// when the widget is disabled.
///
/// By default this cursor resolves to [SystemMouseCursors.click]. If the widget is
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
/// This cursor is the default for many Material widgets.
static const MaterialStateMouseCursor clickable = _EnabledAndDisabledMouseCursor(
enabledCursor: SystemMouseCursors.click,
disabledCursor: SystemMouseCursors.basic,
name: 'clickable',
);
/// A mouse cursor for material widgets related to text, which resolves differently
/// when the widget is disabled.
///
/// By default this cursor resolves to [SystemMouseCursors.text]. If the widget is
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
/// This cursor is the default for many Material widgets.
static const MaterialStateMouseCursor textable = _EnabledAndDisabledMouseCursor(
enabledCursor: SystemMouseCursors.text,
disabledCursor: SystemMouseCursors.basic,
name: 'textable',
);
}
class _EnabledAndDisabledMouseCursor extends MaterialStateMouseCursor {
const _EnabledAndDisabledMouseCursor({
required this.enabledCursor,
required this.disabledCursor,
required this.name,
});
final MouseCursor enabledCursor;
final MouseCursor disabledCursor;
final String name;
@override
MouseCursor resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return disabledCursor;
}
return enabledCursor;
}
@override
String get debugDescription => 'MaterialStateMouseCursor($name)';
}
@Deprecated(
'Use WidgetStateMouseCursor instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStateMouseCursor = WidgetStateMouseCursor;
/// Defines a [BorderSide] whose value depends on a set of [MaterialState]s
/// which represent the interactive state of a component.
@ -316,72 +184,17 @@ class _EnabledAndDisabledMouseCursor extends MaterialStateMouseCursor {
///
/// This class should only be used for parameters which are documented to take
/// [MaterialStateBorderSide], otherwise only the default state will be used.
abstract class MaterialStateBorderSide extends BorderSide implements MaterialStateProperty<BorderSide?> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateBorderSide();
/// Creates a [MaterialStateBorderSide] from a
/// [MaterialPropertyResolver<BorderSide?>] callback function.
///
/// If used as a regular [BorderSide], the border resolved in the default state
/// (the empty set of states) will be used.
///
/// Usage:
///
/// ```dart
/// ChipTheme(
/// data: Theme.of(context).chipTheme.copyWith(
/// side: MaterialStateBorderSide.resolveWith((Set<MaterialState> states) {
/// if (states.contains(MaterialState.selected)) {
/// return const BorderSide(color: Colors.red);
/// }
/// return null; // Defer to default value on the theme or widget.
/// }),
/// ),
/// child: const Chip(
/// label: Text('Transceiver'),
/// ),
/// ),
/// ```
///
/// Alternatively:
///
/// ```dart
/// Chip(
/// label: const Text('Transceiver'),
/// side: MaterialStateBorderSide.resolveWith((Set<MaterialState> states) {
/// if (states.contains(MaterialState.selected)) {
/// return const BorderSide(color: Colors.red);
/// }
/// return null; // Defer to default value on the theme or widget.
/// }),
/// ),
/// ```
const factory MaterialStateBorderSide.resolveWith(MaterialPropertyResolver<BorderSide?> callback) = _MaterialStateBorderSide;
/// Returns a [BorderSide] that's to be used when a Material component is
/// in the specified state. Return null to defer to the default value of the
/// widget or theme.
@override
BorderSide? resolve(Set<MaterialState> states);
}
/// A [MaterialStateBorderSide] created from a
/// [MaterialPropertyResolver<BorderSide>] callback alone.
///
/// If used as a regular side, the side resolved in the default state will
/// be used.
/// See also:
///
/// Used by [MaterialStateBorderSide.resolveWith].
class _MaterialStateBorderSide extends MaterialStateBorderSide {
const _MaterialStateBorderSide(this._resolve);
final MaterialPropertyResolver<BorderSide?> _resolve;
@override
BorderSide? resolve(Set<MaterialState> states) => _resolve(states);
}
/// * [WidgetStateBorderSide], the non-Material version that can be used
/// interchangeably with `MaterialStateBorderSide`.
@Deprecated(
'Use WidgetStateBorderSide instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStateBorderSide = WidgetStateBorderSide;
/// Defines an [OutlinedBorder] whose value depends on a set of [MaterialState]s
/// which represent the interactive state of a component.
@ -403,18 +216,15 @@ class _MaterialStateBorderSide extends MaterialStateBorderSide {
///
/// See also:
///
/// * [WidgetStateOutlinedBorder], the non-Material version that can be used
/// interchangeably with `MaterialStateOutlinedBorder`.
/// * [ShapeBorder] the base class for shape outlines.
abstract class MaterialStateOutlinedBorder extends OutlinedBorder implements MaterialStateProperty<OutlinedBorder?> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateOutlinedBorder();
/// Returns an [OutlinedBorder] that's to be used when a Material component is
/// in the specified state. Return null to defer to the default value of the
/// widget or theme.
@override
OutlinedBorder? resolve(Set<MaterialState> states);
}
@Deprecated(
'Use WidgetStateOutlinedBorder instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStateOutlinedBorder = WidgetStateOutlinedBorder;
/// Defines a [TextStyle] that is also a [MaterialStateProperty].
///
@ -441,42 +251,17 @@ abstract class MaterialStateOutlinedBorder extends OutlinedBorder implements Mat
/// [MaterialStateTextStyle] and override its [resolve] method. You'll also need
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
abstract class MaterialStateTextStyle extends TextStyle implements MaterialStateProperty<TextStyle> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const MaterialStateTextStyle();
/// Creates a [MaterialStateTextStyle] from a [MaterialPropertyResolver<TextStyle>]
/// callback function.
///
/// If used as a regular text style, the style resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null text style in the default
/// state.
const factory MaterialStateTextStyle.resolveWith(MaterialPropertyResolver<TextStyle> callback) = _MaterialStateTextStyle;
/// Returns a [TextStyle] that's to be used when a Material component is in the
/// specified state.
@override
TextStyle resolve(Set<MaterialState> states);
}
/// A [MaterialStateTextStyle] created from a [MaterialPropertyResolver<TextStyle>]
/// callback alone.
///
/// If used as a regular text style, the style resolved in the default state will
/// be used.
/// See also:
///
/// Used by [MaterialStateTextStyle.resolveWith].
class _MaterialStateTextStyle extends MaterialStateTextStyle {
const _MaterialStateTextStyle(this._resolve);
final MaterialPropertyResolver<TextStyle> _resolve;
@override
TextStyle resolve(Set<MaterialState> states) => _resolve(states);
}
/// * [WidgetStateTextStyle], the non-Material version that can be used
/// interchangeably with `MaterialStateTextStyle`.
@Deprecated(
'Use WidgetStateTextStyle instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStateTextStyle = WidgetStateTextStyle;
/// Defines a [OutlineInputBorder] that is also a [MaterialStateProperty].
///
@ -634,106 +419,29 @@ class _MaterialStateUnderlineInputBorder extends MaterialStateUnderlineInputBord
///
/// See also:
///
/// * [WidgetStateProperty], the non-Material version that can be used
/// interchangeably with `MaterialStateProperty`.
/// {@macro flutter.material.MaterialStateProperty.implementations}
abstract class MaterialStateProperty<T> {
/// Returns a value of type `T` that depends on [states].
///
/// Widgets like [TextButton] and [ElevatedButton] apply this method to their
/// current [MaterialState]s to compute colors and other visual parameters
/// at build time.
T resolve(Set<MaterialState> states);
/// Resolves the value for the given set of states if `value` is a
/// [MaterialStateProperty], otherwise returns the value itself.
///
/// This is useful for widgets that have parameters which can optionally be a
/// [MaterialStateProperty]. For example, [InkWell.mouseCursor] can be a
/// [MouseCursor] or a [MaterialStateProperty<MouseCursor>].
static T resolveAs<T>(T value, Set<MaterialState> states) {
if (value is MaterialStateProperty<T>) {
final MaterialStateProperty<T> property = value;
return property.resolve(states);
}
return value;
}
/// Convenience method for creating a [MaterialStateProperty] from a
/// [MaterialPropertyResolver] function alone.
static MaterialStateProperty<T> resolveWith<T>(MaterialPropertyResolver<T> callback) => _MaterialStatePropertyWith<T>(callback);
/// Convenience method for creating a [MaterialStateProperty] that resolves
/// to a single value for all states.
///
/// If you need a const value, use [MaterialStatePropertyAll] directly.
///
// TODO(darrenaustin): Deprecate this when we have the ability to create
// a dart fix that will replace this with MaterialStatePropertyAll:
// https://github.com/dart-lang/sdk/issues/49056.
static MaterialStateProperty<T> all<T>(T value) => MaterialStatePropertyAll<T>(value);
/// Linearly interpolate between two [MaterialStateProperty]s.
static MaterialStateProperty<T?>? lerp<T>(
MaterialStateProperty<T>? a,
MaterialStateProperty<T>? b,
double t,
T? Function(T?, T?, double) lerpFunction,
) {
// Avoid creating a _LerpProperties object for a common case.
if (a == null && b == null) {
return null;
}
return _LerpProperties<T>(a, b, t, lerpFunction);
}
}
class _LerpProperties<T> implements MaterialStateProperty<T?> {
const _LerpProperties(this.a, this.b, this.t, this.lerpFunction);
final MaterialStateProperty<T>? a;
final MaterialStateProperty<T>? b;
final double t;
final T? Function(T?, T?, double) lerpFunction;
@override
T? resolve(Set<MaterialState> states) {
final T? resolvedA = a?.resolve(states);
final T? resolvedB = b?.resolve(states);
return lerpFunction(resolvedA, resolvedB, t);
}
}
class _MaterialStatePropertyWith<T> implements MaterialStateProperty<T> {
_MaterialStatePropertyWith(this._resolve);
final MaterialPropertyResolver<T> _resolve;
@override
T resolve(Set<MaterialState> states) => _resolve(states);
}
@Deprecated(
'Use WidgetStateProperty instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStateProperty<T> = WidgetStateProperty<T>;
/// Convenience class for creating a [MaterialStateProperty] that
/// resolves to the given value for all states.
class MaterialStatePropertyAll<T> implements MaterialStateProperty<T> {
/// Constructs a [MaterialStateProperty] that always resolves to the given
/// value.
const MaterialStatePropertyAll(this.value);
/// The value of the property that will be used for all states.
final T value;
@override
T resolve(Set<MaterialState> states) => value;
@override
String toString() {
if (value is double) {
return 'MaterialStatePropertyAll(${debugFormatDouble(value as double)})';
} else {
return 'MaterialStatePropertyAll($value)';
}
}
}
///
/// See also:
///
/// * [WidgetStatePropertyAll], the non-Material version that can be used
/// interchangeably with `MaterialStatePropertyAll`.
@Deprecated(
'Use WidgetStatePropertyAll instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStatePropertyAll<T> = WidgetStatePropertyAll<T>;
/// Manages a set of [MaterialState]s and notifies listeners of changes.
///
@ -762,16 +470,14 @@ class MaterialStatePropertyAll<T> implements MaterialStateProperty<T> {
/// depend on [MaterialStatesController] may call [update] in their build method.
/// In such cases, listener's that call `setState` - during the build phase - will cause
/// an error.
class MaterialStatesController extends ValueNotifier<Set<MaterialState>> {
/// Creates a MaterialStatesController.
MaterialStatesController([Set<MaterialState>? value]) : super(<MaterialState>{...?value});
/// Adds [state] to [value] if [add] is true, and removes it otherwise,
/// and notifies listeners if [value] has changed.
void update(MaterialState state, bool add) {
final bool valueChanged = add ? value.add(state) : value.remove(state);
if (valueChanged) {
notifyListeners();
}
}
}
///
/// See also:
///
/// * [WidgetStatesController], the non-Material version that can be used
/// interchangeably with `MaterialStatesController`.
@Deprecated(
'Use WidgetStatesController instead. '
'Moved to the Widgets layer to make code available outside of Material. '
'This feature was deprecated after v3.19.0-0.3.pre.'
)
typedef MaterialStatesController = WidgetStatesController;

View file

@ -0,0 +1,633 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
// Examples can assume:
// late BuildContext context;
/// Interactive states that some of the widgets can take on when receiving input
/// from the user.
///
/// States are defined by https://material.io/design/interaction/states.html#usage,
/// but are not limited to the Material design system or library.
///
/// Some widgets track their current state in a `Set<WidgetState>`.
///
/// See also:
///
/// * [MaterialState], the Material specific version of `WidgetState`.
/// * [WidgetStateProperty], an interface for objects that "resolve" to
/// different values depending on a widget's state.
/// {@template flutter.widgets.WidgetStateProperty.implementations}
/// * [WidgetStateColor], a [Color] that implements `WidgetStateProperty`
/// which is used in APIs that need to accept either a [Color] or a
/// `WidgetStateProperty<Color>`.
/// * [WidgetStateMouseCursor], a [MouseCursor] that implements
/// `WidgetStateProperty` which is used in APIs that need to accept either
/// a [MouseCursor] or a [WidgetStateProperty<MouseCursor>].
/// * [WidgetStateOutlinedBorder], an [OutlinedBorder] that implements
/// `WidgetStateProperty` which is used in APIs that need to accept either
/// an [OutlinedBorder] or a [WidgetStateProperty<OutlinedBorder>].
/// * [WidgetStateBorderSide], a [BorderSide] that implements
/// `WidgetStateProperty` which is used in APIs that need to accept either
/// a [BorderSide] or a [WidgetStateProperty<BorderSide>].
/// * [WidgetStateTextStyle], a [TextStyle] that implements
/// `WidgetStateProperty` which is used in APIs that need to accept either
/// a [TextStyle] or a [WidgetStateProperty<TextStyle>].
/// {@endtemplate}
enum WidgetState {
/// The state when the user drags their mouse cursor over the given widget.
///
/// See: https://material.io/design/interaction/states.html#hover.
hovered,
/// The state when the user navigates with the keyboard to a given widget.
///
/// This can also sometimes be triggered when a widget is tapped. For example,
/// when a [TextField] is tapped, it becomes [focused].
///
/// See: https://material.io/design/interaction/states.html#focus.
focused,
/// The state when the user is actively pressing down on the given widget.
///
/// See: https://material.io/design/interaction/states.html#pressed.
pressed,
/// The state when this widget is being dragged from one place to another by
/// the user.
///
/// https://material.io/design/interaction/states.html#dragged.
dragged,
/// The state when this item has been selected.
///
/// This applies to things that can be toggled (such as chips and checkboxes)
/// and things that are selected from a set of options (such as tabs and radio buttons).
///
/// See: https://material.io/design/interaction/states.html#selected.
selected,
/// The state when this widget overlaps the content of a scrollable below.
///
/// Used by [AppBar] to indicate that the primary scrollable's
/// content has scrolled up and behind the app bar.
scrolledUnder,
/// The state when this widget is disabled and cannot be interacted with.
///
/// Disabled widgets should not respond to hover, focus, press, or drag
/// interactions.
///
/// See: https://material.io/design/interaction/states.html#disabled.
disabled,
/// The state when the widget has entered some form of invalid state.
///
/// See https://material.io/design/interaction/states.html#usage.
error,
}
/// Signature for the function that returns a value of type `T` based on a given
/// set of states.
typedef WidgetPropertyResolver<T> = T Function(Set<WidgetState> states);
/// Defines a [Color] that is also a [WidgetStateProperty].
///
/// This class exists to enable widgets with [Color] valued properties
/// to also accept [WidgetStateProperty<Color>] values. A widget
/// state color property represents a color which depends on
/// a widget's "interactive state". This state is represented as a
/// [Set] of [WidgetState]s, like [WidgetState.pressed],
/// [WidgetState.focused] and [WidgetState.hovered].
///
/// [WidgetStateColor] should only be used with widgets that document
/// their support, like [TimePickerThemeData.dayPeriodColor].
///
/// To use a [WidgetStateColor], you can either:
/// 1. Create a subclass of [WidgetStateColor] and implement the abstract `resolve` method.
/// 2. Use [WidgetStateColor.resolveWith] and pass in a callback that
/// will be used to resolve the color in the given states.
///
/// If a [WidgetStateColor] is used for a property or a parameter that doesn't
/// support resolving [WidgetStateProperty<Color>]s, then its default color
/// value will be used for all states.
///
/// To define a `const` [WidgetStateColor], you'll need to extend
/// [WidgetStateColor] and override its [resolve] method. You'll also need
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
///
/// {@tool snippet}
///
/// This example defines a [WidgetStateColor] with a const constructor.
///
/// ```dart
/// class MyColor extends WidgetStateColor {
/// const MyColor() : super(_defaultColor);
///
/// static const int _defaultColor = 0xcafefeed;
/// static const int _pressedColor = 0xdeadbeef;
///
/// @override
/// Color resolve(Set<WidgetState> states) {
/// if (states.contains(WidgetState.pressed)) {
/// return const Color(_pressedColor);
/// }
/// return const Color(_defaultColor);
/// }
/// }
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [MaterialStateColor], the Material specific version of `WidgetStateColor`.
abstract class WidgetStateColor extends Color implements WidgetStateProperty<Color> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const WidgetStateColor(super.defaultValue);
/// Creates a [WidgetStateColor] from a [WidgetPropertyResolver<Color>]
/// callback function.
///
/// If used as a regular color, the color resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null color in the default
/// state.
static WidgetStateColor resolveWith(WidgetPropertyResolver<Color> callback) => _WidgetStateColor(callback);
/// Returns a [Color] that's to be used when a component is in the specified
/// state.
@override
Color resolve(Set<WidgetState> states);
/// A constant whose value is transparent for all states.
static const WidgetStateColor transparent = _WidgetStateColorTransparent();
}
class _WidgetStateColor extends WidgetStateColor {
_WidgetStateColor(this._resolve) : super(_resolve(_defaultStates).value);
final WidgetPropertyResolver<Color> _resolve;
static const Set<WidgetState> _defaultStates = <WidgetState>{};
@override
Color resolve(Set<WidgetState> states) => _resolve(states);
}
class _WidgetStateColorTransparent extends WidgetStateColor {
const _WidgetStateColorTransparent() : super(0x00000000);
@override
Color resolve(Set<WidgetState> states) => const Color(0x00000000);
}
/// Defines a [MouseCursor] whose value depends on a set of [WidgetState]s which
/// represent the interactive state of a component.
///
/// This kind of [MouseCursor] is useful when the set of interactive
/// actions a widget supports varies with its state. For example, a
/// mouse pointer hovering over a disabled [ListTile] should not
/// display [SystemMouseCursors.click], since a disabled list tile
/// doesn't respond to mouse clicks. [ListTile]'s default mouse cursor
/// is a [WidgetStateMouseCursor.clickable], which resolves to
/// [SystemMouseCursors.basic] when the button is disabled.
///
/// To use a [WidgetStateMouseCursor], you should create a subclass of
/// [WidgetStateMouseCursor] and implement the abstract `resolve` method.
///
/// {@tool dartpad}
/// This example defines a mouse cursor that resolves to
/// [SystemMouseCursors.forbidden] when its widget is disabled.
///
/// ** See code in examples/api/lib/material/material_state/material_state_mouse_cursor.0.dart **
/// {@end-tool}
///
/// This class should only be used for parameters which are documented to take
/// [WidgetStateMouseCursor], otherwise only the default state will be used.
///
/// See also:
///
/// * [MaterialStateMouseCursor], the Material specific version of
/// `WidgetStateMouseCursor`.
/// * [MouseCursor] for introduction on the mouse cursor system.
/// * [SystemMouseCursors], which defines cursors that are supported by
/// native platforms.
abstract class WidgetStateMouseCursor extends MouseCursor implements WidgetStateProperty<MouseCursor> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const WidgetStateMouseCursor();
@protected
@override
MouseCursorSession createSession(int device) {
return resolve(<WidgetState>{}).createSession(device);
}
/// Returns a [MouseCursor] that's to be used when a component is in the
/// specified state.
@override
MouseCursor resolve(Set<WidgetState> states);
/// A mouse cursor for clickable widgets, which resolves differently when the
/// widget is disabled.
///
/// By default this cursor resolves to [SystemMouseCursors.click]. If the widget is
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
/// This cursor is the default for many widgets.
static const WidgetStateMouseCursor clickable = _EnabledAndDisabledMouseCursor(
enabledCursor: SystemMouseCursors.click,
disabledCursor: SystemMouseCursors.basic,
name: 'clickable',
);
/// A mouse cursor for widgets related to text, which resolves differently
/// when the widget is disabled.
///
/// By default this cursor resolves to [SystemMouseCursors.text]. If the widget is
/// disabled, the cursor resolves to [SystemMouseCursors.basic].
///
/// This cursor is the default for many widgets.
static const WidgetStateMouseCursor textable = _EnabledAndDisabledMouseCursor(
enabledCursor: SystemMouseCursors.text,
disabledCursor: SystemMouseCursors.basic,
name: 'textable',
);
}
class _EnabledAndDisabledMouseCursor extends WidgetStateMouseCursor {
const _EnabledAndDisabledMouseCursor({
required this.enabledCursor,
required this.disabledCursor,
required this.name,
});
final MouseCursor enabledCursor;
final MouseCursor disabledCursor;
final String name;
@override
MouseCursor resolve(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return disabledCursor;
}
return enabledCursor;
}
@override
String get debugDescription => 'WidgetStateMouseCursor($name)';
}
/// Defines a [BorderSide] whose value depends on a set of [WidgetState]s
/// which represent the interactive state of a component.
///
/// To use a [WidgetStateBorderSide], you should create a subclass of a
/// [WidgetStateBorderSide] and override the abstract `resolve` method.
///
/// This class enables existing widget implementations with [BorderSide]
/// properties to be extended to also effectively support `WidgetStateProperty<BorderSide>`
/// property values. [WidgetStateBorderSide] should only be used with widgets that document
/// their support, like [ActionChip.side].
///
/// This class should only be used for parameters which are documented to take
/// [WidgetStateBorderSide], otherwise only the default state will be used.
///
/// See also:
///
/// * [MaterialStateBorderSide], the Material specific version of
/// `WidgetStateBorderSide`.
abstract class WidgetStateBorderSide extends BorderSide implements WidgetStateProperty<BorderSide?> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const WidgetStateBorderSide();
/// Creates a [WidgetStateBorderSide] from a
/// [WidgetPropertyResolver<BorderSide?>] callback function.
///
/// If used as a regular [BorderSide], the border resolved in the default state
/// (the empty set of states) will be used.
///
/// Usage:
///
/// ```dart
/// ChipTheme(
/// data: Theme.of(context).chipTheme.copyWith(
/// side: WidgetStateBorderSide.resolveWith((Set<WidgetState> states) {
/// if (states.contains(WidgetState.selected)) {
/// return const BorderSide(color: Colors.red);
/// }
/// return null; // Defer to default value on the theme or widget.
/// }),
/// ),
/// child: const Chip(
/// label: Text('Transceiver'),
/// ),
/// ),
/// ```
///
/// Alternatively:
///
/// ```dart
/// Chip(
/// label: const Text('Transceiver'),
/// side: WidgetStateBorderSide.resolveWith((Set<WidgetState> states) {
/// if (states.contains(WidgetState.selected)) {
/// return const BorderSide(color: Colors.red);
/// }
/// return null; // Defer to default value on the theme or widget.
/// }),
/// ),
/// ```
const factory WidgetStateBorderSide.resolveWith(WidgetPropertyResolver<BorderSide?> callback) = _WidgetStateBorderSide;
/// Returns a [BorderSide] that's to be used when a Material component is
/// in the specified state. Return null to defer to the default value of the
/// widget or theme.
@override
BorderSide? resolve(Set<WidgetState> states);
}
class _WidgetStateBorderSide extends WidgetStateBorderSide {
const _WidgetStateBorderSide(this._resolve);
final WidgetPropertyResolver<BorderSide?> _resolve;
@override
BorderSide? resolve(Set<WidgetState> states) => _resolve(states);
}
/// Defines an [OutlinedBorder] whose value depends on a set of [WidgetState]s
/// which represent the interactive state of a component.
///
/// To use a [WidgetStateOutlinedBorder], you should create a subclass of an
/// [OutlinedBorder] and implement [WidgetStateOutlinedBorder]'s abstract
/// `resolve` method.
///
/// {@tool dartpad}
/// This example defines a subclass of [RoundedRectangleBorder] and an
/// implementation of [WidgetStateOutlinedBorder], that resolves to
/// [RoundedRectangleBorder] when its widget is selected.
///
/// ** See code in examples/api/lib/material/material_state/material_state_outlined_border.0.dart **
/// {@end-tool}
///
/// This class should only be used for parameters which are documented to take
/// [WidgetStateOutlinedBorder], otherwise only the default state will be used.
///
/// See also:
///
/// * [ShapeBorder] the base class for shape outlines.
/// * [MaterialStateOutlinedBorder], the Material specific version of
/// `WidgetStateOutlinedBorder`.
abstract class WidgetStateOutlinedBorder extends OutlinedBorder implements WidgetStateProperty<OutlinedBorder?> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const WidgetStateOutlinedBorder();
/// Returns an [OutlinedBorder] that's to be used when a component is in the
/// specified state. Return null to defer to the default value of the widget
/// or theme.
@override
OutlinedBorder? resolve(Set<WidgetState> states);
}
/// Defines a [TextStyle] that is also a [WidgetStateProperty].
///
/// This class exists to enable widgets with [TextStyle] valued properties
/// to also accept [WidgetStateProperty<TextStyle>] values. A widget
/// state text style property represents a text style which depends on
/// a widget's "interactive state". This state is represented as a
/// [Set] of [WidgetState]s, like [WidgetState.pressed],
/// [WidgetState.focused] and [WidgetState.hovered].
///
/// [WidgetStateTextStyle] should only be used with widgets that document
/// their support, like [InputDecoration.labelStyle].
///
/// To use a [WidgetStateTextStyle], you can either:
/// 1. Create a subclass of [WidgetStateTextStyle] and implement the abstract `resolve` method.
/// 2. Use [WidgetStateTextStyle.resolveWith] and pass in a callback that
/// will be used to resolve the color in the given states.
///
/// If a [WidgetStateTextStyle] is used for a property or a parameter that doesn't
/// support resolving [WidgetStateProperty<TextStyle>]s, then its default color
/// value will be used for all states.
///
/// To define a `const` [WidgetStateTextStyle], you'll need to extend
/// [WidgetStateTextStyle] and override its [resolve] method. You'll also need
/// to provide a `defaultValue` to the super constructor, so that we can know
/// at compile-time what its default color is.
/// See also:
///
/// * [MaterialStateTextStyle], the Material specific version of
/// `WidgetStateTextStyle`.
abstract class WidgetStateTextStyle extends TextStyle implements WidgetStateProperty<TextStyle> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const WidgetStateTextStyle();
/// Creates a [WidgetStateTextStyle] from a [WidgetPropertyResolver<TextStyle>]
/// callback function.
///
/// If used as a regular text style, the style resolved in the default state (the
/// empty set of states) will be used.
///
/// The given callback parameter must return a non-null text style in the default
/// state.
const factory WidgetStateTextStyle.resolveWith(WidgetPropertyResolver<TextStyle> callback) = _WidgetStateTextStyle;
/// Returns a [TextStyle] that's to be used when a component is in the
/// specified state.
@override
TextStyle resolve(Set<WidgetState> states);
}
class _WidgetStateTextStyle extends WidgetStateTextStyle {
const _WidgetStateTextStyle(this._resolve);
final WidgetPropertyResolver<TextStyle> _resolve;
@override
TextStyle resolve(Set<WidgetState> states) => _resolve(states);
}
/// Interface for classes that [resolve] to a value of type `T` based
/// on a widget's interactive "state", which is defined as a set
/// of [WidgetState]s.
///
/// Widget state properties represent values that depend on a widget's "state".
/// The state is encoded as a set of [WidgetState] values, like
/// [WidgetState.focused], [WidgetState.hovered], [WidgetState.pressed]. For
/// example the [InkWell.overlayColor] defines the color that fills the ink well
/// when it's pressed (the "splash color"), focused, or hovered. The [InkWell]
/// uses the overlay color's [resolve] method to compute the color for the
/// ink well's current state.
///
/// [ButtonStyle], which is used to configure the appearance of
/// buttons like [TextButton], [ElevatedButton], and [OutlinedButton],
/// has many material state properties. The button widgets keep track
/// of their current material state and [resolve] the button style's
/// material state properties when their value is needed.
///
/// See also:
///
/// * [MaterialStateProperty], the Material specific version of
/// `WidgetStateProperty`.
/// {@macro flutter.widgets.WidgetStateProperty.implementations}
abstract class WidgetStateProperty<T> {
/// Returns a value of type `T` that depends on [states].
///
/// Widgets like [TextButton] and [ElevatedButton] apply this method to their
/// current [WidgetState]s to compute colors and other visual parameters
/// at build time.
T resolve(Set<WidgetState> states);
/// Resolves the value for the given set of states if `value` is a
/// [WidgetStateProperty], otherwise returns the value itself.
///
/// This is useful for widgets that have parameters which can optionally be a
/// [WidgetStateProperty]. For example, [InkWell.mouseCursor] can be a
/// [MouseCursor] or a [WidgetStateProperty<MouseCursor>].
static T resolveAs<T>(T value, Set<WidgetState> states) {
if (value is WidgetStateProperty<T>) {
final WidgetStateProperty<T> property = value;
return property.resolve(states);
}
return value;
}
/// Convenience method for creating a [WidgetStateProperty] from a
/// [WidgetPropertyResolver] function alone.
static WidgetStateProperty<T> resolveWith<T>(WidgetPropertyResolver<T> callback) => _WidgetStatePropertyWith<T>(callback);
/// Convenience method for creating a [WidgetStateProperty] that resolves
/// to a single value for all states.
///
/// If you need a const value, use [WidgetStatePropertyAll] directly.
///
// TODO(darrenaustin): Deprecate this when we have the ability to create
// a dart fix that will replace this with WidgetStatePropertyAll:
// https://github.com/dart-lang/sdk/issues/49056.
static WidgetStateProperty<T> all<T>(T value) => WidgetStatePropertyAll<T>(value);
/// Linearly interpolate between two [WidgetStateProperty]s.
static WidgetStateProperty<T?>? lerp<T>(
WidgetStateProperty<T>? a,
WidgetStateProperty<T>? b,
double t,
T? Function(T?, T?, double) lerpFunction,
) {
// Avoid creating a _LerpProperties object for a common case.
if (a == null && b == null) {
return null;
}
return _LerpProperties<T>(a, b, t, lerpFunction);
}
}
class _LerpProperties<T> implements WidgetStateProperty<T?> {
const _LerpProperties(this.a, this.b, this.t, this.lerpFunction);
final WidgetStateProperty<T>? a;
final WidgetStateProperty<T>? b;
final double t;
final T? Function(T?, T?, double) lerpFunction;
@override
T? resolve(Set<WidgetState> states) {
final T? resolvedA = a?.resolve(states);
final T? resolvedB = b?.resolve(states);
return lerpFunction(resolvedA, resolvedB, t);
}
}
class _WidgetStatePropertyWith<T> implements WidgetStateProperty<T> {
_WidgetStatePropertyWith(this._resolve);
final WidgetPropertyResolver<T> _resolve;
@override
T resolve(Set<WidgetState> states) => _resolve(states);
}
/// Convenience class for creating a [WidgetStateProperty] that
/// resolves to the given value for all states.
///
/// See also:
///
/// * [MaterialStatePropertyAll], the Material specific version of
/// `WidgetStatePropertyAll`.
class WidgetStatePropertyAll<T> implements WidgetStateProperty<T> {
/// Constructs a [WidgetStateProperty] that always resolves to the given
/// value.
const WidgetStatePropertyAll(this.value);
/// The value of the property that will be used for all states.
final T value;
@override
T resolve(Set<WidgetState> states) => value;
@override
String toString() {
if (value is double) {
return 'WidgetStatePropertyAll(${debugFormatDouble(value as double)})';
} else {
return 'WidgetStatePropertyAll($value)';
}
}
}
/// Manages a set of [WidgetState]s and notifies listeners of changes.
///
/// Used by widgets that expose their internal state for the sake of
/// extensions that add support for additional states. See
/// [TextButton] for an example.
///
/// The controller's [value] is its current set of states. Listeners
/// are notified whenever the [value] changes. The [value] should only be
/// changed with [update]; it should not be modified directly.
///
/// The controller's [value] represents the set of states that a
/// widget's visual properties, typically [WidgetStateProperty]
/// values, are resolved against. It is _not_ the intrinsic state of
/// the widget. The widget is responsible for ensuring that the
/// controller's [value] tracks its intrinsic state. For example one
/// cannot request the keyboard focus for a widget by adding
/// [WidgetState.focused] to its controller. When the widget gains the
/// or loses the focus it will [update] its controller's [value] and
/// notify listeners of the change.
///
/// When calling `setState` in a [MaterialStatesController] listener, use the
/// [SchedulerBinding.addPostFrameCallback] to delay the call to `setState` after
/// the frame has been rendered. It's generally prudent to use the
/// [SchedulerBinding.addPostFrameCallback] because some of the widgets that
/// depend on [MaterialStatesController] may call [update] in their build method.
/// In such cases, listener's that call `setState` - during the build phase - will cause
/// an error.
///
/// See also:
///
/// * [MaterialStatesController], the Material specific version of
/// `WidgetStatesController`.
class WidgetStatesController extends ValueNotifier<Set<WidgetState>> {
/// Creates a WidgetStatesController.
WidgetStatesController([Set<WidgetState>? value]) : super(<WidgetState>{...?value});
/// Adds [state] to [value] if [add] is true, and removes it otherwise,
/// and notifies listeners if [value] has changed.
void update(WidgetState state, bool add) {
final bool valueChanged = add ? value.add(state) : value.remove(state);
if (valueChanged) {
notifyListeners();
}
}
}

View file

@ -164,4 +164,5 @@ export 'src/widgets/viewport.dart';
export 'src/widgets/visibility.dart';
export 'src/widgets/widget_inspector.dart';
export 'src/widgets/widget_span.dart';
export 'src/widgets/widget_state.dart';
export 'src/widgets/will_pop_scope.dart';

View file

@ -102,7 +102,7 @@ void main() {
expect(description[8], 'showSelectedLabels: true');
expect(description[9], 'showUnselectedLabels: true');
expect(description[10], 'type: BottomNavigationBarType.fixed');
expect(description[11], 'mouseCursor: MaterialStateMouseCursor(clickable)');
expect(description[11], 'mouseCursor: WidgetStateMouseCursor(clickable)');
});
testWidgets('BottomNavigationBar is themeable', (WidgetTester tester) async {

View file

@ -85,21 +85,21 @@ void main() {
.toList();
expect(description, <String>[
'textStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 10.0))',
'backgroundColor: MaterialStatePropertyAll(Color(0xfffffff1))',
'foregroundColor: MaterialStatePropertyAll(Color(0xfffffff2))',
'overlayColor: MaterialStatePropertyAll(Color(0xfffffff3))',
'shadowColor: MaterialStatePropertyAll(Color(0xfffffff4))',
'surfaceTintColor: MaterialStatePropertyAll(Color(0xfffffff5))',
'elevation: MaterialStatePropertyAll(1.5)',
'padding: MaterialStatePropertyAll(EdgeInsets.all(1.0))',
'minimumSize: MaterialStatePropertyAll(Size(1.0, 2.0))',
'maximumSize: MaterialStatePropertyAll(Size(100.0, 200.0))',
'iconColor: MaterialStatePropertyAll(Color(0xfffffff6))',
'iconSize: MaterialStatePropertyAll(48.1)',
'side: MaterialStatePropertyAll(BorderSide(color: Color(0xfffffff6), width: 4.0))',
'shape: MaterialStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))',
'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(forbidden))',
'textStyle: WidgetStatePropertyAll(TextStyle(inherit: true, size: 10.0))',
'backgroundColor: WidgetStatePropertyAll(Color(0xfffffff1))',
'foregroundColor: WidgetStatePropertyAll(Color(0xfffffff2))',
'overlayColor: WidgetStatePropertyAll(Color(0xfffffff3))',
'shadowColor: WidgetStatePropertyAll(Color(0xfffffff4))',
'surfaceTintColor: WidgetStatePropertyAll(Color(0xfffffff5))',
'elevation: WidgetStatePropertyAll(1.5)',
'padding: WidgetStatePropertyAll(EdgeInsets.all(1.0))',
'minimumSize: WidgetStatePropertyAll(Size(1.0, 2.0))',
'maximumSize: WidgetStatePropertyAll(Size(100.0, 200.0))',
'iconColor: WidgetStatePropertyAll(Color(0xfffffff6))',
'iconSize: WidgetStatePropertyAll(48.1)',
'side: WidgetStatePropertyAll(BorderSide(color: Color(0xfffffff6), width: 4.0))',
'shape: WidgetStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))',
'mouseCursor: WidgetStatePropertyAll(SystemMouseCursor(forbidden))',
'tapTargetSize: shrinkWrap',
'animationDuration: 0:00:01.000000',
'enableFeedback: true',

View file

@ -71,10 +71,10 @@ void main() {
expect(
description,
equalsIgnoringHashCodes(<String>[
'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))',
'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))',
'checkColor: MaterialStatePropertyAll(Color(0xfffffff1))',
'overlayColor: MaterialStatePropertyAll(Color(0xfffffff2))',
'mouseCursor: WidgetStatePropertyAll(SystemMouseCursor(click))',
'fillColor: WidgetStatePropertyAll(Color(0xfffffff0))',
'checkColor: WidgetStatePropertyAll(Color(0xfffffff1))',
'overlayColor: WidgetStatePropertyAll(Color(0xfffffff2))',
'splashRadius: 1.0',
'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap',
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',

View file

@ -129,7 +129,7 @@ void main() {
.toList();
expect(description, equalsIgnoringHashCodes(<String>[
'color: MaterialStatePropertyAll(Color(0xfffffff0))',
'color: WidgetStatePropertyAll(Color(0xfffffff0))',
'backgroundColor: Color(0xfffffff1)',
'deleteIconColor: Color(0xfffffff2)',
'disabledColor: Color(0xfffffff3)',

View file

@ -105,19 +105,19 @@ void main() {
.toList();
expect(description[0], 'decoration: BoxDecoration(color: Color(0xfffffff0))');
expect(description[1], "dataRowColor: Instance of '_MaterialStatePropertyWith<Color>'");
expect(description[1], "dataRowColor: Instance of '_WidgetStatePropertyWith<Color>'");
expect(description[2], 'dataRowMinHeight: 41.0');
expect(description[3], 'dataRowMaxHeight: 42.0');
expect(description[4], 'dataTextStyle: TextStyle(inherit: true, size: 12.0)');
expect(description[5], "headingRowColor: Instance of '_MaterialStatePropertyWith<Color>'");
expect(description[5], "headingRowColor: Instance of '_WidgetStatePropertyWith<Color>'");
expect(description[6], 'headingRowHeight: 52.0');
expect(description[7], 'headingTextStyle: TextStyle(inherit: true, size: 14.0)');
expect(description[8], 'horizontalMargin: 3.0');
expect(description[9], 'columnSpacing: 4.0');
expect(description[10], 'dividerThickness: 5.0');
expect(description[11], 'checkboxHorizontalMargin: 6.0');
expect(description[12], 'headingCellCursor: MaterialStatePropertyAll(SystemMouseCursor(grab))');
expect(description[13], 'dataRowCursor: MaterialStatePropertyAll(SystemMouseCursor(forbidden))');
expect(description[12], 'headingCellCursor: WidgetStatePropertyAll(SystemMouseCursor(grab))');
expect(description[13], 'dataRowCursor: WidgetStatePropertyAll(SystemMouseCursor(forbidden))');
});
testWidgets('DataTable is themeable', (WidgetTester tester) async {

View file

@ -329,17 +329,17 @@ void main() {
'headerHelpStyle: TextStyle(inherit: true, size: 11.0)',
'weekDayStyle: TextStyle(inherit: true, size: 12.0)',
'dayStyle: TextStyle(inherit: true, size: 13.0)',
'dayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff5))',
'dayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff6))',
'dayOverlayColor: MaterialStatePropertyAll(Color(0xfffffff7))',
'dayShape: MaterialStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero))',
'todayForegroundColor: MaterialStatePropertyAll(Color(0xfffffff8))',
'todayBackgroundColor: MaterialStatePropertyAll(Color(0xfffffff9))',
'dayForegroundColor: WidgetStatePropertyAll(Color(0xfffffff5))',
'dayBackgroundColor: WidgetStatePropertyAll(Color(0xfffffff6))',
'dayOverlayColor: WidgetStatePropertyAll(Color(0xfffffff7))',
'dayShape: WidgetStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero))',
'todayForegroundColor: WidgetStatePropertyAll(Color(0xfffffff8))',
'todayBackgroundColor: WidgetStatePropertyAll(Color(0xfffffff9))',
'todayBorder: BorderSide(width: 3.0)',
'yearStyle: TextStyle(inherit: true, size: 13.0)',
'yearForegroundColor: MaterialStatePropertyAll(Color(0xfffffffa))',
'yearBackgroundColor: MaterialStatePropertyAll(Color(0xfffffffb))',
'yearOverlayColor: MaterialStatePropertyAll(Color(0xfffffffc))',
'yearForegroundColor: WidgetStatePropertyAll(Color(0xfffffffa))',
'yearBackgroundColor: WidgetStatePropertyAll(Color(0xfffffffb))',
'yearOverlayColor: WidgetStatePropertyAll(Color(0xfffffffc))',
'rangePickerBackgroundColor: Color(0xfffffffd)',
'rangePickerElevation: 7.0',
'rangePickerShadowColor: Color(0xfffffffe)',
@ -350,11 +350,11 @@ void main() {
'rangePickerHeaderHeadlineStyle: TextStyle(inherit: true, size: 14.0)',
'rangePickerHeaderHelpStyle: TextStyle(inherit: true, size: 15.0)',
'rangeSelectionBackgroundColor: Color(0xffffff2f)',
'rangeSelectionOverlayColor: MaterialStatePropertyAll(Color(0xffffff3f))',
'rangeSelectionOverlayColor: WidgetStatePropertyAll(Color(0xffffff3f))',
'dividerColor: Color(0xffffff4f)',
'inputDecorationTheme: InputDecorationTheme#00000(fillColor: Color(0xffffff5f), border: UnderlineInputBorder())',
'cancelButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xffffff6f)))',
'confirmButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xffffff7f)))'
'cancelButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(Color(0xffffff6f)))',
'confirmButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(Color(0xffffff7f)))'
]));
});

View file

@ -422,7 +422,7 @@ void main() {
'extendedIconLabelSpacing: 12.0',
'extendedPadding: EdgeInsetsDirectional(7.0, 0.0, 8.0, 0.0)',
'extendedTextStyle: TextStyle(inherit: true, letterSpacing: 2.0)',
'mouseCursor: MaterialStateMouseCursor(clickable)',
'mouseCursor: WidgetStateMouseCursor(clickable)',
]);
});

View file

@ -138,7 +138,7 @@ void main() {
'minVerticalPadding: 300.0',
'minLeadingWidth: 400.0',
'enableFeedback: true',
'mouseCursor: MaterialStateMouseCursor(clickable)',
'mouseCursor: WidgetStateMouseCursor(clickable)',
'visualDensity: VisualDensity#00000(h: -1.0, v: -1.0)(horizontal: -1.0, vertical: -1.0)',
'titleAlignment: ListTileTitleAlignment.top',
]),

View file

@ -45,10 +45,10 @@ void main() {
test('toString formats correctly', () {
const MaterialStateProperty<Color?> colorProperty = MaterialStatePropertyAll<Color?>(Color(0xFFFFFFFF));
expect(colorProperty.toString(), equals('MaterialStatePropertyAll(Color(0xffffffff))'));
expect(colorProperty.toString(), equals('WidgetStatePropertyAll(Color(0xffffffff))'));
const MaterialStateProperty<double?> doubleProperty = MaterialStatePropertyAll<double?>(33 + 1/3);
expect(doubleProperty.toString(), equals('MaterialStatePropertyAll(33.3)'));
expect(doubleProperty.toString(), equals('WidgetStatePropertyAll(33.3)'));
});
test("Can interpolate between two MaterialStateProperty's", () {

View file

@ -1293,7 +1293,7 @@ void main() {
expect(
description.join('\n'),
equalsIgnoringHashCodes(
'style: MenuStyle#00000(backgroundColor: MaterialStatePropertyAll(MaterialColor(primary value: Color(0xfff44336))), elevation: MaterialStatePropertyAll(10.0))\n'
'style: MenuStyle#00000(backgroundColor: WidgetStatePropertyAll(MaterialColor(primary value: Color(0xfff44336))), elevation: WidgetStatePropertyAll(10.0))\n'
'clipBehavior: Clip.none'),
);
});
@ -2357,7 +2357,7 @@ void main() {
equalsIgnoringHashCodes(
<String>[
'focusNode: null',
'menuStyle: MenuStyle#00000(backgroundColor: MaterialStatePropertyAll(MaterialColor(primary value: Color(0xff4caf50))), elevation: MaterialStatePropertyAll(20.0), shape: MaterialStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)))',
'menuStyle: MenuStyle#00000(backgroundColor: WidgetStatePropertyAll(MaterialColor(primary value: Color(0xff4caf50))), elevation: WidgetStatePropertyAll(20.0), shape: WidgetStatePropertyAll(RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)))',
'alignmentOffset: null',
'clipBehavior: hardEdge',
],

View file

@ -62,12 +62,12 @@ void main() {
expect(description[2], 'elevation: 20.0');
expect(description[3], 'indicatorColor: Color(0x00000098)');
expect(description[4], 'indicatorShape: CircleBorder(BorderSide(width: 0.0, style: none))');
expect(description[5], 'labelTextStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 7.0))');
expect(description[5], 'labelTextStyle: WidgetStatePropertyAll(TextStyle(inherit: true, size: 7.0))');
// Ignore instance address for IconThemeData.
expect(description[6].contains('iconTheme: MaterialStatePropertyAll(IconThemeData'), isTrue);
expect(description[6].contains('iconTheme: WidgetStatePropertyAll(IconThemeData'), isTrue);
expect(description[6].contains('(color: Color(0x00000097))'), isTrue);
expect(description[7], 'labelBehavior: NavigationDestinationLabelBehavior.alwaysHide');
expect(description[8], 'overlayColor: MaterialStatePropertyAll(Color(0x00000096))');
expect(description[8], 'overlayColor: WidgetStatePropertyAll(Color(0x00000096))');
});
testWidgets('NavigationBarThemeData values are used when no NavigationBar properties are specified', (WidgetTester tester) async {

View file

@ -60,8 +60,8 @@ void main() {
'indicatorColor: Color(0x00000096)',
'indicatorShape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))',
'indicatorSize: Size(10.0, 10.0)',
'labelTextStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 7.0))',
'iconTheme: MaterialStatePropertyAll(IconThemeData#00000(color: Color(0x00000095)))'
'labelTextStyle: WidgetStatePropertyAll(TextStyle(inherit: true, size: 7.0))',
'iconTheme: WidgetStatePropertyAll(IconThemeData#00000(color: Color(0x00000095)))'
],
));
});

View file

@ -117,9 +117,9 @@ void main() {
'shadowColor: Color(0xfffffff2)',
'surfaceTintColor: Color(0xfffffff3)',
'text style: TextStyle(inherit: true, color: Color(0xfffffff4))',
"labelTextStyle: Instance of '_MaterialStatePropertyWith<TextStyle?>'",
"labelTextStyle: Instance of '_WidgetStatePropertyWith<TextStyle?>'",
'enableFeedback: false',
'mouseCursor: MaterialStateMouseCursor(clickable)',
'mouseCursor: WidgetStateMouseCursor(clickable)',
'position: over',
'iconColor: Color(0xfffffff8)',
'iconSize: 31.0'

View file

@ -68,9 +68,9 @@ void main() {
expect(
description,
equalsIgnoringHashCodes(<String>[
'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))',
'fillColor: MaterialStatePropertyAll(Color(0xfffffff0))',
'overlayColor: MaterialStatePropertyAll(Color(0xfffffff1))',
'mouseCursor: WidgetStatePropertyAll(SystemMouseCursor(click))',
'fillColor: WidgetStatePropertyAll(Color(0xfffffff0))',
'overlayColor: WidgetStatePropertyAll(Color(0xfffffff1))',
'splashRadius: 1.0',
'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap',
'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)',

View file

@ -741,12 +741,12 @@ void main() {
.toList();
expect(description, <String>[
"thumbVisibility: Instance of '_MaterialStatePropertyWith<bool?>'",
"thickness: Instance of '_MaterialStatePropertyWith<double?>'",
"thumbVisibility: Instance of '_WidgetStatePropertyWith<bool?>'",
"thickness: Instance of '_WidgetStatePropertyWith<double?>'",
'radius: Radius.circular(3.0)',
"thumbColor: Instance of '_MaterialStatePropertyWith<Color?>'",
"trackColor: Instance of '_MaterialStatePropertyWith<Color?>'",
"trackBorderColor: Instance of '_MaterialStatePropertyWith<Color?>'",
"thumbColor: Instance of '_WidgetStatePropertyWith<Color?>'",
"trackColor: Instance of '_WidgetStatePropertyWith<Color?>'",
"trackBorderColor: Instance of '_WidgetStatePropertyWith<Color?>'",
'crossAxisMargin: 3.0',
'mainAxisMargin: 6.0',
'minThumbLength: 120.0',

View file

@ -85,16 +85,16 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'elevation: MaterialStatePropertyAll(3.0)');
expect(description[1], 'backgroundColor: MaterialStatePropertyAll(Color(0xfffffff1))');
expect(description[2], 'shadowColor: MaterialStatePropertyAll(Color(0xfffffff2))');
expect(description[3], 'surfaceTintColor: MaterialStatePropertyAll(Color(0xfffffff3))');
expect(description[4], 'overlayColor: MaterialStatePropertyAll(Color(0xfffffff4))');
expect(description[5], 'side: MaterialStatePropertyAll(BorderSide(color: Color(0xfffffff5), width: 2.0))');
expect(description[6], 'shape: MaterialStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))');
expect(description[7], 'padding: MaterialStatePropertyAll(EdgeInsets.all(16.0))');
expect(description[8], 'textStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 24.0))');
expect(description[9], 'hintStyle: MaterialStatePropertyAll(TextStyle(inherit: true, size: 16.0))');
expect(description[0], 'elevation: WidgetStatePropertyAll(3.0)');
expect(description[1], 'backgroundColor: WidgetStatePropertyAll(Color(0xfffffff1))');
expect(description[2], 'shadowColor: WidgetStatePropertyAll(Color(0xfffffff2))');
expect(description[3], 'surfaceTintColor: WidgetStatePropertyAll(Color(0xfffffff3))');
expect(description[4], 'overlayColor: WidgetStatePropertyAll(Color(0xfffffff4))');
expect(description[5], 'side: WidgetStatePropertyAll(BorderSide(color: Color(0xfffffff5), width: 2.0))');
expect(description[6], 'shape: WidgetStatePropertyAll(StadiumBorder(BorderSide(width: 0.0, style: none)))');
expect(description[7], 'padding: WidgetStatePropertyAll(EdgeInsets.all(16.0))');
expect(description[8], 'textStyle: WidgetStatePropertyAll(TextStyle(inherit: true, size: 24.0))');
expect(description[9], 'hintStyle: WidgetStatePropertyAll(TextStyle(inherit: true, size: 16.0))');
expect(description[10], 'constraints: BoxConstraints(350.0<=w<=850.0, 0.0<=h<=Infinity)');
expect(description[11], 'textCapitalization: TextCapitalization.characters');
});

View file

@ -99,7 +99,7 @@ void main() {
"rangeValueIndicatorShape: Instance of 'PaddleRangeSliderValueIndicatorShape'",
'showValueIndicator: always',
'valueIndicatorTextStyle: TextStyle(inherit: true, color: Color(0xff000000))',
'mouseCursor: MaterialStateMouseCursor(clickable)',
'mouseCursor: WidgetStateMouseCursor(clickable)',
'allowedInteraction: tapOnly'
]);
});

View file

@ -73,15 +73,15 @@ void main() {
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'thumbColor: MaterialStatePropertyAll(Color(0xfffffff0))');
expect(description[1], 'trackColor: MaterialStatePropertyAll(Color(0xfffffff1))');
expect(description[2], 'trackOutlineColor: MaterialStatePropertyAll(Color(0xfffffff3))');
expect(description[3], 'trackOutlineWidth: MaterialStatePropertyAll(6.0)');
expect(description[0], 'thumbColor: WidgetStatePropertyAll(Color(0xfffffff0))');
expect(description[1], 'trackColor: WidgetStatePropertyAll(Color(0xfffffff1))');
expect(description[2], 'trackOutlineColor: WidgetStatePropertyAll(Color(0xfffffff3))');
expect(description[3], 'trackOutlineWidth: WidgetStatePropertyAll(6.0)');
expect(description[4], 'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap');
expect(description[5], 'mouseCursor: MaterialStatePropertyAll(SystemMouseCursor(click))');
expect(description[6], 'overlayColor: MaterialStatePropertyAll(Color(0xfffffff2))');
expect(description[5], 'mouseCursor: WidgetStatePropertyAll(SystemMouseCursor(click))');
expect(description[6], 'overlayColor: WidgetStatePropertyAll(Color(0xfffffff2))');
expect(description[7], 'splashRadius: 1.0');
expect(description[8], 'thumbIcon: MaterialStatePropertyAll(Icon(IconData(U+0007B)))');
expect(description[8], 'thumbIcon: WidgetStatePropertyAll(Icon(IconData(U+0007B)))');
});
testWidgets('Material2 - Switch is themeable', (WidgetTester tester) async {

View file

@ -91,8 +91,8 @@ void main() {
shape: RoundedRectangleBorder(
side: BorderSide(color: Color(0xfffffff3)),
),
timeSelectorSeparatorColor: MaterialStatePropertyAll<Color>(Color(0xfffffff4)),
timeSelectorSeparatorTextStyle: MaterialStatePropertyAll<TextStyle>(TextStyle(color: Color(0xfffffff5))),
timeSelectorSeparatorColor: WidgetStatePropertyAll<Color>(Color(0xfffffff4)),
timeSelectorSeparatorTextStyle: WidgetStatePropertyAll<TextStyle>(TextStyle(color: Color(0xfffffff5))),
).debugFillProperties(builder);
final List<String> description = builder.properties
@ -102,8 +102,8 @@ void main() {
expect(description, equalsIgnoringHashCodes(<String>[
'backgroundColor: Color(0xfffffff0)',
'cancelButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xfffffff1)))',
'confirmButtonStyle: ButtonStyle#00000(foregroundColor: MaterialStatePropertyAll(Color(0xfffffff2)))',
'cancelButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(Color(0xfffffff1)))',
'confirmButtonStyle: ButtonStyle#00000(foregroundColor: WidgetStatePropertyAll(Color(0xfffffff2)))',
'dayPeriodBorderSide: BorderSide(color: Color(0xfffffff3))',
'dayPeriodColor: Color(0x00000000)',
'dayPeriodShape: RoundedRectangleBorder(BorderSide(color: Color(0xfffffff5)), BorderRadius.zero)',
@ -123,8 +123,8 @@ void main() {
'inputDecorationTheme: InputDecorationTheme#ff861(labelStyle: TextStyle(inherit: true, color: Color(0xfffffff2)))',
'padding: EdgeInsets.all(1.0)',
'shape: RoundedRectangleBorder(BorderSide(color: Color(0xfffffff3)), BorderRadius.zero)',
'timeSelectorSeparatorColor: MaterialStatePropertyAll(Color(0xfffffff4))',
'timeSelectorSeparatorTextStyle: MaterialStatePropertyAll(TextStyle(inherit: true, color: Color(0xfffffff5)))'
'timeSelectorSeparatorColor: WidgetStatePropertyAll(Color(0xfffffff4))',
'timeSelectorSeparatorTextStyle: WidgetStatePropertyAll(TextStyle(inherit: true, color: Color(0xfffffff5)))'
]));
});

View file

@ -0,0 +1,91 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('WidgetStateProperty.resolveWith()', () {
final WidgetStateProperty<WidgetState> value = WidgetStateProperty.resolveWith<WidgetState>(
(Set<WidgetState> states) => states.first,
);
expect(value.resolve(<WidgetState>{WidgetState.hovered}), WidgetState.hovered);
expect(value.resolve(<WidgetState>{WidgetState.focused}), WidgetState.focused);
expect(value.resolve(<WidgetState>{WidgetState.pressed}), WidgetState.pressed);
expect(value.resolve(<WidgetState>{WidgetState.dragged}), WidgetState.dragged);
expect(value.resolve(<WidgetState>{WidgetState.selected}), WidgetState.selected);
expect(value.resolve(<WidgetState>{WidgetState.disabled}), WidgetState.disabled);
expect(value.resolve(<WidgetState>{WidgetState.error}), WidgetState.error);
});
test('WidgetStateProperty.all()', () {
final WidgetStateProperty<int> value = WidgetStateProperty.all<int>(123);
expect(value.resolve(<WidgetState>{WidgetState.hovered}), 123);
expect(value.resolve(<WidgetState>{WidgetState.focused}), 123);
expect(value.resolve(<WidgetState>{WidgetState.pressed}), 123);
expect(value.resolve(<WidgetState>{WidgetState.dragged}), 123);
expect(value.resolve(<WidgetState>{WidgetState.selected}), 123);
expect(value.resolve(<WidgetState>{WidgetState.disabled}), 123);
expect(value.resolve(<WidgetState>{WidgetState.error}), 123);
});
test('WidgetStatePropertyAll', () {
const WidgetStatePropertyAll<int> value = WidgetStatePropertyAll<int>(123);
expect(value.resolve(<WidgetState>{}), 123);
expect(value.resolve(<WidgetState>{WidgetState.hovered}), 123);
expect(value.resolve(<WidgetState>{WidgetState.focused}), 123);
expect(value.resolve(<WidgetState>{WidgetState.pressed}), 123);
expect(value.resolve(<WidgetState>{WidgetState.dragged}), 123);
expect(value.resolve(<WidgetState>{WidgetState.selected}), 123);
expect(value.resolve(<WidgetState>{WidgetState.disabled}), 123);
expect(value.resolve(<WidgetState>{WidgetState.error}), 123);
});
test('toString formats correctly', () {
const WidgetStateProperty<Color?> colorProperty = WidgetStatePropertyAll<Color?>(Color(0xFFFFFFFF));
expect(colorProperty.toString(), equals('WidgetStatePropertyAll(Color(0xffffffff))'));
const WidgetStateProperty<double?> doubleProperty = WidgetStatePropertyAll<double?>(33 + 1/3);
expect(doubleProperty.toString(), equals('WidgetStatePropertyAll(33.3)'));
});
test("Can interpolate between two WidgetStateProperty's", () {
const WidgetStateProperty<TextStyle?> textStyle1 = WidgetStatePropertyAll<TextStyle?>(
TextStyle(fontSize: 14.0),
);
const WidgetStateProperty<TextStyle?> textStyle2 = WidgetStatePropertyAll<TextStyle?>(
TextStyle(fontSize: 20.0),
);
// Using `0.0` interpolation value.
TextStyle textStyle = WidgetStateProperty.lerp<TextStyle?>(
textStyle1,
textStyle2,
0.0,
TextStyle.lerp,
)!.resolve(enabled)!;
expect(textStyle.fontSize, 14.0);
// Using `0.5` interpolation value.
textStyle = WidgetStateProperty.lerp<TextStyle?>(
textStyle1,
textStyle2,
0.5,
TextStyle.lerp,
)!.resolve(enabled)!;
expect(textStyle.fontSize, 17.0);
// Using `1.0` interpolation value.
textStyle = WidgetStateProperty.lerp<TextStyle?>(
textStyle1,
textStyle2,
1.0,
TextStyle.lerp,
)!.resolve(enabled)!;
expect(textStyle.fontSize, 20.0);
});
}
Set<WidgetState> enabled = <WidgetState>{};

View file

@ -0,0 +1,93 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
void main() {
test('WidgetStatesController constructor', () {
expect(WidgetStatesController().value, <WidgetState>{});
expect(WidgetStatesController(<WidgetState>{}).value, <WidgetState>{});
expect(WidgetStatesController(<WidgetState>{WidgetState.selected}).value, <WidgetState>{WidgetState.selected});
});
test('WidgetStatesController dispatches memory events', () async {
await expectLater(
await memoryEvents(() => WidgetStatesController().dispose(), WidgetStatesController),
areCreateAndDispose,
);
});
test('WidgetStatesController update, listener', () {
int count = 0;
void valueChanged() {
count += 1;
}
final WidgetStatesController controller = WidgetStatesController();
controller.addListener(valueChanged);
controller.update(WidgetState.selected, true);
expect(controller.value, <WidgetState>{WidgetState.selected});
expect(count, 1);
controller.update(WidgetState.selected, true);
expect(controller.value, <WidgetState>{WidgetState.selected});
expect(count, 1);
controller.update(WidgetState.hovered, false);
expect(count, 1);
expect(controller.value, <WidgetState>{WidgetState.selected});
controller.update(WidgetState.selected, false);
expect(count, 2);
expect(controller.value, <WidgetState>{});
controller.update(WidgetState.hovered, true);
expect(controller.value, <WidgetState>{WidgetState.hovered});
expect(count, 3);
controller.update(WidgetState.hovered, true);
expect(controller.value, <WidgetState>{WidgetState.hovered});
expect(count, 3);
controller.update(WidgetState.pressed, true);
expect(controller.value, <WidgetState>{WidgetState.hovered, WidgetState.pressed});
expect(count, 4);
controller.update(WidgetState.selected, true);
expect(controller.value, <WidgetState>{WidgetState.hovered, WidgetState.pressed, WidgetState.selected});
expect(count, 5);
controller.update(WidgetState.selected, false);
expect(controller.value, <WidgetState>{WidgetState.hovered, WidgetState.pressed});
expect(count, 6);
controller.update(WidgetState.selected, false);
expect(controller.value, <WidgetState>{WidgetState.hovered, WidgetState.pressed});
expect(count, 6);
controller.update(WidgetState.pressed, false);
expect(controller.value, <WidgetState>{WidgetState.hovered});
expect(count, 7);
controller.update(WidgetState.hovered, false);
expect(controller.value, <WidgetState>{});
expect(count, 8);
controller.removeListener(valueChanged);
controller.update(WidgetState.selected, true);
expect(controller.value, <WidgetState>{WidgetState.selected});
expect(count, 8);
});
test('WidgetStatesController const initial value', () {
int count = 0;
void valueChanged() {
count += 1;
}
final WidgetStatesController controller = WidgetStatesController(const <WidgetState>{WidgetState.selected});
controller.addListener(valueChanged);
controller.update(WidgetState.selected, true);
expect(controller.value, <WidgetState>{WidgetState.selected});
expect(count, 0);
controller.update(WidgetState.selected, false);
expect(controller.value, <WidgetState>{});
expect(count, 1);
});
}

View file

@ -137,40 +137,40 @@ void main() {
// Changes made in https://github.com/flutter/flutter/pull/97972
ThemeData themeData = ThemeData();
themeData = ThemeData(checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
thumbColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
trackColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
@ -178,40 +178,40 @@ void main() {
));
themeData = ThemeData(
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
thumbColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
trackColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
@ -219,40 +219,40 @@ void main() {
),
);
themeData = ThemeData.raw(checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
thumbColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
trackColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
@ -260,40 +260,40 @@ void main() {
));
themeData = ThemeData.raw(
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
thumbColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
trackColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
@ -301,40 +301,40 @@ void main() {
),
);
themeData = themeData.copyWith(checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
thumbColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
trackColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
@ -342,40 +342,40 @@ void main() {
));
themeData = themeData.copyWith(
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
fillColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
), switchTheme: SwitchThemeData(
thumbColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
thumbColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;
}),
trackColor: MaterialStateProperty.resolveWith<Color?>((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
trackColor: WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return null;
}
if (states.contains(MaterialState.selected)) {
if (states.contains(WidgetState.selected)) {
return Colors.black;
}
return null;

View file

@ -0,0 +1,115 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
void main() {
// Changes made in https://github.com/flutter/flutter/pull/142151
MaterialState selected = MaterialState.selected;
MaterialState hovered = MaterialState.hovered;
MaterialState focused = MaterialState.focused;
MaterialState pressed = MaterialState.pressed;
MaterialState dragged = MaterialState.dragged;
MaterialState scrolledUnder = MaterialState.scrolledUnder;
MaterialState disabled = MaterialState.disabled;
MaterialState error = MaterialState.error;
final MaterialPropertyResolver<MouseCursor?> resolveCallback;
Color getColor(Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return Color(0xFF000002);
}
return Color(0xFF000004);
}
if (states.contains(MaterialState.selected)) {
return Color(0xFF000001);
}
return Color(0xFF000003);
}
final MaterialStateProperty<Color> backgroundColor = MaterialStateColor.resolveWith(getColor);
class _MouseCursor extends MaterialStateMouseCursor {
const _MouseCursor(this.resolveCallback);
final MaterialPropertyResolver<MouseCursor?> resolveCallback;
@override
MouseCursor resolve(Set<MaterialState> states) => resolveCallback(states) ?? MouseCursor.uncontrolled;
}
MaterialStateBorderSide? get side {
return MaterialStateBorderSide.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
if (states.contains(MaterialState.selected)) {
return const BorderSide(width: 2.0);
}
return BorderSide(width: 1.0);
}
if (states.contains(MaterialState.selected)) {
return const BorderSide(width: 1.5);
}
return BorderSide(width: 0.5);
});
}
class SelectedBorder extends RoundedRectangleBorder implements MaterialStateOutlinedBorder {
const SelectedBorder();
@override
OutlinedBorder? resolve(Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const RoundedRectangleBorder();
}
return null;
}
}
TextStyle floatingLabelStyle = MaterialStateTextStyle.resolveWith(
(Set<MaterialState> states) {
final Color color =
states.contains(MaterialState.error) ? Theme.of(context).colorScheme.error : Colors.orange;
return TextStyle(color: color, letterSpacing: 1.3);
},
);
final MaterialStateProperty<Icon?> thumbIcon =
MaterialStateProperty.resolveWith<Icon?>((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return const Icon(Icons.check);
}
return const Icon(Icons.close);
});
final Color backgroundColor = MaterialStatePropertyAll<Color>(
Colors.blue.withOpacity(0.12),
);
final MaterialStatesController statesController =
MaterialStatesController(<MaterialState>{if (widget.selected) MaterialState.selected});
class _MyWidget extends StatefulWidget {
const _MyWidget({
required this.controller,
required this.evaluator,
required this.materialState,
});
final bool Function(_MyWidgetState state) evaluator;
/// Stream passed down to the child [_InnerWidget] to begin the process.
/// This plays the role of an actual user interaction in the wild, but allows
/// us to engage the system without mocking pointers/hovers etc.
final StreamController<bool> controller;
/// The value we're watching in the given test.
final MaterialState materialState;
@override
State createState() => _MyWidgetState();
}
}

View file

@ -0,0 +1,115 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
void main() {
// Changes made in https://github.com/flutter/flutter/pull/142151
WidgetState selected = WidgetState.selected;
WidgetState hovered = WidgetState.hovered;
WidgetState focused = WidgetState.focused;
WidgetState pressed = WidgetState.pressed;
WidgetState dragged = WidgetState.dragged;
WidgetState scrolledUnder = WidgetState.scrolledUnder;
WidgetState disabled = WidgetState.disabled;
WidgetState error = WidgetState.error;
final WidgetPropertyResolver<MouseCursor?> resolveCallback;
Color getColor(Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
if (states.contains(WidgetState.selected)) {
return Color(0xFF000002);
}
return Color(0xFF000004);
}
if (states.contains(WidgetState.selected)) {
return Color(0xFF000001);
}
return Color(0xFF000003);
}
final WidgetStateProperty<Color> backgroundColor = WidgetStateColor.resolveWith(getColor);
class _MouseCursor extends WidgetStateMouseCursor {
const _MouseCursor(this.resolveCallback);
final WidgetPropertyResolver<MouseCursor?> resolveCallback;
@override
MouseCursor resolve(Set<WidgetState> states) => resolveCallback(states) ?? MouseCursor.uncontrolled;
}
WidgetStateBorderSide? get side {
return WidgetStateBorderSide.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
if (states.contains(WidgetState.selected)) {
return const BorderSide(width: 2.0);
}
return BorderSide(width: 1.0);
}
if (states.contains(WidgetState.selected)) {
return const BorderSide(width: 1.5);
}
return BorderSide(width: 0.5);
});
}
class SelectedBorder extends RoundedRectangleBorder implements WidgetStateOutlinedBorder {
const SelectedBorder();
@override
OutlinedBorder? resolve(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return const RoundedRectangleBorder();
}
return null;
}
}
TextStyle floatingLabelStyle = WidgetStateTextStyle.resolveWith(
(Set<WidgetState> states) {
final Color color =
states.contains(WidgetState.error) ? Theme.of(context).colorScheme.error : Colors.orange;
return TextStyle(color: color, letterSpacing: 1.3);
},
);
final WidgetStateProperty<Icon?> thumbIcon =
WidgetStateProperty.resolveWith<Icon?>((Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return const Icon(Icons.check);
}
return const Icon(Icons.close);
});
final Color backgroundColor = WidgetStatePropertyAll<Color>(
Colors.blue.withOpacity(0.12),
);
final WidgetStatesController statesController =
WidgetStatesController(<WidgetState>{if (widget.selected) WidgetState.selected});
class _MyWidget extends StatefulWidget {
const _MyWidget({
required this.controller,
required this.evaluator,
required this.materialState,
});
final bool Function(_MyWidgetState state) evaluator;
/// Stream passed down to the child [_InnerWidget] to begin the process.
/// This plays the role of an actual user interaction in the wild, but allows
/// us to engage the system without mocking pointers/hovers etc.
final StreamController<bool> controller;
/// The value we're watching in the given test.
final WidgetState materialState;
@override
State createState() => _MyWidgetState();
}
}