Flutter - 如何存储字符串列表:(GetStorage或Shared Preferences)。使用Android。

3

所以,我发现使用Get_storage解决了这个问题,并得益于有关该主题的一些很好的解释。我成功地使用getx和Provider包在添加新内容并启动应用程序时读取数据来保存数据(这是我想要的行为)。话虽如此,我正在尝试从内存中删除数据时遇到困难。

背景
该项目是一个待办事项列表应用程序,前端运作良好,但当涉及到存储时就变得更加复杂。问题在于我非常新手Flutter和移动开发,在这方面我接受了一些帮助,但这种东西仍然在我的脑海中模糊不清,我无法使用相同的逻辑删除数据。当我像文档中所说的那样调用box.Remove('key')时,我的整个列表都被删除了。我不知道为什么会发生这种情况。

因此,我想通过阅读更多的解释来更好地理解它,我知道Shared Preferences对于处理这种情况非常重要,但如果使用Get_storage解决方案也可以让我感到舒适,因为我更熟悉它。

代码:

我在另一个文件中利用Provider的帮助在listView中调用这些列表。

List<Task> _tasks = [
Task(
  name: "Title",
  description: "Description",
),

向我的列表视图中添加任务 - -

void add(String newTitle, newDesc) {
    final task = Task(name: newTitle, description: newDesc);
    _tasks.add(task);

    notifyListeners();
  }

这里是从ListView中删除任务的方法:

void removeTasks(Task task) {
_tasks.remove(task);

notifyListeners();

我尝试实现一个逻辑来写入和读取数据,它成功了。但是我也试着使用removeTasks方法通过调用box.Remove('tasks') 从存储中删除它 ('tasks'是传递给写入和读取方法的键)。 它将所有东西都从内存中删除了,因此我的listview变成空的了。

由于我经验不够丰富,我查阅了文档,并能理解一些SharedPreferences解释(与got_storage相同),但是当我尝试将其应用到我的代码时,我遇到了困难。

我非常感激任何使用get_storage或shared preferences来解决这个问题的帮助。

这是我调用删除的地方:

// bool variables that control the state of the screen
// since i can change it to show done tasks or on goind tasks
// dont mind that, i think its irrelevant to the problem.
// 
bool isActiveDoing = true;
bool isActiveDone = false;

List finalArray = [];  //it will store the tasks

class TaskList extends StatefulWidget {
  @override
  _TaskListState createState() => _TaskListState();
}

class _TaskListState extends State<TaskList> {
  @override
  Widget build(BuildContext context) {
     //dont mind the if else as well, its not part of the problem 
     //just using it to handle the state of the screen
    if (isActiveDoing) {
      finalArray = Provider.of<TasksFunctions>(context).tasks;
    }
    //TasksFunctions is a class with methods on regards to the storage
    //it contains add tasks, remove, etc... i'm using provider to
    //link those to the screens with the notifyListeners
    if (isActiveDone) {
      finalArray = Provider.of<TasksFunctions>(context).doneTasks;
    }

    //now here is where i call the class tha has the deletion method
    return Consumer<TasksFunctions>(
      builder: (context, tasksFunctions, child) {
        return ListView.builder(
          //list view tha has all the tasks
          itemCount: finalArray.length,
          itemBuilder: (context, index) {
            final task = finalArray[index];

 //using the slidableWidget to wrap the deletion method 
            return SlidableWidget(
              onDismissed: (action) {
                if (isActiveDoing) {
                  Provider.of<TasksFunctions>(context, listen: false)
                      .removeTask(task);
//so here is where i'm deleting those tasks, calling that method
//listed up on this post
               
                }
                if (isActiveDone {
                  Provider.of<TasksFunctions>(context, listen: false)
                      .removeDone(task);
                }
              },
             
            );
          },
        );
      },
    );
  }
}

我花了一些时间来翻译这段代码,但我认为它并不符合Flutter的良好实践原则。

我还尝试过调用storageList.remove(task);然后用box.write('tasks', storageList)重写它,但是没有从内存中删除任何内容(也许是因为我没有遍历整个storageList以查找正确的索引位置吧)。

1个回答

2
听起来你的代码是基于我对此问题的原始答案 (my answer to your original question about this).
如果是这种情况,那么键 tasks 是整个映射列表的关键,而不是单个映射或 Task。 因此,当它抹掉所有任务时,它会按预期运行。
为了保留任何更改,您必须从 storageList 中删除该特定映射 (Task),然后再次使用 box.write('tasks', storageList); 覆盖框中的内容。 它将保存相同的映射列表并保留您进行的任何删除。
如果您分享您试图删除任务的代码以及周围发生的事情,我可以给您一个更具体的示例。
编辑:回答评论中的问题。
如果您想走 UniqueKey 路线,则根本不需要索引。 您可以像这样做。
class Task {
  final String name;
  final String description;
  final String key; // not an actual Key but will take the String value of a UniqueKey

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

当您添加一个新的任务时,它会像这样。
final task = Task(
    description: 'test description',
    name: 'test name',
    key: UniqueKey().toString());

那么,您可以使用一个映射的映射而不是一个映射列表,并且同时使用键。

  Map storageList = {};

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

    final Map storageMap = {}; // temporary map that gets added to storage

    storageMap['name'] = task.name;
    storageMap['description'] = task.description;
    storageMap['key'] = task.key;

    storageList[task.key] = storageMap; // adding temp map to storageList
    box.write('tasks', storageList); // adding map of maps to storage
  }


那么你的还原函数应该像这样:
 void restoreTasks() {
    storageList = box.read('tasks'); // initializing list from storage

    storageList.forEach((key, value) { // value here is each individual map that was stored
      final task =
          Task(name: value['name'], description: value['description'], key: key);
      _tasks.add(task);
    });
  }

然后当你想要删除时,你需要遍历列表并找到匹配的键。


我想我理解了你所解释的内容,我通过应用你所说的方法成功地删除了任务。我认为这里最困难的问题与你提出的逻辑中的索引相关,因为当我从storageList中删除元素时,剩余任务的索引不会减少,而_tasks列表在删除某些内容时会减少索引,因此当我尝试通过你在另一篇帖子中展示的循环读取任务时,它们无法读取nameKeys并返回每个已删除任务之后的null。 - Louis
是的,那个解决方案只是让你开始使用持久性的一些东西,它从来不是你的应用程序的全部解决方案。你面临的索引问题是有道理的。当我有更多时间时,我可以看看你的代码,但在此期间,你已经找出了问题。现在,你可以尝试绕过索引问题或者想出一个更好的解决方案来为每个任务维护一个唯一的键。 - Loren.A
非常感谢你,Loren。我考虑了一种处理索引的逻辑。你帮了我很多,我一定会通过持久化找到更简洁的方法,谢谢你!:D - Louis
没问题。也许可以看看 Unique Keys,你可以将它们作为你的 Task 对象的一部分,然后当你删除某些东西时,可以遍历列表并找到匹配的键,这样只会删除那个。将它们转换为字符串,它们也可以成为映射存储的键。这是众多可能的解决方案之一。https://api.flutter.dev/flutter/widgets/UniqueKey-class.html - Loren.A
如果我想将它们转换为字符串,那么我应该执行你之前做的相同操作,对吧?通过传递到映射,然后将映射传递到storageList中。也许在这种情况下,最好的方法是在任务(名称、描述、密钥)内部创建一个键属性,并将此属性作为addTasks()中的参数传递,类似于.... 终极密钥=“key$index”;我不知道,我必须考虑如何实现将此密钥与读取文件时的_tasks索引进行比较。 - Louis
抱歉我回复晚了,你的想法是正确的,但如果使用 UniqueKey 的话就不需要索引了。因为在我之前的示例中,索引只是一个快速生成唯一映射键的hack。请查看更新后的答案。 - Loren.A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接