结论
- 异步延迟操作,判断页面是否退出, StatefulWidget 要使用 mounted 做判断。StatelessWidget 目前不知道拿啥做判断(欢迎补充)
- 页面退出时,不可以使用
Navigator.maybeOf
,context == null
或任何context 调用, - 没退出页面,仍要使用
Navigator.maybeOf
代替Navigator.of
示例代码:
// StatefulWidget 类中
if (!mounted) {
return;
}
var navigator = Navigator.maybeOf(context);
if (navigator == null) {
return;
}
场景
- 用户在网络回调执行前 退出页面。回调内调用 Navigator,show dialog,
context == null
, 或调用context
原因
- context在 StatefulWidget 内部是调用的getter 方法,不是属性调用。 改方法 最后做了 !非空校验。
release 版本会在 _element!; 处异常
BuildContext get context {
assert(() {
if (_element == null) {
throw FlutterError(
'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n'
'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.',
);
}
return true;
}());
return _element!;
}
bool get mounted => _element != null;
报错方法栈
StateFulWidget 调用 context 报错
I/flutter (31806): Future click
I/flutter (31806): Future running context: noNull
E/flutter (31806): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
E/flutter (31806): #0 StatefulElement.state (package:flutter/src/widgets/framework.dart:4712)
E/flutter (31806): #1 Element.findAncestorStateOfType (package:flutter/src/widgets/framework.dart:4054)
E/flutter (31806): #2 Navigator.maybeOf (package:flutter/src/widgets/navigator.dart:2598)
E/flutter (31806): #3 NetPage.futureCall.<anonymous closure> (package:flutter_app/net/view/NetPage.dart:60)
E/flutter (31806): #4 new Future.delayed.<anonymous closure> (dart:async/future.dart:393)
E/flutter (31806): #5 _rootRun (dart:async/zone.dart:1420)
E/flutter (31806): #6 _CustomZone.run (dart:async/zone.dart:1328)
E/flutter (31806): #7 _CustomZone.runGuarded (dart:async/zone.dart:1236)
E/flutter (31806): #8 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276)
E/flutter (31806): #9 _rootRun (dart:async/zone.dart:1428)
E/flutter (31806): #10 _CustomZone.run (dart:async/zone.dart:1328)
StatelessWidget 调用 Navigator.maybeOf 报错
I/flutter (31806): Future click
I/flutter (31806): Future running context: noNull
E/flutter (31806): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Null check operator used on a null value
E/flutter (31806): #0 StatefulElement.state (package:flutter/src/widgets/framework.dart:4712)
E/flutter (31806): #1 Element.findAncestorStateOfType (package:flutter/src/widgets/framework.dart:4054)
E/flutter (31806): #2 Navigator.maybeOf (package:flutter/src/widgets/navigator.dart:2598)
E/flutter (31806): #3 NetPage.futureCall.<anonymous closure> (package:flutter_app/net/view/NetPage.dart:60)
E/flutter (31806): #4 new Future.delayed.<anonymous closure> (dart:async/future.dart:393)
E/flutter (31806): #5 _rootRun (dart:async/zone.dart:1420)
E/flutter (31806): #6 _CustomZone.run (dart:async/zone.dart:1328)
E/flutter (31806): #7 _CustomZone.runGuarded (dart:async/zone.dart:1236)
E/flutter (31806): #8 _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1276)
E/flutter (31806): #9 _rootRun (dart:async/zone.dart:1428)
E/flutter (31806): #10 _CustomZone.run (dart:async/zone.dart:1328)
实验代码
StateFulWidget 实验代码
import 'dart:async';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class NetPage extends StatefulWidget {
NetPage({Key? key}) : super(key: key);
@override
_NetPageState createState() => _NetPageState();
}
class _NetPageState extends State<NetPage> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('NetPage'),
),
body: Container(
child: Row(
children: [
ElevatedButton(
onPressed: () {
futureCall();
},
child: Text("Click Future")),
ElevatedButton(
onPressed: () {
timerCall();
},
child: Text("Click Timer")),
ElevatedButton(
onPressed: () {
tryException();
},
child: Text("Null Exception")),
],
)),
);
}
Future requestNet() async {
var url = Uri.https('www.baidu.com', '');
var response = await http.get(url);
if (response.statusCode == 200) {
print("Success response body: ");
print('${response.body}');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
String? test;
get message {
return test!;
}
void futureCall() {
print("Future click");
Future.delayed(Duration(seconds: 4), () {
// print("Future mount test: $mounted");
// print("Future running context: ${context == null ? "null" : "noNull"}");
var navigatorState = Navigator.maybeOf(context);
print("Future NavigatorState is ${navigatorState == null ? "null": "noNull"}");
return "hello";
});
}
void timerCall() {
print("Timer click");
Timer(Duration(seconds: 5), () {
print("Timer running context: ${context == null ? "null" : "noNull"}");
var navigatorState = Navigator.maybeOf(context);
print("Timer NavigatorState is ${navigatorState == null ? "null": "noNull"}");
});
}
void tryException() {
print("Timer click getMessage $message");
}
}
StatelessWidget 实验代码
import 'dart:async';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class NetPage extends StatelessWidget {
NetPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('NetPage'),
),
body: Container(
child: Row(
children: [
ElevatedButton(
onPressed: () {
futureCall(context);
},
child: Text("Click Future")),
ElevatedButton(
onPressed: () {
timerCall(context);
},
child: Text("Click Timer")),
ElevatedButton(
onPressed: () {
tryException(context);
},
child: Text("Null Exception")),
],
)),
);
}
Future requestNet() async {
var url = Uri.https('www.baidu.com', '');
var response = await http.get(url);
if (response.statusCode == 200) {
print("Success response body: ");
print('${response.body}');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
String? test;
get message {
return test!;
}
void futureCall(BuildContext context) {
print("Future click");
Future.delayed(Duration(seconds: 4), () {
// print("Future mount test: $mounted");
print("Future running context: ${context == null ? "null" : "noNull"}");
var navigatorState = Navigator.maybeOf(context, rootNavigator: true);
print("Future NavigatorState is ${navigatorState == null ? "null": "noNull"}");
return "hello";
});
}
void timerCall(BuildContext context) {
print("Timer click");
Timer(Duration(seconds: 5), () {
print("Timer running context: ${context == null ? "null" : "noNull"}");
var navigatorState = Navigator.maybeOf(context);
print("Timer NavigatorState is ${navigatorState == null ? "null": "noNull"}");
});
}
void tryException(BuildContext context) {
print("Timer click getMessage $message");
}
}