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

背景

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

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

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

这段代码演示了什么叫作:编程

嵌套地狱架构

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方法后,嵌套层级获得了明显的改善:app

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),
        ],
      ),
    );
  }
}

还能不能继续优化呢?less

自定义扩展函数

举个例子:ide

想要给下面这段代码中的第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??
        ],
      ),
    );
  }

此时,我心里但愿能够这样写:oop

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

https://medium.com/dartlang/d...

实际上从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...

就算你如今用不上,也能够先star收藏之,从此若是有人向你吐槽flutter嵌套深,除了叫他拆分封装以外,还能够把widget_chain甩给他 :D

总结

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

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

补充说明

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

由于我以前编写flutter代码时添加父容器的方式为:

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

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

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

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

但愿你们喜欢!

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

总之也是在这里帮助你们学习提高进阶,也节省你们在网上搜索资料的时间来学习,也能够分享给身边好友一块儿学习

若是你有须要的话,能够点赞+评论关注我加Vx:15388039515(备注思否,须要资料)

相关文章
相关标签/搜索