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

如何在Flutter的ListView.builder中集成shared_preferences实现本地持久化存储

问题根源

  1. 存储逻辑错误:仅将待办文本拼接为无格式的长字符串存储,既没有保存待办的勾选状态,也没有做结构化拆分,应用重启后无法恢复为可用的列表数据
  2. 初始化逻辑缺失:启动时没有将本地存储的数据同步到内存的items列表,所以原有基于items渲染的写法重启后数据全部丢失
  3. 渲染逻辑错误:修改后的写法是直接从完整拼接字符串中按字符索引取值,自然会出现展示异常

修复后可直接运行的完整代码

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

void main() {
  runApp(const MyApp());
}

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

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text("TodoList")),
        body: Container(
          child: Column(
            children: const [
              Expanded(child: TodoListWidget()),
            ],
          ),
        ),
      ),
    );
  }
}

// 待办数据模型,用于结构化存储
class Todo {
  String todoText;
  bool todoToggle;

  Todo({required this.todoText, required this.todoToggle});

  Map<String, dynamic> toJson() => {
    "todoText": todoText,
    "todoToggle": todoToggle,
  };

  static Todo fromJson(Map<String, dynamic> json) => Todo(
    todoText: json["todoText"],
    todoToggle: json["todoToggle"],
  );
}

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

  @override
  _TodoListWidgetState createState() => _TodoListWidgetState();
}

class _TodoListWidgetState extends State<TodoListWidget> {
  final TextEditingController textInputController = TextEditingController();
  List<Todo> items = [];
  late SharedPreferences prefs;
  final String storageKey = "todo_list";

  @override
  void initState() {
    super.initState();
    _initStorage();
  }

  // 初始化本地存储,恢复历史数据
  Future<void> _initStorage() async {
    prefs = await SharedPreferences.getInstance();
    String? savedData = prefs.getString(storageKey);
    if (savedData != null && savedData.isNotEmpty) {
      List<dynamic> jsonList = jsonDecode(savedData);
      setState(() {
        items = jsonList.map((e) => Todo.fromJson(e)).toList();
      });
    }
  }

  // 同步数据到本地存储
  Future<void> _syncToLocal() async {
    List<Map<String, dynamic>> jsonList = items.map((e) => e.toJson()).toList();
    await prefs.setString(storageKey, jsonEncode(jsonList));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: Column(
          children: [
            items.isEmpty
                ? const Expanded(child: Center(child: Text("暂无待办事项")))
                : Expanded(
                    child: ListView.builder(
                      itemCount: items.length,
                      itemBuilder: (BuildContext context, int index) {
                        return Card(
                          child: Container(
                            padding: const EdgeInsets.all(10.0),
                            child: CheckboxListTile(
                              title: _strikeThrough(
                                todoText: items[index].todoText,
                                todoToggle: items[index].todoToggle,
                              ),
                              value: items[index].todoToggle,
                              onChanged: (bool? value) {
                                setState(() {
                                  items[index].todoToggle = value!;
                                });
                                _syncToLocal();
                              },
                              dense: true,
                              activeColor: Colors.green,
                            ),
                          ),
                        );
                      },
                    ),
                  ),
            Center(
              child: FloatingActionButton(
                child: const Icon(Icons.add),
                onPressed: () => _displayAlert(context),
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _displayAlert(BuildContext context) {
    Widget contentDialog = AlertDialog(
      title: const Text("Add Task"),
      content: TextField(controller: textInputController),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            textInputController.clear();
          },
          child: const Text("Cancel"),
        ),
        TextButton(
          onPressed: () {
            if (textInputController.text.trim().isEmpty) return;
            setState(() {
              items.add(Todo(
                todoText: textInputController.text.trim(),
                todoToggle: false,
              ));
            });
            _syncToLocal();
            Navigator.pop(context);
            textInputController.clear();
          },
          child: const Text("Add"),
        )
      ],
    );
    showDialog(
      context: context,
      builder: (BuildContext context) => contentDialog,
    );
  }
}

class _strikeThrough extends StatelessWidget {
  final bool todoToggle;
  final String todoText;

  const _strikeThrough({required this.todoToggle, required this.todoText});

  Widget _strikeWidget() {
    if (!todoToggle) {
      return Text(
        todoText,
        style: const TextStyle(fontSize: 20.0),
      );
    } else {
      return Text(
        todoText,
        style: const TextStyle(
          fontSize: 22.0,
          decoration: TextDecoration.lineThrough,
          color: Colors.redAccent,
          fontStyle: FontStyle.italic,
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return _strikeWidget();
  }
}

核心修改说明

  1. 新增了结构化的Todo数据模型,支持Json序列化/反序列化,可同时保存待办文本和勾选状态
  2. 初始化逻辑自动读取本地存储的历史数据,恢复到内存列表,重启应用数据不会丢失
  3. 每次新增待办、切换勾选状态都会自动同步数据到本地存储,保证数据一致性
  4. 保留了原有的渲染逻辑,展示效果和你之前正常运行的版本完全一致
  5. 移除了不必要的FutureBuilder嵌套和无用依赖,代码运行效率更高

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 01:15:07