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.mzph.cn/bicheng/5965.shtml

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

相关文章

C#知识|Dictionary泛型集合的使用总结

哈喽,你好,我是雷工! 以下是C#Dictionary泛型集合的学习笔记。 01 Dictionary泛型集合 1.1、Dictionary<K,V>通常称为字典, 1.2、其中<K,V>是自定义的,用来约束集合中元素类型。 1.3、在编译时检查类型约束, 1.4、无需装箱拆箱操作, 1.5、操作与哈希表(Ha…

网络工程师必学知识:TCP抓包分析Seq、SYN、ACK的变化过程

网络工程师必学知识:TCP抓包分析Seq、SYN、ACK的变化过程 1.概述:2.抓包3.分析:重点1:重点2:4.总结:1.概述: 下面是我面试时遇到的问题。 问:TCP协议位于tcp/ip协议栈的哪一层呢? 答:这个问题要是答不上来,就不用看下面的内容了。 问:TCP抓包时Seq、SYN、ACK的变化…

Microsoft Threat Modeling Tool 使用(二)

主界面 翻译 详细描述 选择了 “SDL TM Knowledge Base (Core)” 模板并打开了一个新的威胁模型。这个界面主要用于绘制数据流图&#xff08;Data Flow Diagram, DFD&#xff09;&#xff0c;它帮助您可视化系统的组成部分和它们之间的交互。以下是界面中各个部分的功能介绍&a…

[基础] Unity Shader:顶点着色器(vert)函数

顶点着色器&#xff08;Vertex Shader&#xff09;是图形渲染的第一个阶段&#xff0c;它的输入来自于CPU。顶点着色器的处理单位是顶点&#xff0c;CPU输入进来的每个顶点都会调用一次顶点着色器函数&#xff0c;也就是我们在Shader代码里所定义的vert函数。本篇我们将会通过顶…

WPF之可翻转面板

1&#xff0c;创建翻转面板的资源字典&#xff1a;FlippPanel.xaml。 无外观控件同样必须给样式指定类型&#xff08; <ControlTemplate TargetType"ss:FlipPanel">&#xff09;&#xff0c;相关详情参考&#xff1a;WPF之创建无外观控件-CSDN博客&#xff09…

场景文本检测识别学习 day06(Vi-Transformer论文精读、MAE论文阅读)

Vi-Transformer论文精读 在NLP领域&#xff0c;基于注意力的Transformer模型使用的非常广泛&#xff0c;但是在计算机视觉领域&#xff0c;注意力更多是和CNN一起使用&#xff0c;或者是单纯将CNN的卷积替换成注意力&#xff0c;但是整体的CNN 架构没有发生改变VIT说明&#x…

蓝桥杯单片机省赛——第八届“基于单片机的电子钟程序设计与调试”程序部分

往期回顾 第三届蓝桥杯单片机省赛 第四届蓝桥杯单片机省赛 第五届蓝桥杯单片机省赛 第六届蓝桥杯单片机省赛 第七届蓝桥杯单片机省赛 文章目录 往期回顾一、前期准备二、代码详情1.基础代码蜂鸣器/继电器/led/定时器之类的代码 2.按键详解按键写法讲解 3.驱动的处理驱动写法讲…

程序员缓解工作压力——方法分享

前言 作为一名初级程序员&#xff0c;我承认自己在应对工作压力方面还有待提高。在日常工作中&#xff0c;我时常感到压力山大&#xff0c;尤其是在面对复杂问题或紧迫的项目期限时。然而&#xff0c;为了保持高效和持久的工作热情&#xff0c;我还是积极寻求并使用了一…

Scala应用 —— JDBC的创建

文章目录 Scala应用 —— JDBC的创建前言一、JDBC的创建过程1.初始化连接1.1 配置驱动1.2 创建连接对象 2. 初始化执行器2.1 创建执行器对象2.2 初始化执行器参数 3. 执行操作并返回结果 二、Scala JDBC的基本设计思路1. 操作步骤设计2. 解决结果差异化3.实现jdbc方法并输出结果…

WPF之创建无外观控件

1&#xff0c;定义无外观控件。 定义默认样式&#xff0c;在其静态构造函数中调用DefaultStyleKeyProperty.OverrideMetadata()。 //设置默认样式DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker), new FrameworkPropertyMetadata(typeof(ColorPicker))); 在项目…

UE4_Niagara_两个模型之间的粒子幻化

学习笔记&#xff0c;仅供参考&#xff01; 操作步骤&#xff1a; 1、新建niagara system&#xff0c;添加空的发射器&#xff0c;渲染改为网格体渲染器&#xff0c;网格体为1M_Cube. 2、创建粒子材质重载。 3、渲染网格体的材质设置&#xff1a; 4、在发射器属性面板&#x…

基于MSOGI的交叉对消谐波信号提取网络MATLAB仿真

微❤关注“电气仔推送”获得资料&#xff08;专享优惠&#xff09; 模型简介&#xff1a; 此模型利用二阶广义积分器&#xff08;SOGI&#xff09;对基波电流和相应次的谐波电流进行取 &#xff0c;具体是通过多个基于二阶广义积分器的正交信号发生器 &#xff08; S&#xf…

docker挂载数据卷-以nginx为例

目录 一、什么是数据卷 二、数据卷的作用 三、如何挂载数据卷 1、创建nginx容器挂载数据卷 2、查看数据卷 3、查看数据卷详情 4、尝试在宿主机修改数据卷 5、查看容器内对应的数据卷目录 6、 访问nginx查看效果 ​​​​​​​一、什么是数据卷 挂载数据卷本质上就是实…

【跟马少平老师学AI】-【神经网络是怎么实现的】(八)循环神经网络

一句话归纳&#xff1a; 1&#xff09;词向量与句子向量的循环神经网络&#xff1a; x(i)为词向量。h(i)为含前i个词信息的向量。h(t)为句向量。 2&#xff09;循环神经网络的局部。 每个子网络都是标准的全连接神经网络。 3&#xff09;对句向量增加全连接层和激活函数。 每个…

嵌入式开发三:STM32初体验

本节主要向大家介绍如何开发过程中的基本操作&#xff0c;如编译、串口下载、仿真器下载、仿真调试程序&#xff0c;体验一下 STM32 的开发流程&#xff0c;并介绍 MDK5 的一些使用技巧&#xff0c;通过本节的学习&#xff0c;将对 STM32 的开发流程和 MDK5 使用有个大概了解&a…

安装部署大语言模型 | 通义千问

下载安装 进入ollama的仓库下载 「 qwen 7b 」 libraryGet up and running with large language models.https://ollama.com/library查找阿里的 「 qwen 」 根据自己的电脑配置情况&#xff0c;选择合适的模型 总体来说&#xff0c;模型是越大&#xff0c;效果越好&#xff0c…

019、Python+fastapi,第一个Python项目走向第19步:windows 11 下的pycharm远程连接ubuntu 24.04 服务器

一、说明 欲善其事,必先利其器&#xff0c;先把环境整好&#xff0c;我开发的环境是ubuntu是没有gui的服务器版本&#xff0c;所以必须远程搞才行&#xff0c;今天就是安装一下&#xff0c;链接连接&#xff0c;网上有很多文章&#xff0c;能成功&#xff0c;不过我也弄了一个…

SQL——高级教程【菜鸟教程】

SQL连接 左连接&#xff1a;SQL LEFT JOIN 关键字 左表相当于主表&#xff0c;不管与右表匹不匹配都会显示所有数据 右表就只会显示和左表匹配的内容。 //例显示&#xff1a;左表的name&#xff0c;有表的总数&#xff0c;时间 SELECT Websites.name, access_log.count, acc…

GitHub Copilot Workspace:欢迎进入原生Copilot开发环境

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Vue 组件的三大组成部分

Vue 组件通常由三大组成部分构成&#xff1a;模板&#xff08;Template&#xff09;、脚本&#xff08;Script&#xff09;、样式&#xff08;Style&#xff09; 模板部分是组件的 HTML 结构&#xff0c;它定义了组件的外观和布局。Vue 使用基于 HTML 的模板语法来声明组件的模…