Flutter首次加载API返回null 热重载后正常获取数据问题
问题原因
你遇到的首次加载一直显示加载态、热重载后才正常显示数据的问题,是三处代码写法错误共同导致的:
initState是Flutter框架同步调用的生命周期方法,不能加async关键字。加了async后,方法内的异步逻辑不会被框架等待,第一次页面渲染执行时,接口请求还没返回,data必然是null。- 你把接口请求方法
getData、请求地址url的定义全写在了build方法内部,而且请求拿到返回值后,把触发页面刷新的setState(() {})代码注释掉了。就算接口请求成功拿到了数据,没有调用setState通知框架重绘,页面会一直停留在初始的加载状态。 initState中调用getData()时没有加await关键字,就算initState支持异步执行,不await的话调用完请求方法会直接往下走渲染逻辑,不会等接口返回再更新页面。
另外你在类中已经定义了url成员变量,又在build方法里定义了同名的局部url变量,属于容易引发逻辑混乱的冗余写法。
修正方案
把数据请求逻辑、url初始化全部挪到build方法外,用正确的生命周期写法处理异步请求,拿到数据后通过setState触发页面刷新,修正后的核心代码如下:
// 原有import代码保持不变即可 class Profile extends StatefulWidget { var usrid; Profile({Key? key, @required this.usrid}) : super(key: key); @override _Profile createState() => _Profile(); } class _Profile extends State<Profile>{ var data; late Uri requestUrl; @override void initState(){ super.initState(); // 初始化请求地址 requestUrl = Uri.https( 'www.******.net', '/mm_api/index.php', {'act':'profile','UsrID': widget.usrid} ); // 触发数据加载 _loadProfileData(); } // 数据请求方法作为类成员方法,不要写在build内部 Future<void> _loadProfileData() async{ try{ final res = await http.get( requestUrl, headers: {'Content-Type': 'application/json'}, ); // 先判断请求状态码是否正常 if(res.statusCode == 200){ final resData = jsonDecode(res.body); // 赋值后必须调用setState触发页面重绘 setState(() { data = resData; }); } }catch(e){ // 可在此处添加网络错误、接口报错的提示逻辑 debugPrint('个人资料请求失败: $e'); } } @override Widget build(BuildContext context){ final UsrID = widget.usrid; final Size size = MediaQuery.of(context).size; final double padding = 25; final sidePadding = EdgeInsets.symmetric(horizontal: padding); return Scaffold( appBar: AppBar( titleSpacing: 3, backgroundColor: Colors.white, elevation: 0, title: const Text('My Profile', style: TextStyle(color: Colors.black, fontSize: 15,),), leading: Builder( builder: (BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(15, 0, 0, 0), child: IconButton( icon: SvgPicture.asset( width: 30, 'assets/images/Menu.svg', height: 30, ), onPressed: () { Scaffold.of(context).openDrawer(); }, tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, ), ); }, ), actions: <Widget>[ Padding( padding: sidePadding, child: Row( children: [ SvgPicture.asset( width: 30, 'assets/images/search.svg', height: 30, ), ], ) ) ], ), backgroundColor: const Color(0xff8f9df2), body: Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [Colors.white, Colors.white] ), ), width: size.width, height: size.height, child: Stack( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ data != null?Expanded( child: Padding( padding: sidePadding, child: ListView( physics: const BouncingScrollPhysics(), children: [ Text('${data[0]['name']}') ], ), ) ): const Center( child: CircularProgressIndicator(), ) ], ), ], ) ), drawer: Sidebar(), persistentFooterButtons: [ Footer(usrid:UsrID), ], ); } }
开发注意事项
- 不要给
initState、build这类Flutter框架内置的生命周期方法加async关键字,这类方法的执行时序由框架控制,加异步标记会打破原有执行流程,引发时序类bug。 - 不要把数据请求、全局变量定义这类和UI构建无关的逻辑写在
build方法中。只要页面触发重绘(比如键盘弹出、屏幕旋转、setState调用),build内的所有代码都会重新执行,会导致重复发请求、性能损耗、逻辑混乱等问题。 - 调用setState更新状态前,要保证当前组件已经挂载,页面销毁后不要继续调用setState,避免内存泄漏。
- 网络请求必须加异常捕获和状态码校验,避免网络异常、接口报错时直接出现白屏、崩溃问题。
内容的提问来源于stack exchange,提问作者RAF Algowid
相关产品推荐
相关产品推荐

