Flutter嵌套深?扩展函数了解一下

简介: 嵌套层级深的问题让众多刚接触Flutter的同学感到困扰,它不仅是看起来让人感到不适,还非常影响编码体验。 大佬们会告诉你应该拆分自己的嵌套代码(自定义widget或者抽取build方法)来减少嵌套层级。

背景

嵌套层级深的问题让众多刚接触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"),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

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

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

添加marginTop1

显然,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构造方法一致,于是,我们就可以这样写了:

添加marginTop1

除了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 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中的嵌套地狱,并使用扩展函数的方式来解决flutter的嵌套地狱问题。

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

目录
相关文章
|
Dart
[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数
函数(Function)也被称为方法(Method)。其最直观的理解就是数据中的函数,比如y=f(x),在编程中,f对输入x进行处理,返回结果y,就是一个函数......
1034 0
[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数
|
19天前
|
Dart 开发者
flutter_鸿蒙next_Dart基础③函数
本文深入探讨了 Dart 编程语言中的函数概念,包括函数的基本定义、参数、返回值以及高级使用场景。通过 Flutter 和鸿蒙的实际案例,介绍了函数的定义、调用、可选参数、命名参数、匿名函数和高阶函数等内容,帮助读者更好地理解和应用 Dart 函数。
126 1
|
存储 缓存 编解码
Flutter笔记:图片的 precacheImage 函数
precacheImage 是 Flutter 中提供的一个函数。顾名思义,pre预先、cache缓存、Image图片,很容易看出 precacheImage 函数用于预加载图像并将其缓存到图像缓存中。这个函数是非常有用的,因为它可以在用户实际需要显示图像之前,提前将图像加载到内存中,以提高图像的加载速度。这对于提供更好的用户体验和性能非常重要,特别是当项目中有多个图像需要显示时。
209 0
|
自然语言处理 Dart 算法
[Flutter]足够入门的Dart语言系列之函数:匿名函数、作用域闭包、类型别名和内联函数类型
函数在Dart中是对象,因此,可以将函数作为参数传递给另一个函数、作为一个'值'赋值给一个变量。下面主要介绍匿名函数、内联函数类型的使用,以及了解下作用域和闭包...
767 0
[Flutter]足够入门的Dart语言系列之函数:匿名函数、作用域闭包、类型别名和内联函数类型
|
Dart 开发者
【Flutter】Dart 函数 ( 函数构成 | 私有函数 | 匿名函数 | 函数总结 )(二)
【Flutter】Dart 函数 ( 函数构成 | 私有函数 | 匿名函数 | 函数总结 )(二)
163 0
|
Dart
【Flutter】Dart 函数 ( 函数构成 | 私有函数 | 匿名函数 | 函数总结 )(一)
【Flutter】Dart 函数 ( 函数构成 | 私有函数 | 匿名函数 | 函数总结 )(一)
225 0
|
Dart 索引
【Flutter】Dart 数据类型 List 集合类型 ( 定义集合 | 初始化 | 泛型用法 | 初始化后添加元素 | 集合生成函数 | 集合遍历 )
【Flutter】Dart 数据类型 List 集合类型 ( 定义集合 | 初始化 | 泛型用法 | 初始化后添加元素 | 集合生成函数 | 集合遍历 )
637 0
|
移动开发
Flutter 无状态小部件中启动时调用函数
本文主要介绍如何在 Flutter 无状态小部件中启动时调用函数 有没有想过如何从无状态小部件在 Flutter 启动时调用异步函数? 移动开发中最常见的场景之一是在显示新视图时调用异步函数。在 Flutter 中,这可以使用有状态的小部件并在initState函数中调用您的代码来完成。
215 0
|
1月前
|
Android开发 iOS开发 容器
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
|
1月前
|
开发者
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter混合开发支持两种模式:1) 基于har包,便于主项目开发者无需关心Flutter细节,但不支持热重载;2) 基于源码依赖,利于代码维护与热重载,需配置Flutter环境。项目结构包括AppScope、flutter_module等目录,适用于不同开发需求。
74 3