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

Hydrated Bloc重启/热重载后无法持久化状态的问题

问题描述

通过单选按钮切换并更新PrayerTypeBloc状态时,点击操作可正常变更状态,但热重载或重启应用后,状态无法保留。已使用hydrated_bloc库,相关配置与代码如下:


pubspec.yaml 依赖配置

dependencies:
  bloc: ^8.1.4
  equatable: ^2.0.5
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.5
  http: ^1.2.0
  hydrated_bloc: ^9.1.5
  path_provider: ^2.1.3
  permission_handler: ^11.3.1

代码文件

1. prayer_type_bloc.dart

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:meta/meta.dart';

part 'prayer_type_event.dart';
part 'prayer_type_state.dart';

class PrayerTypeBloc extends HydratedBloc<PrayerTypeEvent, PrayerTypeState> {
  PrayerTypeBloc() : super(const PrayerTypeInitial()) {
    on<LocationSelected>(
      (LocationSelected event, Emitter emit) =>
          emit(const LocationSelectedState()),
    );
    on<GpsSelected>(
      (GpsSelected event, Emitter emit) => emit(const GpsSelectedState()),
    );
  }

  @override
  PrayerTypeState? fromJson(Map<String, dynamic> json) {
    if (json['prayer_type'] == null) {
      return null; 
    }
    switch (json['prayer_type']) {
      case 'LocationSelectedState':
        return const LocationSelectedState();
      case 'GpsSelectedState':
        return const GpsSelectedState();
      default:
        return null;
    }
  }

  @override
  Map<String, dynamic>? toJson(PrayerTypeState state) {
    return {'prayer_type': state.toString()};
  }
}

2. prayer_type_event.dart

part of 'prayer_type_bloc.dart';

@immutable
sealed class PrayerTypeEvent extends Equatable {
  const PrayerTypeEvent();

  @override
  List<Object?> get props => [];
}

class LocationSelected extends PrayerTypeEvent {
  const LocationSelected();
}

class GpsSelected extends PrayerTypeEvent {
  const GpsSelected();
}

3. prayer_type_state.dart

part of 'prayer_type_bloc.dart';

@immutable
sealed class PrayerTypeState extends Equatable {
  const PrayerTypeState();

  @override
  List<Object?> get props => [];
}

class PrayerTypeInitial extends PrayerTypeState {
  const PrayerTypeInitial();
}

class LocationSelectedState extends PrayerTypeState {
  const LocationSelectedState();
}

class GpsSelectedState extends PrayerTypeState {
  const GpsSelectedState();
}

4. main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:muezzyn/app_bloc_observer.dart';
import 'package:muezzyn/bloc/prayer_type/bloc/prayer_type_bloc.dart';
import 'package:muezzyn/cubit/bottom_nav_cubit.dart';
import 'package:muezzyn/widget/main_wrapper.dart';
import 'package:path_provider/path_provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  Bloc.observer = AppBlocObserver();
  HydratedBloc.storage = await HydratedStorage.build(
    storageDirectory: await getTemporaryDirectory(),
  );
  runApp(MainApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MultiBlocProvider(
        providers: [
          BlocProvider(create: (context) => BottomNavCubit()),
          BlocProvider<PrayerTypeBloc>(create: (context) => PrayerTypeBloc()),
        ],
        child: MainWrapper(),
      ),
    );
  }
}

Settings_Page.dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:muezzyn/bloc/prayer_type/bloc/prayer_type_bloc.dart';

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

  @override
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  PrayerTypeBloc? _prayerTypeBloc;

  @override
  void initState() {
    super.initState();
    _prayerTypeBloc =
        BlocProvider.of<PrayerTypeBloc>(context); 
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<PrayerTypeBloc, PrayerTypeState>(
        bloc: _prayerTypeBloc!,
        builder: (context, state) {
          PrayerTypeState currentState = state; 

          return Center(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text("Location Preference"),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    Radio<PrayerTypeState>(
                      value: const LocationSelectedState(),
                      groupValue: currentState,
                      onChanged: (value) {
                        if (value != null) {
                          context
                              .read<PrayerTypeBloc>()
                              .add(LocationSelected());
                        }
                      },
                    ),
                    Text('Location'),
                    Radio<PrayerTypeState>(
                      value: const GpsSelectedState(),
                      groupValue: currentState,
                      onChanged: (value) {
                        if (value != null) {
                          context.read<PrayerTypeBloc>().add(GpsSelected());
                        }
                      },
                    ),
                    Text('GPS'),
                  ],
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}

问题解决方法

1. 更换持久化存储目录

当前使用的getTemporaryDirectory()是临时目录,系统会随时清理其中数据,导致重启后状态丢失。替换为应用专属的持久化目录:

// 修改main.dart中的存储目录配置
HydratedBloc.storage = await HydratedStorage.build(
  storageDirectory: await getApplicationDocumentsDirectory(),
);

2. 修复状态序列化/反序列化逻辑

state.toString()返回的字符串可能包含额外信息(如内存地址),导致反序列化匹配失败。给每个状态定义唯一标识:

  • 更新prayer_type_state.dart:
@immutable
sealed class PrayerTypeState extends Equatable {
  const PrayerTypeState();
  
  // 新增状态唯一标识字段
  String get type;

  @override
  List<Object?> get props => [];
}

class PrayerTypeInitial extends PrayerTypeState {
  const PrayerTypeInitial();
  
  @override
  String get type => 'PrayerTypeInitial';
}

class LocationSelectedState extends PrayerTypeState {
  const LocationSelectedState();
  
  @override
  String get type => 'LocationSelectedState';
}

class GpsSelectedState extends PrayerTypeState {
  const GpsSelectedState();
  
  @override
  String get type => 'GpsSelectedState';
}
  • 更新prayer_type_bloc.dart的序列化方法:
@override
PrayerTypeState? fromJson(Map<String, dynamic> json) {
  final type = json['prayer_type'] as String?;
  switch (type) {
    case 'LocationSelectedState':
      return const LocationSelectedState();
    case 'GpsSelectedState':
      return const GpsSelectedState();
    default:
      return const PrayerTypeInitial(); // 匹配失败时返回初始状态
  }
}

@override
Map<String, dynamic>? toJson(PrayerTypeState state) {
  return {'prayer_type': state.type};
}

3. 完善初始状态处理

原fromJson方法在匹配失败时返回null,会触发HydratedBloc使用默认初始状态,明确返回PrayerTypeInitial可避免潜在问题。


内容的提问来源于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 06:29:51