Flutter 核心技术与实战
陈航
前美团点评高级技术专家
42432 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 48 讲
Flutter 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

15 | 组合与自绘,我该选用何种方式自定义Widget?

扩展Cake控件
扩展UpdatedItem控件
学习新知识的过程类比
拆解大问题为小问题
自定义UI时的思考
例子:饼图控件Cake
CustomPaint和CustomPainter
用画笔去绘制不规则的视图
例子:新闻Item控件
提高控件的复用性
对外暴露的接口较少
组合的方式自定义Widget
思考题
总结
自绘
组装
组合与自绘:我该选用何种方式自定义Widget?

该思维导图由 AI 生成,仅供参考

你好,我是陈航。
在上一次分享中,我们认识了 Flutter 中最常用也最经典的布局 Widget,即单子容器 Container、多子容器 Row/Column,以及层叠容器 Stack 与 Positioned,也学习了这些不同容器之间的摆放子 Widget 的布局规则,我们可以通过它们,来实现子控件的对齐、嵌套、层叠等,它们也是构建一个界面精美的 App 所必须的布局概念。
在实际开发中,我们会经常遇到一些复杂的 UI 需求,往往无法通过使用 Flutter 的基本 Widget,通过设置其属性参数来满足。这个时候,我们就需要针对特定的场景自定义 Widget 了。
在 Flutter 中,自定义 Widget 与其他平台类似:可以使用基本 Widget 组装成一个高级别的 Widget,也可以自己在画板上根据特殊需求来画界面。
接下来,我会分别与你介绍组合和自绘这两种自定义 Widget 的方式。

组装

使用组合的方式自定义 Widget,即通过我们之前介绍的布局方式,摆放项目所需要的基础 Widget,并在控件内部设置这些基础 Widget 的样式,从而组合成一个更高级的控件。
这种方式,对外暴露的接口比较少,减少了上层使用成本,但也因此增强了控件的复用性。在 Flutter 中,组合的思想始终贯穿在框架设计之中,这也是 Flutter 提供了如此丰富的控件库的原因之一。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Flutter中自定义Widget有两种方式:组合和自绘。组合是通过将基础Widget组装成高级别的Widget,提高控件的复用性,而自绘则是根据特殊需求在画板上绘制界面。文章通过一个App Store升级项UI的例子,详细介绍了如何通过组装方式自定义控件。首先定义了数据结构UpdateItemModel,然后按照子Widget的摆放方向,分别拆解了UI的垂直和水平布局结构。在拆解的基础上,详细讲解了控件间的边距设置、文本伸缩规则、图片圆角实现等关键步骤。最终通过组合方式完成了升级项UI的定制。这种从上到下、从左到右拆解UI的布局结构的方法对于以组装方式自定义UI非常有用。 在自绘部分,文章介绍了Flutter中自绘控件的实现方式。通过CustomPaint容器和CustomPainter,可以实现自定义的绘制逻辑,例如绘制饼图等不规则的视图。CustomPainter的paint方法中,通过Canvas与Paint的配合,可以实现定制化的绘制逻辑。自绘控件的实现并不复杂,可以通过画笔和画布实现更丰富的功能。 总结来说,Flutter提供了组装与自绘两种自定义Widget的方式,来满足对视图的自定义需求。组合方式适用于将目标视图分解成各个UI小元素,而自绘方式则适用于实现少数通过组合难以实现的需求。在自定义UI时,需要将目标视图化繁为简,拆解成小控件,然后再思考如何将这些小控件串联起来。这种思维方式类似于学习新知识的过程,需要将复杂的知识化繁为简,逐个攻破。 通过本文的学习,读者可以了解到组合和自绘这两种自定义Widget的具体使用方法,并在实际应用中体会它们在不同场景下的优劣势。同时,文章还留下了两道思考题,可以帮助读者进一步巩固所学知识。 Overall, the article provides a comprehensive overview of customizing widgets in Flutter through composition and custom painting, offering practical examples and thought-provoking exercises for readers to deepen their understanding.

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Flutter 核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(33)

  • 最新
  • 精选
  • 吴小安
    大前端的界面不是提倡尽量减少图层的数量?这样一直嵌套下去图层似乎太多,这些布局的控件是不是不算图层?不参与渲染?

    作者回复: 1.大部分都是非可视的容器,并不参与绘制;对于非透明的视图叠加,Flutter在绘制完毕后会做图层合并的

    2019-08-01
    14
  • 和小胖
    关于第二道思考题目的解决方法,请老师看一下: //绘制自定义view,其中画笔 paint ,画布 canvas,而 CustomPainter 负责具体的绘制逻辑处理 class WheelPainter extends CustomPainter { List<double> _list; List<Color> _listColor; double _total; //总份数 WheelPainter(this._list, this._listColor); @override void paint(Canvas canvas, Size size) { double wheelSize = min(size.width, size.height) / 2; //饼图的尺寸 //用一个矩形框来包裹饼图 Rect boundingRect = Rect.fromCircle( center: Offset(wheelSize, wheelSize), radius: wheelSize); //求出数组中所有数值的和 _total = _list.reduce((value, element) => value + element); print("总份额是:$_total"); //求出每一份所占的角度 double radius = (2 * pi) / _total; //求出每一份的弧度 print("总角度是${2 * pi},每份额角度是:$radius"); //循环绘制每一份扇形 for (int i = 0; i < _list.length; i++) { // 求出初始角度 double startRadius; if (i == 0) { startRadius = 0; } else { startRadius = _list.sublist(0, i).reduce((value, element) => value + element) * radius; } //求出滑过角度,即当前份额乘以每份所占角度 double sweepRadius = radius * _list[i]; print("起始角度:$startRadius,划过角度:$sweepRadius"); /// 绘制扇形,第一个参数是绘制矩形所在的矩形,第二个参数是起始角度,第三个参数是矩形划过的角度 /// 第三个参数代表扇形是否是实心,否则就只是绘制边框是空心的,最后一个参数是画笔 canvas.drawArc(boundingRect, startRadius, sweepRadius, true, getPaintByColor(_listColor[i])); } } //根据不同的 color 来获取对应的画笔 Paint getPaintByColor(Color color) { Paint paint = Paint(); paint.color = color; return paint; } //判断是否需要重绘 @override bool shouldRepaint(CustomPainter oldDelegate) => oldDelegate != this; } Padding getCustomPaint() { return Padding( padding: EdgeInsets.all(10), child: CustomPaint( size: Size(200, 200), painter: WheelPainter( List.of([1.0, 2.0, 3.0, 4.0, 20]), List.of([ Colors.red, Colors.blue, Colors.green, Colors.amber, Colors.black54 ])), )); }

    作者回复: 很厉害👍!

    2019-09-05
    4
    12
  • wanggw
    功能算是差不多实现了,但是还存在一个核心的问题不知道应该解决,就是我怎么获取Text展示文本的行数,我需要行数才能控制 more 按钮的显示和隐藏,否则我默认超过2行显示 more 按钮,当文本只有1行的时候,more 按钮也显示了😂。暂时还没找到解决方案。这是我写的例子:https://github.com/wanggw911/flutter_hello/blob/master/lib/widget/Listview02.dart

    作者回复: 如果想简单点,可以预估一个文本长度(比如100),超过这个文本长度的两行文字,统一都展示“More”按钮;如果想精确点,算文本高度可以用TextPainter。具体用法可以参考auto_size_text控件:https://github.com/leisim/auto_size_text

    2019-08-22
    3
    5
  • davidzhou
    我的思路这样,先自定义一个statefulwidget,里面用过一个变量控制两个text,因为text是statelesswidget,无法动态去刷新,一个widget设置Maxlines=2,另一个不设置,more是一个floatbutton,点击事件里面实现setstate改变先前定义的变量就行了

    作者回复: 赞

    2019-08-01
    2
    4
  • 獸丶
    老师,Flutter代码规范方面会讲吗,如果代码全写在一个Dart文件里,有点太冗杂了,还是说遵循SRP,一个类一个Dart文件?

    作者回复: 后面会捎带讲一点点

    2019-08-02
    3
  • zzz
    请问下,再混合开发的场景下,module类型的对于root Widget,我们必须使用MaterialApp或者CupertinoApp二选一么?如果不使用这两个基础的widgetApp,除了不能享受封装好的Theme、封装好的Widget组件这些便利外,是否有无法实现的基础功能(比如在iOS中的右滑返回等等)? 同时想问下目前国内很多App在安卓上的表现都不是Material风格的,同时也不完全是Cupertino风格,所以在实际应用中,主流的做法是使用 MaterialApp/CupertinoApp/完全自定义 这三种的哪一种呢?以及在开发过程中的是否有什么弊端呢?

    作者回复: MaterialApp和CupertinoApp封装了一些基本的App功能,比如导航栈管理、屏幕管理,国际化,以及对应的脚手架等,自己实现没有必要也基本不可能。样式就是一个配置而已,你可以自己定义不同平台的配置规则(具体参考夜间模式这一节)。另外他俩对Android/iOS的关键设计差异(如导航栏样式、状态栏样式、弹窗样式等),在框架内就进行了区分实现,所以如果你不定制样式,也无需过多担心在iOS上长得太像Android。

    2019-10-08
    3
    2
  • Neil 陈荣
    老师,我想绘制一个围棋游戏的棋盘,并在上面实现落子、提子等各种操作。大致估计了一下,如果用组合的方案,算上棋子,棋盘,各种线,至少会有接近400个 widget. 这种情况下性能会有问题吗?我想知道 widget 数量一般在多少以内采用组合不会出现性能问题,这个有没有一个指导性的最佳实践?如果一上来就用自绘的方案的话,担心各种操作的交互功能不容易实现。谢谢!

    作者回复: 没啥问题,组件比较多的情况下,唯一需要关注的是限制界面的刷新范围,可以参考37节分享,用重绘边界去提前做一些缓存。

    2019-12-11
    2
    1
  • 毛哥来了
    我是用Builder(builder: (Buildcontext context){})来创建description那个Text组件,然后就可以通过context.size.height获取Text的高度,然后就能判断按钮何时展示了

    作者回复: 赞

    2019-10-24
    3
    1
  • 浣熊特工队
    请问老师,more按钮那里的左右渐隐是怎么实现的啊,我用TextOverflow.fade是向下渐隐的啊

    作者回复: 可以看下这个issue:https://github.com/flutter/flutter/issues/13631 另外这种效果可以考虑用Button遮挡来实现

    2019-09-03
    2
    1
  • 🌙
    如果说尽量使用stateless,但是只要需要交互都必须是stateful吧?怎么尽量来使用成stateless呢?

    作者回复: 把组件拆小

    2019-08-23
    1
收起评论
显示
设置
留言
33
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部