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

Flutter中如何结合REST API使用hydrated_bloc实现状态持久化?

解决HydratedBloc结合REST API的状态持久化问题

我看了你的代码,发现几个关键问题导致无法实现关闭应用后保留状态的需求,咱们一步步来修复:

首先,明确核心问题点

  1. Bloc没有注入API仓库,也没有实现事件处理逻辑,无法触发API请求加载用户数据
  2. UserblocInitial状态里持有了CloudApiRepository,这不符合Bloc的设计原则——State应该只存储数据,不持有业务逻辑或依赖
  3. 初始状态的写法有误,HydratedBloc的初始状态应该正确复用持久化的状态
  4. UI层没有触发加载用户的事件,导致一直停留在初始状态

步骤1:添加事件类

首先创建LoadUsers事件,用于触发API请求:

part of 'userbloc_bloc.dart';

abstract class UserblocEvent extends Equatable {
  const UserblocEvent();

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

class LoadUsers extends UserblocEvent {}

步骤2:修正State类

移除State中的仓库依赖,只保留数据存储,并添加错误状态用于处理请求失败:

part of 'userbloc_bloc.dart';

abstract class UserblocState extends Equatable {
  const UserblocState();

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

class UserblocInitial extends UserblocState {}

class UserblocLoading extends UserblocState {}

class UserblocLoaded extends UserblocState {
  final UserModel userModel;

  const UserblocLoaded(this.userModel);

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

class UserblocError extends UserblocState {
  final String message;

  const UserblocError(this.message);

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

步骤3:重构Bloc类

注入API仓库,实现事件处理逻辑,正确处理持久化:

import 'dart:async';
import 'package:di/users/model/user.dart';
import 'package:di/users/repository/cloud_api_repository.dart';
import 'package:equatable/equatable.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
part 'userbloc_event.dart';
part 'userbloc_state.dart';

class UserblocBloc extends HydratedBloc<UserblocEvent, UserblocState> {
  final CloudApiRepository cloudApiRepository;

  // 通过构造函数注入仓库,指定初始状态
  UserblocBloc({required this.cloudApiRepository}) : super(UserblocInitial()) {
    on<LoadUsers>(_mapLoadUsersToState);
  }

  // 处理加载用户事件
  Future<void> _mapLoadUsersToState(LoadUsers event, Emitter<UserblocState> emit) async {
    // 可选:如果已有持久化的加载完成状态,直接跳过请求(根据需求调整)
    if (state is UserblocLoaded) return;

    emit(UserblocLoading());
    try {
      final userModel = await cloudApiRepository.getlistUser();
      emit(UserblocLoaded(userModel));
    } catch (e) {
      emit(UserblocError(e.toString()));
    }
  }

  @override
  UserblocState? fromJson(Map<String, dynamic> json) {
    try {
      final userModel = UserModel.fromJson(json);
      return UserblocLoaded(userModel);
    } catch (_) {
      return null; // 解析失败时返回null,会使用初始状态
    }
  }

  @override
  Map<String, dynamic>? toJson(UserblocState state) {
    if (state is UserblocLoaded) {
      return state.userModel.toJson();
    }
    return null; // 只有加载完成的状态需要持久化
  }
}

步骤4:修正UI层

正确注入Bloc和仓库,在页面初始化时触发加载事件:

import 'package:di/users/bloc/userbloc/userbloc_bloc.dart';
import 'package:di/users/model/user.dart';
import 'package:di/users/repository/cloud_api_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

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

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

class _HomeUserState extends State<HomeUser> {
  late UserblocBloc _userBloc;

  @override
  void initState() {
    super.initState();
    // 获取Bloc实例并触发加载事件
    _userBloc = BlocProvider.of<UserblocBloc>(context);
    _userBloc.add(LoadUsers());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocProvider(
        // 注入CloudApiRepository实例(根据你的实际创建方式调整)
        create: (context) => UserblocBloc(cloudApiRepository: CloudApiRepository()),
        child: BlocBuilder<UserblocBloc, UserblocState>(
          builder: (BuildContext context, UserblocState state) {
            if (state is UserblocInitial) {
              return const ShowAnyText();
            } else if (state is UserblocLoading) {
              return const BuildLoading();
            } else if (state is UserblocLoaded) {
              return BuildColumnWithData(state.userModel.data);
            } else if (state is UserblocError) {
              return Center(child: Text(state.message));
            }
            return const ShowAnyText();
          },
        ),
      ),
    );
  }
}

// 重构为StatelessWidget更符合Flutter最佳实践
class BuildColumnWithData extends StatelessWidget {
  final List<User> users;

  const BuildColumnWithData(this.users, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (BuildContext context, int i) {
        return Container(
          padding: const EdgeInsets.all(15),
          child: Text("${users[i].firstName}", style: const TextStyle(color: Colors.white)),
          decoration: const BoxDecoration(color: Colors.black),
        );
      },
    );
  }
}

class BuildLoading extends StatelessWidget {
  const BuildLoading({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Center(child: CircularProgressIndicator());
  }
}

class ShowAnyText extends StatelessWidget {
  const ShowAnyText({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text("Construyendo el widget"));
  }
}

最后,别忘记初始化HydratedBloc

在main.dart中初始化Hydrated存储,这是持久化生效的前提:

import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // 初始化Hydrated存储
  HydratedBloc.storage = await HydratedStorage.build(
    storageDirectory: await getApplicationDocumentsDirectory(),
  );
  runApp(const MyApp());
}

(需要在pubspec.yaml中添加path_provider依赖)

额外说明

  • 确保UserModel和User类的toJson/fromJson方法实现正确,这是序列化和反序列化的关键
  • 如果希望每次打开应用都重新请求最新数据,可删除_mapLoadUsersToState中的if (state is UserblocLoaded) return;代码行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 12:47:29