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

Flutter移动端App:真机出现空检查运行时错误,模拟器正常

问题分析与修复方案

问题概述

  • 模拟器上页面运行正常,真机触发空检查运行时错误
  • 真机中StreamBuilder进入// TODO() : implement connection state error check的else分支,模拟器执行顶层if块,导致代码重复
  • 核心逻辑:从Firebase Storage获取用户照片,无照片时用空字符串标识

问题根源

  1. 强制解包风险:Builder开头直接用snapshot.data!强制解包,当真机因网络延迟进入连接状态(如ConnectionState.waiting)时,snapshot.data可能为空,触发空指针异常
  2. 分支逻辑不完善:未处理ConnectionState.waiting等中间状态,导致真机进入else分支后依然尝试访问空数据
  3. 代码冗余:照片网格的渲染代码重复,维护成本高

修复后的代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 19:16:00