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

Flutter中如何为登录失败的ElevatedButton添加AlertDialog

问题:登录失败时如何弹出AlertDialog?

我在Flutter应用中实现了一个登录按钮,代码如下:

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

  @override
  Widget build(BuildContext context) {
    final userDataNotifier = Provider.of<UserDataNotifier>(context);

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            'Please login',
            style: Theme.of(context).textTheme.headlineMedium,
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 10),
          ElevatedButton(
            onPressed: userDataNotifier.login,
            child: Opacity(
              opacity: userDataNotifier.isLoading ? 0.2 : 1.0,
              child: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.login),
                  SizedBox(width: 10),
                  Text('Login'),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

其中,UserDataNotifier类使用SharedPreferences保存用户状态,其login方法代码如下:

class UserDataNotifier with ChangeNotifier {
  final SharedPreferences prefs;
  bool _isLogged = false;
  bool _isLoading = false;
  UserData? _userData;

  bool get isLogged => _isLogged;
  bool get isLoading => _isLoading;
  UserData? get userData => _userData;

  Future<bool> login() async {
    _isLoading = true;
    notifyListeners();

    var url = Uri.https('mydomain', 'api/login');

    // if there is no internet connection, throw an error
    try {
      var response = await http.get(url);
      var responseBody = response.body;

      // parse response and set user-data (on success)
      // or throw an error (on login failure)

      _isLogged = true;
      _isLoading = false;
      prefs.setBool('isLogged', _isLogged);
      notifyListeners();
      return true;

    } catch (e) {
      _isLoading = false;
      notifyListeners();
      return false;
    }
  }
}

我希望在登录失败(无网络连接、凭证错误等场景)时弹出AlertDialog,但尝试在按钮或UserDataNotifier类中添加均未成功,请问该如何正确实现这一功能?


解决方案

方法一:UI层直接处理登录结果(推荐)

UserDataNotifier的login方法返回Future<bool>,可以在按钮的onPressed中异步调用该方法,根据返回值判断是否弹出对话框。这种方式符合关注点分离原则,业务逻辑(登录)和UI操作(弹框)分开,避免ViewModel依赖UI上下文。

修改PublicHomePage中ElevatedButton的onPressed:

ElevatedButton(
  onPressed: () async {
    final loginSuccess = await userDataNotifier.login();
    // 检查页面是否仍挂载,避免已销毁时调用context导致错误
    if (!loginSuccess && mounted) {
      showDialog(
        context: context,
        builder: (dialogContext) => AlertDialog(
          title: const Text('登录失败'),
          content: const Text('请检查网络连接或登录信息后重试'),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(dialogContext).pop(),
              child: const Text('确定'),
            ),
          ],
        ),
      );
    }
  },
  // 原child代码不变
  child: Opacity(
    opacity: userDataNotifier.isLoading ? 0.2 : 1.0,
    child: const Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Icon(Icons.login),
        SizedBox(width: 10),
        Text('Login'),
      ],
    ),
  ),
)

方法二:ViewModel传递错误信息,UI监听显示

如果需要在ViewModel中统一处理错误细节(比如区分无网络、凭证错误等不同提示),可以在UserDataNotifier中添加错误信息字段,登录失败时更新该字段,UI监听字段变化弹出对话框。

1. 修改UserDataNotifier

添加错误信息字段及清除方法:

class UserDataNotifier with ChangeNotifier {
  // 原有字段不变
  String? _errorMessage;
  String? get errorMessage => _errorMessage;

  // 原有login方法修改catch块
  Future<bool> login() async {
    _isLoading = true;
    _errorMessage = null; // 清空之前的错误
    notifyListeners();

    var url = Uri.https('mydomain', 'api/login');

    try {
      // 原有登录逻辑不变
    } catch (e) {
      _isLoading = false;
      // 根据错误类型设置友好提示,示例直接用错误字符串
      _errorMessage = e.toString();
      notifyListeners();
      return false;
    }
  }

  // 添加清除错误方法,避免重复弹出对话框
  void clearError() {
    _errorMessage = null;
    notifyListeners();
  }
}

2. 修改PublicHomePage监听错误信息

在build方法中添加监听逻辑,使用addPostFrameCallback确保在Widget构建完成后检查错误:

@override
Widget build(BuildContext context) {
  final userDataNotifier = Provider.of<UserDataNotifier>(context);

  // 监听错误信息变化,弹出对话框
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (userDataNotifier.errorMessage != null && mounted) {
      showDialog(
        context: context,
        builder: (dialogContext) => AlertDialog(
          title: const Text('登录失败'),
          content: Text(userDataNotifier.errorMessage!),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(dialogContext).pop();
                // 清除错误,防止再次进入页面时重复弹出
                userDataNotifier.clearError();
              },
              child: const Text('确定'),
            ),
          ],
        ),
      );
    }
  });

  // 原有build代码不变
  return Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        // 原有组件不变
      ],
    ),
  );
}

注意事项

  • 必须检查mounted状态:防止页面已销毁时调用showDialog,避免内存泄漏或Flutter框架错误。
  • 错误信息清除:方法二中必须在对话框关闭后清除错误信息,否则页面重建时会重复弹出对话框。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:13:22