Flutter - 每次关闭应用程序时存储对象列表的最佳方式? [英] Flutter - Best way to store a List of Objects every time I close the application?

查看:61
本文介绍了Flutter - 每次关闭应用程序时存储对象列表的最佳方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

情况:
我对 Flutter 和移动开发很陌生,因此我对 Dart 了解不多;我已经从有类似问题的人那里阅读了一些解决方案,但没有设法将这些解决方案应用于我自己的事情.

问题:
我有一个有 2 个对象列表的待办事项应用,我想在用户重新打开应用时存储这些列表.

我知道它的简单内容,但由于缺乏经验,我觉得我正急于解决这个问题......所以我决定来寻求一些帮助.

我尝试过的:
我遇到过针对此问题的不同解决方案,并且对于这种情况,所有解决方案似乎都过于复杂(与我在将列表保存到存档时所做的相比),包括:将列表编码为地图并转换为字符串之前使用 SharedPreferences,使用 SQlite 数据库(我遇到的每个教程都让我觉得我会使用战车杀死一只蚂蚁,我对 firebase 也是如此).

问题的结构:
带有 ListView.builder 的 ToDo 屏幕调用 2 个数组:正在进行的任务和已完成的任务,当用户进行更改时,我想将每个任务写入手机.IDK 如果我应该只尝试通过调用一些包方法从它们所属的类中保存这些数组,或者如果可能的话我应该尝试存储整个应用程序.

结论:
有没有办法以简单的方式解决这个问题,或者我应该为此使用像 firebase 这样强大的东西?即使我不习惯使用 firestore,所以我一无所知,不知道如何应用这样的东西来保存数据.

我的列表的结构:

List_任务 = [任务(名称:做某事",描述:努力做到快!!!",),];列表<任务>_doneTasks = [任务(名称:任务标记为完成",描述:某事",),];

解决方案

所以一般来说,一旦你想存储原始类型以外的任何东西,即.String int 等等...事情变得有点复杂,因为它们必须转换为任何存储解决方案都可以读取的内容.

因此,尽管 Tasks 是一个带有几个字符串的基本对象,但 SharedPreferences 或其他任何东西都不知道 Task 是什么或如何处理它.

我建议一般阅读有关 json 序列化的内容,因为无论哪种方式您都需要了解它.这是一个很好的起点这是另一篇关于它的好文章.

说了这么多,也可以在没有 json 的情况下完成,将您的任务转换为 Map(无论如何 json 序列化所做的事情)并将其存储到映射列表中.我将向您展示一个不使用 json 手动执行此操作的示例.但是,再次强调,最好花点时间学习它.

此示例将使用 Get Storage,这与 SharedPreferences 类似但更容易,因为您不需要不同数据类型的不同方法,只需readwrite.

我不知道您是如何在应用中添加任务的,但这只是存储 Task 对象列表的一个基本示例.任何不涉及在线存储的解决方案都需要在本地存储,并在应用启动时从存储中检索.

假设这里是您的 Task 对象.

class 任务 {最终字符串名称;最终字符串描述;任务({this.name, this.description});}

在运行你的应用之前把它放在你的 main 方法中

await GetStorage.init();

您需要将 async 添加到 main 中,所以如果您不熟悉它的工作原理,它看起来像这样.

void main() async {等待 GetStorage.init();运行应用程序(我的应用程序());}

通常我永远不会在有状态小部件中执行所有这些逻辑,而是实现一个状态管理解决方案并在 UI 之外的类中执行它,但这是一个完全不同的讨论.我还建议您查看 GetX、Riverpod 或 Provider,阅读有关它们的信息,看看哪一个最容易让您印象深刻.GetX 赢得了我对简单性和功能性的投票.

但由于您刚刚开始,我将省略这部分内容,暂时将所有这些功能放在 UI 页面中.

此外,它不仅可以在应用程序关闭时存储(也可以这样做),而且可以更轻松地在列表发生更改时随时存储.

这是一个页面,其中包含一些用于添加、清除和打印存储的按钮,因此您可以在应用重启后准确查看列表中的内容.

如果您了解这里发生了什么,您应该能够在您的应用程序中执行此操作,或者研究 json 并以这种方式执行此操作.无论哪种方式,您都需要了解 Maps 以及本地存储如何与任何可用解决方案配合使用.

class StorageDemo 扩展 StatefulWidget {@覆盖_StorageDemoState createState() =>_StorageDemoState();}class _StorageDemoState 扩展 State{列表<任务>_tasks = [];最后一个框 = GetStorage();//地图列表存储在这里//用于存储地图/restoreTask 函数的单独列表//在initState上从这个列表中填充_tasks列表存储列表 = [];void addAndStoreTask(Task task) {_tasks.add(task);最终存储映射 = {};//添加到存储中的临时映射最终索引 = _tasks.length;//对于唯一的映射键最终 nameKey = 'name$index';final descriptionKey = 'description$index';//将任务属性添加到临时映射storageMap[nameKey] = task.name;storageMap[descriptionKey] = task.description;storageList.add(storageMap);//将临时映射添加到 storageListbox.write('tasks', storageList);//将地图列表添加到存储}无效恢复任务(){storageList = box.read('tasks');//从存储初始化列表String nameKey, descriptionKey;//遍历存储列表以从映射中解析出任务对象for (int i = 0; i < storageList.length; i++) {最终地图 = storageList[i];//检索键的索引占索引从 0 开始最终索引 = i + 1;nameKey = 'name$index';descriptionKey = '描述$索引';//从存储中重新创建任务对象最终任务=任务(名称:地图[名称密钥],描述:地图[描述密钥]);_tasks.add(task);//将任务添加回您的正常任务列表}}//遍历您的列表以查看里面的内容无效打印任务(){for (int i = 0; i < _tasks.length; i++) {调试打印('任务 ${i + 1} 名称 ${_tasks[i].name} 描述:${_tasks[i].description}');}}void clearTasks() {_tasks.clear();storageList.clear();box.erase();}@覆盖无效的初始化状态(){super.initState();恢复任务();//从存储在 initState 中恢复列表}@覆盖小部件构建(BuildContext 上下文){返回脚手架(正文:列(mainAxisAlignment: MainAxisAlignment.center,孩子们: [中心(孩子:容器(),),文本按钮(按下: () {最终任务=任务(描述:'测试描述',名称:'测试名称');addAndStoreTask(task);},孩子:文本('添加任务'),),文本按钮(按下: () {打印任务();},孩子:文本('打印存储'),),文本按钮(按下: () {清除任务();},孩子:文本('清除任务'),),],),);}}

The situation:
I'm very new to Flutter and mobile development, thus I don't know much about Dart; And I've read some solutions from people with similar problems but didn't manage to work these solutions to my own thing.

The problem:
I have a to-do app that has 2 Lists of Objects, and I want to store those Lists for whenever the user re-open the app.

I know its simple stuff but I feel like I'm storming towards this problem due to the lack of experience... And so I decided to come asking for some light.

What I've tried:
I have come across different solutions for this problem and all of them seemed way too complex to this case (compared to what I'm used to do when saving lists to the archive), including: encoding the list to a map and converting to a string before using SharedPreferences, using SQlite database (every tutorial I've come across made me feel like I'd be using a war tank to kill an ant, I'd say the same about firebase).

Structure of the problem:
ToDo screen with a ListView.builder calling 2 arrays: ongoing tasks and done tasks each of which I want to write to the phone whenever the user makes a change. IDK if I should only try to save those arrays from within the class from which they belong by calling some packages methods, or if I should try to store the entire application if such thing is possible.

Conclusion:
Is there a way to solve this in a simple way or I should use something robust like firebase just for that? even though I'm not used to work with firestore, and so I'm in the dark not knowing how to apply such thing to save data.

How my lists are structured:

List<Task> _tasks = [
    Task(
      name: "do something",
      description: "try to do it fast!!!",
    ),
  ];

List<Task> _doneTasks = [
 Task(
      name: "task marked as done",
      description: "something",
    ),
];

解决方案

So generally speaking, once you want to store anything other than a primitive type ie. String int etc... things get a bit more complex because they have to converted to something that's readable by any storage solution.

So despite Tasks being a basic object with a couple strings, SharedPreferences or anything else doesn't know what a Task is or what to do with it.

I suggest in general reading about json serialization, as you'll need to know about it either way. This is a good place to start and here is another good article about it.

All that being said, it can also be done without json by converting your task to a Map (which is what json serialization does anyway) and storing it to a list of maps. I'll show you an example of doing this manually without json. But again, its in your best interest to buckle down and spend some time learning it.

This example will use Get Storage, which is like SharedPreferences but easier because you don't need separate methods for different data types, just read and write.

I don't know how you're adding tasks in your app, but this is just a basic example of storing a list of Task objects. Any solution that doesn't involve online storage requires storing locally, and retrieving from storage on app start.

So let's say here is your Task object.

class Task {
  final String name;
  final String description;

  Task({this.name, this.description});
}

Put this in your main method before running your app

await GetStorage.init();

You'll need to add async to your main, so if you're not familiar with how that works it looks like this.

void main() async {
  await GetStorage.init();

  runApp(MyApp());
}

Normally I would NEVER do all this logic inside a stateful widget, but instead implement a state management solution and do it in a class outside of the UI, but that's a whole different discussion. I also recommend checking out GetX, Riverpod, or Provider reading about them and seeing which one strikes you as the easiest to learn. GetX gets my vote for simplicity and functionality.

But since you're just starting out I'll omit that part of it and just put all these functions in the UI page for now.

Also instead of only storing when app closes, which can also be done, its easier to just store anytime there is a change to the list.

Here's a page with some buttons to add, clear, and print storage so you can see exactly whats in your list after app restart.

If you understand whats going on here you should be able to do this in your app, or study up on json and do it that way. Either way, you need to wrap your head around Maps and how local storage works with any of the available solutions.

class StorageDemo extends StatefulWidget {
  @override
  _StorageDemoState createState() => _StorageDemoState();
}

class _StorageDemoState extends State<StorageDemo> {
  List<Task> _tasks = [];

  final box = GetStorage(); // list of maps gets stored here

  // separate list for storing maps/ restoreTask function
  //populates _tasks from this list on initState

  List storageList = [];

  void addAndStoreTask(Task task) {
    _tasks.add(task);

    final storageMap = {}; // temporary map that gets added to storage
    final index = _tasks.length; // for unique map keys
    final nameKey = 'name$index';
    final descriptionKey = 'description$index';

// adding task properties to temporary map

    storageMap[nameKey] = task.name;
    storageMap[descriptionKey] = task.description;

    storageList.add(storageMap); // adding temp map to storageList
    box.write('tasks', storageList); // adding list of maps to storage
  }

  void restoreTasks() {
    storageList = box.read('tasks'); // initializing list from storage
    String nameKey, descriptionKey;

// looping through the storage list to parse out Task objects from maps
    for (int i = 0; i < storageList.length; i++) {
      final map = storageList[i];
      // index for retreival keys accounting for index starting at 0
      final index = i + 1;

      nameKey = 'name$index';
      descriptionKey = 'description$index';

      // recreating Task objects from storage

      final task = Task(name: map[nameKey], description: map[descriptionKey]);

      _tasks.add(task); // adding Tasks back to your normal Task list
    }
  }

// looping through you list to see whats inside
  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(); // restore list from storing in initState
  }

  @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'),
          ),
        ],
      ),
    );
  }
}

这篇关于Flutter - 每次关闭应用程序时存储对象列表的最佳方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆