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

Flutter热重载后出现Null check operator空值错误求助

Flutter热重载后出现"Null check operator used on a null value"错误解决

问题背景

原本运行正常的Flutter程序,热重载并重启后抛出该错误,现有代码无法删除(曾解决其他问题),需找到修复办法。

错误信息

Performing hot reload...
Syncing files to device AOSP on IA Emulator...
I/flutter ( 5909): [🌎 Easy Localization] [DEBUG] Build
I/flutter ( 5909): [🌎 Easy Localization] [DEBUG] Init Localization Delegate
I/flutter ( 5909): [🌎 Easy Localization] [DEBUG] Init provider

======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building MyApp(dirty):
Null check operator used on a null value

The relevant error-causing widget was: 
  MyApp MyApp:file:///D:/StudioProjects/task_masters/lib/main.dart:22:18
When the exception was thrown, this was the stack: 
#0      getProportionateScreenWidth (package:task_masters/utils/sizes.dart:57:44)
#1      TextThemes.textTheme (package:task_masters/themes/theme.dart:65:21)
#2      AppTheme.lightTheme (package:task_masters/themes/theme.dart:11:29)
#3      MyApp.build (package:flutter/src/widgets/framework.dart:5541:49)
#4      StatelessElement.build (package:flutter/src/widgets/framework.dart:5541:49)
#5      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5471:15)
#6      Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#7      BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2895:19)
#8      WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:984:21)
#9      RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5)
#10     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15)
#11     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9)
#12     SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:978:7)
#16     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
(elided 3 frames from class _Timer and dart:async-patch)
====================================================================================================
Reloaded 4 of 1151 libraries in 403ms (compile: 41 ms, reload: 156 ms, reassemble: 101 ms).

相关代码

main.dart中MyApp类

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
  SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
      statusBarColor: Colors.transparent,
      statusBarBrightness: Brightness.dark));
  runApp(EasyLocalization(
    supportedLocales: const [Locale('en')],
    path: 'assets/translations',
    fallbackLocale: const Locale('en'),
    startLocale: const Locale('en'),
    useOnlyLangCode: true,
    child: const MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Task Masters',
      theme: AppTheme.lightTheme(context),
      supportedLocales: context.supportedLocales,
      localizationsDelegates: context.localizationDelegates,
      locale: context.locale,
      initialRoute: SplashScreen.routeName,
      routes: routes,
    );
  }
}

sizes.dart中尺寸计算代码

import 'package:flutter/material.dart';

// const num figmaDesignWidth = 428;
// const num figmaDesignHeight = 926;
// const num figmaDesignStatusBar = 0;
//
// MediaQueryData mediaQueryData = MediaQueryData.fromView(
//     WidgetsBinding.instance.platformDispatcher.views.single);
//
// double getWidth(num percentage) {
//   return mediaQueryData.size.width * percentage / figmaDesignWidth;
// }
//
// double getHeight(num percentage) {
//   num statusBar = mediaQueryData.viewPadding.top;
//   num bottomBar = mediaQueryData.viewPadding.bottom;
//   num screenHeight = mediaQueryData.size.height - statusBar - bottomBar;
//   return screenHeight * percentage / (figmaDesignHeight - figmaDesignStatusBar);
// }
//
// double getAdaptSize(num percentage) {
//   var height = getHeight(percentage);
//   var width = getWidth(percentage);
//   return height < width ? height : width;
// }
//
// double getFontSize(num percentage) {
//   return getAdaptSize(percentage);
// }

class SizeConfig {
  MediaQueryData? _mediaQueryData;
  static double? screenWidth;
  static double? screenHeight;
  static double? defaultSize;
  static Orientation? orientation;

  void init(context) {
    _mediaQueryData = MediaQuery.of(context);
    screenWidth = _mediaQueryData?.size.width;
    screenHeight = _mediaQueryData?.size.height;
    orientation = _mediaQueryData?.orientation;
  }
}

//Get the proportionate height as per screen size
double getProportionateScreenHeight(double inputHeight) {
  double? screenHeight = SizeConfig.screenHeight;
  //926 is the layout height that designer uses
  return (inputHeight / 926.0) * screenHeight!;
}

//Get the proportionate width as per screen size
double getProportionateScreenWidth(double inputWidth) {
  double? screenWidth = SizeConfig.screenWidth;
  //428 is the layout width that designer uses
  return (inputWidth / 428.0) * screenWidth!;
}

theme.dart中主题配置代码

import 'package:flutter/material.dart';

import '../constants.dart';
import '../utils/sizes.dart';

class AppTheme {
  static ThemeData lightTheme(BuildContext context) {
    return ThemeData(
      visualDensity: VisualDensity.standard,
      colorScheme: primaryColorScheme,
      textTheme: TextThemes.textTheme(primaryColorScheme),
      scaffoldBackgroundColor: primaryColorScheme.onPrimaryContainer,
      brightness: Brightness.light,
      appBarTheme: AppBarTheme(
        color: Colors.transparent,
        elevation: 0,
        iconTheme: IconThemeData(color: primaryColorScheme.onPrimaryContainer),
        titleTextStyle: TextThemes
            .textTheme(primaryColorScheme)
            .displayMedium,
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ElevatedButton.styleFrom(
          backgroundColor: primaryColorScheme.primary,
          shape: RoundedRectangleBorder(
            borderRadius:
            BorderRadius.circular(getProportionateScreenWidth(20)),
          ),
          visualDensity: const VisualDensity(
            vertical: -4,
            horizontal: -4,
          ),
          padding: EdgeInsets.zero,
        ),
      ),
      outlinedButtonTheme: OutlinedButtonThemeData(
        style: OutlinedButton.styleFrom(
          backgroundColor: Colors.transparent,
          side: BorderSide(
            color: PrimaryColors.blueGray100,
            width: getProportionateScreenWidth(1),
          ),
          shape: RoundedRectangleBorder(
            borderRadius:
            BorderRadius.circular(getProportionateScreenWidth(10)),
          ),
          visualDensity: const VisualDensity(
            vertical: -4,
            horizontal: -4,
          ),
          padding: EdgeInsets.zero,
        ),
      ),
      dividerTheme: const DividerThemeData(
        thickness: 1,
        space: 1,
        color: PrimaryColors.indigo50,
      ),
    );
  }
}

class TextThemes {
  static TextTheme textTheme(ColorScheme colorScheme) =>
      TextTheme(
        bodyLarge: TextStyle(
          color: PrimaryColors.black900,
          fontSize: getProportionateScreenWidth(17), // 17.fSize
          fontFamily: 'Roboto',
          fontWeight: FontWeight.w400,
        ),
        bodyMedium: TextStyle(
          color: PrimaryColors.gray600,
          fontSize: getProportionateScreenWidth(15), //15.fSize
          fontFamily: 'Roboto',
          fontWeight: FontWeight.w400,
        ),
        bodySmall: TextStyle(
          color: PrimaryColors.black900,
          fontSize: getProportionateScreenWidth(10), //10.fSize
          fontFamily: 'Poppins',
          fontWeight: FontWeight.w400,
        ),

        //Label Text Task Master
        displayMedium: TextStyle(
          color: colorScheme.onPrimaryContainer,
          fontSize: getProportionateScreenWidth(40), //40.fSize
          fontFamily: 'Marcellus SC',
          fontWeight: FontWeight.w400,
        ),
        headlineLarge: TextStyle(
          color: colorScheme.onErrorContainer,
          fontSize: getProportionateScreenWidth(30), //30.fSize
          fontFamily: 'Urbanist',
          fontWeight: FontWeight.w700,
        ),
        headlineMedium: TextStyle(
          color: colorScheme.onErrorContainer,
          fontSize: getProportionateScreenWidth(26), //26.fSize
          fontFamily: 'Urbanist',
          fontWeight: FontWeight.w700,
        ),

        // Title text style
        titleLarge: TextStyle(
          color: PrimaryColors.blueGray40001,
          fontSize: getProportionateScreenWidth(20), //20.fSize,
          fontFamily: 'Roboto',
          fontWeight: FontWeight.w400,
        ),

        titleMedium: TextStyle(
          color: PrimaryColors.blueGray40001,
          fontSize: getProportionateScreenWidth(16), //16.fSize,
          fontFamily: 'Urbanist',
          fontWeight: FontWeight.w500,
        ),
        titleSmall: TextStyle(
          color: colorScheme.onPrimaryContainer,
          fontSize: getProportionateScreenWidth(14), //14.fSize,
          fontFamily: 'Montserrat',
          fontWeight: FontWeight.w500,
        ),
      );
}

问题根源

从栈追踪可以看到,错误发生在getProportionateScreenWidth方法里的screenWidth!空值断言操作——SizeConfig.screenWidth是静态变量,热重载时该变量未被重新初始化,导致值为null,触发空值检查错误。

修复方案

方案1:确保SizeConfig在使用前初始化

在MyApp的build方法中先初始化SizeConfig,保证每次构建(包括热重载后)都能正确获取屏幕尺寸:

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // 初始化SizeConfig
    SizeConfig().init(context);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Task Masters',
      theme: AppTheme.lightTheme(context),
      supportedLocales: context.supportedLocales,
      localizationsDelegates: context.localizationDelegates,
      locale: context.locale,
      initialRoute: SplashScreen.routeName,
      routes: routes,
    );
  }
}

方案2:移除空值断言,添加默认值

修改sizes.dart中的尺寸计算方法,用空值合并运算符提供设计稿尺寸作为默认值,避免空值错误:

double getProportionateScreenHeight(double inputHeight) {
  // 若screenHeight为null,用设计稿高度926作为默认值
  double screenHeight = SizeConfig.screenHeight ?? 926.0;
  return (inputHeight / 926.0) * screenHeight;
}

double getProportionateScreenWidth(double inputWidth) {
  // 若screenWidth为null,用设计稿宽度428作为默认值
  double screenWidth = SizeConfig.screenWidth ?? 428.0;
  return (inputWidth / 428.0) * screenWidth;
}

方案3:直接通过MediaQuery获取尺寸(不依赖静态变量)

放弃SizeConfig静态变量,修改尺寸计算方法直接从BuildContext获取MediaQuery数据,彻底避免静态变量初始化问题:

  1. 修改sizes.dart:
import 'package:flutter/material.dart';

double getProportionateScreenHeight(BuildContext context, double inputHeight) {
  double screenHeight = MediaQuery.of(context).size.height;
  return (inputHeight / 926.0) * screenHeight;
}

double getProportionateScreenWidth(BuildContext context, double inputWidth) {
  double screenWidth = MediaQuery.of(context).size.width;
  return (inputWidth / 428.0) * screenWidth;
}
  1. 修改theme.dart中所有调用处,传入context:
class AppTheme {
  static ThemeData lightTheme(BuildContext context) {
    return ThemeData(
      // ...其他配置
      textTheme: TextThemes.textTheme(context, primaryColorScheme),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ElevatedButton.styleFrom(
          // ...其他配置
          shape: RoundedRectangleBorder(
            borderRadius:
            BorderRadius.circular(getProportionateScreenWidth(context, 20)),
          ),
        ),
      ),
      // ...其他配置
    );
  }
}

class TextThemes {
  static TextTheme textTheme(BuildContext context, ColorScheme colorScheme) =>
      TextTheme(
        bodyLarge: TextStyle(
          // ...其他配置
          fontSize: getProportionateScreenWidth(context, 17),
        ),
        // ...其他文本样式
      );
}

内容的提问来源于stack exchange,提问作者Fred_Wolfe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 06:10:56