Flutter笔记:谈Material状态属性-为什么FlatButton等旧版按钮就废弃了

Flutter笔记
谈Material状态属性-为什么FlatButton等旧版按钮就废弃了

- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at CSDN: https://jclee95.blog.csdn.net
My WebSitehttp://thispage.tech/
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/138270269
HuaWei:https://bbs.huaweicloud.com/blogs/426762

【介绍】:本文梳理来龙去脉,介绍Flutter中Material状态属性(MaterialState和MaterialStateProperty)以及相关实现类用法。

flutter-ljc


1. 概述

在 Flutter 中,MaterialState 是一个枚举,用于表示 Material 组件在用户交互时的不同状态。这些状态包括:悬停(hovered)、聚焦(focused)、按压(pressed)、拖动(dragged)、选中(selected)、滚动覆盖(scrolledUnder)、禁用(disabled)和错误(error)。这些状态帮助开发者根据组件的不同交互状态来调整其视觉表现。
MaterialStateProperty 简介
MaterialStateProperty 是一个接口,它允许开发者根据组件的状态来解析出不同的值。这是一种非常灵活的机制,用于在组件的不同状态下提供不同的视觉反馈。例如,按钮在正常状态下可能显示一种颜色,在按下时显示另一种颜色。
本文将…

2. 实际问题背景

【FlatButton】:资料地址-https://blog.csdn.net/qq_28550263/article/details/131387856#2

在Flutter的早期版本中,开发者在使用如FlatButton(现已废弃)等部件时,常常面临一个问题:如何通过参数传递来确保UI组件在不同状态下具有恰当的视觉表现。例如,一个按钮可能需要在不同的状态(如正常、禁用、聚焦、高亮等)下显示不同的颜色和边框样式。传统的方法是为每种状态提供一个对应的颜色参数:

FlatButton(color: Color,disabledColor: Color,focusColor: Color,highlightColor: Color,textColor: Color,disabledTextColor: Color,focusTextColor: Color,highlightTextColor: Color,borderColor: Color,disabledBorderColor: Color,focusBorderColor: Color,highlightBorderColor: Color,
)

这种方法不仅使得组件的构造函数参数冗长,而且难以管理。随着状态种类的增加,参数数量也成倍增长,这使得维护和扩展变得复杂和困难。此外,即使提供了多达十二个或更多的参数,开发者仍然可能无法精确控制组件在所有可能的状态组合下的表现。例如,如果需要处理悬停和聚焦状态的颜色组合,传统方法很难灵活应对。

这种方法的根本问题在于,它试图通过静态值来定义一个本质上依赖于运行时状态的属性。每当组件的状态改变时,都需要重新构建整个组件来反映这些变化,这不仅效率低下,而且在实际应用中很难适应复杂的用户交互需求。

Flutter 1.20版本引入了MaterialStateProperty,这标志着对这一问题的根本性改进。MaterialStateProperty不再要求为每个状态单独传递静态颜色值,而是接受一个函数,这个函数基于组件当前的状态集合(如悬停、聚焦等)动态解析出相应的值。这种方法不仅简化了参数列表,提高了代码的可读性和可维护性,而且极大增强了组件在不同状态下的自定义能力和灵活性。

通过使用MaterialStateProperty,开发者可以编写更加简洁和强大的代码,实现在组件状态变化时动态调整其视觉表现,而无需重新构建组件。这种方法更符合Flutter的响应式编程范式,使得状态管理变得更加直观和高效。

3. MaterialState和MaterialStateProperty

MaterialState 枚举和 MaterialStateProperty 接口在Flutter中的应用展示了如何有效地利用状态管理来增强用户界面的交互性和视觉吸引力。通过这些工具,开发者可以更容易地实现复杂的状态依赖行为,同时保持代码的清晰和可维护性。这种状态驱动的设计方法不仅提高了开发效率,也为最终用户提供了更流畅和直观的交互体验。

3.1 MaterialStates

MaterialStates是Material语言定义的一种交互状态,该集合包括hovered、pressed、focused和disabled以及其它隐性状态,如Material中引入的scrolledUnder。在Flutter中他们都被实现为简单的枚举(material_state.dart中)。

在Material设计中(参考https://m2.material.io/design/interaction/states.html或者https://m3.material.io/foundations/interaction/states/overview),些状态反映了用户与Material组件的交互。MaterialStates 是 Material 设计语言中定义的一系列交互状态,这些状态包括 hovered(悬停)、pressed(按压)、focused(聚焦)、disabled(禁用)以及其他隐性状态如 scrolledUnder。这些状态被实现为 MaterialState 枚举类型,存储在 material_state.dart 文件中。

MaterialState 枚举包含以下值:

枚举值描述
hovered当用户将鼠标悬停在组件上时,组件处于此状态。这通常用于桌面平台,用于提供视觉反馈,表明组件是可交互的。
focused当组件获得输入焦点时,无论是通过键盘导航还是通过触摸设备的交互,都会触发此状态。焦点状态对于无障碍访问尤其重要,它帮助用户理解哪个组件正在等待输入。
pressed当用户点击或触摸组件并按下时,组件进入此状态。这是一个临时状态,通常用于触发按钮或其他控件。
dragged此状态适用于用户拖动组件时。它可以用于滑块(如调整音量或进度条)或在拖放接口中使用的元素。
selected当组件被选中时,如在复选框、单选按钮、选项卡或列表项中,组件将显示为选中状态。这有助于用户识别哪些选项已经被激活或选择。
scrolledUnder特定于滚动场景,当组件(如AppBar)被滚动内容覆盖时,会出现此状态。这常用于提供上下文提示,例如改变AppBar的阴影或颜色以突出显示其上的内容。
disabled当组件被禁用,不允许用户交互时,会显示此状态。禁用状态对于告知用户哪些操作当前不可用非常重要,通常伴随着视觉上的变化,如颜色淡化。
error当组件处于错误状态时,通常因为数据验证失败或其他用户输入错误,会显示此状态。这有助于向用户反馈问题所在,通常通过红色或错误图标来标识。

这些状态枚举使得开发者可以根据用户与组件的交互来调整组件的视觉表现。例如,一个按钮在被按压时可能需要显示不同的颜色,或者在被禁用时显示灰色。以下是一个简单的函数示例,展示了如何根据不同的 MaterialState 来动态调整颜色:

Color? getColor(Set<MaterialState> states) {const Color defaultColor = Colors.blue; // 默认颜色const Color pressedColor = Colors.blueAccent; // 按压时的颜色const Color hoveredColor = Colors.lightBlue; // 悬停时的颜色const Color focusedColor = Colors.blueGrey; // 聚焦时的颜色const Color disabledColor = Colors.grey; // 禁用时的颜色if (states.contains(MaterialState.pressed)) {return pressedColor;} else if (states.contains(MaterialState.hovered)) {return hoveredColor;} else if (states.contains(MaterialState.focused)) {return focusedColor;} else if (states.contains(MaterialState.disabled)) {return disabledColor;}return defaultColor; // 如果没有特定状态,返回默认颜色
}

这种方法虽然在一定程度上解决了状态管理的问题,但在处理多个状态组合或更复杂的状态逻辑时,代码会变得冗长且难以维护。为了提供更灵活的解决方案,Flutter引入了 MaterialStateProperty,这是一个能够根据组件当前的状态集合动态解析属性值的接口。在下一节中,我们将详细介绍 MaterialStateProperty 的使用和优势。

3.2 MaterialStateProperty

在Flutter中,MaterialState 的应用非常广泛,它更多地是通过与 MaterialStateProperty 接口与各种属性(如颜色、边框、文本样式等)结合使用,允许开发者定义基于状态的动态变化。这种设计使得组件可以根据其交互状态显示不同的视觉样式,而无需为每种状态编写大量的条件代码。那么 什么是MaterialStateProperty呢。

MaterialStatePropertyFlutter 中一个接口,它为开发者提供了一种高度灵活的机制,用于根据组件的交互状态动态解析属性值。这种机制在多种 Material 组件中得到应用,特别是在那些需要根据用户交互改变视觉表现的组件中,如按钮、文本字段、滑块等。

MaterialStateProperty 的主要功能是允许开发者为一个属性定义多个状态依赖的值。这意味着开发者可以为组件在不同的交互状态(如悬停、聚焦、按压等)下设置不同的视觉样式,而无需为每种状态单独更新组件。这种方法提高了代码的可维护性和扩展性,同时保持了代码的简洁性。

MaterialStateProperty 是一个泛型接口,它可以用于不同类型的属性,如颜色、尺寸、边框等。它的核心是 resolve 方法,该方法接受一个包含当前组件状态的 Set,并返回对应状态下的属性值。

开发者可以通过两种主要方式使用 MaterialStateProperty

  1. 直接实现:创建一个 MaterialStateProperty 的子类,并实现 resolve 方法。这种方式适用于需要高度定制的场景,可以根据组件的具体需求来精细控制状态变化的逻辑。

  2. 使用工厂方法MaterialStateProperty 提供了 resolveWith 工厂方法,允许直接传入一个回调函数来定义如何根据状态集解析值。这种方式简洁方便,适用于大多数常见的需求。

以下是一个使用 MaterialStateProperty.resolveWith 方法来定义一个按钮的背景颜色的示例:

TextButton(style: ButtonStyle(backgroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {if (states.contains(MaterialState.pressed)) {return Colors.blue; // 按下状态下的颜色} else if (states.contains(MaterialState.disabled)) {return Colors.grey; // 禁用状态下的颜色}return Colors.red; // 默认颜色}),),onPressed: () {},child: Text('点击我'),
)

在这个示例中,按钮的背景颜色根据其状态动态解析:

  • 当按钮被按下时显示蓝色;
  • 当禁用时显示灰色;
  • 其余情况显示红色。

例如,按钮组件可以通过 MaterialStateProperty.resolveWith 方法,根据不同的状态(如按下、悬停、禁用)解析出不同的背景颜色。这种方法不仅简化了代码,提高了可维护性,而且增强了用户界面的响应性和交互性。

ButtonStyle style = ButtonStyle(backgroundColor: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {if (states.contains(MaterialState.pressed))return Theme.of(context).colorScheme.primary.withOpacity(0.5);else if (states.contains(MaterialState.disabled))return Colors.grey;return Theme.of(context).colorScheme.primary; // 默认颜色}),
);

通过这种方式,MaterialState 不仅定义了一组标准的交互状态,还提供了一种高效的机制来根据这些状态动态调整组件的属性。这种灵活性是Flutter框架设计中的一大优势,它允许开发者以声明式的方式处理状态变化,而不是依赖于繁琐的逻辑判断或多个参数设置。

4. MaterialStatePropertyAll<T> 类

4.1 功能描述

MaterialStatePropertyAll<T> 是一个便利类,用于创建一个 MaterialStateProperty,该属性对所有状态解析为给定的值。这个类特别适用于那些属性值在不同状态下保持不变的场景,简化了状态管理的复杂性。

4.2 实现细节与使用方式

MaterialStatePropertyAll<T> 实现了 MaterialStateProperty<T> 接口,其主要功能是无论组件处于何种状态,都返回初始化时指定的固定值。这种实现方式适用于那些不需要根据状态改变属性值的场景。

class MaterialStatePropertyAll<T> implements MaterialStateProperty<T> {final T value;const MaterialStatePropertyAll(this.value);T resolve(Set<MaterialState> states) => value;String toString() {if (value is double) {return 'MaterialStatePropertyAll(${debugFormatDouble(value as double)})';} else {return 'MaterialStatePropertyAll($value)';}}
}

MaterialStateProperty.resolveWith 等其他 MaterialStateProperty 实现相比,MaterialStatePropertyAll<T> 提供了一种更简单、更直接的方式来处理不依赖状态变化的属性值。当你需要一个属性在所有状态下都保持一致时,使用 MaterialStatePropertyAll<T> 是一个理想的选择,而 MaterialStateProperty.resolveWith 更适合那些需要根据不同状态动态改变值的场景。

4.3 代码示例

下面是如何在不同组件中使用 MaterialStatePropertyAll<T> 来简化状态管理的示例:

ButtonStyle flatButtonStyle = ButtonStyle(backgroundColor: MaterialStatePropertyAll(Colors.blue),foregroundColor: MaterialStatePropertyAll(Colors.white),
);TextButton(style: flatButtonStyle,onPressed: () {},child: Text('Always Blue'),
)

在这个示例中,无论按钮处于何种状态(如按下、悬停、禁用等),背景色和前景色始终保持蓝色和白色。这种方式简化了代码,并确保了界面的一致性。

5. MaterialStateColor

5.1 功能描述

MaterialStateColor 用于定义依赖于组件状态的颜色。这个类实现了 MaterialStateProperty<Color> 接口,允许颜色根据组件的不同状态(如悬停、聚焦、按压等)动态变化。这为开发者提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现。

MaterialStateColor 适用于任何需要根据状态改变颜色的场景,例如按钮的背景色、文本颜色或边框颜色。它特别适用于那些对视觉反馈有高要求的交互元素,如表单控件、导航项和交互式列表项。

5.2 实现细节与使用方式

abstract class MaterialStateColor extends Color implements MaterialStateProperty<Color> {/// 抽象常量构造函数。这个构造函数使得子类可以提供/// 常量构造函数,以便它们可以在常量表达式中使用。const MaterialStateColor(super.defaultValue);/// 从 [MaterialPropertyResolver<Color>] 回调函数创建一个 [MaterialStateColor]。////// 如果作为普通颜色使用,则在默认状态下(空状态集)解析的颜色将被使用。////// 给定的回调参数必须在默认状态下返回一个非空颜色。static MaterialStateColor resolveWith(MaterialPropertyResolver<Color> callback) => _MaterialStateColor(callback);/// 返回一个 [Color],用于当 Material 组件处于指定状态时使用。Color resolve(Set<MaterialState> states);
}

要使用 MaterialStateColor,你可以通过两种方式:

  1. 直接实现:创建一个 MaterialStateColor 的子类,并实现其 resolve 方法。这种方式允许完全自定义状态逻辑。

  2. 使用工厂方法:通过 MaterialStateColor.resolveWith 方法,传入一个回调函数来定义状态和颜色的关系。

5.3 代码示例

下面是一个使用 MaterialStateColor.resolveWith 方法来定义按钮背景颜色的示例:

ButtonStyle style = ButtonStyle(backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {if (states.contains(MaterialState.pressed)) {return Colors.deepPurple; // 按下状态下的颜色} else if (states.contains(MaterialState.hovered)) {return Colors.deepPurpleAccent; // 悬停状态下的颜色}return Colors.purple; // 默认颜色}),
);TextButton(style: style,onPressed: () {},child: Text('Press Me'),
)

6. MaterialStateBorderSide

6.1 功能描述

MaterialStateColor 是一个抽象类,用于定义依赖于组件状态的颜色。这个类实现了 MaterialStateProperty<Color> 接口,允许颜色根据组件的不同状态(如悬停、聚焦、按压等)动态变化。这为开发者提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现。

6.2 实现细节与使用方式

MaterialStateColor 是一个抽象类,需要通过继承并实现其 resolve 方法来使用,或者通过 resolveWith 静态方法创建,传入一个回调函数来定义状态和颜色的关系。

abstract class MaterialStateColor extends Color implements MaterialStateProperty<Color> {const MaterialStateColor(super.defaultValue);/// 从回调函数创建 MaterialStateColor。static MaterialStateColor resolveWith(MaterialPropertyResolver<Color> callback) => _MaterialStateColor(callback);Color resolve(Set<MaterialState> states);
}

6.3 代码示例

下面是一个使用 MaterialStateColor.resolveWith 方法来定义按钮背景颜色的示例:

ButtonStyle style = ButtonStyle(backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {if (states.contains(MaterialState.pressed)) {return Colors.deepPurple; // 按下状态下的颜色} else if (states.contains(MaterialState.hovered)) {return Colors.deepPurpleAccent; // 悬停状态下的颜色}return Colors.purple; // 默认颜色}),
);TextButton(style: style,onPressed: () {},child: Text('Press Me'),
);

在这个示例中,按钮的背景颜色根据其状态动态解析:当按钮被按下时显示深紫色,悬停时显示深紫色加亮,其余情况显示普通紫色。

7. MaterialStateMouseCursor

7.1 功能描述

MaterialStateMouseCursor 用于定义依赖于组件状态的鼠标光标样式。这个类实现了 MaterialStateProperty<MouseCursor> 接口,允许鼠标光标根据组件的不同状态(如悬停、聚焦、按压等)动态变化。这为开发者提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现和用户反馈。

MaterialStateMouseCursor 适用于任何需要根据状态改变鼠标光标的场景,例如按钮、链接或其他交互式元素。它特别适用于那些对用户交互反馈有高要求的元素。

7.2 实现细节与使用方式

MaterialStateMouseCursor 是一个抽象类,需要通过继承来实现。开发者可以通过以下两种方式使用它:

  • 直接实现:创建一个 MaterialStateMouseCursor 的子类,并实现其 resolve 方法。这种方式允许完全自定义状态逻辑。

  • 使用工厂方法:通过 MaterialStateMouseCursor.resolveWith 方法,传入一个回调函数来定义状态和鼠标光标的关系。

7.3 代码示例

下面是一个使用 MaterialStateMouseCursor.resolveWith 方法来定义按钮鼠标光标样式的示例:

ButtonStyle style = ButtonStyle(mouseCursor: MaterialStateMouseCursor.resolveWith((Set<MaterialState> states) {if (states.contains(MaterialState.hovered)) {return SystemMouseCursors.click; // 悬停状态下的光标} else if (states.contains(MaterialState.pressed)) {return SystemMouseCursors.grabbing; // 按下状态下的光标}return MouseCursor.defer; // 默认光标}),
);TextButton(style: style,onPressed: () {},child: Text('Hover or Press'),
)

在这个示例中,按钮的鼠标光标根据其状态动态解析:当按钮被悬停时显示点击光标,被按下时显示抓取光标,其余情况使用默认光标。这种方式简化了代码,并确保了界面的交互性和一致性。

在这里插入图片描述
插播一张作者头像,禁止盗用

8. MaterialStateOutlinedBorder

8.1 功能描述

MaterialStateOutlinedBorder 是一个抽象类,用于定义依赖于组件状态的边框样式。这个类实现了 MaterialStateProperty<OutlinedBorder> 接口,允许边框样式根据组件的不同状态(如悬停、聚焦、按压等)动态变化。这为开发者提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现。

MaterialStateOutlinedBorder 适用于任何需要根据状态改变边框样式的场景,例如按钮的边框、文本字段的边框或任何其他装饰性边框。它特别适用于那些对视觉反馈有高要求的交互元素,如表单控件、导航项和交互式列表项。

8.2 实现细节与使用方式

MaterialStateOutlinedBorder 是一个抽象类,要使用它,你需要通过以下两种方式之一:

  1. 直接实现:创建一个 MaterialStateOutlinedBorder 的子类,并实现其 resolve 方法。这种方式允许完全自定义状态逻辑。

  2. 使用工厂方法:通过 MaterialStateOutlinedBorder.resolveWith 方法,传入一个回调函数来定义状态和边框样式的关系。

8.3 代码示例

下面是一个使用 MaterialStateOutlinedBorder.resolveWith 方法来定义按钮边框样式的示例:

ButtonStyle style = ButtonStyle(shape: MaterialStateOutlinedBorder.resolveWith((Set<MaterialState> states) {if (states.contains(MaterialState.pressed)) {return RoundedRectangleBorder(borderRadius: BorderRadius.circular(10),side: BorderSide(color: Colors.deepPurple, width: 2),); // 按下状态下的边框样式} else if (states.contains(MaterialState.hovered)) {return RoundedRectangleBorder(borderRadius: BorderRadius.circular(10),side: BorderSide(color: Colors.deepPurpleAccent, width: 2),); // 悬停状态下的边框样式}return RoundedRectangleBorder(borderRadius: BorderRadius.circular(10),side: BorderSide(color: Colors.purple, width: 1),); // 默认边框样式}),
);TextButton(style: style,onPressed: () {},child: Text('Press Me'),
)

在这个示例中,按钮的边框样式根据其状态动态解析:当按钮被按下时显示更粗的深紫色边框,悬停时显示深紫色调的边框,其余情况显示普通的紫色边框,悬停时显示深紫色调的边框,其余情况显示普通的深紫色边框,悬停时显示深紫色调的边框,其余情况显示普通的紫色边框。这种方法不仅简化了代码,提高了可维护性,而且增强了用户界面的响应性和交互性。通过这种方式,MaterialStateOutlinedBorder 不仅定义了一组标准的交互状态,还提供了一种高效的机制来根据这些状态动态调整组件的边框样式。这种灵活性是Flutter框架设计中的一大优势,它允许开发者以声明式的方式处理状态变化,而不是依赖于繁琐的逻辑判断或多个参数设置。

9. MaterialStateOutlineInputBorder

9.1 功能描述

MaterialStateOutlineInputBorder 是一个专门用于定义依赖于组件状态的边框样式的类。这个类实现了 MaterialStateProperty<OutlineInputBorder> 接口,允许边框样式根据组件的不同状态(如悬停、聚焦、按压等)动态变化。这为开发者提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现。

MaterialStateOutlineInputBorder 适用于任何需要根据状态改变边框样式的场景,例如输入框、按钮或任何其他需要突出显示状态变化的界面元素。

9.2 实现细节与使用方式

MaterialStateOutlineInputBorder 是一个抽象类,要使用它,你需要通过以下两种方式之一:

  1. 直接实现:创建一个 MaterialStateOutlineInputBorder 的子类,并实现其 resolve 方法。这种方式允许完全自定义状态逻辑。

  2. 使用工厂方法:通过 MaterialStateOutlineInputBorder.resolveWith 方法,传入一个回调函数来定义状态和边框样式的关系。

9.3 代码示例

下面是一个使用 MaterialStateOutlineInputBorder.resolveWith 方法来定义输入框边框样式的示例:

InputDecoration inputDecoration = InputDecoration(border: MaterialStateOutlineInputBorder.resolveWith((Set<MaterialState> states) {if (states.contains(MaterialState.focused)) {return OutlineInputBorder(borderSide: BorderSide(color: Colors.blue, width: 2.0),); // 聚焦状态下的边框样式} else if (states.contains(MaterialState.error)) {return OutlineInputBorder(borderSide: BorderSide(color: Colors.red, width: 2.0),); // 错误状态下的边框样式}return OutlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 1.0),); // 默认边框样式}),
);TextField(decoration: inputDecoration,
)

在这个示例中,输入框的边框样式根据其状态动态解析:当输入框聚焦时显示蓝色边框,出现错误时显示红色边框,其余情况显示灰色边框。这种方式简化了代码,并确保了界面的一致性和响应性。

10. MaterialStateTextStyle

10.1 功能描述

MaterialStateTextStyle 是一个实现了 MaterialStateProperty<TextStyle> 接口的类,用于定义依赖于组件状态的文本样式。这使得文本组件在不同的用户交互状态下(如悬停、聚焦、按压等)可以显示不同的样式,提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现。

10.2 实现细节与使用方式

MaterialStateTextStyle 是一个抽象类,要使用它,开发者可以通过以下两种方式:

  1. 直接实现:创建一个 MaterialStateTextStyle 的子类,并实现其 resolve 方法。这种方式允许开发者根据具体的状态逻辑自定义文本样式的变化。

  2. 使用工厂方法:通过 MaterialStateTextStyle.resolveWith 方法,传入一个回调函数来定义状态和文本样式的关系。这种方式简洁方便,适用于大多数常见需求。

10.3 代码示例

下面是一个使用 MaterialStateTextStyle.resolveWith 方法来定义按钮文本样式的示例:

ButtonStyle style = ButtonStyle(textStyle: MaterialStateTextStyle.resolveWith((Set<MaterialState> states) {if (states.contains(MaterialState.pressed)) {return TextStyle(color: Colors.white,fontSize: 16,fontWeight: FontWeight.bold); // 按下状态下的文本样式} else if (states.contains(MaterialState.hovered)) {return TextStyle(color: Colors.white,fontSize: 14,decoration: TextDecoration.underline); // 悬停状态下的文本样式}return TextStyle(color: Colors.black,fontSize: 14); // 默认文本样式}),
);TextButton(style: style,onPressed: () {},child: Text('Press Me'),
)

在这个示例中,按钮的文本样式根据其状态动态解析:当按钮被按下时显示白色粗体字,悬停时显示带下划线的白色文本,其余情况显示黑色文本。这种方式不仅简化了代码,提高了可维护性,而且增强了用户界面的响应性和交互性。

11. MaterialStateUnderlineInputBorder

11.1 功能描述

MaterialStateUnderlineInputBorder 是一个专门用于定义依赖于组件状态的下划线边框的类。这个类实现了 MaterialStateProperty<UnderlineInputBorder> 接口,允许下划线边框根据组件的不同状态(如悬停、聚焦、按压等)动态变化。这为开发者提供了一种灵活的方式来调整组件在不同交互状态下的视觉表现。

11.2 实现细节与使用方式

MaterialStateUnderlineInputBorder 是一个抽象类,需要通过继承并实现其 resolve 方法来使用。这种方式允许开发者根据具体的交互状态定制边框的样式,例如改变边框颜色或厚度。

abstract class MaterialStateUnderlineInputBorder extends UnderlineInputBorder implements MaterialStateProperty<UnderlineInputBorder> {/// 抽象常量构造函数。这个构造函数使子类能够提供常量构造函数,以便它们可以在常量表达式中使用。const MaterialStateUnderlineInputBorder();/// 返回一个 [UnderlineInputBorder],用于指定状态下使用的 Material 组件。UnderlineInputBorder resolve(Set<MaterialState> states);
}

11.3 代码示例

下面是一个使用 MaterialStateUnderlineInputBorder 来定义输入框边框样式的示例:

class CustomUnderlineInputBorder extends MaterialStateUnderlineInputBorder {UnderlineInputBorder resolve(Set<MaterialState> states) {// 如果输入框处于聚焦状态,则返回蓝色边框if (states.contains(MaterialState.focused)) {return const UnderlineInputBorder(borderSide: BorderSide(color: Colors.blue, width: 2.0),);}// 否则返回灰色边框return const UnderlineInputBorder(borderSide: BorderSide(color: Colors.grey, width: 1.0),);}
}TextField(decoration: InputDecoration(// 使用自定义的边框样式border: CustomUnderlineInputBorder(),),
)

在这个示例中,输入框的下划线边框会根据是否聚焦来改变颜色和宽度,聚焦时显示蓝色和较宽的边框,未聚焦时显示灰色和较细的边框。这种方式简化了代码,并确保了界面的一致性和响应性。

12. 结论

MaterialState 枚举和相关的 MaterialStateProperty 类在 Flutter 中提供了一种高效且灵活的方式来根据组件的交互状态动态调整其属性。这些工具极大地增强了 UI 组件的交互性和视觉反馈,使得开发者能够以声明式的方式处理状态变化,而不是依赖于繁琐的逻辑判断或多个参数设置。

通过使用如 MaterialStateColor、MaterialStateTextStyle、MaterialStateOutlinedBorder 等,开发者可以为不同的状态定义不同的视觉表现,如颜色、文本样式和边框样式。这些状态包括但不限于悬停、聚焦、按压等,每种状态都可以有其独特的样式响应。

此外,MaterialState 提供的 resolve 方法和 resolveWith 工厂方法进一步简化了状态管理,使得开发者可以更加专注于创造性的 UI 设计和用户体验的提升。

可见,MaterialState 和 MaterialStateProperty 在 Flutter 中是处理组件状态和属性的强大工具,它们的灵活性和易用性为开发高质量、响应式的用户界面提供了坚实的基础。

F. 附录

F.1 MaterialState枚举源码

/// 交互状态,一些 Material 部件在接收用户输入时可以处于的状态。
///
/// 状态由 https://material.io/design/interaction/states.html#usage 定义。
///
/// 一些 Material 部件会在 `Set<MaterialState>` 中跟踪它们当前的状态。
///
enum MaterialState {/// 用户将鼠标光标拖动到给定部件上时的状态。////// 参见:https://material.io/design/interaction/states.html#hover.hovered,/// 用户使用键盘导航到给定部件时的状态。////// 有时也会在部件被点击时触发。例如,当 [TextField] 被点击时,它会变为 [focused]。////// 参见:https://material.io/design/interaction/states.html#focus.focused,/// 用户在给定部件上主动按下时的状态。////// 参见:https://material.io/design/interaction/states.html#pressed.pressed,/// 用户正在通过拖动将此部件从一个位置拖动到另一个位置时的状态。////// https://material.io/design/interaction/states.html#dragged.dragged,/// 当此项被选中时的状态。////// 适用于可以切换的内容(如芯片和复选框)以及从一组选项中选择的内容(如选项卡和单选按钮)。////// 参见:https://material.io/design/interaction/states.html#selected.selected,/// 当此部件与滚动视图下方的内容重叠时的状态。////// 由 [AppBar] 使用,指示主滚动视图的内容已向上滚动并位于应用栏后面。scrolledUnder,/// 当此部件被禁用且无法进行交互时的状态。////// 禁用的部件不应对悬停、聚焦、按压或拖动交互做出响应。////// 参见:https://material.io/design/interaction/states.html#disabled.disabled,/// 当部件进入某种无效状态时的状态。////// 参见 https://material.io/design/interaction/states.html#usage.error,
}

F.2 MaterialStateProperty接口源码

/// 用于根据部件的交互“状态”(定义为一组 MaterialState)返回类型为 `T` 的值的接口。
/// MaterialStateProperty 代表依赖于部件的材料“状态”的值。状态被编码为一组 MaterialState 值,如 MaterialState.focused、MaterialState.hovered、MaterialState.pressed。例如,InkWell.overlayColor 定义了当 InkWell 被按下(“涟漪颜色”)、聚焦或悬停时填充的颜色。InkWell 使用 overlay color 的 resolve 方法来计算当前状态下 InkWell 的颜色。
/// ButtonStyle 用于配置按钮(如 TextButton、ElevatedButton 和 OutlinedButton)的外观,具有许多 material state 属性。按钮部件会跟踪它们当前的 material state,并在需要值时解析按钮样式的 material state 属性。
abstract class MaterialStateProperty<T> {/// 根据 [states] 返回类型为 `T` 的值。////// 像 TextButton 和 ElevatedButton 这样的部件会将此方法应用于它们当前的 MaterialState,以在构建时计算颜色和其他视觉参数。T resolve(Set<MaterialState> states);/// 如果 `value` 是 MaterialStateProperty,则根据给定的状态集解析值,否则返回值本身。////// 对于那些参数可以选择是 MaterialStateProperty 的部件很有用。例如,InkWell.mouseCursor 可以是 MouseCursor 或 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;}/// 从仅有 MaterialPropertyResolver 函数创建 MaterialStateProperty 的便捷方法。/// 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);/// 在两个 [MaterialStateProperty] 之间进行线性插值。static MaterialStateProperty<T?>? lerp<T>(MaterialStateProperty<T>? a,MaterialStateProperty<T>? b,double t,T? Function(T?, T?, double) lerpFunction,) {// 避免为常见情况创建 _LerpProperties 对象。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;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;T resolve(Set<MaterialState> states) => _resolve(states);
}/// 用于创建一个 [MaterialStateProperty],该属性对所有状态都解析为给定值的便利类。
class MaterialStatePropertyAll<T> implements MaterialStateProperty<T> {/// 构造一个总是解析为给定值的 [MaterialStateProperty]。const MaterialStatePropertyAll(this.value);/// 将用于所有状态的属性值。final T value;T resolve(Set<MaterialState> states) => value;String toString() {if (value is double) {return 'MaterialStatePropertyAll(${debugFormatDouble(value as double)})';} else {return 'MaterialStatePropertyAll($value)';}}
}/// 管理一组 [MaterialState] 并在更改时通知监听器。
///
/// 由那些为了支持额外状态而公开其内部状态的扩展而使用。参见 [TextButton] 作为示例。
///
/// 控制器的 [value] 是其当前的状态集。每当 [value] 更改时,监听器都会收到通知。应该只使用 [update] 来更改 [value];不应直接修改它。
///
/// 控制器的 [value] 表示一组状态,通常是 [MaterialStateProperty] 值解析的状态。它 _不是_ 组件的内在状态。组件负责确保控制器的 [value] 跟踪其内在状态。例如,不能通过将 [MaterialState.focused] 添加到其控制器来请求组件的键盘焦点。当组件获得焦点或失去焦点时,它将 [update] 其控制器的 [value] 并通知更改的监听器。
class MaterialStatesController extends ValueNotifier<Set<MaterialState>> {/// 创建一个 MaterialStatesController。MaterialStatesController([Set<MaterialState>? value]) : super(<MaterialState>{...?value});/// 如果 [add] 为 true,则将 [state] 添加到 [value],否则将其移除,并在 [value] 更改时通知监听器。void update(MaterialState state, bool add) {final bool valueChanged = add ? value.add(state) : value.remove(state);if (valueChanged) {notifyListeners();}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/318384.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习:深入解析SVM的核心概念【四、软间隔与正则化】

软间隔与正则化 问题一&#xff1a;优化目标函数是如何得到的&#xff1f;得到的过程是怎样的&#xff1f;问题二&#xff1a;拉格朗日乘子法计算详细过程问题三&#xff1a;KKT条件求解过程问题四&#xff1a;结构风险最小化&#xff08;SRM&#xff09;的原理 在前面的讨论中…

基于 Spring Boot 博客系统开发(五)

基于 Spring Boot 博客系统开发&#xff08;五&#xff09; 本系统是简易的个人博客系统开发&#xff0c;为了更加熟练地掌握 SprIng Boot 框架及相关技术的使用。&#x1f33f;&#x1f33f;&#x1f33f; 基于 Spring Boot 博客系统开发&#xff08;四&#xff09;&#x1f…

[机器学习系列]深入解析K-Means聚类算法:理论、实践与优化

目录 一、KMeans (一)Kmeans简介 (二)Kmeans作用和优点 (三)Kmeans局限和缺点 (四)Kmeans步骤 (五)如何选取最佳的K值的三种方法 (六)手肘法和目标函数的变化两种确定K值方法的区别 (七)如何选取第一次迭代的K个类中心------KMeans方法 (八)KMeans的常用参数介绍 二、…

LeetCode 139 —— 单词拆分

阅读目录 1. 题目2. 解题思路3. 代码实现 1. 题目 2. 解题思路 定义 d p [ i ] dp[i] dp[i] 表示 s [ 0 , i ] s[0, i] s[0,i] 是否可以被字典中出现的单词拼接&#xff0c;那么状态转移方程为&#xff1a; d p [ i ] t r u e &#xff0c;如果存在任意 j ∈ [ 0 , i − 1…

智慧光伏电站管理系统构建与功能分析

在全球光伏产业蓬勃发展背景下&#xff0c;我国光伏制造以及光伏发电规模均位于世界首位。但是由于集中式光伏电站投资大、建设周期长、占地面积大。出于土地成本考虑&#xff0c;电站通常地处偏远地区&#xff0c;给运维管理带来了诸多不便。随着互联网、云计算、大数据、人工…

Mac环境下ollama部署和体验

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 关于ollama ollama和LLM&#xff08;大型语言模型&#xff09;的关系&#xff0c;类似于docker和镜像&#xff0c;可以在ollama服务中管理和运行各种LLM&…

【算法基础实验】图论-最小生成树Prim的延迟实现

最小生成树-Prim的延迟实现 理论基础 树的基本性质 用一条边连接树中的任意两个顶点都会产生一个新的环&#xff1b; 从树中删去一条边将会得到两棵独立的树。 切分定理的定义 定义。图的一种切分是将图的所有顶点分为两个非空且不重叠的两个集合。横切边 是一条连接两个属…

python学习笔记B-16:序列结构之字典--字典的遍历与访问

下面是字典的访问和遍历方法&#xff1a; d {10:"hello",20:"python",30:"world"} print(d[10],"--",d[20],"--",d[30]) print(d.get(10)) print("以上两种访问方式的区别是&#xff0c;d[key]若键是空值&#xff0c…

c#创建新项目

确保已安装.NET Core SDK。&#xff08;visual studio installer中可安装&#xff09; cmd中先引用到文件夹目录下。 mkdir MyConsoleApp MyConsoleApp是项目文件夹的名字。 mkdir 是一个命令行工具&#xff0c;用于在文件系统中创建新的目录&#xff08;文件夹&#xff09;…

C 语言笔记:字符串处理函数

一、获取字符串长度函数 头文件&#xff1a;#include <string.h> 函数定义&#xff1a;size_t strlen(const char *s); 函数功能&#xff1a; 测字符指针 s 指向的字符串中字符的个数&#xff0c;不包括’\0’ 返回值&#xff1a;字符串中字符个数 #include <stdio.…

DRF版本组件源码分析

DRF版本组件源码分析 在restful规范中要去&#xff0c;后端的API中需要体现版本。 3.6.1 GET参数传递版本 from rest_framework.versioning import QueryParameterVersioning单视图应用 多视图应用 # settings.pyREST_FRAMEWORK {"VERSION_PARAM": "versi…

Android(Java)项目支持Kotlin语言开发

Android&#xff08;Java&#xff09;项目通过相关Kotlin设置后&#xff0c;允许同时使用Java语言和Kotlin语言进行开发代码的。 示例环境&#xff1a; Android Studio Giraffe | 2022.3.1 Patch 3 Java 8 Kotlin 1.9.20 设置Kotlin选项&#xff1a; 第一步&#xff1a;在项…

区块链 | IPFS:Merkle DAG(进阶版)

&#x1f98a;原文&#xff1a;Merkle DAGs: Structuring Data for the Distributed Web &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。 1 Merkle DAG 当我们在计算机上表示图时&#xff0c;必须通过提供节点和边的具体表示来编码我们的数据…

笔记-PPT绘图导出高清无失真图片

问题描述&#xff1a;PPT绘图已经用了高清图&#xff08;jpg、tif格式&#xff09;&#xff0c;但论文图片还是不清晰&#xff0c;打印出来还是有点糊 以下是PPT导出高清不失真图片&#xff08;emf格式&#xff09;的具体描述。 目录 一、绘图工具二、操作步骤 一、绘图工具 …

JAVA面试题---WEB部分

网络通讯 TCP与UDP TCP(Transmission Control Protocol 传输控制协议)是一种面向连接(连接导向)的、 可靠的、 基于 IP 的传输层协议。 UDP 是 User Datagram Protocol 的简称&#xff0c;中文名是用户数据报协议&#xff0c;是 OSI 参考模 型中的传输层协议&#xff0c;它是…

UE5入门学习笔记(六)——编译低版本插件

对于有些低版本的插件&#xff0c;可以通过此方法自己编译到高版本而无需等待插件作者更新 使用工具&#xff1a;如图所示 步骤1&#xff1a;打开cmd&#xff0c;并使用cd命令切换到此目录 步骤2&#xff1a;输入如下指令 RunUAT.bat BuildPlugin -Plugin“路径1” -Package“…

WPF基础应用

WPF参考原文 MVVM介绍 1.常用布局控件 1.1 布局控件 WPF&#xff08;Windows Presentation Foundation&#xff09;提供了多种布局容器来帮助开发者设计用户界面&#xff0c;以下是一些常用的布局&#xff1a; Grid: Grid是最常用的布局容器之一&#xff0c;它允许你通过定…

类和对象【四】运算符重载

文章目录 运算符重载的概念运算符重载&#xff08;函数&#xff09;返回值类型&#xff1a;任意类型函数名&#xff1a;operator已有操作符 运算符重载&#xff08;函数&#xff09;的特点和注意点3个比较特殊的运算符重载赋值运算符&#xff08;&#xff09;重载返回值类型和返…

人工智能论文:BERT和GPT, GPT-2, GPT-3 的简明对比和主要区别

在BERT的论文里面&#xff1a; 2018.10 BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding&#xff0c;BERT已经解释了BERT&#xff0c;GPT&#xff0c;ELMo的区别。 *ELMo为双向RNN&#xff0c;请忽略。 主要区别&#xff1a; BERT使用的是…

15、ESP32 Wifi

ESP32 的 WIFI 功能是模块内置的&#xff0c;通过 ESP32 的基础库调用一些函数就可以轻松使用它。 Wifi STA 模式&#xff1a; 让 ESP32 连接附近 WIFI&#xff0c;可以上网访问数据。 // 代码显示搜索连接附近指定的 WIFI // 通过 pin 按键可断开连接#include <WiFi.h>…