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

如何在该Todo App代码中实现Shared Preferences本地存储?

Flutter Todo 应用 SharedPreferences 持久化实现方案

1. 定义 Todo 数据模型

SharedPreferences 仅支持存储基本数据类型,所以需要把 Todo 对象序列化为 JSON 字符串存储。先创建包含序列化/反序列化方法的 Todo 类:

import 'dart:convert';

class Todo {
  final String id;
  final String title;
  bool isCompleted;

  Todo({
    required this.id,
    required this.title,
    this.isCompleted = false,
  });

  // 转成 JSON Map
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'isCompleted': isCompleted,
    };
  }

  // 从 JSON Map 解析为 Todo 对象
  factory Todo.fromJson(Map<String, dynamic> json) {
    return Todo(
      id: json['id'],
      title: json['title'],
      isCompleted: json['isCompleted'],
    );
  }
}

2. 封装存储与读取方法

直接在页面状态类中实现以下方法,也可以单独抽成工具类:

存储 Todo 列表

把 Todo 列表转为 JSON 字符串列表,存入 SharedPreferences:

Future<void> saveTodos(List<Todo> todos) async {
  final prefs = await SharedPreferences.getInstance();
  final List<String> todosJson = todos.map((todo) => jsonEncode(todo.toJson())).toList();
  await prefs.setStringList('todos', todosJson);
}

读取本地 Todo 列表

从 SharedPreferences 取出字符串列表,解析为 Todo 对象列表:

Future<List<Todo>> loadTodos() async {
  final prefs = await SharedPreferences.getInstance();
  final List<String>? todosJson = prefs.getStringList('todos');
  
  if (todosJson == null || todosJson.isEmpty) {
    return [];
  }
  
  return todosJson.map((jsonStr) => Todo.fromJson(jsonDecode(jsonStr))).toList();
}

3. 页面集成与数据展示

基于 StatefulWidget 实现页面,初始化时读取本地数据,操作 Todo 后同步更新存储:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class TodoPage extends StatefulWidget {
  const TodoPage({super.key});

  @override
  State<TodoPage> createState() => _TodoPageState();
}

class _TodoPageState extends State<TodoPage> {
  final TextEditingController _titleController = TextEditingController();
  late List<Todo> _todos;

  @override
  void initState() {
    super.initState();
    // 页面启动时加载本地数据
    _loadLocalTodos();
  }

  Future<void> _loadLocalTodos() async {
    final loadedTodos = await loadTodos();
    setState(() {
      _todos = loadedTodos;
    });
  }

  // 添加 Todo
  Future<void> _addTodo() async {
    if (_titleController.text.trim().isEmpty) return;
    
    final newTodo = Todo(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      title: _titleController.text.trim(),
    );
    
    setState(() {
      _todos.add(newTodo);
    });
    
    await saveTodos(_todos);
    _titleController.clear();
  }

  // 切换完成状态
  Future<void> _toggleTodoStatus(int index) async {
    setState(() {
      _todos[index].isCompleted = !_todos[index].isCompleted;
    });
    await saveTodos(_todos);
  }

  // 删除 Todo
  Future<void> _deleteTodo(int index) async {
    setState(() {
      _todos.removeAt(index);
    });
    await saveTodos(_todos);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Todo List')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _titleController,
                    decoration: const InputDecoration(hintText: '输入 Todo 内容'),
                  ),
                ),
                IconButton(onPressed: _addTodo, icon: const Icon(Icons.add)),
              ],
            ),
          ),
          Expanded(
            child: _todos.isEmpty
                ? const Center(child: Text('暂无 Todo'))
                : ListView.builder(
                    itemCount: _todos.length,
                    itemBuilder: (context, index) {
                      final todo = _todos[index];
                      return ListTile(
                        leading: Checkbox(
                          value: todo.isCompleted,
                          onChanged: (_) => _toggleTodoStatus(index),
                        ),
                        title: Text(
                          todo.title,
                          style: TextStyle(
                            decoration: todo.isCompleted ? TextDecoration.lineThrough : null,
                          ),
                        ),
                        trailing: IconButton(
                          icon: const Icon(Icons.delete),
                          onPressed: () => _deleteTodo(index),
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _titleController.dispose();
    super.dispose();
  }
}

核心注意点

  • 每次修改 Todo 列表(添加、切换状态、删除)后,必须调用 saveTodos 同步到本地。
  • 页面初始化时调用 loadTodos,确保重启应用后数据能被加载展示。
  • 记得在文件顶部导入 dart:convert 和 shared_preferences 包。

内容的提问来源于stack exchange,提问作者Aman Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 00:35:26