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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 03:07:49