玖叶教程网

前端编程开发入门

Flutter嵌套地狱!看完此文你就掌握了解决方案

背景

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

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

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

这段代码演示了什么叫做:

嵌套地狱

class?Test?extends?StatelessWidget?{
??@override
??Widget?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"),
??????????????????],
????????????????),
??????????????),
????????????],
??????????),
????????),
??????),
????);
??}
}
Thread?thread?=?new?Thread(new?Runnable()?{
????@Override
????public?void?run()?{
????????Looper.prepare();
????????Toast.makeText(MainActivity.this,"子线程弹出Toast",Toast.LENGTH_SHORT).show();
????????Looper.loop();
????}
});
thread.start();

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

class?Test?extends?StatelessWidget?{
??@override
??Widget?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属性

??@override
??Widget?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)

https://medium.com/dartlang/dart-2-7-a3710ec54e97

实际上从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?{
??@override
??Widget?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?WidgetChain?{
??static?Widget?addNeighbor(Widget?widget)?{
????return?widget;
??}
}

现在,代码是这样的:

class?Test?extends?StatelessWidget?{
??@override
??Widget?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()
????);
??}
}

值得指出的是,扩展函数(无嵌套)跟构造函数(有嵌套)是可以混用的。

上面的代码也可以写成这样(Container和Offstage这2层改成了构造函数):

class?Test?extends?StatelessWidget?{
??@override
??Widget?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源码地址:

https://github.com/luckybilly/widget_chain

就算你现在用不上,也可以先star收藏之,今后如果有人向你吐槽flutter嵌套深,除了叫他拆分封装之外,还可以把widget_chain甩给他 :D

总结

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

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

补充说明

本文初次发布时包含了使用扩展函数提升编码体验的相关内容。

因为我之前编写flutter代码时添加父容器的方式为:

  • 先剪切要添加父容器的widget代码
  • 在剪切处编写父容器及其属性
  • 在父容器节点下添加child,粘贴刚才剪切的内容作为child的值

后来经大佬告知我:IDE中其实提供了添加父容器的快捷键(光标定位在Widget上,然后Alt + Enter,选择需要添加的父容器即可),非常方便。

之前我确实不知道,特别感谢大佬!

于是,重新编辑了本文,重点介绍如何使用扩展函数以不一样的编码风格来解决flutter嵌套层级过深的问题。

希望大家喜欢!

在这我也分享一份自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

如果你有需要的话,可以点赞+评论+转发关注我,然后私信我【进阶】我发给你

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言