You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter如何使用SharedPreferences持久化存储List<String>待办列表

Flutter 待办列表基于shared_preferences的持久化实现方案

注意:shared_preferences 2.0及以上稳定版本原生支持setStringList/getStringList方法,可直接读写List<String>类型数据,不需要额外做json序列化转换,建议使用前先升级到最新稳定版。

shared_preferences 原生就支持List<String>类型的直接读写,你只需要补全初始化读取、变更后写入的逻辑即可,同时可以同步优化现有代码的规范问题。


一、核心实现步骤

  1. 定义固定的存储key,避免硬编码拼写错误
  2. 在页面初始化生命周期initState中异步读取本地已存储的待办列表,替换原有硬编码测试数据
  3. 抽离独立的列表保存方法,每次新增、删除、修改待办项后,同步把最新列表写入本地存储
  4. 补全资源释放、输入校验等边界逻辑

修改后的完整可运行代码

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tooodo/views/task_lists_containers.dart';

class TasksScreen extends StatefulWidget {
  const TasksScreen({Key? key}) : super(key: key);

  @override
  State<TasksScreen> createState() => _TasksScreenState();
}

class _TasksScreenState extends State<TasksScreen> {
  // 本地存储key常量,避免硬编码出错
  static const String _todoStorageKey = 'local_todo_list';
  List<String> toDoItems = [];
  final TextEditingController _taskTextFieldController = TextEditingController();

  @override
  void initState() {
    super.initState();
    // 页面初始化时读取本地存储的待办数据
    _loadLocalTodoList();
  }

  @override
  void dispose() {
    // 页面销毁时释放文本控制器,避免内存泄漏
    _taskTextFieldController.dispose();
    super.dispose();
  }

  // 读取本地存储的待办列表
  Future<void> _loadLocalTodoList() async {
    final prefs = await SharedPreferences.getInstance();
    final savedList = prefs.getStringList(_todoStorageKey);
    // 如果本地有存储数据则赋值,没有则保持空列表
    if (savedList != null) {
      setState(() {
        toDoItems = savedList;
      });
    }
  }

  // 保存最新待办列表到本地
  Future<void> _saveTodoList() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setStringList(_todoStorageKey, toDoItems);
  }

  // 抽离新增任务弹窗,减少build方法嵌套
  void _showAddTaskDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          shape: const RoundedRectangleBorder(
              borderRadius: BorderRadius.all(Radius.circular(15))),
          content: TextField(
            controller: _taskTextFieldController,
            textInputAction: TextInputAction.done,
            decoration: const InputDecoration(hintText: 'Task'),
            // 键盘点击完成直接保存
            onSubmitted: (_) => _addNewTask(),
          ),
          actions: <Widget>[
            TextButton(
              onPressed: _addNewTask,
              child: const Text("Save"),
            ),
          ],
        );
      },
    );
  }

  // 抽离新增任务逻辑,统一做校验和存储
  void _addNewTask() {
    final inputText = _taskTextFieldController.text.trim();
    // 空内容不保存
    if (inputText.isEmpty) {
      Navigator.of(context).pop();
      return;
    }
    setState(() {
      toDoItems.add(inputText);
    });
    // 保存最新列表到本地
    _saveTodoList();
    _taskTextFieldController.clear();
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color.fromARGB(255, 7, 152, 70),
        title: const Text('Tasks'),
        centerTitle: true,
      ),
      body: Container(
          padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
          child: ListView.builder(
              itemCount: toDoItems.length,
              itemBuilder: (context, index) {
                return TaskContainer(
                  child: toDoItems[index],
                );
              })),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: FloatingActionButton(
        onPressed: _showAddTaskDialog,
        enableFeedback: true,
        child: const Icon(Icons.add),
      ),
    );
  }
}

二、代码规范&可维护性优化建议

  • 不要用// ignore: 类型的注释压制警告,比如原来的prefer_const_literals_to_create_immutables警告,只需要给EdgeInsets加const修饰即可消除,从根源解决问题比忽略警告更合理
  • 文本控制器一定要在dispose生命周期里释放,否则会造成内存泄漏
  • 业务逻辑尽量抽离成独立方法,不要全部嵌套在build方法里,比如弹窗、新增任务逻辑抽离后,代码可读性会大幅提升
  • 所有用户输入都要做非空校验,避免用户输入全空格、空字符串的无效任务
  • 常量统一管理:存储key、颜色值、尺寸、圆角这类固定值建议抽离到统一的常量文件,后续修改不需要翻业务代码
  • 功能扩展预留:如果后续要实现任务勾选完成的功能,只存String类型的任务内容无法记录完成状态,建议提前封装Task实体类,包含content(任务内容)、isCompleted(完成状态)两个字段,存储时转成Json字符串列表即可,后续加勾选、删除、编辑功能都不需要重构存储逻辑
  • 状态更新要精准:setState调用时尽量只包裹需要更新的状态变量,不要无意义的空调用,减少不必要的组件重绘

内容的提问来源于stack exchange,提问作者M.Nuh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:06:26