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

Flutter中使用Provider实现API响应成功时页面跳转的正确方式

问题解决:结合Provider避免BuildContext异步间隙错误

核心问题原因

你遇到的Don't use 'BuildContext's across async gaps错误,是因为在await异步操作后直接使用传入的BuildContext——此时该上下文对应的Widget可能已经被销毁(比如用户中途退出页面),导致上下文失效。

正确实现思路

让Provider只负责业务逻辑与状态管理,UI层通过监听Provider的状态变化来执行导航操作,彻底避免在Provider方法中持有BuildContext。


步骤1:修改AuthProvider(移除BuildContext依赖)

class AuthProvider extends ChangeNotifier {
  DanhMucNguoiSuDungLoginRequest? _currentUser;
  bool isLoading = false;
  String loginMsg = '';

  DanhMucNguoiSuDungLoginRequest? get currentUser => _currentUser;

  void login({
    required String userName,
    required String password,
  }) async {
    isLoading = true;
    notifyListeners();

    final loginResult = await DoLoginApi(DanhMucNguoiSuDungLoginRequest(
      Ma: userName,
      MatKhau: password,
    ));
    
    isLoading = false;
    if (loginResult.Status == '0') {
      _currentUser = loginResult;
      notifyListeners();
    } else {
      loginMsg = loginResult.ErrorMsg;
      notifyListeners();
    }
  }

  // 用于重置登录状态,避免重复跳转
  void resetLoginState() {
    _currentUser = null;
    loginMsg = '';
    notifyListeners();
  }
}

步骤2:登录页面UI层监听状态并处理导航

class LoginScreen extends StatelessWidget {
  final TextEditingController _userNameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Consumer<AuthProvider>(
        builder: (context, authProvider, child) {
          // 状态更新后执行导航,确保上下文有效
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (authProvider.currentUser != null) {
              Navigator.of(context).pushReplacement(
                MaterialPageRoute(builder: (_) => HomeScreen()),
              );
              // 跳转后重置状态,防止页面重建时重复触发跳转
              authProvider.resetLoginState();
            }
          });

          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextField(
                controller: _userNameController,
                hintText: '用户名',
                enabled: !authProvider.isLoading,
              ),
              TextField(
                controller: _passwordController,
                hintText: '密码',
                obscureText: true,
                enabled: !authProvider.isLoading,
              ),
              const SizedBox(height: 20),
              ElevatedButton(
                onPressed: authProvider.isLoading
                    ? null
                    : () {
                        authProvider.login(
                          userName: _userNameController.text.trim(),
                          password: _passwordController.text.trim(),
                        );
                      },
                child: authProvider.isLoading
                    ? const SizedBox(
                        width: 16,
                        height: 16,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : const Text('登录'),
              ),
              if (authProvider.loginMsg.isNotEmpty)
                Padding(
                  padding: const EdgeInsets.only(top: 10),
                  child: Text(
                    authProvider.loginMsg,
                    style: const TextStyle(color: Colors.red),
                  ),
                ),
            ],
          );
        },
      ),
    );
  }
}

关键优化点

  1. 单一职责分离:Provider只处理登录逻辑和状态存储,不涉及UI导航,符合架构设计原则。
  2. 安全的上下文使用:通过addPostFrameCallback确保在Widget构建完成后再执行导航,此时BuildContext一定处于活跃状态。
  3. 避免重复跳转:跳转后重置登录状态,防止页面因重建(如屏幕旋转)重复触发导航。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:04:57