设置ElevatedButton的样式

关于ButtonStyleButton样式的设置问题,包括ElevatedButton的全局样式,单个按钮的样式设置,还有MaterialStateProperty的使用。

修改单个按钮样式

当你要修改单个ElevatedButton的样式时,可以调用静态方法styleFrom设置它的style属性,如下

dart 复制代码
ElevatedButton(
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue
  ),
  onPressed: () => print('clicked'),
  child: const Text('custom style for a ElevatedButton')
)

上面通过styleFrom方法只是设置了背景色,保留了其他默认的样式,ElevatedButton的默认样式有两种风格:一种是使用了M3的,调用了_ElevatedButtonDefaultsM3方法来设置的(具体看源码);一种是没有使用M3的,具体如下:

dart 复制代码
{
  backgroundColor: colorScheme.primary, // 文本颜色
  foregroundColor: colorScheme.onPrimary, // 背景颜色
  disabledBackgroundColor: colorScheme.onSurface.withOpacity(0.12),
  disabledForegroundColor: colorScheme.onSurface.withOpacity(0.38),
  shadowColor: theme.shadowColor, // 鼠标hover时出现的阴影颜色
  elevation: 2,
  textStyle: theme.textTheme.labelLarge, // 文本样式
  padding: _scaledPadding(context), // 内边距
  minimumSize: const Size(64, 36),
  maximumSize: Size.infinite,
  // 外型
  shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
  enabledMouseCursor: SystemMouseCursors.click,
  disabledMouseCursor: SystemMouseCursors.basic,
  visualDensity: theme.visualDensity,
  tapTargetSize: theme.materialTapTargetSize,
  animationDuration: kThemeChangeDuration,
  enableFeedback: true,
  // 子组件的对齐方式
  alignment: Alignment.center,
  splashFactory: InkRipple.splashFactory
}

修改所有ElevatedButton样式

需要设置MaterialApp的theme属性,如下

dart 复制代码
MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
    colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
    useMaterial3: true,
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: Colors.blueGrey,
        foregroundColor: Colors.orangeAccent,
        shadowColor: Colors.orange,
        surfaceTintColor: Colors.red,
        shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4)))
      ),
    )
  ),
  home: const MyHomePage(title: 'Flutter Demo Home Page'),
)

使用ButtonStyle

上面不管是ElevatedButton.style还是ElevatedButtonThemeData.style都是ButtonStyle类型,所以我们还可以直接实例化一个ButtonStyle来设置ElevatedButton的样式。

dart 复制代码
ElevatedButton(
	onPressed: () {},
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.all(Colors.black),
    textStyle: MaterialStateProperty.all(
      const TextStyle(
        fontSize: 24
      )
    ),
  ),
  child: const Text('My Button')
)

上面的例子中直接设置了style属性为ButtonStyle实例,不过跟直接使用styleFrom方法不同的是,这里的属性都是MaterialStateProperty类型(styleFrom中是直接的数值类型,如Color,double等)。

结合上面的例子,这里还有一个隐藏的问题。我们前面通过ElevatedButtonThemeData全局设置了ElevatedButton的样式,然后又通过ButtonStyle设置了单个ElevatedButton的样式,如果这两个分别都设置了textStyle样式,那你最好两个地方设置相同的TextStyle属性,方便flutter在热启动时,做样式的过度动画,否则热启动时会报错。

shell 复制代码
In general, TextStyle.lerp only works well when both TextStyles have the same "inherit" value, and
specify the same fields.
If the TextStyles were directly created by you, consider bringing them to parity to ensure a smooth
transition.

If one of the TextStyles being lerped is significantly more elaborate than the other, and has
"inherited" set to false, it is often because it is merged with another TextStyle before being
lerped. Comparing the "debugLabel"s of the two TextStyles may help identify if that was the case.
For example, you may see this error message when trying to lerp between "ThemeData()" and
"Theme.of(context)". This is because TextStyles from "Theme.of(context)" are merged with TextStyles
from another theme and thus are more elaborate than the TextStyles from "ThemeData()" (which is
reflected in their "debugLabel"s -- TextStyles from "Theme.of(context)" should have labels in the
form of "(<A TextStyle>).merge(<Another TextStyle>)"). It is recommended to only lerp ThemeData with
matching TextStyles.

例如全局设置了TextStyle的backgroundColor,fontSize

dart 复制代码
elevatedButtonTheme: ElevatedButtonThemeData(
  style: ElevatedButton.styleFrom(
    shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))),
    textStyle: const TextStyle(
      fontSize: 24,
      backgroundColor: Colors.red
    )
  ),
)

那么,你必须在设置单个ElevatedButton的textStyle样式时使用相同的属性,如下

dart 复制代码
ElevatedButton(
  onPressed: () {},
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.all(Colors.black),
    textStyle: MaterialStateProperty.all(
      const TextStyle(
        fontSize: 24.0,
        backgroundColor: Colors.red
      )
    )
  ),
  child: const Text('My Button'),
),

上面的报错只会存在于热启动中,如果你重新启动应用就不会报错。

MaterialStateProperty

ButtonStyle的属性都是MaterialStateProperty类型,这究竟是什么,为何不直接使用对于的Colorsdouble等类型?关于这些问题,StackOverflow上有一篇很好的回答:stackoverflow.com/questions/6...

MaterialStateProperty作用在于能方便简单地根据按钮的不同状态来设置某个属性的不同值。按钮一般有upoverdown几个状态,如果你要分别设置每个状态下的背景色,之前在Adobe Flex中我们需要分别设置upSkinoverSkindownSkin几个属性,但Flutter不需要这么麻烦。

resolveWith

MaterialStateProperty提供了一个resolveWith(MaterialPropertyResolver callback)静态方法,该方法的callback有一个states参数,它会返回按钮当前状态的一个集合(按钮的当前状态可能不止一个),我们只需要取出对应状态设置对应的值即可。

dart 复制代码
typedef MaterialPropertyResolver<T> = T Function(Set<MaterialState> states);

static MaterialStateProperty<T> resolveWith<T>(MaterialPropertyResolver<T> callback) => _MaterialStatePropertyWith<T>(callback);

先看官方的一个例子

dart 复制代码
@override
Widget build(BuildContext context) {
  Color getColor(Set<MaterialState> states) {
    const Set<MaterialState> interactiveStates = <MaterialState>{
      MaterialState.pressed,
      MaterialState.hovered,
      MaterialState.focused,
    };
    if (states.any(interactiveStates.contains)) {
      return Colors.blue;
    }
    return Colors.red;
  }

  return TextButton(
    style: ButtonStyle(
      foregroundColor: MaterialStateProperty.resolveWith(getColor),
    ),
    onPressed: () {},
    child: const Text('TextButton'),
  );
}

interactiveStates定义了一个需要处理的状态集合,这里我们只想设置pressedhoveredfocused三个状态的颜色。states.any(interactiveStates.contains)的作用是判断states集合中是否至少有一个元素存在于interactiveStates集合中,如果存在返回true,否则false

MaterialState定义了很多不同的状态,除了上面的三个外,还有:draggedselectedscrolledUnder(当与可滚动的内容有重叠时)、disablederror(无效状态,表单中的组件?)

all

静态方法MaterialStateProperty.all会设置所有所有状态为同一个值,如

dart 复制代码
ButtonStyle(
  backgroundColor: MaterialStateProperty.all(Colors.red)
)
例子

MaterialStateProperty可以给某个样式属性在不同的状态设置不同值,如果你同时设置多个样式属性,且设置相同的状态,那就可以设置相同的状态下,不同样式属性表现不一样,如下代码

dart 复制代码
ElevatedButton(
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.resolveWith((states) {
      if (states.contains(MaterialState.disabled)) {
        return Colors.grey;
      }
      if (states.contains(MaterialState.pressed)) {
        return Colors.green;
      }
      return Colors.blue;
    }),
    textStyle: MaterialStateProperty.resolveWith((states) {
      if (states.contains(MaterialState.pressed)) {
        return const TextStyle(fontSize: 40);
      }
      return const TextStyle(fontSize: 20);
    })
  )
)

常用样式属性说明

textStyle - 用于设置按钮文本样式,文本在按钮中默认居中。

backgroundColor - 设置按钮的背景色,文本也有背景色,该背景色是占满整个按钮。

foregroundColor - 设置子组件的文本颜色,如果子组件是icon,那就是设置Icon的颜色。

overlayColor - 设置按钮的focused,hovered或者pressed状态的颜色。

shadowColor - 设置按钮的阴影颜色。

elevation - 设置阴影的大小。

padding - 内边距,即按钮边框与子组件之间的距离。

minimumSize - 设置按钮的最小宽高。

maximumSize - 设置按钮的最大宽高。

fixedSize - 设置按钮的固定宽高,按钮不再随环境变化大小。

side - 设置按钮的边框大小与颜色。

shape - 设置按钮的外形,启用M3后,默认是跑到形状的。

visualDensity - 设置按钮布局的紧凑程度。

tapTargetSize - 指定按钮可点击的区域大小。

animationDuration - shape与elevation变化时的动画时长。

enableFeedback - 按钮交互时是否有提示,如点击时发声。

alignment - 子组件的对齐方式。

splashFactory - 水波纹的设置。

总结

全局设置ElevatedButton的样式,可以通过MaterialApptheme来设置。

单个ElevatedButton的样式,可以直接设置其style属性,设置style有两种方式一种是通过ElevatedButton的静态方法styleFrom来设置,这个设置某个样式属性是不区分按钮状态的,比如背景色,如果你设置为红色,那么按钮的pressedhovered等状态都是这个颜色;另一种方式是直接创建一个ButtonStyle实例来设置style。

ButtonStyle中的样式属性都是MaterialStateProperty类型,它的作用是让开发者能方便清晰的根据按钮不同的状态来设置不同的样式。

上面的设置样式方法同样适用于TextButtonOutlinedButton

相关推荐
xingfanjiuge4 小时前
Flutter框架跨平台鸿蒙开发——ListView.builder深度解析
flutter·华为·harmonyos
2601_949847755 小时前
Flutter for OpenHarmony 剧本杀组队App实战:邀请好友功能实现
开发语言·javascript·flutter
2601_949868365 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 数据持久化实现
java·数据库·flutter
ITUnicorn5 小时前
Flutter x HarmonyOS 6:依托小艺开放平台创建智能体并在应用中调用
flutter·harmonyos·鸿蒙·智能体·harmonyos6
子春一6 小时前
Flutter for OpenHarmony: 从颜色模型到可访问性:一个 Flutter 高对比度 UI 的完整实践
flutter·ui
小白郭莫搞科技6 小时前
鸿蒙跨端框架Flutter学习:CurvedAnimation曲线动画详解
学习·flutter·harmonyos
一起养小猫6 小时前
Flutter for OpenHarmony 实战:ListView与GridView滚动列表完全指南
开发语言·javascript·flutter
程序员清洒6 小时前
Flutter for OpenHarmony:ListView — 高效滚动列表
开发语言·flutter·华为·鸿蒙
Miguo94well7 小时前
Flutter框架跨平台鸿蒙开发——旅行攻略规划APP的开发流程
flutter·华为·harmonyos·鸿蒙
zilikew7 小时前
Flutter框架跨平台鸿蒙开发——食物采购清单APP的开发流程
flutter·华为·harmonyos·鸿蒙