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 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()

    );

  }

} 

值得指出的是,扩展函数(无嵌套)跟构造函数(有嵌套)是可以混用的。上面的代码也可以写成这样(ContainerOffstage这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源码地址: [widget_chain](

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

[](

)总结


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

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

[](

)2019-12-30编辑后补充说明


本文初次发布时包含了使用扩展函数提升编码体验的相关内容。因为我之前编写flutter代码时添加父容器的方式为:

  • 先剪切要添加父容器的widget代码

  • 在剪切处编写父容器及其属性

  • 在父容器节点下添加child,粘贴刚才剪切的内容作为child的值