Flutter 中使用条件表达式的奇技淫巧
在工作中我们可能会写出以下代码:
class MyExampleState extends State<MyExample> {
bool loading = false;
Data? data;
@override
Widget build(BuildContext context) {
return Scaffold(
body: loading
? const CircularProgressIndicator()
: (data == null ? const Text('error_area') : const Text('content_area')),
);
}
}
这样的三元表达式看起来很丑,如果条件再多点,就很难维护了。
如果你对 Dart 语言了解得比较深的话,应该知道在 Dart 的集合中是可以使用 if、for 循环语法的。这个特性是 Dart 专为 Flutter 定制的,Dart 一直声称自己是一个为全平台构建快速应用的客户端优化的编程语言。后面我们有机会可以来仔细盘点一下 Dart 都为客户端开发(主要是 Flutter)做了哪些优化。
这种 if 和 for 语法标准的叫法是集合中的 if 和集合中的 for。它们是 if 和 for 语句的阉割版。比如两者都没法使用花括号 {} 来把语句体包起来。后者你没法使用 break、continue。下面的示例几乎展现了它们的全部语法:
List<Widget> widgets = [
if (loading)
const CircularProgressIndicator()
else if (data == null)
const Text('error_area')
else
const Text('content_area'),
for (int i = 0; i < 10; i++) Text('item_$i')
];
集合中的 if 和集合中的 for相比第一个例子中的三元表达式要好多了,条件再多也会很清晰。很遗憾的是它只能在集合中使用,而在大量的场景下,方法的形参接收的是单一变量而非集合。能不能针对这类方法也用上集合中的 if 和集合中的 for呢?答案是肯定的!看代码:
class MyExampleState extends State<MyExample> {
bool loading = false;
Data? data;
@override
Widget build(BuildContext context) {
return Scaffold(
body: [
if (loading)
const CircularProgressIndicator()
else if (data == null)
const Text('error_area')
else
const Text('content_area')
][0],
);
}
}
尽管 body 只接收一个 Widget,但我们构造了一个集合,并将集合的第 0 个元素赋值给 body,而条件表达式的作用就是去生成第 0 个元素。是不是很 nice?这种用法可以扩展到所有只接收单一参数而非集合的地方,比如 color、width、height 的设置。
除了集合中的 if 和集合中的 for以外,2.3 版本的 Dart 还引入了 扩展操作符(...)和 空感知扩展操作符(...?),它们提供了一种将多个元素插入集合的简洁方法,比如:
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
以往我们封装的 buildXXX 方法只返回一个 Widget,有了这个特性以后,我们可以返回一个 List。并使用 ... 操作符将其添加到 children 列表中:
class MyExampleState extends State<MyExample> {
bool loading = false;
Data? data;
List<Widget> buildTitleArea() {
return [];
}
Widget buildBody() {
return const Text('body_area');
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ConstraintLayout(
children: [
...buildTitleArea(),
buildBody(),
],
),
);
}
}
结合集合中的 if,我们可以实现对多个 Widget 的显示、隐藏。
以上所讲的这些,其实在 Flutter ConstraintLayout 中有更好的办法,因为它创造性的支持了开放式语法(Open Grammar),你可以使用 Dart 的任何语法来组织子元素:
class OpenGrammarExample extends StatelessWidget {
const OpenGrammarExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: ConstraintLayout().open(() {
if (DateTime.now().millisecond % 2 == 0) {
Container(
color: Colors.red,
).applyConstraint(
size: 200,
centerTo: parent,
);
} else {
Container(
color: Colors.yellow,
).applyConstraint(
size: 200,
centerTo: parent,
);
}
for (int i = 0; i < 5; i++) {
Row().open(() {
for (int j = 0; j < 10; j++) {
Text("$i x $j").enter();
const SizedBox(
width: 20,
).enter();
}
}).applyConstraint(
height: 100,
left: parent.left.margin(100),
top: i == 0 ? parent.top : sId(-1).bottom,
);
}
int i = 0;
while (i < 100) {
Text("$i").applyConstraint(
left: parent.left,
top: i == 0 ? parent.top : sId(-1).bottom,
);
i++;
}
}),
);
}
}
是不是很 nice?
文章转自公众号:FlutterFirst