使用空安全进行2022年更新
我的原始代码示例比必要的更冗长。使用Dart的factory
构造函数可以用较少的代码完成此操作。同时,这也已更新为使用空安全和Hive而非GetStorage。
首先,添加一个toMap
方法,将对象转换为Map
,然后添加一个fromMap
构造函数,从存储中保存的Map
返回一个Task
对象。
class Task {
final String name;
final String description;
Task({required this.name, required this.description});
Map<String, dynamic> toMap() {
return {'name': this.name, 'description': this.description};
}
factory Task.fromMap(Map map) {
return Task(
name: map['name'],
description: map['description'],
);
}
String toString() {
return 'name: $name description: $description';
}
}
更新的演示页面
class StorageDemo extends StatefulWidget {
@override
_StorageDemoState createState() => _StorageDemoState();
}
class _StorageDemoState extends State<StorageDemo> {
List<Task> _tasks = [];
final box = Hive.box('taskBox');
List storageList = [];
void addAndStoreTask(Task task) {
_tasks.add(task);
storageList.add(task.toMap());
box.put('tasks', storageList);
}
void restoreTasks() {
storageList = box.get('tasks') ?? [];
for (final map in storageList) {
_tasks
.add(Task.fromMap(map));
}
}
void printTasks() {
for (final task in _tasks) {
log(task.toString());
}
}
void clearTasks() {
_tasks.clear();
storageList.clear();
box.clear();
}
@override
void initState() {
super.initState();
restoreTasks();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Container(),
),
TextButton(
onPressed: () {
final task =
Task(description: 'test description', name: 'test name');
addAndStoreTask(task);
},
child: Text('Add Task'),
),
TextButton(
onPressed: () {
printTasks();
},
child: Text('Print Storage'),
),
TextButton(
onPressed: () {
clearTasks();
},
child: Text('Clear Tasks'),
),
],
),
);
}
}
更新存储初始化
void main() async {
await Hive.initFlutter();
await Hive.openBox('taskBox');
runApp(MyApp());
}
原始答案
一般来说,一旦您想要存储除了基本类型(例如 String
int
等)之外的任何内容,情况就会变得更加复杂,因为它们必须转换为任何存储解决方案都可以读取的东西。
所以尽管 Tasks
是一个只有几个字符串的基本对象,但是 SharedPreferences 或其他任何东西都不知道 Task
是什么或该怎么处理它。
我建议一般来说阅读有关 JSON 序列化的文章,因为无论如何您都需要了解它。 这是一个好的开始,这是另一篇好的文章。
尽管如此,也可以通过将任务转换为 Map
(这正是 json 序列化所做的)并将其存储到映射列表中来完成,而不使用 json。我将向您展示手动执行此操作而不使用 json 的示例。但是再次强调,花些时间学习它对您非常有利。
此示例将使用 Get Storage,它类似于 SharedPreferences,但更容易使用,因为您不需要为不同的数据类型编写不同的方法,只需要使用 read
和 write
即可。
我不知道您在应用程序中如何添加任务,但这只是存储 Task
对象列表的基本示例。任何不涉及在线存储的解决方案都需要在本地进行存储,并在应用程序启动时从存储中检索。
所以假设这里是您的 Task
对象。
class Task {
final String name;
final String description;
Task({this.name, this.description});
}
将此代码放在主方法中,在运行应用程序之前。
await GetStorage.init()
你需要在你的主函数中添加
async
,如果你不熟悉它的工作原理,它看起来像这样。
void main() async {
await GetStorage.init();
runApp(MyApp());
}
通常情况下,我不会在有状态的小部件中执行所有这些逻辑,而是实现一种状态管理解决方案并将其放在 UI 之外的一个类中,但这是完全不同的讨论。我也建议您查看 GetX、Riverpod 或 Provider,并阅读它们的介绍,并选择其中最容易学习的一个。对于简单和功能而言,我推荐 GetX。
但是,由于您刚开始学习,我将忽略此部分内容,暂时将所有这些函数放在 UI 页面中。
另外,不仅可以在应用关闭时存储,还可以在列表更改时随时存储,这样更容易。
这里有一个页面,带有一些按钮,可添加、清除和打印存储,以便在应用重新启动后查看列表中确切的内容。
如果您理解这里正在发生的事情,那么您应该能够在您的应用程序中做到这一点,或者学习 JSON 并以那种方式完成它。无论哪种方法,您都需要了解如何使用任何可用解决方案中的 Maps 和本地存储。
class StorageDemo extends StatefulWidget {
@override
_StorageDemoState createState() => _StorageDemoState();
}
class _StorageDemoState extends State<StorageDemo> {
List<Task> _tasks = [];
final box = GetStorage();
List storageList = [];
void addAndStoreTask(Task task) {
_tasks.add(task);
final storageMap = {};
final index = _tasks.length;
final nameKey = 'name$index';
final descriptionKey = 'description$index';
storageMap[nameKey] = task.name;
storageMap[descriptionKey] = task.description;
storageList.add(storageMap);
box.write('tasks', storageList);
}
void restoreTasks() {
storageList = box.read('tasks');
String nameKey, descriptionKey;
for (int i = 0; i < storageList.length; i++) {
final map = storageList[i];
final index = i + 1;
nameKey = 'name$index';
descriptionKey = 'description$index';
final task = Task(name: map[nameKey], description: map[descriptionKey]);
_tasks.add(task);
}
}
void printTasks() {
for (int i = 0; i < _tasks.length; i++) {
debugPrint(
'Task ${i + 1} name ${_tasks[i].name} description: ${_tasks[i].description}');
}
}
void clearTasks() {
_tasks.clear();
storageList.clear();
box.erase();
}
@override
void initState() {
super.initState();
restoreTasks();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Container(),
),
TextButton(
onPressed: () {
final task =
Task(description: 'test description', name: 'test name');
addAndStoreTask(task);
},
child: Text('Add Task'),
),
TextButton(
onPressed: () {
printTasks();
},
child: Text('Print Storage'),
),
TextButton(
onPressed: () {
clearTasks();
},
child: Text('Clear Tasks'),
),
],
),
);
}
}