(二)Flutter学习之Dart展开操做符 和 Control Flow Collections

展开操做符(spread operators)

展开操做符 ... 可以把 list、set、map 字面量里的元素插入到一个集合中。一个对象是否可用于展开操做符取决因而否继承了Iterable,Map集合例外,对 map 进行展开操做 其实是 调用了 Mapentries.iterator()android

在实际开发中,咱们可能须要建立新的集合,集合的元素一般依赖另外一个已经存在集合,而后再次基础上再添加写新的元素,如:git

var args = testArgs.toList()
  ..add('--packages=${PackageMap.globalPackagesPath}')
  ..add('-rexpanded')
  ..addAll(filePaths);
复制代码

上面的代码案例,来自官网。其中 toList() 函数会建立一个新的集合,集合里包含了 testArgs 全部元素 而后经过 级联操做符 实现链式调用来简化代码,最后经过 addAll 添加另外一个集合的全部元素github

可是这种写法依然很是笨重,下面来看下如何经过 展开操做符简化代码:json

var args = [
  ...testArgs, // 经过展开操做符将testArgs集合解包而后将里面的元素逐个放进args里
  '--packages=${PackageMap.globalPackagesPath}',
  '-rexpanded',
  ...filePaths // 经过展开操做符将filePaths集合解包而后将里面的元素逐个放进args里
];
复制代码

Flutter 中是使用 声明式UI(declarative style), Android/iOS 是使用 命令式UI(imperative style)bash

什么是 声明式UI? 在 Flutter 中,若是 Widget 发生变化经过 setState 函数触发,而后 Flutter 会构建新的 Widget 实例和 Widget 子树 而 命令式UI 是拿到原来的 Widget 实例,而后调用该实例的方法来触发变化,而不是建立新的 Widgetfrontend

他们之间的代码风格区别以下:函数

// 声明式UI代码风格
return ViewB(
  color: red,
  child: ViewC(...),
)

// 命令式UI代码风格
b.setColor(red)
b.clearChildren()
ViewC c3 = new ViewC(...)
b.add(c3)
复制代码

展开操做符 在 Flutter 这种声明式 UI 中应用很是普遍,例如咱们构建一个 ListView:oop

Widget build(BuildContext context) {
  return CupertinoPageScaffold(
    child: ListView(
      children: [ //children 就是一个List
        Tab2Header(),
        // buildTab2Conversation()返回值也是一个List
        ...buildTab2Conversation(), 
      ],
    ),
  );
}
复制代码

上面的例子是在 List 中和使用展开操做符,在 Map 集合中也是同样的,例如将 queryParams 和 formParams 合并产生一个新的 Map 集合学习

var map = {
    ...options.queryParameters,
    ...options.data
};
复制代码

在 Set 中的使用展开操做符也同样的,如:ui

var items = [2, 3, 4];
var set = { 1, 2, ...items };
复制代码

可空的展开操做符(Null-aware spread)

上面的咱们讲到的展开操做符,若是被展开的对象是 null,那么会抛出运行时异常

var oops = null;
//NoSuchMethodError: The getter 'iterator' was called on null
var list = [...oops];
复制代码

若是被展开的对象可能为 null,须要在展开操做符后面加上 ? 号 (...?):

var oops = null;
var list = [...?oops];
复制代码

展开操做符语义分析

List 中使用展开操做符分析

var list  = [elem_1 ... elem_n]
1. 首先会建立新的集合,用于保存集合字面量里的元素
2. 遍历字面量里的全部元素
    1)把元素表达式(展开操做符)赋值给value遍历
    2)若是遍历的元素是展开操做符
        a)若是使用的是可空的展开操做符,而且 value 是 null,直接 continue
        b)将 value.iterator 赋值给 iterator 变量
        c)而后遍历 iterator,将里面的元素追加到上面新建的集合里
    3)若是遍历的元素不是展开操做符,直接将元素添加到集合中
3. 就这样,集合字面量的元素所有在上面新建的集合里了
复制代码

像其余的集合如 Set、Map 也是相似的机制

控制流集合(Control Flow Collections)

控制流集合 就是在构建集合字面量的时候能够使用 if、for 控制流语句,简称 collection-if/for

上面咱们提到 Flutter 是声明式 UI,例如:

Widget build(BuildContext context) {
  return Row(
    children: [ // 集合字面量
      IconButton(icon: Icon(Icons.menu)),
      Expanded(child: title),
      IconButton(icon: Icon(Icons.search)),
    ],
  );
}
复制代码

若是咱们想在上面的代码的基础上加一些判断,例如只在 Android 平台展现搜索按钮(Icons.search),咱们可能会这么写:

Widget build(BuildContext context) {
  // 构建button集合
  var buttons = <Widget>[
      IconButton(icon: Icon(Icons.menu)),
      Expanded(child: title),
  ];
  // 若是是 android 平台,添加搜索按钮
  if (isAndroid) {
    buttons.add(IconButton(icon: Icon(Icons.search)));
  }

  return Row(
    children: buttons,
  );
}
复制代码

有了 Control Flow Collection,咱们能够这样类简化代码:

Widget build(BuildContext context) {
  return Row(
    children: [
      IconButton(icon: Icon(Icons.menu)),
      Expanded(child: title),
      // 若是是 android 平台,添加搜索按钮
      if (isAndroid) IconButton(icon: Icon(Icons.search)),
    ]
  );
}
复制代码

除了使用 if,还能够使用 else,例如在 Android 平台展现搜索按钮, 其余平台展现关于按钮 :

Widget build(BuildContext context) {
  return Row(
    children: [
      IconButton(icon: Icon(Icons.menu)),
      Expanded(child: title),
      if (isAndroid)
        IconButton(icon: Icon(Icons.search))
      else
        IconButton(icon: Icon(Icons.about)),
    ]
  );
}
复制代码

再好比咱们须要根据一个集合而后生成另外一个集合,可能会这样写:

var command = [
  engineDartPath,
  frontendServer,
];
for (var root in fileSystemRoots) {
  command.add('--filesystem-root=$root');
}
for (var entryPointsJson in entryPointsJsonFiles) {
  if (fileExists("$entryPointsJson.json")) {
    command.add(entryPointsJson);
  }
}
command.add(mainPath);
复制代码

有了 Control Flow Collection,咱们能够这样写,可读性更强:

var command = [
  engineDartPath,
  frontendServer,
  for (var root in fileSystemRoots) '--filesystem-root=$root',
  for (var entryPointsJson in entryPointsJsonFiles)
    if (fileExists("$entryPointsJson.json")) entryPointsJson,
  mainPath
];
复制代码

还能够在构建集合字面量的时候 for 循环计算:

var integers = [for (var i = 1; i < 5; i++) i]; // [1, 2, 3, 4]
var squares = [for (var n in integers) n * n]; // [1, 4, 9, 16]
复制代码

构建 Map 字面量的时候,咱们可能会这样写:

Map<String, WidgetBuilder>.fromIterable(
  kAllGalleryDemos,
  key: (demo) => '${demo.routeName}',
  value: (demo) => demo.buildRoute,
);
复制代码

collection-for 可让咱们的代码简化成以下所示:

return {
  for (var demo in kAllGalleryDemos)
    '${demo.routeName}': demo.buildRoute,
};
复制代码

collection if-for 还能够组合,例如 for 循环嵌套,for-if 嵌套:

//for-for
[for (var x in hor) for (var y in vert) Point(x, y)]

//for-if
[for (var i in integers) if (i.isEven) i * i]
复制代码

总结

Dart 的展开操做符 (Null-aware spread、Non-null-ware spread) 和 Control flow collections(collection if/for)

让咱们编写的代码的时候代码的可读性更高

特别是编写 Flutter UI 的时候更符合 declarative style 代码风格

从中能够看出 Dart 代码的表现力仍是很强的

Reference

关于 Dart 展开操做符 和 Control Flow Collections 就讲到这里, 更多的关于 Android 学习资料能够查看个人GitHub: github.com/chiclaim/An…

flutter.dev/docs/get-st… github.com/dart-lang/l… github.com/dart-lang/l…


联系我

下面是个人公众号,干货文章不错过,有须要的能够关注下,有任何问题能够联系我:

公众号:  chiclaim
相关文章
相关标签/搜索