在一个空值上使用了空检查运算符

192

我刚接触 Flutter

运行我的简单 flutter 应用时,遇到了这个错误。我无法弄清楚为什么会出现此错误。

错误信息:

Null check operator used on a null value

我的 main.dart 代码:

import 'package:flutter/material.dart';
import './ui/login.dart';

void main() {
  
  runApp(new MaterialApp(
    title: "Login Template",
    home: new Login(),
  ));
}

我的login.dart代码

import 'package:flutter/material.dart';

class Login extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new LoginState();
  }
}

class LoginState extends State<Login> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Login"),
        centerTitle: true,
        backgroundColor: Colors.blueAccent.shade50,
      ),
      backgroundColor: Colors.blueGrey,
      body: new Container(

      ),
    );
  }
}

代码的错误跟踪

Running Gradle task 'assembleDebug'...
✓ Built build/app/outputs/flutter-apk/app-debug.apk.
Installing build/app/outputs/flutter-apk/app.apk...
Waiting for SM J710F to report its views...
D/vndksupport(29495): Loading /vendor/lib/hw/android.hardware.graphics.mapper@2.0-impl.so from current namespace instead of sphal namespace.
Debug service listening on ws://127.0.0.1:39899/9RorUiKtUb4=/ws
Syncing files to device SM J710F...
D/ViewRootImpl@4ac1ef4[MainActivity](29495): MSG_RESIZED_REPORT: frame=Rect(0, 0 - 720, 1280) ci=Rect(0, 48 - 0, 582) vi=Rect(0, 48 - 0, 582) or=1
D/ViewRootImpl@4ac1ef4[MainActivity](29495): MSG_WINDOW_FOCUS_CHANGED 1
V/InputMethodManager(29495): Starting input: tba=android.view.inputmethod.EditorInfo@3049fea nm : com.sivaram.login_template ic=null
D/InputMethodManager(29495): startInputInner - Id : 0
I/InputMethodManager(29495): startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(29495): Input channel constructed: fd=96
V/InputMethodManager(29495): Starting input: tba=android.view.inputmethod.EditorInfo@aad92db nm : com.sivaram.login_template ic=null
D/InputMethodManager(29495): startInputInner - Id : 0
D/ViewRootImpl@4ac1ef4[MainActivity](29495): MSG_RESIZED: frame=Rect(0, 0 - 720, 1280) ci=Rect(0, 48 - 0, 0) vi=Rect(0, 48 - 0, 0) or=1
D/ViewRootImpl@4ac1ef4[MainActivity](29495): Relayout returned: old=[0,0][720,1280] new=[0,0][720,1280] result=0x1 surface={valid=true 3791374336} changed=false
D/libGLESv2(29495): STS_GLApi : DTS, ODTC are not allowed for Package : com.sivaram.login_template

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
Null check operator used on a null value
Login file:///home/kadavul/IdeaProjects/login_template/lib/main.dart:8:15
════════════════════════════════════════════════════════════════════════════════════════════════════
V/InputMethodManager(29495): Starting input: tba=android.view.inputmethod.EditorInfo@a0ff0af nm : com.sivaram.login_template ic=null
D/InputMethodManager(29495): startInputInner - Id : 0
I/InputMethodManager(29495): startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(29495): Input channel constructed: fd=87
D/InputTransport(29495): Input channel destroyed: fd=96
D/SurfaceView(29495): windowStopped(true) false 77b9092 of ViewRootImpl@4ac1ef4[MainActivity]
D/SurfaceView(29495): BG show() Surface(name=Background for - SurfaceView - com.sivaram.login_template/com.sivaram.login_template.MainActivity@77b9092@0) io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
D/SurfaceView(29495): surfaceDestroyed 1 #1 io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
V/InputMethodManager(29495): Starting input: tba=android.view.inputmethod.EditorInfo@a78fcbc nm : com.sivaram.login_template ic=null
D/InputMethodManager(29495): startInputInner - Id : 0
I/InputMethodManager(29495): startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(29495): Input channel constructed: fd=91
D/InputTransport(29495): Input channel destroyed: fd=87
D/SurfaceView(29495): windowStopped(false) true 77b9092 of ViewRootImpl@4ac1ef4[MainActivity]
D/SurfaceView(29495): BG show() Surface(name=Background for - SurfaceView - com.sivaram.login_template/com.sivaram.login_template.MainActivity@77b9092@1) io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
V/Surface (29495): sf_framedrop debug : 0x4f4c, game : false, logging : 0
D/SurfaceView(29495): surfaceCreated 1 #1 io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
D/mali_winsys(29495): EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, egl_color_buffer_format *, EGLBoolean) returns 0x3000,  [720x1280]-format:1
D/SurfaceView(29495): surfaceChanged (720,1280) 1 #1 io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
D/SurfaceView(29495): BG destroy() Surface(name=Background for - SurfaceView - com.sivaram.login_template/com.sivaram.login_template.MainActivity@77b9092@0) io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
D/ViewRootImpl@4ac1ef4[MainActivity](29495): Relayout returned: old=[0,0][720,1280] new=[0,0][720,1280] result=0x3 surface={valid=true 3791374336} changed=false
D/ViewRootImpl@4ac1ef4[MainActivity](29495): MSG_RESIZED_REPORT: frame=Rect(0, 0 - 720, 1280) ci=Rect(0, 48 - 0, 0) vi=Rect(0, 48 - 0, 0) or=1
V/InputMethodManager(29495): Starting input: tba=android.view.inputmethod.EditorInfo@7ed1445 nm : com.sivaram.login_template ic=null
D/InputMethodManager(29495): startInputInner - Id : 0
I/InputMethodManager(29495): startInputInner - mService.startInputOrWindowGainedFocus
D/InputTransport(29495): Input channel constructed: fd=92
D/InputTransport(29495): Input channel destroyed: fd=91
D/SurfaceView(29495): windowStopped(true) false 77b9092 of ViewRootImpl@4ac1ef4[MainActivity]
D/SurfaceView(29495): BG show() Surface(name=Background for - SurfaceView - com.sivaram.login_template/com.sivaram.login_template.MainActivity@77b9092@1) io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}
D/SurfaceView(29495): surfaceDestroyed 1 #1 io.flutter.embedding.android.FlutterSurfaceView{77b9092 V.E...... ........ 0,0-720,1280}

我的Flutter Doctor输出结果


fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
Doctor summary (to see all details, run flutter doctor -v):
Failed to find the latest git commit date: VersionCheckError: Command exited with code 128: git -c log.showSignature=false log -n 1 --pretty=format:%ad --date=iso
Standard out: 
Standard error: fatal: your current branch 'master' does not have any commits yet

Returning 1970-01-01 05:30:00.000 instead.
[✓] Flutter (Channel unknown, 0.0.0-unknown, on Linux, locale en_US.UTF-8)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Android Studio (version 4.0)
[!] VS Code (version 1.50.0)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[✓] Connected device (1 available)

! Doctor found issues in 1 category.

有人能提供解决这个问题的方法吗?


在终端中执行“flutter doctor”。 - Peter Haddad
1
你能复制粘贴完整的错误跟踪吗?也许我们可以找到问题出现的地方。 - Siddharth Agrawal
添加错误跟踪 - Sivaram Rasathurai
你是使用稳定版还是主版本? - Peter Haddad
这就结束了吗?它即将显示哪个小部件导致了错误。肯定还有更多的问题。你是否使用Flutter构建了另一个应用程序?这可能只是SDK配置的问题,你可以删除并重新下载。 - Siddharth Agrawal
显示剩余4条评论
14个回答

274

不要降低Flutter版本

问题:

当您在未初始化可空实例上使用感叹号运算符(!)时,会出现此错误。

例如:

String? string; // Nullable String

void main() {
  var len = string!.length; // Runtime error: Null check operator used on a null value
}

解决方案:

打开日志文件并查找指向项目中出现错误的文件行:

在空值上使用了空值检查运算符

#0 main (package:example/main.dart:22:16)

一旦找到,您可以使用以下任何一种方法进行修复:

  • 使用本地变量

  • var s = string;
    if (s != null) {
      var len = s.length; // Safe 
    }
    
  • 使用 ?. 和 ??

  • var len = string?.length ?? 0; // Provide a default value if string was null.
    

堆栈跟踪还可能指向不属于您项目的文件。例如:

1. 对于那些使用 NavigatorMediaQuery 的人

当您尝试异步访问 BuildContext 时,也会出现此错误。

因此,在访问 BuildContext 之前,应首先检查小部件是否已被 mounted

Future<void> foo() async {
  // Some async operation
  await compute();  

  // Check `mounted` before accessing 'context'.
  if (mounted) {
    MediaQuery.of(context).size;
    Navigator.of(context).pop();  
  }
}

2. 对于那些正在使用Color的人

你正在使用

Colors.blueAccent.shade50

它没有第50个阴影。如果你查看源代码,你会发现:

Color get shade50 => this[50]!; // <-- This bang operator is causing the error.
为解决此错误,请使用其他颜色,而非null,也许是第100个色调。
Colors.blueAccent[100]
// or
Colors.blue.shade100

3. 对于正在使用FutureBuilder/StreamBuilder的人:

你可以用两种方式解决错误:

  • 为你的FutureBuilder/StreamBuilder指定一个类型

    FutureBuilder<List<int>>( // <-- type 'List<int>' is specified.
      future: _listOfInt(),
      builder: (_, snapshot) {
        if (snapshot.hasData) {
          List<int> myList = snapshot.data!; // <-- Your data
        }
        return Container();
      },
    )
    
    使用asObject降级为您的类型,例如ListMap
    FutureBuilder(
      future: _listOfInt(),
      builder: (_, snapshot) {
        if (snapshot.hasData) {
          var myList = snapshot.data! as List<int>; // <-- Your data using 'as'
        }
        return Container();
      },
    )
    

这里还有一件重要的事情需要注意,就是永远不要取消未处理的异常断点。如果取消了,那么在出现此类错误时,您将无法获得准确的行号。 - Mohammad Aamir
2
还有一个值得一提的情况——如果您尝试在StatefulWidgetState类构造函数中访问widget,那么您将会获得此空值检查错误。 - Luke Hutchison
1
@DennisAshford 类似于 Future<List<int>> _listOfInt() async => [0, 1] - CopsOnRoad
@LukeHutchison 那种情况有什么解决办法?我觉得我遇到了一个相关的问题。 - Django
@Django 你不能直接绕过这个问题。在initState运行时,你无法访问相关的widget(如果你查看代码,当initState运行时,_widget还没有被设置,因为initState的整个目的是进行初始化。widget getter返回的是_widget!,这就是错误的来源)。你只能在build中访问widget。如果你必须在build之前运行代码,你可能可以在didChangeDependencies中访问widget,它在布局和渲染过程的不同阶段运行--我还没有检查过这一点。 - Luke Hutchison
显示剩余2条评论

41

如果您正在使用get_it软件包并遇到类似问题,这里有一个最简单的解决方法。 只需在主函数顶部添加WidgetsFlutterBinding.ensureInitialized();即可。

像这样更改您的主函数:

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await di.init()
runApp(MyApp());}

2
同样适用于path_provider包。 - dilshan
非常感谢 @AaNeal O'Neal,应用程序已经启动,我只想知道这是什么意思?你能解释一下吗? - CTMA
基本上,它返回一个WidgetsBinding实例,如果需要的话会创建并初始化它。如果创建了一个实例,它将是一个WidgetsFlutterBinding。如果之前已经初始化过,则至少会实现WidgetsBinding。只有在调用runApp之前需要绑定被初始化时才需要调用此方法。 - Anil Bhomi

33

如果CopsOnRoad的回答有效,请优先考虑使用该方法,仅在该方法无法解决问题时再降级。

以下是我们需要遵循的步骤:


 - flutter channel stable 
 - flutter upgrade
 - flutter pub cache repair //To perform a clean reinstall of the packages in your system cache, use pub cache repair
 - flutter clean //flutter clean will delete the /build folder

当我尝试运行flutter channel stable时,由于我有两个Flutter,一个来自snapd,另一个来自git clone方法,因此出现了另一个错误。

然后,我使用git clone配置了SDK。最后,我使用Git克隆的SDK Flutter执行了以下所有命令。

flutter channel stable 
 - ~/flutter/bin/flutter upgrade
 - ~/flutter/bin/flutter pub cache repair //To perform a clean reinstall of the packages in your system cache, use pub cache repair
 - ~/flutter/bin/flutter clean //flutter clean will delete the /build folder


2
在我的情况下,这并没有解决问题,但是在升级/清理后错误消息更加清晰,因此我可以找到“真正的原因”(缺少cupertino小部件的本地化)。 - Christoph P
2
有没有办法在需要运行beta通道时规避此消息?我有类似的问题,但我正在MacBook M1上运行beta版本,所以无法使用稳定通道。 - Tunnelvisie
我也无法切换到稳定通道,因为我的程序依赖于最低sdk版本2.12.0(或者我认为这可能是问题,我还不完全理解Flutter),所以我正在运行开发通道。那么有没有办法在不切换通道的情况下解决这个问题? - A M

14

对于像我这样的Flutter新手,建议尝试重新启动应用程序而不是热重载。


每次我们调试都要花费大约3分钟的时间! - greendino

9

Provider反模式

如果我们在包外面遇到此错误,那么我们可能需要查看我们如何使用第三方依赖。在我的情况下,Provider反模式导致了问题。

Provider对象的生命周期与常规小部件不同,因为它可以根据使用情况意外地重新创建或根本不创建

示例1

当在create:块之外初始化ChangeNotifierProvider时,我遇到了一个简单可重现的示例。

好的

ChangeNotifierProvider(
  create: (_) => ToDoContainerModel(),
  builder: (context, _) {
    return home;
}));

糟糕

ChangeNotifierProvider(
  create: (_) => model,
  builder: (context, _) {
    return home;
}));

这是在 仓库 中有记录的反模式:

不要使用默认构造函数重新使用现有的 ChangeNotifier。

示例2

另一个可能导致类似副作用的反模式是:

如果想创建对象,请不要使用 .value 构造函数,否则可能会产生意想不到的副作用。

好的做法

ChangeNotifierProvider.value(
  create: (_) => MyModel(),
  child: ...
)

糟糕的

ChangeNotifierProvider.value(
  value: MyModel(),
  child: ...
)

错误原因

sqflite数据库应该只调用一次openDatabase以保持一致性。由于使用了ChangeNotifierProvider反模式,我在多次触发openDatabase时出现了此错误。因此,由于在重复的调用中指定相同的db路径时sqflite内部会返回null,从而触发未处理异常。

提示:当您在包外部收到此错误时,请查看第三方文档。


8

问题可能与正在集成到Dart语言中的空值安全相关。目前,您可以切换通道并使用稳定版本:

flutter channel stable
flutter upgrade

在稳定通道上,使用最新的Flutter时,Firebase插件会抛出以下错误:/// 用户的唯一ID。 String get uid { return _data['uid']!; } - Yadu
当监听用户更改并打印最新用户时,它会抛出“Null check operator used on a null value”错误,这将导致在user_info.dart文件中上面的注释行。 - Yadu
谢谢,但我应该在哪里输入这个? - shankyyyyyyy
在终端中 - Peter Haddad

5
该问题还会在您使用已经完成的上下文时发生。例如,Navigator.pop(context),因此您可以再次检查您的代码顺序。

4

可能有不同的原因。但对我来说,原因是GetX。如果有人使用GetX,请确保在您的GetX中添加init参数。这里是一个示例:

late final DemoController _controller= Get.put(()=>DemoController());

GetX(
    init: _controller, 
    builder(_) =>...,
 );

1
完全忘记用 Get 包装材料了,谢谢你。 - Just Jason

4

我解决了这个问题,使用 late 替代了 ?

之前的代码是: UserRepository? userRepository;

替换成: late UserRepository userRepository;


2
当我使用late时,我得到了这个错误 LateInitializationError: 字段“taskname”未初始化。 - Faizan Ahmad

3

如果您尝试设置可空值的文本,则可以添加此行以解决此问题。

如果名称的值为null并且您必须分配可空值,则添加

name ?? ""

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接