Fultter
学习日志
基础概念的笔记大多摘自第二版序 | 《Flutter实战·第二版》 为什么需要再copy一遍?
- 自己用心cv的笔记才能记得住🐶
- 遇到一些疑惑可以手敲代码或查阅资料后进行标注
- 后续开发时在自己的笔记中查找要更快一些
创建一个基本实例
基于项目根目录的 lib/main.dart
:
import 'package:flutter/material.dart'; // 1. 引入控件
void main(List<String> args) { // 2.程序入口
runApp(
MaterialApp( // 应用
home: Home() // 主页
)
); // 启动应用
}
class Home extends StatelessWidget {
// demo1 文本控件
Widget build(BuildContext context) {
return const Text('Hello World',
style: TextStyle(
fontSize: 30.0,
color: Colors.blue,
)
);
}
}
- Material (opens new window)是一种标准的移动端和web端的视觉设计语言, Flutter 默认提供了一套丰富的 Material 风格的UI组件。
- Flutter 应用中 main 函数为应用程序的入口。main 函数中调用了runApp 方法,它的功能是启动Flutter应用。runApp它接受一个 Widget参数,在本示例中它是一个MaterialApp对象,是 Flutter 应用的根组件。
重点概念
StatefulBuilder
和Builder
- 使用
Builder
来获取一个合适的上下文,特别是在需要一个特定的祖先widget的上下文时。 - 使用
StatefulBuilder
来在无状态的widget中引入局部状态管理,特别是在对话框或临时UI结构中,这样你就不需要将整个widget转换为StatefulWidget
。
// `Builder`主要用于获得正确的上下文,特别是当你需要的上下文与当前上下文不同时,比如在`Scaffold`中使用`context`来显示`SnackBar`时
// `Builder`提供了一个新的上下文,用于`ScaffoldMessenger.of(context)`来找到最近的`Scaffold`祖先
Builder(
builder: (BuildContext context) {
return ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Hello from Builder'),
));
},
child: Text('Show SnackBar'),
);
},
)
// StatefulBuilder 可以在一个无状态的widget中引入局部的状态管理。这对于在无状态的对话框或底部表单中添加状态非常有用,而不必将整个widget转换为`StatefulWidget`。
// `StatefulBuilder`提供了一个`setState`函数,你可以使用它来更新局部状态(在这个例子中是复选框的选中状态),并且只重建包含在`StatefulBuilder`中的widget,而不是整个对话框
showDialog(
context: context,
builder: (BuildContext context) {
bool isChecked = false;
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text('StatefulBuilder Example'),
content: CheckboxListTile(
value: isChecked,
title: Text('Check me!'),
onChanged: (bool? value) {
setState(() {
isChecked = value!;
});
},
),
actions: <Widget>[
TextButton(
child: Text('OK'),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
},
);
flutter bloc好还是mvvm好
Flutter BLoC 是一种将业务逻辑从用户界面代码中分离出来的方法,并使用Streams和RxDart库进行数据流管理。它很适合用于处理复杂的业务逻辑,并在多个地方重用代码。
MVVM是一种架构模式,其中“Model”代表数据模型,“View”代表用户界面,而“View Model”则是将数据模型与用户界面分离的代码层。它很适合用于简单的业务逻辑,并且可以在视图和模型间提供双向绑定。
总的来说,如果您需要处理复杂的业务逻辑,则可以使用Flutter BLoC。如果您需要简单的业务逻辑并希望视图和模型之间有双向绑定,则可以使用MVVM。
常见需求QA
撑满宽度,就像div那样
- 将
width
设置为double.infinity
可以让一个widget尝试填充其父widget提供的最大宽度:body: Container(color: Colors.blue, width: double.infinity, height: 50)
width
设置为MediaQuery.of(context).size.width
Expanded
控件包裹目标控件
Xcode模拟器失败Unable to boot the Simulator.
Open Xcode -> Preferences -> Locations -> Derived Data (Click the little arrow) -> Delete the folder. 打开该目录删除,通过xcode启动模拟器后,再用vscode连接模拟器
this.$forceUpdate()
在调用setState
方法后StatefulWidget
就会重新build,那setState
方法做了什么呢?我们能不能从中找到方法?顺着这个思路,我们就得看一下setState
的核心源码:
void setState(VoidCallback fn) {
... //省略无关代码
_element.markNeedsBuild();
}
markNeedsBuild
是 Flutter 框架内部使用的一个方法,用于标记一个 Widget
需要重建(重新构建)。当一个 widget 的状态改变时(通常是在状态管理中,如使用 setState
方法后),Flutter 会将该 widget 标记为 "需要重建"。在下一个动画帧时,Flutter 会重建所有被标记的 widget,以确保它们反映最新的状态:
// 在StateLessWidget中的AlertDialog中添加需要更新UI状态的Checkbox (该方法并非最优,推荐使用StatefulBuilder)
AlertDialog(
title: Text("提示"),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("您确定要删除当前文件吗?"),
Row(
children: <Widget>[
Text("同时删除子目录?"),
Checkbox( // 依然使用Checkbox组件
value: _withTree,
onChanged: (bool value) {
// 此时context为对话框UI的根Element,我们
// 直接将对话框UI对应的Element标记为dirty
(context as Element).markNeedsBuild();
_withTree = !_withTree;
},
),
],
),
],
)
正则中的r前缀
在Dart语言中,正则表达式由RegExp
类表示。当你看到代码中的r
前缀,它表示这是一个原始字符串(raw string)。在Dart的原始字符串中,除了\
和$
之外,大多数特殊字符和转义序列都被忽略,这使得编写复杂的正则表达式更加容易,因为你不需要对反斜杠进行转义。
例如,考虑以下正则表达式:
final RegExp regex = new RegExp(r'^1[3-9]\d{9}$');
在这个例子中:
^
表示匹配字符串的开始。1
表示匹配数字1。[3-9]
是一个字符集,表示匹配任何在3到9之间的单个数字。\d
是一个转义序列,表示任意一个数字,等同于[0-9]
。{9}
表示前面的元素(在这个情况下是数字)恰好重复9次。$
表示匹配字符串的结束。 由于使用了r
前缀,所以在正则表达式内部的反斜杠\
没有特殊含义,因此不需要使用额外的反斜杠来进行转义。如果没有使用r
前缀,那么你必须对正则表达式中的每个反斜杠进行转义,例如使用\\d
来表示一个数字。 使用原始字符串(r'...'
)在处理正则表达式时非常有用,特别是当正则表达式包含许多特殊字符或转义序列时。