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

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,
      };
}

问题原因与修复方案

核心问题

  1. 异步执行顺序错误:postRequest是异步方法,但调用时未用await,导致Navigator跳转在请求完成前执行,此时uid尚未赋值。
  2. 状态未更新:uid赋值后未调用setState通知Flutter重建UI,build方法仍读取旧值。
  3. 全局变量冗余:类外定义的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 18:55:23