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

Riverpod StateNotifier冷启动异常但热重启正常的问题求助

Riverpod StateNotifier冷启动异常但热重启正常的问题求助

各位大佬好,我现在用Riverpod做状态管理开发Flutter应用,碰到了一个很奇怪的问题:
在**冷启动(执行flutter run后第一次打开App)**时,明明网络是正常的,启动页却一直显示「无网络连接或服务器离线」的提示;但只要做一次热重启,整个流程就完全正常了——网络状态能正确识别,也会正常跳转到登录页或主应用壳。

我自己调试后怀疑是竞态条件导致的:UI在异步网络检查还没完成的时候,就用了NetworkStatus的初始状态(isNetworkOnline: false)进行渲染,而且后续网络检查完成后,状态更新没有正确触发UI刷新?或者是网络检查的逻辑在冷启动时没正确执行?

先给大家说清楚我的启动页逻辑:
启动页需要同时检查两个状态:

  1. 网络连接状态:不通的话直接显示错误提示
  2. 用户认证状态:网络通的前提下,根据认证状态跳转到对应页面
    • 认证中:显示「服务器在线,检查认证中...」
    • 未认证:跳登录页
    • 已认证:跳主应用壳

下面是相关的代码片段:

splash_screen.dart

class SplashScreen extends ConsumerStatefulWidget {
  const SplashScreen({super.key});
  static final String routeName = "/splashScreen";

  @override
  ConsumerState createState() => _SplashScreenState();
}

class _SplashScreenState extends ConsumerState<SplashScreen> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      ref.read(networkStatusNotifierProvider);
    });
  }

  @override
  Widget build(BuildContext context) {
    final networkStatus = ref.watch(networkStatusNotifierProvider);
    final authState = ref.watch(authStateNotifierProvider);

    ref.listen<AuthState>(authStateNotifierProvider, (prev, next) {
      final networkStatus = ref.watch(networkStatusNotifierProvider);
      if (networkStatus.isNetworkOnline) {
        if (next.status == AuthStatus.authenticated) {
          if (mounted) {
            Navigator.pushReplacementNamed(context, MainAppShell.routeName);
          }
        } else if (next.status == AuthStatus.unauthenticated) {
          if (mounted) {
            Navigator.pushReplacementNamed(context, LoginScreen.routeName);
          }
        }
      }
    });

    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Image.asset(
              Constants.logoPath,
              height: MediaQuery.sizeOf(context).height * 0.5,
              width: MediaQuery.sizeOf(context).width * 0.5,
            ),
            SizedBox(height: 20),
            if (!networkStatus.isNetworkOnline)
              _buildErrorMessage("No Internet Connection or Server Offline")
            else if (authState.status == AuthStatus.loading ||
                authState.status == AuthStatus.initial)
              _buildLoadingIndicator(
                "Server online. Checking authentication...",
              )
            else if (authState.status == AuthStatus.error)
              _buildErrorMessage(
                "Authentication failed: ${authState.errorMessage ?? 'Unknown auth error'}",
                showLoginButton: true,
              ),
          ],
        ),
      ),
    );
  }

  _buildLoadingIndicator(String message) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(message),
          SizedBox(height: 10),
          CircularProgressIndicator(),
        ],
      ),
    );
  }

  _buildErrorMessage(String message, {bool showLoginButton = false}) {
    return Center(
      child: Column(
        children: [
          Text(message),
          if (showLoginButton) ...[
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                Navigator.pushReplacementNamed(context, LoginScreen.routeName);
              },
              child: const Text("Login"),
            ),
          ],
        ],
      ),
    );
  }
}

network_status_notifier.dart

final networkStatusNotifierProvider = StateNotifierProvider<NetworkStatusNotifier, NetworkStatus>((ref) {
  return NetworkStatusNotifier(ref);
});

class NetworkStatusNotifier extends StateNotifier<NetworkStatus> {
  NetworkStatusNotifier(this._ref) : super(NetworkStatus()) {
    getNetworkStatus();
  }

  final Ref _ref;

  void getNetworkStatus() async {
    state = state.copyWith(isNetworkOnline: false);
    final networkRepo = _ref.read(networkRepositoryProvider);
    final response = await networkRepo.getNetworkStatus();
    if (response.isSuccess && response.data != null) {
      state = state.copyWith(isNetworkOnline: true);
      return;
    } else {
      state = state.copyWith(isNetworkOnline: false);
      return;
    }
  }

  void clearNetworkStatus() {
    state = state.copyWith(isNetworkOnline: false);
  }
}

我已经尝试过的解决方法

为了修复这个问题,我试了好几种方式,但都没效果:

  • 在initState里用Future.microtask来读取network provider
  • 直接在initState里调用ref.read(networkStatusNotifierProvider)
  • 在build方法里直接用ref.watch监听状态(就是现在的写法)
  • 用WidgetsBinding.instance.addPostFrameCallback来延迟读取provider

现在实在没思路了,想请教大家:我到底哪里考虑漏了?怎么才能确保冷启动时,网络检查完成后UI能正确更新状态,而不是一直停留在初始的「无网络」错误提示?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 08:54:52