为何Provider返回初始值而非预期的Firestore用户名称值?
问题描述
我尝试从Firestore获取文档字段值,并通过Provider在其他页面展示。以下是我的代码:
Provider文件代码
class UserState extends ChangeNotifier { String userName = 'default error'; Future<void> getName() async { await FirebaseFirestore.instance .collection("Users") .doc(FirebaseAuth.instance.currentUser!.uid) .get() .then((value) { userName = (value.data()?[' uname'] ?? "Default userName"); print(userName); }); notifyListeners(); } }
此处print语句能输出正确的userName值,但通过Provider传递该值时,页面显示的却是初始字符串'default error'。
展示页面代码
class testscreen extends StatefulWidget { const testscreen({Key? key}) : super(key: key); _testscreenState createState() => _testscreenState(); } class _testscreenState extends State<testscreen> { @override Widget build(BuildContext context) { Provider.of<UserState>(context, listen: false).getName(); final String name = Provider.of<UserState>(context).userName; return Scaffold(body: Text(name)); } }
请问如何让页面显示正确的userName值而非初始值?我的代码存在什么问题?
问题原因
build方法重复触发异步请求:每次页面重建都会调用getName(),而Firestore请求是异步操作,第一次build时userName还未更新,因此先显示初始值;后续notifyListeners()触发页面重建时,又会再次调用getName(),陷入循环。- 异步更新时机不匹配:第一次
build执行时,Firestore请求尚未完成,Provider.of读取的是初始状态的userName,请求完成后虽然更新了值,但此时当前build周期已经结束,无法即时刷新。
修复方案
方案1:将异步请求移至initState
只在页面初始化时调用一次getName(),避免重复请求:
class _testscreenState extends State<testscreen> { @override void initState() { super.initState(); // 仅在页面初始化时发起一次请求 Provider.of<UserState>(context, listen: false).getName(); } @override Widget build(BuildContext context) { final String name = Provider.of<UserState>(context).userName; return Scaffold(body: Text(name)); } }
页面初始化时发起Firestore请求,请求完成后notifyListeners()会触发页面重建,此时就能读取到更新后的userName。
方案2:优化Provider异步逻辑(可选)
合并await与then,简化代码逻辑,同时检查字段名是否存在空格错误:
Future<void> getName() async { final docSnapshot = await FirebaseFirestore.instance .collection("Users") .doc(FirebaseAuth.instance.currentUser!.uid) .get(); // 注意:如果Firestore字段名是`uname`而非` uname`,请去掉空格 userName = docSnapshot.data()?['uname'] ?? "Default userName"; notifyListeners(); }
方案3:使用Consumer简化监听(可选)
用Consumer替代直接调用Provider.of,代码更简洁且自动监听状态变化:
class _testscreenState extends State<testscreen> { @override void initState() { super.initState(); Provider.of<UserState>(context, listen: false).getName(); } @override Widget build(BuildContext context) { return Scaffold( body: Consumer<UserState>( builder: (context, userState, child) { return Text(userState.userName); }, ), ); } }
内容的提问来源于stack exchange,提问作者Devika
相关产品推荐
相关产品推荐

