背景

嵌套层级深的问题让众多刚接触Flutter的同学感到困扰,它不仅是看起来让人感到不适,还非常影响编码体验。

大佬们会告诉你应该拆分自己的嵌套代码(自定义widget或者抽取build方法)来减少嵌套层级。这确实是个行之有效的方法,除此之外,还有没有别的方法呢,本文将向您介绍另一种减少嵌套层级的方法。

嵌套过深影响代码的视觉观感

这段代码演示了什么叫做:嵌套地狱

class Test extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Demo'),),body: Container(child: Offstage(offstage: false,child: ListView(children: <Widget>[Container(color: Colors.white,padding: EdgeInsets.all(20),child: Row(crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Icon(Icons.phone),Text("amy"),],),),Container(color: Colors.white,padding: EdgeInsets.all(20),child: Row(crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Icon(Icons.phone),Text("billy"),],),),],),),),);}
}

提取build方法后,嵌套层级得到了明显的改善

class Test extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Demo'),),body: Container(child: Offstage(offstage: false,child: ListView(children: <Widget>[buildItem("amy"),buildItem("billy"),],),),),);}Container buildItem(String name) {return Container(color: Colors.white,padding: EdgeInsets.all(20),child: Row(crossAxisAlignment: CrossAxisAlignment.center,children: <Widget>[Icon(Icons.phone),Text(name),],),);}
}

还能不能继续优化呢?

自定义扩展函数

举个例子:想要给下面这段代码中的第2个Textwidget加上marginTop:10属性

  @overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.all(10),child: Column(children: <Widget>[Text('billy'),Text('say hello'), //add margin top??],),);}

此时,我内心希望可以这样写:

显然,flutter不支持这么写,幸运的是:dart2.7发布时正式宣布支持扩展函数(Extension Methods)

实际上从dart 2.6.0就开始支持扩展函数了
如果pubspec.yaml中设置的dart版本低于2.6.0则会出现警告提示如:
environment:sdk: ">=2.1.0 <3.0.0"警告提示:
Extension methods weren’t supported until version 2.6.0

先来定义一个扩展函数

extension WidgetExt on Widget {Container intoContainer({//复制Container构造函数的所有参数(除了child字段)Key key,AlignmentGeometry alignment,EdgeInsetsGeometry padding,Color color,Decoration decoration,Decoration foregroundDecoration,double width,double height,BoxConstraints constraints,EdgeInsetsGeometry margin,Matrix4 transform,}) {//调用Container的构造函数,并将当前widget对象作为child参数return Container(key: key,alignment: alignment,padding: padding,color: color,decoration: decoration,foregroundDecoration: foregroundDecoration,width: width,height: height,constraints: constraints,margin: margin,transform: transform,child: this,);}
}

现在,所有widget对象都多了一个intoContainer(...)扩展函数,而且参数与Container构造方法一致,于是,我们就可以这样写了:

除了Container,其它容器也可以通过同样的方式来扩展。于是,编程体验大大提升,再也不用动不动就大段选择代码剪切粘贴了。

还可以支持链式调用:

Text("billy").intoExpanded(flex: 1).intoContainer(color: Colors.white)

有些widget有多个子widget (children), 可以添加如下的扩展函数:

extension WidgetExt on Widget {//添加一个相邻的widget,返回List<Widget>List<Widget> addNeighbor(Widget widget) {return <Widget>[this, widget];}//添加各种单child的widget容器//如:Container、Padding等...
}extension WidgetListExt<T extends Widget> on List<T> {//子List<Widget>列表中再添加一个相邻的widget,并返回当前列表List<Widget> addNeighbor(Widget widget) {return this..add(widget);}Row intoRow({Key key,MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,MainAxisSize mainAxisSize = MainAxisSize.max,CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,TextDirection textDirection,VerticalDirection verticalDirection = VerticalDirection.down,TextBaseline textBaseline,}) {return Row(key: key,mainAxisAlignment: mainAxisAlignment,mainAxisSize: mainAxisSize,crossAxisAlignment: crossAxisAlignment,textDirection: textDirection,verticalDirection: verticalDirection,textBaseline: textBaseline,children: this,);}//添加其它多child的widget容器//如:Column、ListView等...
}

使用扩展函数解决嵌套过深的问题

回到本文最初的嵌套地狱,现在我们的代码可以写成这样

class Test extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Demo'),),body: buildItem("amy").addNeighbor(buildItem("billy"),).intoListView().intoOffstage(offstage: false).intoContainer());}Container buildItem(String name) {return Icon(Icons.phone).addNeighbor(Text(name)).intoRow(crossAxisAlignment: CrossAxisAlignment.center,).intoContainer(color: Colors.white, padding: EdgeInsets.all(20),);}
}

为了让我们的代码更加符合链式编程风格,再定义一个静态方法吧

class WidgetChain {static Widget addNeighbor(Widget widget) {return widget;}
}

另外,再定义一个从数据到widget的映射扩展方法

extension ListExt<T> on List<T> {List<Widget> buildAllAsWidget(Widget Function(T) builder) {return this.map<Widget>((item) {return builder(item);}).toList();}}

现在,代码是这样的:

class Test extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Demo'),),body: ["amy", "billy"].buildAllAsWidget((name) =>WidgetChain.addNeighbor(Icon(Icons.phone)).addNeighbor(Text(name)).intoRow(crossAxisAlignment: CrossAxisAlignment.center,).intoContainer(color: Colors.white, padding: EdgeInsets.all(20),)).intoListView().intoOffstage(offstage: false).intoContainer());}
}

值得指出的是,扩展函数(无嵌套)跟构造函数(有嵌套)是可以混用的。上面的代码也可以写成这样(ContainerOffstage这2层改成了构造函数):

class Test extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Demo'),),body: Container(child: Offstage(offstage: false,child: ["amy", "billy"].buildAllAsWidget((name) =>WidgetChain.addNeighbor(Icon(Icons.phone)).addNeighbor(Text(name)).intoRow(crossAxisAlignment: CrossAxisAlignment.center,).intoContainer(color: Colors.white, padding: EdgeInsets.all(20),)).intoListView()),),);}
}

这样的扩展函数你想不想试试呢?我已经替大家封装好了常用Widget对应的into扩展函数,可以直接食用:

dependencies:widget_chain: ^0.1.0

导入:

import 'package:widget_chain/widget_chain.dart';

然后就可以起飞了!

Github源码地址: widget_chain 敬请star收藏

总结

本文介绍了Flutter中的嵌套地狱,并使用扩展函数的方式来解决flutter的嵌套地狱问题。

由于大篇幅的扩展函数调用会影响代码阅读体验,还是需要保留部分关键嵌套层级结构以使得布局的层级结构保持清晰,文中的扩展函数支持与构造函数混用,具体使用到什么程度,就看大家自己的选择了

原文链接
本文为阿里云原创内容,未经允许不得转载。

Flutter嵌套深?扩展函数了解一下相关推荐

  1. Flutter 嵌套深、刷新乱?少年,你怕是连Flutter的门槛都没摸到!

    引子: 鲁迅<摩罗诗力说> 人有读古国文化史者循代而下至于卷末必凄以有所觉如脱春温而入于秋肃勾萌绝朕枯槁在前吾无以名姑谓之萧条而止盖人文之留遗后世者最有力莫如心声古民神思接天然之宫冥契万有 ...

  2. Flutter Widget嵌套深,修改代码麻烦?

    Flutter Widget嵌套深,修改代码麻烦? 背景 解决方法 代码 修改 pubspec.yaml 以支持 Extension: Widget扩展代码: 实际使用: 背景 许多人初次接触Flut ...

  3. 盗梦空间科普札记之一:梦里乾坤嵌套深,醒来可知在哪层?

    盗梦空间科普札记之一:梦里乾坤嵌套深,醒来可知在哪层?   (唐常杰) (这是发在科学博客上一个系列博文的1/4,涉及到递归算法,第四篇涉及到图灵机停机问题,稍难一些) 上周五晚上,和几位中学生忘年交 ...

  4. Flutter优化之将小部件拆分为方法将影响渲染性能

    欢迎大家关注[跨平台开发那些事]公众号,定期推送跨平台开发技术实践. 在React Native跨平台开发框中,我们经常会看到,当界面组件层次嵌套深,组件交互涉及业务逻辑时,为了代码层次简洁.清晰,都 ...

  5. 初识Flutter中的Layer

    初识Flutter中的Layer 开篇 接触Flutter开发一段时间后发现自己对Flutter渲染流程重要的一环Layer的认知比较少,虽然Flutter对Widget的封装非常全面了开发者基本上只 ...

  6. 学 Flutter 不理解 Widget/Element/Render 三棵树?啥也不是!

    一.导语 Hi,大家好,这里是承香墨影! Flutter 是 Google 发布的跨平台 UI 框架,而其中与 UI 相关的,最重要的就是 Widget & Element & Ren ...

  7. 已经成功拿到了几个offer的我来告诉你,Android面试官问的一些问题,看完这一篇就没有拿不到的offer

    前言 我是2020年毕业于中南大学的计算机学院的,大家可以叫我小吴,我嘞毕业之后在华为实习了差不多一年多,一直都从事着Android开发. 然后2021年的时候因为我自己的一些原因打算离职到外面看看, ...

  8. mongodb jar包_MongoDB是什么?看完你就知道了!

    Java社区回复[520社区],领取资源500G惊喜 来源:http://suo.im/5e8a94 一.概述 1.MongoDB是什么?用一句话总结 MongoDB是一款为web应用程序和互联网基础 ...

  9. add-apt-repository:找不到命令_手把手教你使用nodejs编写cli(命令行)

    手把手教你使用nodejs编写cli(命令行) 前端日常开发中,会遇见各种各样的cli,比如一行命令帮你打包的webpack,一行命令帮你生成vue项目模板的vue-cli,还有创建react项目的c ...

最新文章

  1. 数据科学教程:R语言与DataFrame[2016版]
  2. 每天一道LeetCode-----只可能有'.'和'*'的字符串正则匹配
  3. 红外测距模块 51单片机_[51单片机] HC-SR04超声波测距仪
  4. ARMv8体系结构基础02:搭建实验环境
  5. C++奇特的递归模板式
  6. 无法打开包括文件: “SDKDDKVer.h”问题解决办法
  7. 天秀,Excel居然还可以制作二维码
  8. JS - 计算直角三角形的边长及角度
  9. Jupyter Nbextensions插件功能大全
  10. Idea,webStorm工具栏显示,添加快捷方式建文件
  11. 逻辑函数的两种标准形式
  12. Java基础之入门(一)
  13. 求菲波那切数列第n项
  14. 码云图床失效解决方案
  15. [flow] 1.Spyglass CDC
  16. BaiduFaceApi.dll: Can't load AMD 64-bit .dll on a IA 32-bit platform
  17. 斐讯N1刷无线打印服务器,N1刷op固件的小白步骤 - 斐讯无线路由器以及其它斐迅网络设备 - 恩山无线论坛 - Powered by Discuz!...
  18. 24位真彩和32位真彩
  19. mysql主要学什么_mysql学什么
  20. python pip 多个版本冲突问题 完美解决

热门文章

  1. 电脑反应慢卡怎么解决_电脑键盘失灵怎么解决
  2. java和网易我的世界有什么区别_网易我的世界手机版对比正版JAVA版我的世界有什么区别?...
  3. keras卷积处理rgb输入_CNN卷积神经网络模型搭建
  4. 2021计算机技术调剂,2021年华南师范大学计算机技术考研调剂信息
  5. Java有快速打好基础的方法?
  6. android+内存清理+代码,最新版本:Android一键式清理,内存清理功能的实现
  7. logger 参数列表过长_[源码级解析] 巧妙解决并深度分析Linux下rm命令提示参数列表过长的问题...
  8. 云教版认识计算机说课,【教学设计】第1册第1课《认识电脑交朋友》
  9. s8050三极管经典电路_曝光一个产品级的红外发射电路
  10. 高职计算机专业用什么软件,【计算机专业论文】高职计算机专业CAD软件教学思考(共4772字)...