Flutter中无法从User模型类获取uid至buildContext的问题
问题:User模型赋值uid后在buildContext中访问为null
我无法从User模型类中获取uid,于是在LoginScreen类的postRequest方法中为其赋值,该方法内成功将uid数据存入User模型并能正常打印,但在buildContext中访问时uid却显示为null。
LoginPage类代码
import 'dart:convert'; import 'dart:io'; import 'package:./model/user.dart'; import 'package:http/http.dart' as http; import 'package:./screens/secret_screen.dart'; import 'package:./screens/signup.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State<LoginPage> createState() => _LoginPageState(); } final password = TextEditingController(); User user = User(); class _LoginPageState extends State<LoginPage> { final email = TextEditingController(); bool? isEmailValid; final password = TextEditingController(); bool? isPassworddValid; bool _passwordVisible = false; postRequest(email, password) async { var url = Uri.parse("http://myApiIpHere/login"); Map data = {"email": email.text, "password": password.text}; //encode Map to JSON var body = json.encode(data); var response = await http.post(url, headers: { "accept": "application/json", "content-type": "application/json" }, body: body); var res = json.decode(response.body); // below uid is assigned to user model var uid and it prints properly also user.uid = res["uid"]; print("uid is ${user.uid}"); if (user.uid == null) print("------uid isss null"); else print("------uis is properly assinged "); } @override Widget build(BuildContext context) { print("uid - ${user!.uid}"); // but in above print statement its not printing uid from model class var size = MediaQuery.of(context).size; return Scaffold( body: Row( children: [ Container( child: Expanded( child: Image.asset( height: size.height * 1, 'assets/images/login.jpg', fit: BoxFit.fill, ), ), ), Expanded( //<-- Expanded widget child: Container( color: Colors.white, // constraints: const BoxConstraints(maxWidth: 21), padding: EdgeInsets.symmetric( horizontal: size.width * 0.1, vertical: size.height * 0.05), child: SingleChildScrollView( physics: ClampingScrollPhysics(), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( width: size.width * 0.3, height: size.height * 0.1, child: Image.asset( 'assets/images/logo1.jpg', fit: BoxFit.contain, ), ), SizedBox(height: 25), Text( 'Welcome back', style: GoogleFonts.inter( fontSize: 17, color: Colors.black, ), ), const SizedBox(height: 8), Text( 'Login to your account', style: GoogleFonts.inter( fontSize: 23, color: Colors.black, fontWeight: FontWeight.w700, ), ), const SizedBox(height: 35), const Padding( child: Text( 'Email', style: TextStyle(color: Colors.black, fontSize: 16), ), padding: EdgeInsets.only(bottom: 10)), TextField( onChanged: (val) { setState(() {}); // print(val); }, cursorColor: Colors.black, style: TextStyle( color: Colors.black, ), cursorHeight: 30, maxLines: 1, maxLength: 30, cursorWidth: 1, controller: email, decoration: InputDecoration( focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(4)), borderSide: BorderSide(width: 1, color: Colors.black), ), enabledBorder: const OutlineInputBorder( borderSide: const BorderSide(color: Colors.grey, width: 0.0), ), focusColor: Colors.white, fillColor: Colors.white30, filled: true, ), ), const Padding( child: Text( 'Password', style: TextStyle(color: Colors.black, fontSize: 16), ), padding: EdgeInsets.only(bottom: 10)), TextField( obscureText: !_passwordVisible, onChanged: (val) { setState(() {}); // print(val); }, cursorColor: Colors.black, style: TextStyle( color: Colors.black, ), cursorHeight: 30, maxLines: 1, maxLength: 30, cursorWidth: 1, controller: password, decoration: InputDecoration( suffixIcon: IconButton( onPressed: () { setState(() { _passwordVisible = !_passwordVisible; }); }, icon: Icon( _passwordVisible ? Icons.remove_red_eye : Icons.visibility_off, color: Colors.blue, ), ), focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(4)), borderSide: BorderSide(width: 1, color: Colors.black), ), enabledBorder: const OutlineInputBorder( borderSide: BorderSide(color: Colors.grey, width: 0.0), ), focusColor: Colors.white, fillColor: Colors.white30, filled: true, ), ), const Padding( child: Text( 'Forgot password?', style: TextStyle(color: Colors.black, fontSize: 16), ), padding: EdgeInsets.only(bottom: 2)), const SizedBox(height: 25), ElevatedButton( style: ElevatedButton.styleFrom( fixedSize: const Size(100, 50), backgroundColor: Colors.blue), onPressed: () async { setState(() { user!.uid; print("setstate ${user!.uid}"); }); final SharedPreferences sp = await SharedPreferences.getInstance(); sp.setString("email", email.text); sp.setString("password", password.text); try { postRequest(email, password); } catch (e) { print("$e is your error"); } Navigator.of(context).push(MaterialPageRoute( builder: (context) => SecretScreen(uid: user!.uid!))); }, child: const Text('Login now')), const SizedBox(height: 25), ], ), ), ), ), ], )); } }
User模型类代码
import 'dart:convert'; User userFromJson(String str) => User.fromJson(json.decode(str)); String userToJson(User data) => json.encode(data.toJson()); class User { User({ this.email, this.password, this.accessCode, this.uid, this.secretToken, }); String? uid; String? email; String? password; String? accessCode; String? secretToken; factory User.fromJson(Map<String, dynamic> json) => User( email: json["email"], uid: json["uid"], password: json["password"], accessCode: json["access_code"], secretToken: json["secret_token"], ); Map<String, dynamic> toJson() => { "email": email, "uid":uid, "password": password, "access_code": accessCode, "secret_token": secretToken, }; }
问题原因与修复方案
核心问题
- 异步执行顺序错误:
postRequest是异步方法,但调用时未用await,导致Navigator跳转在请求完成前执行,此时uid尚未赋值。 - 状态未更新:uid赋值后未调用
setState通知Flutter重建UI,build方法仍读取旧值。 - 全局变量冗余:类外定义的
user和password控制器与类内重复定义的变量冲突,导致数据混乱。
修复步骤
1. 修正异步调用逻辑
在按钮点击事件中,等待请求完成后再执行跳转:
ElevatedButton( onPressed: () async { final SharedPreferences sp = await SharedPreferences.getInstance(); sp.setString("email", email.text); sp.setString("password", password.text); try { // 等待请求完成 await postRequest(email, password); // 验证uid非空后再跳转 if(user.uid != null){ Navigator.of(context).push(MaterialPageRoute( builder: (context) => SecretScreen(uid: user.uid!))); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("登录失败,请检查账号密码")) ); } } catch (e) { print("$e is your error"); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("请求出错:$e")) ); } }, child: const Text('Login now') )
2. 赋值后更新状态
在postRequest中用setState包裹uid赋值操作,触发UI重建:
postRequest(email, password) async { var url = Uri.parse("http://myApiIpHere/login"); Map data = {"email": email.text, "password": password.text}; var body = json.encode(data); var response = await http.post(url, headers: { "accept": "application/json", "content-type": "application/json" }, body: body); var res = json.decode(response.body); setState(() { user.uid = res["uid"]; }); print("uid is ${user.uid}"); if (user.uid == null) print("------uid isss null"); else print("------uis is properly assinged "); }
3. 清理冗余全局变量
删除类外的冗余定义,将user移到State类内部:
// 删除类外的这两行 // final password = TextEditingController(); // User user = User(); class _LoginPageState extends State<LoginPage> { final email = TextEditingController(); bool? isEmailValid; final password = TextEditingController(); bool? isPassworddValid; bool _passwordVisible = false; // 将user移到此处作为状态变量 User user = User(); // ...其他方法 }
4. 修正build中的打印逻辑
直接访问内部状态变量,避免不必要的非空断言:
@override Widget build(BuildContext context) { print("uid - ${user.uid}"); // ...其他代码 }
内容的提问来源于stack exchange,提问作者Abhishek
相关产品推荐
相关产品推荐

