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

Posted

技术标签:

【中文标题】Flutter - 如何存储字符串列表:(GetStorage 或 Shared Preferences)。使用安卓【英文标题】:Flutter - how to store lists of Strings: (GetStorage or Shared Preferences). using android 【发布时间】:2021-06-28 00:30:45 【问题描述】:

因此,由于对该主题的一些很好的解释,我已经使用 Get_storage 找到了解决此问题的方法。我设法使用 getx 和 Provider 包在添加新内容时保存数据并在启动应用程序时读取它(这就是我在这里要采取的行为)。也就是说,我很难从内存中删除数据。

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

所以我想知道我是否可以通过阅读更多解释来更多地理解它,我知道共享首选项在这种情况下非常有用,但我也会对使用 get_storage 的解决方案感到满意,因为我'我比较熟悉。

代码:\

我在 Provider 的帮助下在不同文件的 listView 中调用这些列表 - -

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

];

将任务添加到我的 ListView - -

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();

我尝试实现一个逻辑来写入和读取数据,它奏效了。但我也尝试通过调用 box.Remove('tasks'); 来使用这个 removeTasks 方法从存储中删除。 ('tasks' 是传递给写入和读取方法的键)。自从我的列表视图变空后,它从内存中删除了所有内容。

由于我没有那么有经验,我浏览了文档并且可以理解一些 SharedPreferences 解释(与 got_storage 相同),但我在尝试将其应用于我的代码时遇到了困难。

如果使用 get_storage 或共享偏好来解决此问题,我将不胜感激任何帮助。

我在哪里调用删除:

// 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); 重写它但没有从内存中删除任何内容(可能是因为我没有遍历整个 storageLists 来搜索我猜的正确索引)

【问题讨论】:

【参考方案1】:

听起来您的代码是基于my answer to your original question about this。

如果是这样,那么键 tasks 是整个映射列表的键,而不是单个映射或 Task。因此,当它擦除您的所有任务时,它的行为符合预期。

为了保留任何更改,您必须从 storageList 中删除该特定地图(任务),然后用 box.write('tasks', storageList); 再次覆盖该框,它将保存相同的地图列表并保留您所做的任何删除.

如果您在尝试删除任务的地方分享您的代码以及周围发生的事情,我可以给您一个更具体的示例。

编辑:在 cmets 中回答问题。

如果您想走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);

当您添加一个新的Task 时,它会如下所示。

final task = Task(
    description: 'test description',
    name: 'test name',
    key: UniqueKey().toString());

然后您可以使用地图的Map 而不是地图列表,并为两者使用密钥。

  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。 是的,该解决方案只是让您在持久性世界中开始的东西,它绝不是您的应用程序的全部。您面临的索引问题是有道理的。当我有更多时间时,我可以查看您的代码,但与此同时,您已经解决了问题。现在,您也许可以找到解决索引问题的方法,或者一起提出更好的解决方案来为每个任务维护一个唯一的键。 非常感谢 Loren,我想到了处理索引的逻辑。你帮了我很多,多亏了你,我一定会通过坚持不懈地走一条更清洁的路! :D 没问题。也许看看Unique Keys,你可以让它们成为你Task对象的一部分,然后当你删除某些东西时,可以循环遍历列表并找到匹配的键,它只会删除它。将它们转换为字符串,它们也可以成为映射存储的关键。许多可能的解决方案之一。 api.flutter.dev/flutter/widgets/UniqueKey-class.html 如果我想将它们转换为字符串,那么我应该和你之前做的一样吗?通过将其传递给地图,然后将地图传递给 storageList。也许在这种情况下,事情将是在 Task(name, description, key) 中创建一个 key 属性,并在 addTasks() 中传递这个属性,就像.... final keyKey = 'key$index"; idk, i必须考虑如何在读取文件时将此键与 _tasks 索引进行比较。

以上是关于Flutter - 如何存储字符串列表:(GetStorage 或 Shared Preferences)。使用安卓的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:如何在两个小部件之间传递相同的数据?

Flutter - 如何在 GetStorage 中存储列表?

如何在浏览器本地存储 Flutter Web 应用程序中保存对象列表

你了解gets()和scanf()吗

如何从列表中映射 Flutter JSON 字符串?

如何在 Flutter 中创建自定义下拉列表?