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

Hive字段值不更新问题:登录后热重载仍跳转登录页

问题:Hive热重载后isLoggedIn值不更新导致页面跳转异常

我用Hive的users box存储用户数据,在main.dart里注册并打开适配器:

void main() async {
  await Hive.initFlutter();
  Hive.registerAdapter(UserDBAdapter());
  await Hive.openBox<UserDB>('users');
  return runApp(
    MyApp(),
  );
}

在Bloc的登录函数中,登录成功后把user.isLoggedIn设为true,并调用save()持久化:

class UserBloc extends Bloc<UserEvent, UserState> {
  UserDB user = UserDB();
  final usersBox = Hive.box<UserDB>('users');
  UserRepository userRepository;

  UserBloc(this.userRepository) : super(UserInitial()) {
    createUser();
    verifyUser();
    loginUser();
    logoutUser();
    usersBox.put('user', user);
 }
   Future<void> loginUser() async {
    return on<LoginUser>((event, emit) async {
      emit(UserLoading());
      await Future.delayed(const Duration(milliseconds: 500));
      try {
        final result = await userRepository.login(
          event.email,
          event.password,
        );
        if (result['success'] == false) {
          emit(UserError(result['message']));
        } else {
          emit(
            UserLoggedIn(),
          );
          user.isLoggedIn = true; // 此处设为true
          user.token = result['token'];
          user.save(); // 保存变更到本地存储
        }
      } catch (error) {
        emit(
          UserError(
            error.toString(),
          ),
        );
      }
    });
  }

但热重载后应用会跳转到登录页而非HomeScreen,在页面的build方法中读取userDB.isLoggedIn打印为false:

@override
  void initState() {
    if (Hive.box<UserDB>('users').get('user') == null) {
      print('empty'); // 未打印,说明本地存储有用户数据
      Hive.box<UserDB>('users').put(
        'user',
        UserDB(isVerified: false, isLoggedIn: false),
      );
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final userDB = Hive.box<UserDB>('users').get('user')!;
    print(userDB.isLoggedIn); // 此处打印false而非true
    if (userDB.isLoggedIn) {
      return const NavigationScreen();
    } else if (userDB.isVerified && !userDB.isLoggedIn) {
      return AnimatedSplashScreen(
        splash: const SplashScreen(),
        duration: 4000,
        nextScreen: const LoginSignUpSwitch(),
      );
    } else {
      return AnimatedSplashScreen(
        splash: const SplashScreen(),
        duration: 4000,
        nextScreen: const WelcomeScreen(),
      );
    }
  }

UserDB类定义:

import 'package:hive/hive.dart';

part 'user.g.dart';

@HiveType(typeId: 1)
class UserDB extends HiveObject {
  UserDB({
    this.name,
    this.email,
    this.isVerified = false,
    this.isLoggedIn = false,
    this.token,
  });
  // NAME (0)
  @HiveField(0)
  String? name;

//EMAIL(1)
  @HiveField(1)
  String? email;

//IS-VERIFIED(2)
  @HiveField(2, defaultValue: false)
  bool isVerified;

//IS-LOGGEDIN(3)
  @HiveField(3, defaultValue: false)
  bool isLoggedIn;

//TOKEN(4)
  @HiveField(4)
  String? token;
}

注意:在HomeScreen中打印userDB.isLoggedIn会输出true:

final userDB = Hive.box<UserDB>('users').get('user');
    print(userDB!.isLoggedIn); // 此处打印true

原因分析

  1. Bloc初始化覆盖本地数据:UserBloc构造函数中直接创建新的UserDB()实例,然后执行usersBox.put('user', user),热重载时Bloc重新初始化,会把本地存储中已有的登录状态覆盖成初始的isLoggedIn=false。
  2. 未读取本地已有数据:Bloc初始化时没有先从Hive读取已存在的user数据,而是直接用新实例覆盖,导致登录后的状态在热重载时丢失。
  3. 页面未监听数据变化:当前页面build方法仅初始化时读取一次Hive数据,即使数据更新也不会触发重新build,但核心问题还是Bloc的初始化逻辑错误。

解决方案

1. 修正Bloc初始化逻辑

修改UserBloc构造函数,先读取本地已有数据,不存在时再创建新实例,避免覆盖已有状态:

class UserBloc extends Bloc<UserEvent, UserState> {
  late UserDB user;
  final usersBox = Hive.box<UserDB>('users');
  UserRepository userRepository;

  UserBloc(this.userRepository) : super(UserInitial()) {
    // 优先读取本地存储的用户数据,无数据时再创建新实例
    user = usersBox.get('user') ?? UserDB();
    createUser();
    verifyUser();
    loginUser();
    logoutUser();
    // 仅当本地无数据时才写入初始值
    if (!usersBox.containsKey('user')) {
      usersBox.put('user', user);
    }
 }
   // 其他代码保持不变
}

2. 页面监听Hive数据变化(可选优化)

如果需要数据更新时页面自动刷新,使用ValueListenableBuilder监听box变化:

@override
Widget build(BuildContext context) {
  return ValueListenableBuilder(
    valueListenable: Hive.box<UserDB>('users').listenable(),
    builder: (context, box, child) {
      final userDB = box.get('user')!;
      print(userDB.isLoggedIn);
      if (userDB.isLoggedIn) {
        return const NavigationScreen();
      } else if (userDB.isVerified && !userDB.isLoggedIn) {
        return AnimatedSplashScreen(
          splash: const SplashScreen(),
          duration: 4000,
          nextScreen: const LoginSignUpSwitch(),
        );
      } else {
        return AnimatedSplashScreen(
          splash: const SplashScreen(),
          duration: 4000,
          nextScreen: const WelcomeScreen(),
        );
      }
    },
  );
}

验证逻辑

修改后,热重载时Bloc会先读取本地已存储的user数据,不会再用新实例覆盖,isLoggedIn会保持登录后的true状态,页面就能正确跳转到NavigationScreen。而HomeScreen能读到正确值,是因为它没有在初始化时覆盖数据,直接读取了本地存储的最新值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:21:12