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数据,彻底避免静态变量初始化问题:
- 修改
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; }
- 修改
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
相关产品推荐
相关产品推荐

