flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
166.32k stars 27.53k forks source link

Null check operator used on a null value on building widgets #128940

Closed ImSeventy closed 1 year ago

ImSeventy commented 1 year ago

Is there an existing issue for this?

Steps to reproduce

  1. using flutter_screen_util library
  2. Making widgets using the library (Make it so it goes to different screen when you click on it) 3.Making a list of these widgets . 4.Click on the widget (to go to a different screen) 5.Return back from the screen (Navigator.pop())

Expected results

As you can see in the provided video after returning back from the screen it should work like how it was working before I navigated to the screen.

During building the widget the screen_util library requires to access MediaQuery of the widget which in return tries to access the widget using the property in package:flutter/src/widgets/framework.dart

  @override
  Widget get widget => _widget!;
  Widget? _widget;

it uses null check operator and it seems that some how _widget variable is being null during the build process

Actual results

It shows the Null check operator used on a null value exception

Code sample

Code sample Data Card Widget: ```dart class DataCard extends StatefulWidget { final String title; final Map> data; final VoidCallback? onTap; const DataCard({ Key? key, required this.title, this.onTap, required this.data, }) : super(key: key); factory DataCard.fromItem(ItemEntity item, BuildContext context) { return DataCard( title: item.name, onTap: () { context.pushNamed( Routes.item, arguments: ItemArgs( item: item, ), ); }, data: { context.loc!.wholesalePrice: Tuple2(true, item.wholesalePrice.toString()), context.loc!.price: Tuple2(true, item.price.toString()), context.loc!.unit: Tuple2(false, item.unit), context.loc!.inStock: Tuple2(false, item.stockQuantity.toString()), }, ); } factory DataCard.fromItemStockWarning( ItemStockWarningEntity warning, BuildContext context) { return DataCard( title: warning.name, data: { context.loc!.inStock: Tuple2(false, warning.stockQuantity.toString()), }, ); } factory DataCard.fromTransaction( TransactionEntity transaction, BuildContext context) { return DataCard( onTap: () { context.pushNamed( Routes.transaction, arguments: TransactionArgs( transactionId: transaction.id, ), ); }, title: "${context.loc!.transaction} #${transaction.displayId}", data: { context.loc!.date: Tuple2(false, transaction.date.dateFormat), context.loc!.totalWholeSaleCost: Tuple2(true, transaction.totalWholesaleCost.toString()), context.loc!.totalCost: Tuple2(true, transaction.totalCost.toString()), context.loc!.totalProfit: Tuple2(true, transaction.profit.toString()), }, ); } factory DataCard.fromReturn( ReturnEntity returnEntity, BuildContext context) { return DataCard( onTap: () { context.pushNamed( Routes.returnRoute, arguments: ReturnArgs( returnId: returnEntity.id, ), ); }, title: "${context.loc!.return_text} #${returnEntity.displayId}", data: { context.loc!.date: Tuple2(false, returnEntity.date.dateFormat), context.loc!.totalWholeSaleCost: Tuple2(true, returnEntity.totalWholesaleCost.toString()), context.loc!.totalCost: Tuple2(true, returnEntity.totalCost.toString()), context.loc!.totalProfit: Tuple2(true, returnEntity.profit.toString()), }, ); } factory DataCard.fromRevenue(RevenueEntity revenue, BuildContext context) { return DataCard( title: "${context.loc!.revenue} #${revenue.revenueDate}", data: { context.loc!.totalWholeSaleCost: Tuple2(true, revenue.totalWholesaleCost.toString()), context.loc!.totalCost: Tuple2(true, revenue.totalCost.toString()), context.loc!.revenue: Tuple2(true, revenue.revenue.toString()), }, ); } factory DataCard.fromEmployee(EmployeeEntity employee, BuildContext context) { return DataCard( onTap: () { context.pushNamed( Routes.employee, arguments: EmployeeArgs( employee: employee, ), ); }, title: "${context.loc!.employee} #${employee.id}", data: { context.loc!.name: Tuple2(false, employee.name), context.loc!.emailAddress: Tuple2(false, employee.email), }, ); } factory DataCard.fromCustomer( CustomerEntity customer, BuildContext context, {VoidCallback? onTap}) { return DataCard( onTap: onTap ?? () { context.pushNamed(Routes.customer, arguments: CustomerScreenArgs(customer: customer)); }, title: customer.name, data: { context.loc!.owesYou: Tuple2(true, customer.owes.toString()), context.loc!.phoneNumber: Tuple2(false, customer.phoneNumber ?? context.loc!.unknown), }, ); } @override State createState() => _DataCardState(); } class _DataCardState extends State { bool isHovered = false; List get propertyHolders { List holders = []; widget.data.forEach((key, value) { holders.add( PropertyHolder( propertyName: key, data: value.value2, isCurrency: value.value1, ), ); }); return holders; } @override Widget build(BuildContext context) { return GestureDetector( onTap: widget.onTap, child: MouseRegion( onEnter: (_) { if (isHovered) return; setState(() { isHovered = true; }); }, onExit: (_) { if (!isHovered) return; setState(() { isHovered = false; }); }, child: Card( margin: const EdgeInsets.only(top: 10, left: 5, right: 5), color: isHovered ? Colors.grey[900] : null, elevation: 2, child: Padding( padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.title, style: context.theme.textTheme.titleMedium, ), const SizedBox( height: 25, ), Wrap( spacing: 20.w, runSpacing: 10.h, children: propertyHolders, ) ], ), ), ), ), ); } } ``` ListView initiating the dataCard widget: ```dart ListView.builder( physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics()), itemBuilder: (_, index) { bool isLastWidget = index == transactions.length - 1; if (isLastWidget && transactionsCubit.canLoadMore() && state is GetTransactionsSuccessState) { transactionsCubit.getTransactions(); } Widget transactionWidget = DataCard.fromTransaction( transactions[index], context, ); if (state is GetTransactionsLoadingState && isLastWidget) { return Column( children: [ transactionWidget, const Padding( padding: EdgeInsets.symmetric(vertical: 10), child: LoadingIndicator(), ) ], ); } return transactionWidget; }, itemCount: transactions.length, ), ``` My MainApp: ```dart class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider( create: (_) { return getIt(); }, ), BlocProvider( lazy: true, create: (_) { ItemsCubit itemsCubit = getIt(); itemsCubit.getItems(); return itemsCubit; }, ), BlocProvider( lazy: true, create: (_) { TransactionsCubit transactionsCubit = getIt(); transactionsCubit.getTransactions(); transactionsCubit.getRevenues(); return transactionsCubit; }, ), BlocProvider( lazy: true, create: (_) { ReturnsCubit returnsCubit = getIt(); returnsCubit.getReturns(); return returnsCubit; }, ), BlocProvider( lazy: true, create: (_) { EmployeesCubit employeesCubit = getIt(); employeesCubit.getEmployees(); return employeesCubit; }, ), BlocProvider( lazy: true, create: (_) { CustomersCubit customersCubit = getIt(); customersCubit.getCustomers(); return customersCubit; }, ), ], child: MaterialApp( title: 'Shop Manager', theme: ThemeData( useMaterial3: true, colorSchemeSeed: Colors.blue, brightness: Brightness.dark, ), initialRoute: "/", onGenerateRoute: AppRouter.onGenerateRoute, debugShowCheckedModeBanner: false, localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: const [ Locale('ar'), ], ), ); } } ``` A code wrapping every screen on navigator.push() ```dart class DarkThemeWrapper extends StatelessWidget { final Widget child; const DarkThemeWrapper({Key? key, required this.child}) : super(key: key); @override Widget build(BuildContext context) { return ScreenUtilInit( designSize: const Size(428, 926), minTextAdapt: true, builder: (context, _) { return MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: Theme( data: ThemeData( useMaterial3: true, colorSchemeSeed: ThemeColors.seed, brightness: Brightness.dark, ), child: MultiBlocListener( listeners: [ BlocListener( listenWhen: (oldState, newState) => oldState != newState && newState is AuthenticationFailureState, listener: (context, state) { if (state is AuthenticationFailureState) { if (state.msg == ErrorMessages.invalidRefreshToken || state.msg == ErrorMessages.invalidAccessToken) { context.pushNamedAndRemove( Routes.login, ); } } }, ), BlocListener( listenWhen: (oldState, newState) => oldState != newState && newState is ItemsFailureState, listener: (context, state) { if (state is ItemsFailureState) { if (state.msg == ErrorMessages.invalidRefreshToken || state.msg == ErrorMessages.invalidAccessToken) { context.pushNamedAndRemove( Routes.login, ); } } }, ), BlocListener( listenWhen: (oldState, newState) => oldState != newState && newState is EmployeeFailureState, listener: (context, state) { if (state is EmployeeFailureState) { if (state.msg == ErrorMessages.invalidRefreshToken || state.msg == ErrorMessages.invalidAccessToken) { context.pushNamedAndRemove( Routes.login, ); } } }, ), BlocListener( listenWhen: (oldState, newState) => oldState != newState && newState is TransactionsFailureState, listener: (context, state) { if (state is TransactionsFailureState) { if (state.msg == ErrorMessages.invalidRefreshToken || state.msg == ErrorMessages.invalidAccessToken) { context.pushNamedAndRemove( Routes.login, ); } } }, ), ], child: LocaleWrapper(child: child), ), ), ); }); } } ```

Screenshots or Video

Screenshots / Video demonstration [![BUG](https://youtu.be/67tVZ9EfKH4)](https://youtu.be/67tVZ9EfKH4) link: https://youtu.be/67tVZ9EfKH4

Logs

Logs ```console [+1264 ms] ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ The following _TypeError was thrown building DataCard(dirty, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#87140]], state: _DataCardState#15cc6): Null check operator used on a null value The relevant error-causing widget was: DataCard DataCard:file:///C:/Shop-Manager/shop_manager/lib/features/transactions/presentation/screens/transactions.dart:85:59 When the exception was thrown, this was the stack: #0 Element.widget (package:flutter/src/widgets/framework.dart:3361:31) #1 debugCheckHasMediaQuery. (package:flutter/src/widgets/debug.dart:296:17) #2 debugCheckHasMediaQuery (package:flutter/src/widgets/debug.dart:311:4) #3 MediaQuery._of (package:flutter/src/widgets/media_query.dart:1017:12) #4 MediaQuery.of (package:flutter/src/widgets/media_query.dart:1013:12) #5 ScreenUtil.screenWidth (package:flutter_screenutil/src/screen_util.dart:148:37) #6 ScreenUtil.scaleWidth (package:flutter_screenutil/src/screen_util.dart:167:28) #7 ScreenUtil.setWidth (package:flutter_screenutil/src/screen_util.dart:182:41) #8 SizeExtension.w (package:flutter_screenutil/src/size_extension.dart:9:32) #9 _DataCardState.build (package:shop_manager/core/widgets/data_card.dart:199:58) #10 StatefulElement.build (package:flutter/src/widgets/framework.dart:5198:27) #11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5086:15) #12 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5251:11) #13 Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7) #14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2780:19) #15 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:903:21) #16 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:358:5) #17 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1284:15) #18 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1214:9) #19 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1072:5) #20 _invoke (dart:ui/hooks.dart:142:13) #21 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:359:5) #22 _drawFrame (dart:ui/hooks.dart:112:31) ════════════════════════════════════════════════════════════════════════════════════════════════════ [ +2 ms] Another exception was thrown: Null check operator used on a null value ```

Flutter Doctor output

Doctor output ```console [✓] Flutter (Channel stable, 3.10.5, on Microsoft Windows [Version 10.0.22621.1702], locale en-GB) [✓] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 33.0.0-rc2) • Android SDK at C:\Android\sdk\ • Platform android-33, build-tools 33.0.0-rc2 • Java binary at: C:\Program Files\JetBrains\apps\AndroidStudio\ch-0\222.4459.24.2221.10121639\jbr\bin\java • Java version OpenJDK Runtime Environment (build 17.0.6+0-b2043.56-9586694) • All Android licenses accepted. [✓] Chrome - develop for the web [✓] Visual Studio - develop for Windows (Visual Studio Community 2022 17.6.2) [✓] Android Studio (version 2021.1) [✓] IntelliJ IDEA Community Edition (version 2022.1) [✓] VS Code (version 1.79.1) [✓] Connected device (3 available) [✓] Network resources ```

Minimal, Reproducible Code

Minimal, Reproducible Code Code: ```dart import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await ScreenUtil.ensureScreenSize(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Issue test', theme: ThemeData( useMaterial3: true, colorSchemeSeed: Colors.blue, brightness: Brightness.dark, ), debugShowCheckedModeBanner: false, home: const DarkThemeWrapper(child: HomeScreen()), ); } } class DataScreen extends StatelessWidget { final String name; const DataScreen({super.key, required this.name}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Data"), leading: IconButton( onPressed: () { Navigator.of(context).pop(); }, icon: const Icon(Icons.arrow_back), )), body: Container( padding: EdgeInsets.all(16.sp), child: Text(name), ), ); } } class DataCard extends StatelessWidget { final String name; const DataCard({super.key, required this.name}); factory DataCard.fromName(String name) { return DataCard(name: name); } @override Widget build(BuildContext context) { return ListTile( onTap: () { Navigator.of(context).push( MaterialPageRoute( builder: (context) => DarkThemeWrapper(child: DataScreen(name: name)), ), ); }, title: Text(name, style: TextStyle( fontSize: 24.sp ),), ); } } class HomeScreen extends StatelessWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: ListView.builder( itemBuilder: (context, index) { return DataCard.fromName("Name $index"); }, itemCount: 100, ), ); } } class DarkThemeWrapper extends StatelessWidget { final Widget child; const DarkThemeWrapper({Key? key, required this.child}) : super(key: key); @override Widget build(BuildContext context) { return Theme( data: ThemeData( useMaterial3: true, brightness: Brightness.dark, ), child: ScreenUtilInit( designSize: const Size(428, 926), minTextAdapt: true, builder: (context, _) { return MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child, ); }, ), ); } } ``` ```yaml name: issue_project description: A new Flutter project. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. version: 1.0.0+1 environment: sdk: '>=3.0.5 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions # consider running `flutter pub upgrade --major-versions`. Alternatively, # dependencies can be manually updated by changing the version numbers below to # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: flutter_screenutil: ^5.8.4 flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages ```

A video showing how the minimal code behaves

link: https://www.youtube.com/watch?v=1Fa4bB3lOkg

ImSeventy commented 1 year ago

a side note:

This exception didn't occur using older version of flutter. It showed up after upgrading to the latest stable version

darshankawar commented 1 year ago

@ImSeventy In order to properly address the issue, we would need a complete but minimal reproducible code sample without third party package code implementation which throws the reported error. Currently, the code you shared seems to have third party implementation. So if you could narrow it down only to framework code which triggers the error, please provide that so we can make it actionable.

ImSeventy commented 1 year ago

@ImSeventy In order to properly address the issue, we would need a complete but minimal reproducible code sample without third party package code implementation which throws the reported error. Currently, the code you shared seems to have third party implementation. So if you could narrow it down only to framework code which triggers the error, please provide that so we can make it actionable.

I have updated the issue providing the minimal code and a video of how it behaves

darshankawar commented 1 year ago

Thanks for the update and providing trimmed code, but it still is using flutter_screenutil which is third party. Does it work without using the package ?

ImSeventy commented 1 year ago

It's the one causing the issue as It calls MediaQuery during the build of the widget but _widget in framework.dart is being null during the build somehow and I think it's a flutter issue.

It was working in previous versions of flutter

darshankawar commented 1 year ago

@ImSeventy I suggest you to file the issue in package's dedicated repo to see what the package author has to say about it, accordingly we can take this issue forward.

https://github.com/OpenFlutter/flutter_screenutil/issues

github-actions[bot] commented 1 year ago

Without additional information, we are unfortunately not sure how to resolve this issue. We are therefore reluctantly going to close this bug for now. If you find this problem please file a new issue with the same description, what happens, logs and the output of 'flutter doctor -v'. All system setups can be slightly different so it's always better to open new issues and reference the related ones. Thanks for your contribution.

github-actions[bot] commented 1 year ago

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.