flutter技巧.md

组件封装

封装 flutter 组件

当出现两个 变量 同时决定页面展示时,使用同一个函数封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var taskLs = [fakeTask, fakeSonTask, fakeSonSonTask, fakeSonTask2, fakeTask2];
TaskManifest? selectedManifest;

class TaskManager with ChangeNotifier {
List<Task> getTaskLs() {
return taskLs;
}

bool createTaskOrUpdateManifest({Task? task, TaskManifest? manifest}) {
if (manifest != null) {
selectedManifest = manifest;
notifyListeners();
} else if (task != null) {
taskLs.add(task);
notifyListeners();
} else {
print("unknown");
return false;
}
return true;
}

List<Task> getRootTaskLs() {
print("---: $selectedManifest ${selectedManifest?.name}");
var ls = <Task>[];

for (final task in getTaskLs()) {
if (task.manifestId == selectedManifest?.id) {
if (task.isRoot == true) {
ls.add(task);
}
}
}
getTaskLs().forEach((element) {
print("el: $element");}
);
return ls;
}
}

类型转换

cast

在Dart中,cast函数是一个非常有用的工具,它允许将一个集合的元素类型转换为另一个类型。这在处理dynamic类型或不同类型之间的转换时尤其有用,但需要谨慎使用以避免运行时错误。下面是使用cast函数的一些最佳实践:

1. 明确知道元素类型时使用

只有当确定集合中的所有元素都可以安全地被转换为目标类型时,才使用cast。这通常适用于从动态类型(如从数据库、外部数据源或第三方库中获取的数据)转换到具体类型时。

2. 避免不必要的类型转换

如果能通过设计来确保数据的类型安全,那么尽量避免使用cast。比如,使用泛型集合来存储和传递具体类型的数据,这样可以让Dart的静态类型检查帮助捕获类型错误。

3. 处理cast可能失败的情况

使用cast时,如果集合中有任何元素不能被转换为目标类型,都会抛出运行时异常。因此,使用cast时应该尽量确保转换的安全性,或者使用try-catch结构来处理可能的异常。

4. 优先使用类型安全的替代方案

在某些情况下,可以通过映射(.map())集合并手动转换每个元素来避免使用cast。这样做的好处是可以在转换每个元素时提供一个回退方案,比如:

1
2
var list = <dynamic>[1, 2, 3];
var intList = list.map((e) => e as int).toList();

这种方法比cast更加类型安全,因为可以控制转换的过程,并且在转换失败时捕获异常。

5. 谨慎使用cast改变集合的可变性

当对集合使用cast时,返回的是原始集合的一个视图,这意味着原始集合和转换后的集合在底层是相同的数据。对任一集合的修改都会影响另一个,但如果集合的类型不兼容,这种修改可能会引起问题。

6. 在混合类型数据流中使用

在处理混合类型的数据流时(比如一个列表包含了不同类型的对象),cast可以用来过滤出特定类型的对象集合,以便进行进一步处理。

1
2
3
4
5
6
7
8
List<Task> getTaskLs() {
// 直接返回Hive box中所有的Task对象
if (taskBox != null) {
return taskBox!.values.cast<Task>().toList();
} else {
return [];
}
}

as 类型转换

在Dart语言中,as关键字用于类型转换,将一个对象显式地转换为另一个类型。尽管这是一个强大的功能,但如果不当使用,可能会导致运行时错误。以下是使用as进行类型转换的一些最佳实践:

1. 当确定转换是安全的时使用

只在确信对象可以被安全地转换为目标类型时使用as。这通常是在通过某些方式(例如通过类型检查)已经验证了对象的类型后。

1
2
3
if (object is TargetType) {
final targetTypeObject = object as TargetType;
}

2. 优先使用类型测试运算符is

在尝试将一个对象转换为特定类型之前,先使用is运算符检查对象是否为目标类型。这可以避免在转换失败时抛出异常。

1
2
3
if (object is TargetType) {
// 安全地使用object作为TargetType
}

3. 避免不必要的as使用

如果Dart的类型推断能够正确识别对象的类型,就没有必要使用as进行显式转换。过度使用as可能会降低代码的可读性和维护性。

4. 在处理泛型时谨慎使用

当与泛型类型打交道时,特别是当类型参数在运行时可能变化时,使用as进行类型转换需要格外小心。在这种情况下,确保转换是安全的,或者考虑使用更安全的替代方案。

5. 处理as转换可能失败的情况

尽管通过is检查类型可以避免大多数问题,但在某些情况下,转换可能仍然失败。使用as时,考虑包含异常处理逻辑,特别是当处理外部数据或在对数据源不完全确定时。

1
2
3
4
5
try {
final targetTypeObject = object as TargetType;
} catch (e) {
// 处理转换失败的情况
}

6. 使用类型安全的集合操作

当从集合中获取元素时,如果确信集合中的所有元素都属于一个特定类型,使用as进行转换可以使代码更简洁。但是,仍然推荐使用is进行检查,或者使用泛型来保证类型安全。

7. 避免将as用作隐藏逻辑错误的手段

错误地使用as可能会隐藏潜在的逻辑错误,导致难以调试的运行时异常。在使用as之前,仔细考虑是否有其他方式(如改进程序逻辑或使用更合适的类型)来解决问题。

结论

as关键字是Dart中强大的类型转换工具,但应谨慎使用。始终确保转换的安全性,优先使用类型检查运算符is,并在适当的地方处理可能的异常,以编写更安全、更健壯的Dart代码。

_TaskContainerState 状态延迟刷新问题

背景描述

TaskContainer 组件下的 _TaskContainerState build 定义为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Widget build(BuildContext context) {
// 初始状态 只有一个 SingleTaskP
// 展开状态 有多个 SingleTaskP
List<Widget> loadSonTaskContainerLs(Set set) {
if (_isFolded || !_haveSon) {
return [const SizedBox()];
} else {
// 获取所有下一层级的 task
List<TaskContainer> ls = <TaskContainer>[];
for (final id in set) {
ls.add(TaskContainer(indentTime: increaseIndentTime(widget.indentTime), task: taskDBRead.getTaskById(id)));
}
return ls;
}
}

return Consumer<TaskUpdateModel>(
builder: (context, taskUpdateModel, child) {
final set = taskUpdateModel.getSonTaskIdLsFromParentId(widget.task!.id);

_haveSon = set.isEmpty ? false : true;
print("set: ${set.length} , haveson: $_haveSon, isFold: $_isFolded");
//print("foldIconButton : $foldIconButton");
return Row(
crossAxisAlignment: CrossAxisAlignment.start, // 确保Row的子项从顶部开始对齐
children: [
Container(
width: 48, // 或其他适当的值,确保足够容纳IconButton
child: _haveSon ? Container(
margin: const EdgeInsets.all(12),
child: IconButton( // 这里之前写在 return 之前
onPressed: () {
setState(() {
_isFolded = !_isFolded;
});
},
icon: Icon(
_isFolded
? Icons.arrow_drop_down_rounded
: Icons.arrow_right_rounded,
size: 24,
),
))
: Container(),
),
Expanded( // 使用Expanded来填充剩余空间
child: Column(
children: [
SingleTaskP(widget.indentTime, widget.task),
if (!_isFolded && _haveSon) ...loadSonTaskContainerLs(set),
],
),
),
],
);
}
);
}

flutter技巧.md
https://abrance.github.io/2024/02/18/mdstorage/domain/dart/flutter技巧/
Author
xiaoy
Posted on
February 18, 2024
Licensed under