Flutter 中如何优雅的封装 Widget?

2022-06-11 19:47:55 +08:00
 Trello

举个栗子,封装一个 Text Widget 。

方式一

优点:

不用担心为了自定义某个预设样式,而导致所有预设样式丢失。

缺点:

这里只是暴露了 Text 控件的几个属性,实际开发中一个 Widget 可能需要暴露很多属性,后面扩展下来,属性列表可能会拉的很长。在这里极限情况就要暴露 Text 控件的所有属性给外界。

class TextWidget extends StatelessWidget {
  final String text;
  final Color? color;
  final double? fontSize;
  final FontWeight? fontWeight;

  const TextWidget({
    Key? key,
    required this.text,
    this.color = Colors.black,
    this.fontSize = 16,
    this.fontWeight = FontWeight.normal,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(
      text,
      style: TextStyle(
        color: color,
        fontSize: fontSize,
        fontWeight: fontWeight,
      ),
    );
  }
}

方式二

优点:

不用暴露太多属性,直接暴露一个 textStyle 属性,需要用到 text 的什么属性,使用者自己传入 textStyle 即可。

缺点:

一旦 Widget 使用者,需要传入 textStyle 自定义部分样式,会丢失预设的 textStyle 所有默认样式,所有样式都需要重新传,对于只改其中单个或少数几个样式来说,很不方便。

这里预设了颜色、字号、字重这三个样式属性,使用者如果仅仅需要修改颜色,那在将自定义 color 传入 textStyle 的同时,还要传入字号、字重这俩属性默认的值。

这里预设样式比较少还好。如果预设样式列表特别长,有十几个甚至更多属性,为了自定义其中一个属性的样式,需要重新传所有样式,就很难受很不友好。

class TextWidget extends StatelessWidget {
  final String text;
  final TextStyle textStyle;

  const TextWidget({
    Key? key,
    required this.text,
    this.textStyle = const TextStyle(
      color: Colors.black,
      fontSize: 16,
      fontWeight: FontWeight.normal,
    ),
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(text, style: textStyle);
  }
}
1146 次点击
所在节点    问与答
11 条回复
Trello
2022-06-11 20:03:54 +08:00
Flutter 中有没有哪种方式,能兼容上述两种方式的优点?诸如像 RN 中封装组件那样,就很优雅简洁。

方便扩展,不用传入多个样式属性,一个 style 即可。

同时也不用担心预设样式丢失,直接使用扩展运算符增量覆盖即可。

以下是 RN 写法。

interface Props {
text: String,
textStyle?: TextStyle,
}

class TextComponent extends React.Component<Props> {
render() {
const { text, textStyle } = this.props;

return (
<Text
style={{
color: '#000',
fontSize: 16,
fontWeight: 'normal',
...textStyle
}}
>
{text}
</Text>
)
}
}
shetz163
2022-06-11 20:08:26 +08:00
TextStyle 里面有一个方法 merge, 可以合并另外一个 TextStyle
其实可以写一个默认的 TextStyle, 然后传入想要部分修改的 TextStyle, 合并使用就好
Trello
2022-06-11 20:08:42 +08:00
友友们请不吝赐教哈
Trello
2022-06-11 20:21:01 +08:00
@shetz163 谢谢,原来还要这么个方法,那文本样式方面的不用愁了。请问 EdgeInsets 之类的有类似的合并方法吗?比如我预设了边距,后面再传一个 EdgeInsets ,以后来的为准。
shetz163
2022-06-11 20:37:30 +08:00
edgeinsets 好像没有这样的合并方式, 可以用 ?? 来做默认的, 但如果要合并的话, 可能得自己去做一下处理, 比如判断一下 上下左右是否大于 0 来确认是否继承
Trello
2022-06-11 20:44:40 +08:00
@shetz163 好的吧,谢谢。我在寻求 Flutter 内一种通用的 Widget 传参属性扩展方式。

在 React 内给组件传 Props 属性,如果要扩展的话,也是直接扩展运算符就可以扩充了。

诸如有这么个 A 组件,有两个确定要传的 a 、b 属性,同时使用{...props}保留了扩展性。

<Component a={xxx} b={xxx} {...props}>

不知道 Flutter 中各式各样的 widget 传属性时,如果要动态扩充,有没有什么通用的办法。
Trello
2022-06-11 20:47:39 +08:00
@Trello React 中这种形式,不仅可以扩充新的属性,还能覆盖原有属性,就很方便。
Trello
2022-06-11 20:51:47 +08:00
@shetz163 我看 Stack Overflow 上的回复,好像没有这种方式,就很无奈。需要哪个属性就得写哪个属性,一点都不灵活。
https://stackoverflow.com/questions/56934960/flutter-inherit-all-props-from-parent-widget
shetz163
2022-06-11 20:52:33 +08:00
现有的看起来就只有自己去做继承, 也可以用 dart extension 来自己做一个继承的方法, 最新的 flutter 3 上对于 extension 支持更好了, 好像是可以不用提前导入 extension 的文件就可以直接联想出来方法, 并且自动导入
Trello
2022-06-11 21:22:35 +08:00
@shetz163 哇,那岂不是得用一个 widget 就得写一个继承类来实现合并,这也太累了呀。而且写的这个继承类还不能通用,因为不同场景下预设值不一样,比如 EdgeInsets 我写了个继承类,里面属性赋上某个场景下预设的默认边距,别的场景这个 EdgeInsets 的边距默认值又变了,那还得重写一个关于 EdgeInsets 的继承类,或者在原来写的继承类里加判断或其他命名构造函数做场景区分(这些常用的 widget 场景太多,一个继承类根本维护不下去的)。吐了呀,要崩溃。。。太反人类吧。
shetz163
2022-06-12 01:15:00 +08:00
我的意思是说做个继承的方法 比如扩展一个 edgeinsets.merge(edgeinsets other)这样的方法然后 other 里有非 0 的值就填进去 没有就用默认的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/858958

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX