dart Flutter上下文Null检查运算符用于空值

1hdlvixo  于 2023-07-31  发布在  Flutter
关注(0)|答案(5)|浏览(170)

我想指导如何避免这个问题发生在一些用户。这不是一般性错误。看起来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方法中,我初始化远程配置设置、通知等,并从我的提供程序调用一个方法。在某些设备中,当调用提供程序方法时会给出此错误:第一个月
错误:在空值上使用了空检查运算符#0 State.context(package:flutter/src/widgets/framework.dart:954)#1 _HomePageState.getDataIni...)#2 _FutureBuilderState._subscribe.(package:flutter/src/widgets/async.dart:628)
我知道这是一个Flutter错误,而不是供应商。但是如何避免这种情况并保证整个应用程序的状态呢?我的Widget是stateFullWidget。
我希望上下文总是可用的。正如我所说,错误不是一般的。

du7egjpx

du7egjpx1#

但是应用程序不能多次调用getDataIni()。什么原因导致didChangeDependencies运行?getDataIni只能在屏幕打开时运行一次。

nxagd54h

nxagd54h2#

编辑:
为了更好的解释,我添加了一个小代码片段:

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的原因。
但是你用的是Statecontext,而FutureBuilderState的内部,对吗?因为通常情况下,未来的构建器应该能够捕获这样的错误(这也是为什么在我上面的示例中,future不抛出异常,而只有future2抛出异常的原因)。
你用的是最新的flutter版本吗?

9udxz4iz

9udxz4iz3#

可以使用didChangeDependencies生命周期方法代替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();
  });
}

6tr1vspr

6tr1vspr4#

是的,我正在使用最新版本的Flutter和Provider。就像我说的,这只发生在一些用户身上。这可能是因为某些异步调用需要更长的时间,并且上下文丢失。我会尝试把我的代码放在这里:

late final UserDataController _userDataController;
bool _isInitialized = false;

@override
void didChangeDependencies() {

if (this._isInitialized == null || !this._isInitialized) {// Only execute once
  _userDataController = Provider.of<UserDataController>(context, listen: false);//already try with context.read too
  futureResult = getData();
  this._isInitialized = true; 
}
super.didChangeDependencies();

字符串
}

Future<bool> getData() async {
try {
  await DataUtils.loadDateTimeServer();
  await GetIt.I<RemoteConfigService>().loadData(); 
  await GetIt.I<AuthService>().init(context);
  await GetIt.I<PurchaseService>().load(context);
  await _initDynamicLinks();
  await _initializeNotificationsSetttings();
  await TkrsHelper.load();
  await _userDataController.loadSync(context);//**<--- Error: Null check here**
  _requestPermissions();
  _configureDidReceiveLocalNotificationSubject();
  _configureSelectNotificationSubject();
  _loadAndSetTabIcons();
  await GetIt.I<UserApiService>().callAddTokenPush();
  await GetIt.I<NotificationHelper>().programNotifications(context);
  await _showDialogUmpCMP();
  await _autentBiometric();
} catch (ex, stack) {
 ...
}
}

 @override
 Widget build(BuildContext context) {
 return WillPopScope(
  onWillPop: _onWillPop,
  child: Container(
    color: Color(0xff160055),
    child: Scaffold(
      key: _scaffoldKey,
      resizeToAvoidBottomInset:true,
      backgroundColor: Colors.transparent,
      body: FutureBuilder<bool>(
        future: futureResult,
        builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
         ...}}}

33qvvth1

33qvvth15#

我想我在调用await _userDataController.loadSync(context)之前运行的initDynamicLinks方法中发现了可能的问题。如果用户通过电子邮件链接进入应用程序,我会将他引导到另一个小部件。这可能只发生在那些以这种方式进入应用程序的人身上。
我会看着日志。如果是这样的话,我会回到这里来解决这个问题。

相关问题