Flutter中SharedPreferences数据持久化失效问题求助
解决方案:修复支出数据持久化问题
问题核心在于SharedPreferences仅支持存储基础类型/JSON字符串,且你的实现缺少从SP加载初始数据、以及数据变更时同步保存的逻辑,以下是针对性修改:
1. 确保Expense类可序列化/反序列化
修改expense.dart,添加JSON转换方法,让对象能被转为字符串存储到SP:
class Expense { final String id; final String title; final double amount; final DateTime date; Expense({ required this.id, required this.title, required this.amount, required this.date, }); // 转成JSON对象 Map<String, dynamic> toJson() { return { 'id': id, 'title': title, 'amount': amount, 'date': date.toIso8601String(), }; } // 从JSON对象重建Expense factory Expense.fromJson(Map<String, dynamic> json) { return Expense( id: json['id'], title: json['title'], amount: json['amount'].toDouble(), date: DateTime.parse(json['date']), ); } }
2. 修改ExpenseBloc,添加SP读写逻辑
修改expense_bloc.dart,在Bloc初始化时加载SP数据,数据变更时自动保存:
import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> { final SharedPreferences prefs; static const String _expensesStorageKey = 'saved_expenses'; ExpenseBloc(this.prefs) : super(ExpenseInitial()) { // 初始化时先加载本地存储的数据 _loadSavedExpenses(); on<AddExpense>((event, emit) { if (state is ExpensesLoaded) { final updatedList = List<Expense>.from((state as ExpensesLoaded).expenses) ..add(event.expense); emit(ExpensesLoaded(updatedList)); // 保存到SP _saveExpenses(updatedList); } }); on<DeleteExpense>((event, emit) { if (state is ExpensesLoaded) { final updatedList = (state as ExpensesLoaded).expenses .where((e) => e.id != event.id) .toList(); emit(ExpensesLoaded(updatedList)); // 保存到SP _saveExpenses(updatedList); } }); } // 从SP加载数据 void _loadSavedExpenses() { final savedJsonList = prefs.getStringList(_expensesStorageKey); if (savedJsonList != null) { final expenses = savedJsonList .map((jsonStr) => Expense.fromJson(jsonDecode(jsonStr))) .toList(); emit(ExpensesLoaded(expenses)); } else { emit(ExpensesLoaded([])); } } // 将数据保存到SP void _saveExpenses(List<Expense> expenses) { final jsonList = expenses .map((expense) => jsonEncode(expense.toJson())) .toList(); prefs.setStringList(_expensesStorageKey, jsonList); } }
3. 初始化SharedPreferences并注入Bloc
修改main.dart,确保SP在App启动时提前初始化,再传递给Bloc:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // 提前初始化SP final prefs = await SharedPreferences.getInstance(); runApp(MyApp(prefs: prefs)); } class MyApp extends StatelessWidget { final SharedPreferences prefs; const MyApp({super.key, required this.prefs}); @override Widget build(BuildContext context) { return BlocProvider( create: (context) => ExpenseBloc(prefs), child: MaterialApp( title: 'Expense Tracker', home: HomePageScreen(), ), ); } }
4. 确保HomePageScreen正确响应加载状态
检查HomePageScreen.dart的BlocBuilder,确保能处理初始加载的ExpensesLoaded状态:
BlocBuilder<ExpenseBloc, ExpenseState>( builder: (context, state) { if (state is ExpensesLoaded) { return state.expenses.isEmpty ? const Center(child: Text('No expenses yet')) : ListView.builder( itemCount: state.expenses.length, itemBuilder: (context, index) { final expense = state.expenses[index]; return ListTile( title: Text(expense.title), subtitle: Text(expense.date.toLocal().toString().split(' ')[0]), trailing: Text('\$${expense.amount.toStringAsFixed(2)}'), onLongPress: () { context.read<ExpenseBloc>().add(DeleteExpense(expense.id)); }, ); }, ); } // 初始加载时显示加载状态 return const Center(child: CircularProgressIndicator()); }, )
关键注意点
- 必须保证每次数据变更(添加/删除)后都调用
_saveExpenses同步到SP - 不能直接将
Expense对象存入SP,必须转为JSON字符串 - App启动时必须先初始化SP,再创建Bloc,否则会读取不到数据
内容的提问来源于stack exchange,提问作者Dinesh Kumar M
相关产品推荐
相关产品推荐

