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

Flutter异步场景下BuildContext跨异步间隙使用问题及优化方案

解决Flutter异步操作中跨间隙使用BuildContext的问题

核心问题

原AuthService代码直接在异步HTTP请求完成后使用BuildContext,会导致以下风险:

  • Context失效:异步操作等待期间,发起请求的组件可能已被销毁,此时访问Context会触发异常
  • 业务与UI耦合:认证逻辑层直接依赖UI上下文调用showSnackBar、Navigator等操作,违反单一职责原则
  • 错误处理不灵活:异常反馈强绑定Context,无法在无UI场景下复用认证逻辑

重构方案

使用全局NavigatorKey获取当前有效Context,同时将UI操作从业务层剥离,让AuthService专注于认证逻辑,通过回调通知UI层处理结果。

步骤1:配置全局NavigatorKey

在项目入口文件(如main.dart)中定义全局key,并关联到MaterialApp:

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey, // 绑定全局导航key
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const LoginScreen(),
    );
  }
}

步骤2:重构AuthService

移除所有BuildContext参数,通过回调传递操作结果,优化HTTP响应处理、Provider调用和SharedPreferences逻辑:

import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:provider/provider.dart';
import 'dart:convert';
import '../models/user.dart';
import '../providers/user_provider.dart';
import '../utils/global.dart'; // 导入全局navigatorKey
import '../utils/snackbar.dart'; // 封装的SnackBar工具类

class AuthService {
  // 注册用户
  Future<void> signUpUser({
    required String email,
    required String password,
    required String name,
    required Function() onSuccess,
    required Function(String) onError,
  }) async {
    try {
      final User user = User(
        id: '',
        name: name,
        password: password,
        email: email,
        address: '',
        type: '',
        token: '',
        cart: [],
      );

      final http.Response res = await http.post(
        Uri.parse('$uri/api/signup'),
        body: user.toJson(),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
      );

      await _handleHttpResponse(
        res: res,
        onSuccess: onSuccess,
        onError: onError,
      );
    } catch (e) {
      onError(e.toString());
    }
  }

  // 登录用户
  Future<void> signInUser({
    required String email,
    required String password,
    required Function() onSuccess,
    required Function(String) onError,
  }) async {
    try {
      final http.Response res = await http.post(
        Uri.parse('$uri/api/signin'),
        body: jsonEncode({
          'email': email,
          'password': password,
        }),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
      );

      await _handleHttpResponse(
        res: res,
        onSuccess: () async {
          final SharedPreferences prefs = await SharedPreferences.getInstance();
          final userData = jsonDecode(res.body);
          
          // 通过全局key获取有效Context更新Provider
          final userProvider = Provider.of<UserProvider>(
            navigatorKey.currentContext!,
            listen: false,
          );
          userProvider.setUser(res.body);
          
          await prefs.setString('x-auth-token', userData['token']);
          onSuccess();
        },
        onError: onError,
      );
    } catch (e) {
      onError(e.toString());
    }
  }

  // 获取用户数据
  Future<void> getUserData({
    required Function() onSuccess,
    required Function(String) onError,
  }) async {
    try {
      final SharedPreferences prefs = await SharedPreferences.getInstance();
      String? token = prefs.getString('x-auth-token');

      if (token == null) {
        await prefs.setString('x-auth-token', '');
        return;
      }

      final tokenRes = await http.post(
        Uri.parse('$uri/tokenIsValid'),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
          'x-auth-token': token,
        },
      );

      final response = jsonDecode(tokenRes.body);

      if (response == true) {
        final http.Response userRes = await http.get(
          Uri.parse('$uri/'),
          headers: <String, String>{
            'Content-Type': 'application/json; charset=UTF-8',
            'x-auth-token': token,
          },
        );

        final userProvider = Provider.of<UserProvider>(
          navigatorKey.currentContext!,
          listen: false,
        );
        userProvider.setUser(userRes.body);
        onSuccess();
      }
    } catch (e) {
      onError(e.toString());
    }
  }

  // 统一处理HTTP响应
  Future<void> _handleHttpResponse({
    required http.Response res,
    required Function() onSuccess,
    required Function(String) onError,
  }) async {
    final data = jsonDecode(res.body);
    if (res.statusCode == 200) {
      onSuccess();
    } else {
      onError(data['message'] ?? '请求失败');
    }
  }
}

步骤3:UI层调用示例

在登录/注册页面中调用AuthService,通过回调处理UI反馈:

// 注册页面按钮示例
ElevatedButton(
  onPressed: () async {
    await AuthService().signUpUser(
      email: emailController.text,
      password: passwordController.text,
      name: nameController.text,
      onSuccess: () {
        showSnackBar(navigatorKey.currentContext!, '账号创建成功,请使用相同凭证登录!');
      },
      onError: (message) {
        showSnackBar(navigatorKey.currentContext!, message);
      },
    );
  },
  child: const Text('注册'),
)

// 登录页面按钮示例
ElevatedButton(
  onPressed: () async {
    await AuthService().signInUser(
      email: emailController.text,
      password: passwordController.text,
      onSuccess: () {
        Navigator.pushNamedAndRemoveUntil(
          navigatorKey.currentContext!,
          BottomBar.routeName,
          (route) => false,
        );
      },
      onError: (message) {
        showSnackBar(navigatorKey.currentContext!, message);
      },
    );
  },
  child: const Text('登录'),
)

额外优化说明

  • 错误处理解耦:业务层只返回错误信息,UI层负责显示,提升代码复用性
  • Provider调用安全:通过全局NavigatorKey获取有效Context,避免传递已销毁的Context
  • SharedPreferences操作:添加await确保异步存储完成,避免数据不一致
  • 代码复用:封装_handleHttpResponse统一处理HTTP状态,减少重复代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 13:05:55