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

Flutter中CircularProgressIndicator不生效,能否用FutureBuilder实现?

用FutureBuilder实现Firebase手机认证加载状态显示

当然可以用FutureBuilder实现这个需求,它能自动管理异步任务的状态,不用手动维护isLoading变量,还能避免手动调用setState可能出现的问题。以下是具体实现步骤和代码示例:

核心思路

通过将Firebase手机认证的异步任务(phoneSign)绑定到FutureBuilder,根据异步任务的状态(等待、完成、错误)自动切换UI:

  • 任务等待时:显示CircularProgressIndicator
  • 任务完成/初始状态:显示正常页面内容(手机号输入框、认证按钮)
  • 任务出错时:显示错误提示并保留操作入口

具体代码实现

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';

// 假设你的AuthMethods类定义如下
class AuthMethods {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Future<void> phoneSign(String phoneNumber, BuildContext context) async {
    await _auth.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      verificationCompleted: (PhoneAuthCredential credential) async {
        await _auth.signInWithCredential(credential);
      },
      verificationFailed: (FirebaseAuthException e) {
        throw e; // 抛出错误,让FutureBuilder捕获
      },
      codeSent: (String verificationId, int? resendToken) {
        // 跳转到验证码输入页面,传递verificationId
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => VerificationPage(verificationId: verificationId),
          ),
        );
      },
      codeAutoRetrievalTimeout: (String verificationId) {},
    );
  }
}

class PhoneAuthPage extends StatefulWidget {
  const PhoneAuthPage({super.key});

  @override
  State<PhoneAuthPage> createState() => _PhoneAuthPageState();
}

class _PhoneAuthPageState extends State<PhoneAuthPage> {
  final TextEditingController _phoneController = TextEditingController();
  Future<void>? _phoneSignFuture; // 保存异步任务的Future

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("手机认证")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: FutureBuilder(
          future: _phoneSignFuture,
          builder: (context, snapshot) {
            // 任务处于等待状态时,显示加载指示器
            if (snapshot.connectionState == ConnectionState.waiting) {
              return const Center(child: CircularProgressIndicator());
            }

            // 任务出错时,显示错误信息+原表单
            if (snapshot.hasError) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  TextField(
                    controller: _phoneController,
                    keyboardType: TextInputType.phone,
                    decoration: const InputDecoration(hintText: "输入手机号"),
                  ),
                  const SizedBox(height: 16),
                  ElevatedButton(
                    onPressed: _triggerPhoneSign,
                    child: const Text("发送验证码"),
                  ),
                  const SizedBox(height: 16),
                  Text(
                    "认证失败: ${snapshot.error.toString()}",
                    style: const TextStyle(color: Colors.red),
                  ),
                ],
              );
            }

            // 初始状态或任务完成后,显示正常表单
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextField(
                  controller: _phoneController,
                  keyboardType: TextInputType.phone,
                  decoration: const InputDecoration(hintText: "输入手机号"),
                ),
                const SizedBox(height: 16),
                ElevatedButton(
                  onPressed: _triggerPhoneSign,
                  child: const Text("发送验证码"),
                ),
              ],
            );
          },
        ),
      ),
    );
  }

  void _triggerPhoneSign() {
    final phoneNumber = _phoneController.text.trim();
    if (phoneNumber.isNotEmpty) {
      setState(() {
        // 将异步任务赋值给Future变量,触发FutureBuilder更新状态
        _phoneSignFuture = AuthMethods().phoneSign(phoneNumber, context);
      });
    }
  }

  @override
  void dispose() {
    _phoneController.dispose();
    super.dispose();
  }
}

// 示例验证码输入页面
class VerificationPage extends StatelessWidget {
  final String verificationId;
  const VerificationPage({super.key, required this.verificationId});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("输入验证码")),
      body: const Center(child: TextField(decoration: InputDecoration(hintText: "输入6位验证码"))),
    );
  }
}

关键说明

  1. _phoneSignFuture变量:用来保存phoneSign方法的异步任务,每次点击按钮时通过setState更新它,触发FutureBuilder重新构建UI。
  2. ConnectionState判断:通过snapshot.connectionState == ConnectionState.waiting直接判断任务是否在执行,无需手动维护isLoading。
  3. 错误处理:在phoneSign方法中抛出认证错误,FutureBuilder会通过snapshot.hasError捕获并显示错误信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:35:52