我希望获得有关如何避免某些用户出现此问题的指导。这不是一般性错误。 似乎 flutter 在我的应用程序的初始化流程中禁用/处置上下文。 我在 initState 中使用 FutureBuilder。 仅在 future 方法完成调用多个例程后才会加载屏幕。 我在 initiState 中初始化我的 Provider,如下所示:
` @override void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_userDataController = Provider.of<UserDataController>(context, listen: false);
futureResult = getDataIni();
}`
在我的 getDataIni 方法中,我初始化远程配置设置、通知等,并从我的提供程序调用方法。在某些设备中,调用提供程序方法时会出现此错误:
await _userDataController.loadData(context);
错误: 对空值使用空检查运算符 #0 State.context (package:flutter/src/widgets/framework.dart:954)#1 _HomePageState.getDataIni.....)#2 _FutureBuilderState._subscribe. (包:flutter/src/widgets/async.dart:628)
我知道这是一个颤振错误,而不是提供者。但如何避免这种情况并保证整个应用程序的状态呢? 我的小部件是一个 stateFullWidget。
更新 不幸的是问题没有解决,我不知道还能做什么。 如果有人可以帮助我。
出现在队列中:
await _userDataController.loadSync(context, signatureController: _signatureController);
错误: 对空值使用空检查运算符
#0 State.context (package:flutter/src/widgets/framework.dart:958)#1
_HomePageState.getData (package:appxxxx/pages/home_page.dart:211)<asynchronous suspend>#2
_FutureBuilderState._subscribe.<anonymous close> (package:flutter/src/widgets/async.dart:624)<asynchronous suspend>
后续加载的方法完整代码:
Future<bool> getData() async {
try {
_abaController = context.read<AbaController>();
_assinaturaController = context.read<AssinaturaController>();
var _userDataController = context.read<UserDataController>();
isOnline = await Conexao.isOnline();
//test your internet connection online
if (isOnline == false)
return Future<bool>.value(false);
//get current date and time from server
await DataUtils.loadDateTimeServer();
//initialize remote config
await GetIt.I<RemoteConfigService>().loadData();
///Checks if app update is mandatory and aborts
int versaoAppLocal = int.parse((await PackageInfo.fromPlatform()).buildNumber);
int versaoAppRemoteConfig = GetIt.I<RemoteConfigService>().versaoMinimaApp;
if (versaoAppLocal < versaoAppRemoteConfig){
atualizacaoAppObrigatoria = true;
return Future<bool>.value(false);
}
///Check if the environment is under maintenance and abort
if (GetIt.I<RemoteConfigService>().isAmbienteEmManutencao){
isAmbienteEmManutencao = true;
return Future<bool>.value(false);
}
//retrieves instance Firebase auth and sets user logged in provider
await GetIt.I<AuthService>().init(context);
//initializes the subscriptions plugin (revenuecat) and sets if the user is a subscriber or not
await GetIt.I<PurchaseService>().load(context);
//initialize notifications plugin settings
await _initializeNotificationsSetttings();
//call service to load a list of session data
await TickersHelper.load();
await _userDataController.loadSync(context, assinaturaController: _assinaturaController);
_requestPermissions();
_configureDidReceiveLocalNotificationSubject();
_configureSelectNotificationSubject();
_loadAndSetTabIcons();
await GetIt.I<UsuarioApiService>().callIncluirTokenPush();
await GetIt.I<NotificacoesHelper>().programaNotifications(context);
await _showDialogUmpCMP();
await _autenticaBiometria();
await _initDynamicLinks();
} catch (ex, stack) {
....
}
但是应用程序不能多次调用 getDataIni()。是什么导致 didChangeDependency 运行? getDataIni 只能在屏幕打开时运行一次。
编辑:
为了更好的解释,我添加了一个小代码片段:
class YourPage extends StatefulWidget {
const YourPage({super.key});
@override
State<YourPage> createState() => _YourPageState();
}
class _YourPageState extends State<YourPage> {
late Future<int> future;
late Future<int> future2;
Future<int> _someAsyncChain() async {
await Future<void>.delayed(const Duration(seconds: 3));
return Provider.of<int>(context, listen: false); // provider simulation
}
@override
void initState() {
future = _someAsyncChain();
future2 = _someAsyncChain(); // throws the exception
}
@override
void dispose() {
print("disposed");
super.dispose();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<int>(
future: future,
builder: (BuildContext context, AsyncSnapshot<int> data) {
if (data.hasData) {
return Text("DATA: ${data.data}");
}
return const Text("no data :(");
},
);
}
}
Widget _build(BuildContext context) {
bool showFirst = false;
return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
return Column(
children: [
showFirst ? Provider<int>(create: (_) => 100, child: YourPage()) : Text("second page"),
ElevatedButton(onPressed: () => setState(() => showFirst = !showFirst), child: Text("change")),
],
);
});
}
我猜想,如果您以某种方式比加载数据未来完成的速度更快地处理 State 对象,就会发生类似的情况。
正如您在此示例中所看到的,如果您足够快地使用
change
按钮,则会引发相同的异常,因为状态不再可用。但如果你等3秒再按下,那就一切都好了。
但是如果不知道你的整个小部件结构和你的代码
getDataIni
,很难说你的错误到底在哪里。
这也是为什么一般不建议跨异步间隙使用
BuildContext
。
但是您正在使用
context
的 State
并且您的 FutureBuilder
位于您的 State
内部,对吧?因为通常未来的构建器应该能够捕获这样的错误(这也是为什么在上面的示例中,future
不会抛出异常,只有future2
会抛出异常)。
您使用的是最新的flutter版本吗?
您可以使用 didChangeDependency 生命周期方法代替 initState。在某些情况下,调用 initState 方法时上下文可能尚未完全初始化或可用,因此通过将初始化代码移至 didChangeDependencies,可以确保上下文可用并可供使用。
...
@override
void didChangeDependencies() {
super.didChangeDependencies();
_userDataController = Provider.of<UserDataController>(context, listen: false);
futureResult = getDataIni();
}
@override
void initState() {
...
其他解决方案使用WidgetsBinding.instance.addPostFrameCallback,我已经使用此回调来执行它在构建完成时工作,我认为它也适用于您的情况。
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((_) {
_userDataController = Provider.of<UserDataController>(context, listen: false);
futureResult = getDataIni();
});
}