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

Flutter中Firebase初始化与网络重连后导航失效问题排查

Flutter无网启动后联网重试时导航失效问题

我用Flutter开发一款依赖Firebase数据库的移动应用,仅能在联网状态下运行。在main函数中初始化Firebase,用户无网启动时会弹出提示对话框,联网后重新执行初始化与导航流程。正常联网启动时导航正常,但无网启动后联网重试的场景下,尽管所有函数调用都已正常执行(日志已确认),导航却无法生效。

启动页代码

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../View Models/Splash View Model.dart';
import 'Signup Screen.dart'; // 替换为实际注册页路径

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

  @override
  Widget build(BuildContext context) {
    final viewModel = Provider.of<SplashViewModel>(context, listen: false);

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _initializeApp(context, viewModel ,false);
    });

    return Scaffold(
      body: Container(
        decoration: const BoxDecoration(
          image: DecorationImage(
            image: AssetImage('assets/drawables_png/splash_bg.png'),
            fit: BoxFit.cover,
          ),
        ),
        child: Column(
          children: [
            Expanded(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Image.asset(
                    'assets/drawables_png/splash_ic.png',
                    width: 150,
                    height: 150,
                  ),
                  const SizedBox(height: 20),
                  const Text(
                    'EASY FOOD JOURNAL',
                    style: TextStyle(
                      fontFamily: 'Viga',
                      fontSize: 24,
                      color: Colors.white,
                    ),
                  ),
                  const SizedBox(height: 10),
                  const Text(
                    'Effortless Tracking for Delicious Living!',
                    style: TextStyle(
                      fontFamily: 'Outfit',
                      fontSize: 16,
                      color: Colors.white,
                    ),
                  ),
                  const SizedBox(height: 40),
                ],
              ),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 60.0, vertical: 30),
              child: Consumer<SplashViewModel>(
                builder: (context, viewModel, child) {
                  return LinearProgressIndicator(
                    value: viewModel.progress,
                    backgroundColor: const Color(0xff29ffffff),
                    valueColor: const AlwaysStoppedAnimation<Color>(Colors.white),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  void _initializeApp(BuildContext context, SplashViewModel viewModel , bool callFirebase) async {
    print("finally here011");
    bool connected = await viewModel.checkInternetConnection();

    if (connected) {
      bool firebaseInitialized = await viewModel.initializeFirebase(callFirebase);
      if (firebaseInitialized) {
        print("finally here211");
        await viewModel.startProgress();
        print("finally here311");
        _navigateBasedOnLoginStatus(context, viewModel);
        print("finally here411");
      } else {
        _showNoConnectionDialog(context, viewModel);
      }
    } else {
      _showNoConnectionDialog(context, viewModel);
    }
  }

  void _showNoConnectionDialog(BuildContext context, SplashViewModel viewModel) {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("No Internet Connection"),
          content: const Text("Please connect to the internet to continue."),
          actions: [
            TextButton(
              child: const Text("Retry"),
              onPressed: () {
                Navigator.of(context).pop();
                _initializeApp(context, viewModel , true); // 重试初始化
              },
            ),
          ],
        );
      },
    );
  }

  void _navigateBasedOnLoginStatus(BuildContext context, SplashViewModel viewModel) async {
    bool isLoggedIn = await viewModel.isUserLoggedIn();

    // 使用addPostFrameCallback确保上下文有效
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (isLoggedIn) {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (_) => const SignupScreen()), // 登录状态下跳转首页
        );
      } else {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (_) => const SignupScreen()), // 未登录跳转注册页
        );
      }
    });
  }
}

ViewModel代码

import 'dart:async';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/cupertino.dart';

import '../Utils/SharedPrefs.dart';
class SplashViewModel with ChangeNotifier {
  double _progress = 0.0;
  Timer? _timer;

  double get progress => _progress;

  /// 单独初始化Firebase,控制调用时机
  Future<bool> initializeFirebase( bool callFirebase) async {
    try {
      if(callFirebase){
        if (Firebase.apps.isEmpty) {
          await Firebase.initializeApp();
        }
      }
      return true;
    } catch (e) {
      print("Firebase initialization failed: $e");
      return false;
    }
  }

  /// 启动进度条动画
  Future<void> startProgress() async {
    _resetProgress();

    // 启动进度条动画
    _timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
      if (_progress < 1.0) {
        _progress += 0.02; // 每次增加2%进度
        notifyListeners();
      } else {
        timer.cancel();
      }
    });

    // 为动画一致性,2秒后完成进度
    if (Firebase.apps.length != 0) {
      await Future.delayed(const Duration(seconds: 2));
      _progress = 1.0;
      notifyListeners();
      _timer?.cancel();
    }
  }

  void _resetProgress() {
    _progress = 0.0;
    notifyListeners();
    _timer?.cancel();
  }

  Future<bool> checkInternetConnection() async {
    var connectivityResult = await Connectivity().checkConnectivity();
    return connectivityResult != ConnectivityResult.none;
  }

  Future<bool> isUserLoggedIn() async {
    User? currentUser = FirebaseAuth.instance.currentUser;
    if (currentUser == null) return false;

    bool onboardingComplete = await SharedPreferencesUtil.getValue<bool>('onboarding_complete', false);
    return onboardingComplete;
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
}

问题原因与修复方案

核心原因

重试时导航失效的关键问题是上下文使用错误:弹窗的builder方法会生成新的上下文,你在弹窗的onPressed中传入的是弹窗上下文而非SplashScreen的根上下文,导致Navigator.pushReplacement操作的是弹窗的导航栈,而非应用的根导航栈,因此无法触发页面跳转。

修复步骤

1. 保存并使用正确的上下文

修改_showNoConnectionDialog方法,提前保存SplashScreen的上下文,避免使用弹窗上下文:

void _showNoConnectionDialog(BuildContext context, SplashViewModel viewModel) {
  final rootContext = context; // 保存SplashScreen的根上下文
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (BuildContext dialogContext) { // 这里是弹窗的上下文
      return AlertDialog(
        title: const Text("无网络连接"),
        content: const Text("请连接网络后继续。"),
        actions: [
          TextButton(
            child: const Text("重试"),
            onPressed: () {
              Navigator.of(dialogContext).pop();
              _initializeApp(rootContext, viewModel, true); // 使用根上下文
            },
          ),
        ],
      );
    },
  );
}

2. 简化导航逻辑

移除_navigateBasedOnLoginStatus中不必要的WidgetsBinding.instance.addPostFrameCallback,直接使用有效上下文导航:

void _navigateBasedOnLoginStatus(BuildContext context, SplashViewModel viewModel) async {
  bool isLoggedIn = await viewModel.isUserLoggedIn();
  
  if (isLoggedIn) {
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (_) => const SignupScreen()), // 替换为实际首页
    );
  } else {
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (_) => const SignupScreen()), // 替换为实际注册页
    );
  }
}

3. 优化进度动画逻辑(可选)

避免定时器与固定延迟的冲突,让进度动画自然完成:

Future<void> startProgress() async {
  _resetProgress();

  _timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
    if (_progress < 1.0) {
      _progress += 0.02;
      notifyListeners();
    } else {
      timer.cancel();
    }
  });

  // 等待进度完全加载完成
  await Future.doWhile(() async {
    await Future.delayed(const Duration(milliseconds: 50));
    return _progress < 1.0;
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 11:12:07