解决Flutter报错:Null check operator used on a null value
问题根因
空检查报错和接口是否返回正确数据无关,核心是3个逻辑错误:
- 初始化时序错误:
initState是同步执行的,你在initState里生成_swipeItems、初始化_matchEngine时,异步方法getData()还没执行完成,此时_names、_images都是空列表,生成的滑动卡片列表本身就是空的;后续接口返回数据后你只往_names、_images里加了数据,没有重新生成_swipeItems和重建_matchEngine,滑动卡片组件拿不到有效数据。 - 空检查运算符滥用:
- 类属性定义处直接写
Auth.prefs!.getString('usrid'),如果Auth.prefs未完成初始化,这里会直接抛空错 _scaffoldKey定义在MyHomePage里,但你没有给任何Scaffold绑定这个key(Scaffold在父组件CardApp中),所以_scaffoldKey.currentState永远为null,在onStackFinished回调里写_scaffoldKey.currentState!.showSnackBar必然触发空错- 只判断了
data != null就直接使用_matchEngine!,但首次build时_matchEngine绑定的是空的_swipeItems,组件加载时没有可渲染的卡片也会触发异常。
- 类属性定义处直接写
- 加载逻辑判断不完整:仅判断
data是否非空就渲染卡片组件,没有判断_names、_images、_swipeItems的数据是否真的准备完成。
修复步骤
- 把
Auth.prefs的取值放到initState或getData里做安全判断,不要在类属性定义时直接用强制空检查 - 把生成
_swipeItems、初始化_matchEngine的逻辑挪到getData()方法里,等接口数据全部填充到_names、_images之后再执行,执行完再调用setState刷新页面 - 移除无效的
_scaffoldKey相关逻辑,用Flutter自带的ScaffoldMessenger来弹出SnackBar,不需要依赖Scaffold的key - 把加载判断条件改成同时判断
data、_matchEngine、_swipeItems是否有效,数据没准备完成时一直显示加载圈
修复后的核心代码
class _MyHomePageState extends State<MyHomePage> { String? usrId; dynamic data; String usrpic = ""; List<SwipeItem> _swipeItems = <SwipeItem>[]; MatchEngine? _matchEngine; List<String> _names = []; List<String> _images = []; getData() async{ // 安全读取本地存储用户ID usrId = Auth.prefs?.getString('usrid'); var res = await http.get(Uri.https('www.*******.net', '/mm_api/index.php',{'act':'usrlists'})); data = jsonDecode(res.body); // 先清空旧数据避免重复添加 _names.clear(); _images.clear(); _swipeItems.clear(); for (var i = 0; i < data.length; i++) { _names.add("${data[i]['fulname']}"); } for (var i = 0; i < data.length; i++) { _images.add("${data[i]['profimage']}"); } // 数据填充完成后再生成滑动卡片项 for (int i = 0; i < _names.length; i++) { _swipeItems.add(SwipeItem( content: Content(text: _names[i], Swipeimage: _images[i]), likeAction: () { // 用ScaffoldMessenger弹提示,不需要key ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Liked ${_names[i]}"), duration: Duration(milliseconds: 500), )); }, nopeAction: () { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Nope ${_names[i]}"), duration: Duration(milliseconds: 500), )); }, superlikeAction: () { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Superliked ${_names[i]}"), duration: Duration(milliseconds: 500), )); }, onSlideUpdate: (SlideRegion? region) async { print("Region $region"); } )); } // 卡片项生成完再初始化MatchEngine _matchEngine = MatchEngine(swipeItems: _swipeItems); print(_names); print(_images); // 所有数据准备完成再刷新页面 setState(() {}); print(res.body); } @override void initState() { super.initState(); getData(); } @override Widget build(BuildContext context) { final Size size = MediaQuery.of(context).size; final double padding = 25; final sidePadding = EdgeInsets.symmetric(horizontal: padding); return // 同时判断数据和组件是否初始化完成,避免强制空检查报错 data != null && _matchEngine != null && _swipeItems.isNotEmpty? Stack( children: [ NoMoreDataWidget(), SwipeCards( matchEngine: _matchEngine!, itemBuilder: (BuildContext context, int index) { return Column( children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), gradient: LinearGradient( begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [Colors.white, Colors.white] ), ), width: MediaQuery.of(context).size.width, child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(15.0), child: Image.network( "https://www.*******.net/mm_api/files/profile/"+ _images[index], height: 400, width: MediaQuery.of(context).size.width, fit: BoxFit.cover, ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children:[ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(width: 5.0), Text( _names[index], style: TextStyle( color: Colors.black, fontSize: 20 ), ), addVerticalSpace(5), Text( "Dieppe, Canada", style: TextStyle( color: Colors.black, fontSize: 15 ), ) ], ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ InkWell( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => Persondetail() ), ); }, child: SvgPicture.asset( 'assets/images/Info.svg', ), ), ], ), ] ), ], ), ) ], ); }, onStackFinished: () { // 替换原来的key调用方式,避免空错 ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text("Stack Finished"), duration: Duration(milliseconds: 500), )); }, itemChanged: (SwipeItem item, int index) { print("item: ${item.content.text}, index: $index"); }, upSwipeAllowed: true, fillSpace: true, ), ] ): const Center( child: CircularProgressIndicator(), ) ; } }
额外注意:如果后续接口返回的
fulname或者profimage字段可能为null,需要在添加到列表前加非空判断,避免后续渲染时出现空值问题。
内容的提问来源于stack exchange,提问作者RAF Algowid
相关产品推荐
相关产品推荐

