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

如何使用Flutter Bloc实现应用关闭后的状态与数据持久化?

解决Flutter Bloc应用重启后状态丢失的问题

要实现Bloc状态的持久化,核心是将状态保存到本地存储,并在Bloc初始化时读取恢复状态。以下是基于shared_preferences(轻量本地存储方案)的完整解决方案:


步骤1:添加依赖

在pubspec.yaml中加入shared_preferences依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.3
  shared_preferences: ^2.2.2

执行flutter pub get安装依赖。

步骤2:改造状态类,支持序列化

给状态类添加唯一标识,方便将状态转换为可存储的字符串:

abstract class LocationState {}

class GPSState extends LocationState {
  final String type = "gps";
}

// 重命名子类避免与父类同名冲突
class ManualLocationState extends LocationState {
  final String type = "location";
}

步骤3:修改Bloc,实现状态持久化逻辑

在Bloc中添加读取本地状态和保存状态到本地的逻辑,同时改用新版Bloc的on语法处理事件:

class LocationBloc extends Bloc<LocationEvent, LocationState> {
  final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
  static const String _stateStorageKey = "location_selected_state";

  LocationBloc() : super(GPSState()) {
    // 初始化时加载本地保存的状态
    _loadSavedState();
    // 处理事件并持久化状态
    on<LocationEvent>((event, emit) async {
      LocationState newState;
      switch (event) {
        case LocationEvent.selectGPS:
          newState = GPSState();
          break;
        case LocationEvent.selectLocation:
          newState = ManualLocationState();
          break;
      }
      emit(newState);
      await _saveStateToLocal(newState);
    });
  }

  // 从本地存储加载状态
  Future<void> _loadSavedState() async {
    final prefs = await _prefs;
    final savedStateType = prefs.getString(_stateStorageKey);
    if (savedStateType != null) {
      LocationState restoredState;
      switch (savedStateType) {
        case "gps":
          restoredState = GPSState();
          break;
        case "location":
          restoredState = ManualLocationState();
          break;
        default:
          restoredState = GPSState();
      }
      emit(restoredState);
    }
  }

  // 将状态保存到本地存储
  Future<void> _saveStateToLocal(LocationState state) async {
    final prefs = await _prefs;
    if (state is GPSState) {
      await prefs.setString(_stateStorageKey, state.type);
    } else if (state is ManualLocationState) {
      await prefs.setString(_stateStorageKey, state.type);
    }
  }
}

步骤4:修复UI中的RadioListTile选中问题

原代码中直接用new GPSState()作为Radio的value,会导致与Bloc中的state对象引用不匹配,选中状态异常。改用状态标识字符串作为value:

// SettingsScreen中的RadioListTile修改
RadioListTile(
  title: const Text('GPS'),
  value: "gps",
  groupValue: state is GPSState ? "gps" : "location",
  onChanged: (value) {
    context.read<LocationBloc>().add(LocationEvent.selectGPS);
  },
),
RadioListTile(
  title: const Text('Location'),
  value: "location",
  groupValue: state is ManualLocationState ? "location" : "gps",
  onChanged: (value) {
    context.read<LocationBloc>().add(LocationEvent.selectLocation);
  },
),

完整修改后的代码

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BlocProvider(
        create: (context) => LocationBloc(),
        child: const HomeScreen(),
      ),
    );
  }
}

enum LocationEvent {
  selectGPS,
  selectLocation,
}

abstract class LocationState {}

class GPSState extends LocationState {
  final String type = "gps";
}

class ManualLocationState extends LocationState {
  final String type = "location";
}

class LocationBloc extends Bloc<LocationEvent, LocationState> {
  final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
  static const String _stateStorageKey = "location_selected_state";

  LocationBloc() : super(GPSState()) {
    _loadSavedState();
    on<LocationEvent>((event, emit) async {
      LocationState newState;
      switch (event) {
        case LocationEvent.selectGPS:
          newState = GPSState();
          break;
        case LocationEvent.selectLocation:
          newState = ManualLocationState();
          break;
      }
      emit(newState);
      await _saveStateToLocal(newState);
    });
  }

  Future<void> _loadSavedState() async {
    final prefs = await _prefs;
    final savedStateType = prefs.getString(_stateStorageKey);
    if (savedStateType != null) {
      LocationState restoredState;
      switch (savedStateType) {
        case "gps":
          restoredState = GPSState();
          break;
        case "location":
          restoredState = ManualLocationState();
          break;
        default:
          restoredState = GPSState();
      }
      emit(restoredState);
    }
  }

  Future<void> _saveStateToLocal(LocationState state) async {
    final prefs = await _prefs;
    if (state is GPSState) {
      await prefs.setString(_stateStorageKey, state.type);
    } else if (state is ManualLocationState) {
      await prefs.setString(_stateStorageKey, state.type);
    }
  }
}

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: BlocBuilder<LocationBloc, LocationState>(
        builder: (context, state) {
          return Center(
            child: Text(
              state is GPSState ? 'GPS' : 'Location',
              style: const TextStyle(fontSize: 24),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => const SettingsScreen()),
          );
        },
        child: const Icon(Icons.settings),
      ),
    );
  }
}

class SettingsScreen extends StatelessWidget {
  const SettingsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Settings'),
      ),
      body: BlocBuilder<LocationBloc, LocationState>(
        builder: (context, state) {
          return Column(
            children: [
              RadioListTile(
                title: const Text('GPS'),
                value: "gps",
                groupValue: state is GPSState ? "gps" : "location",
                onChanged: (value) {
                  context.read<LocationBloc>().add(LocationEvent.selectGPS);
                },
              ),
              RadioListTile(
                title: const Text('Location'),
                value: "location",
                groupValue: state is ManualLocationState ? "location" : "gps",
                onChanged: (value) {
                  context.read<LocationBloc>().add(LocationEvent.selectLocation);
                },
              ),
            ],
          );
        },
      ),
    );
  }
}

关键说明

  • 对于复杂状态(包含多个字段),可以结合json_serializable将状态序列化为JSON字符串存储;
  • 状态变更后立即保存到本地,确保应用退出前状态已持久化;
  • Bloc初始化时优先加载本地状态,保证重启应用后恢复用户之前的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 02:45:53