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

news2024/10/7 20:32:48
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.coloradmin.cn/o/1639756.html

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

相关文章

免安装SQL管理工具HeidiSQL建库如何选Collation字符校对

免安装SQL管理工具HeidiSQL 文章目录 免安装SQL管理工具HeidiSQL一、安装二、建库因此&#xff0c;通常我们选择&#xff1a; 一、安装 到官方网址&#xff1a;https://www.heidisql.com/ 下载后按不同版本安装或解压&#xff0c;运行目录中的heidisql应用程序。 该工具可以对…

「生存即赚」链接现实与游戏,打造3T平台生态

当前&#xff0c;在线角色扮演游戏&#xff08;RPG&#xff09;在区块链游戏市场中正迅速崛起&#xff0c;成为新宠。随着区块链技术的不断进步&#xff0c;众多游戏开发者纷纷将其游戏项目引入区块链领域&#xff0c;以利用这一新兴技术实现商业价值的最大化。在这一趋势中&am…

加州大学欧文分校英语中级语法专项课程02:Adjectives and Adjective Clauses 学习笔记

Adjectives and Adjective Clauses course certificate 本文是 https://www.coursera.org/learn/adjective-clauses 这门课的学习笔记。 文章目录 Adjectives and Adjective ClausesWeek 01: Adjectives and Adjective PhrasesLearning Objectives Adjectives Introduction Le…

基于Java的智慧社团综合管理系统的设计与实现(论文+源码)_kaic

摘 要 随着校园文化的不断丰富&#xff0c;大学里各种社团越来越多&#xff0c;社团活动也越来越频繁&#xff0c;社员也越来越多&#xff0c;而且大学生退社、入社比较频繁&#xff0c;社团管理就显得非常繁琐而又复杂,如果采用人工管理,对管理员来说将是一件很头疼的事情。设…

5个本地流畅运行大模型的免费工具

大家好&#xff0c;随着大型语言模型&#xff08;LLM&#xff09;驱动的聊天机器人逐渐普及&#xff0c;给人们的工作和生活带来了前所未有的便利。然而&#xff0c;这种便捷性背后潜藏着个人隐私信息被泄露的风险&#xff0c;例如AI公司会收集聊天记录和元数据来优化模型&…

【Vulhub靶场】Nginx 漏洞复现

Nginx 漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09;1、影响版本2、漏洞原理3、漏洞复现 二、Nginx 解析漏洞1、版本信息&#xff1a;2、漏洞详情3、漏洞复现 一、Nginx 文件名逻辑漏洞&#xff08;CVE-2013-4547&#xff09; 1、影响版本 Nginx …

mysql 数据转excel文件

mysql 数据转excel文件 缘由 为售后拉取数据&#xff0c;用navicat太墨迹了&#xff0c;用python写一个main方法跑一下&#xff1b; 1.抽取共同方法&#xff0c;封装成传入mysql&#xff0c;直接下载成excel&#xff1b; 2.写入所有sql语句&#xff0c;传入参数&#xff1b; 代…

2024-05-02 商业分析-杭州小万科技-商业模式分析

摘要: 对杭州小万科技的商业模式进行分析,以对其做出客观的评估。 杭州小万科技的资料: 杭州小万科技有限公司 - 企知道 (qizhidao.com) 杭州小万科技有限公司网站备案查询 - 天眼查 (tianyancha.com) 杭州小万科技有限公司 - 爱企查 (baidu.com) ​ 2023年年报:

Android手写自己的路由SDK

实现自己的路由框架 ​ 在较大型的Android app中常会用到组件化技术&#xff0c;针对不同的业务/基础功能对模块进行划分&#xff0c;从上到下为壳工程、业务模块、基础模块。其中业务模块依赖基础模块&#xff0c;壳工程依赖业务模块。同级的横向模块&#xff08;比如多个业务…

BeanFactory 源码浅析

BeanFactory 功能介绍 BeanFactory 是核心容器&#xff0c;负责管理 Bean 对象 BeanFactory 接口的功能只有一个 getBean() 方法BeanFactory 的实现类&#xff08;DefaultListableBeanFactory&#xff09;包含&#xff1a;控制反转、基本的依赖注入、Bean 生命周期的各种功能…

【Python】函数设计

1.联系函数的设计 2.找质数 3.找因子 4.判断水仙花数 5.斐波拉契数列递归调用&#xff0c;并用数组存储已计算过的数&#xff0c;减少重复计算 1、计算利息和本息 编写两个函数分别按单利和复利计算利息,根据本金、年利率、存款年限得到本息和和利息。调用这两个函数计算1…

【算法刷题日志】吸氧羊的StarryCoding之旅 - 贡献法计算

题目链接&#xff1a;https://www.starrycoding.com/problem/3 题目描述 吸氧羊终于注册了一个StarryCoding账号&#xff01;&#xff08;她很开心&#xff09; 但是吸氧羊忘记了它的密码&#xff0c;她想起你是计算机大师&#xff0c;于是就来请教你。 她虽然不记得密码了…

java版数据结构:深入理解栈和队列:数据结构与应用(vector,stack,queue)

目录 前言 动态数组类&#xff08;vector&#xff09; 特点&#xff1a; 应用&#xff1a; 栈&#xff08;Stack&#xff09; 栈的基础概念&#xff1a; 栈的常用方法&#xff1a; 模拟栈操作&#xff1a; 队列&#xff08;Queue&#xff09; 队列的基础概念 队列的常…

golang学习笔记(协程的基础知识)

golang的协程 协程是一种轻量级的线程&#xff0c;它可以实现并发执行的并行操作。协程是Go语言中的一个核心特性&#xff0c;它使得程序能够以并发的方式运行&#xff0c;并且非常高效。与传统的线程相比&#xff0c;协程的创建和销毁成本非常低&#xff0c;可以方便地启动大…

三维坐标点按剖面分类

一、写在前面 ①配套文件&#xff1a;根据剖面对三维坐标点&#xff08;X,Y,Z&#xff09;分类资源-CSDN文库 ②脱敏处理&#xff1a;蚀变数据已采用随机数生成覆盖 ③剖面坐标按顺序排列在“剖面坐标点.xlsx”文件中 二、3点确定空间中平面方程 原理&#xff1a; 设3点A&…

C++深度解析教程笔记2

C深度解析教程笔记2 第3课 - 进化后的 const 分析实验-C与C的const区别实验-C与C的const区别&const作用域 第4课 - 布尔类型和引用小结 本文学习自狄泰软件学院 唐佐林老师的 C深度解析教程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 第3课 - 进化后…

列转行(spark 与presto语法)

一、Presto 语法 原始数据&#xff1a; 期望数据&#xff1a; 代码&#xff1a; SELECT info, value FROM ( select 张三 as name,18 as age,男 as gender,清华 as schoolunion allselect 李四 as name,18 as age,男 as gender,清华 as school ) as a CROSS JOIN UNNEST(…

Unreal 编辑器工具 批量重命名资源

右键 - Editor Utilities - Editor Utility Blueprint&#xff0c;基类选择 Asset Action Utility 在类默认值内&#xff0c;可以添加筛选器&#xff0c;筛选指定的类型 然后新建一个函数&#xff0c;加上4个输入&#xff1a;ReplaceFrom&#xff0c;ReplaceTo&#xff0c;Add…

使用Android Studio 搭建AOSP FrameWork 源码阅读开发环境

文章目录 概述安装Android Studio编译源码使用Android Studio打开源码制作ipr文件直接编译成功后自动打开Android Studio 修改SystemUI验证开发环境 概述 我们都知道Android的系统源码量非常之大&#xff0c;大致有frameworka层源码&#xff0c;硬件层(HAL)源码&#xff0c;内…

机器学习笔记-18

异常检测问题 异常检测虽然主要用于无监督学习问题上&#xff0c;但是和监督学习问题很相似。 异常检测(Anomaly Detection)&#xff1a;给定正确样本集{ x ( 1 ) , x ( 2 ) . . . x ( n ) x^{(1)},x^{(2)}...x^{(n)} x(1),x(2)...x(n)}&#xff0c;记新样本即要检测的样本为…