Flutter移动端App:真机出现空检查运行时错误,模拟器正常
问题分析与修复方案
问题概述
- 模拟器上页面运行正常,真机触发空检查运行时错误
- 真机中StreamBuilder进入
// TODO() : implement connection state error check的else分支,模拟器执行顶层if块,导致代码重复 - 核心逻辑:从Firebase Storage获取用户照片,无照片时用空字符串标识
问题根源
- 强制解包风险:Builder开头直接用
snapshot.data!强制解包,当真机因网络延迟进入连接状态(如ConnectionState.waiting)时,snapshot.data可能为空,触发空指针异常 - 分支逻辑不完善:未处理
ConnectionState.waiting等中间状态,导致真机进入else分支后依然尝试访问空数据 - 代码冗余:照片网格的渲染代码重复,维护成本高
修复后的代码
import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:locusu/classes/firebase_methods.dart'; import 'package:locusu/classes/jcu_colors.dart'; import 'package:locusu/screens/dorm_pref.dart'; import 'package:locusu/widgets/picture_container.dart'; class OnboardPhotos extends StatefulWidget { const OnboardPhotos({super.key}); @override State<OnboardPhotos> createState() => OnboardPhotosState(); } class OnboardPhotosState extends State<OnboardPhotos> { late Stream<List<String>> streamData = FirebaseMethods().getProfilePhotos(); @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.topRight, colors: [ JcuColors().gradientColor1, JcuColors().gradientColor2, ], ), ), child: SingleChildScrollView( child: SizedBox( height: MediaQuery.of(context).size.height, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 100), const Center( child: Icon( Icons.photo_album_outlined, size: 75, ), ), const SizedBox(height: 10), Center( child: Text( "Add some photos to your profile", textAlign: TextAlign.center, style: GoogleFonts.montserrat( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 25, ), ), ), const SizedBox(height: 30), StreamBuilder<List<String>>( stream: streamData, initialData: const [""], builder: (context, AsyncSnapshot<List<String>> snapshot) { // 空安全处理:如果data为空,使用默认的空字符串列表 final photos = snapshot.data ?? const [""]; final hasEmptyPlaceholder = photos.isNotEmpty && photos[0] == ""; // 处理不同连接状态 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator(color: Colors.white)); } if (snapshot.hasError) { return Center( child: Text( "加载照片失败,请重试", textAlign: TextAlign.center, style: GoogleFonts.montserrat( fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white, ), ), ); } if (snapshot.connectionState == ConnectionState.none) { return Center( child: Text( "无网络连接,请稍后重试", textAlign: TextAlign.center, style: GoogleFonts.montserrat( fontWeight: FontWeight.bold, fontSize: 18, color: Colors.white, ), ), ); } // 渲染照片网格(抽离为单独方法,消除重复) return _buildPhotoGrid(photos, hasEmptyPlaceholder); }, ), const SizedBox(height: 50), Padding( padding: const EdgeInsets.symmetric(horizontal: 35.0), child: GestureDetector( onTap: () { Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const DormPreference())); }, child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(50), ), child: Center( child: Text( 'Continue', style: GoogleFonts.montserrat( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 22, ), ), ), ), ), ), ], ), ), ), ), ); } // 抽离照片网格渲染逻辑,避免代码重复 Widget _buildPhotoGrid(List<String> photos, bool hasEmptyPlaceholder) { return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ PictureContainer(photoURL: hasEmptyPlaceholder ? "" : photos[0]), PictureContainer(photoURL: hasEmptyPlaceholder ? "" : photos[1]), PictureContainer(photoURL: hasEmptyPlaceholder ? "" : photos[2]), ], ), const SizedBox(height: 15), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ PictureContainer(photoURL: hasEmptyPlaceholder ? "" : photos[3]), PictureContainer(photoURL: hasEmptyPlaceholder ? "" : photos[4]), PictureContainer(photoURL: hasEmptyPlaceholder ? "" : photos[5]), ], ), ], ); } }
关键修改说明
- 空安全处理:用
final photos = snapshot.data ?? const [""];代替强制解包,确保永远不会出现空指针 - 完善状态处理:新增
ConnectionState.waiting(显示加载指示器)和snapshot.hasError(显示错误提示)的分支,覆盖所有可能的状态 - 消除代码重复:把照片网格的渲染逻辑抽成
_buildPhotoGrid方法,所有分支统一调用该方法 - 逻辑简化:提前计算
hasEmptyPlaceholder,避免重复判断snapshot.data![0] == ""
内容的提问来源于stack exchange,提问作者antonr00ls
相关产品推荐
相关产品推荐

