我收到“查找已停用小部件的祖先是不安全的”。错误

问题描述 投票:0回答:2

当用户单击按钮时,我正在尝试从 Firebase 中删除数据。

await context
线路有问题!

class SecurityOption extends StatelessWidget {//StatelessWidget
  const SecurityOption({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Divider(
          height: 3,
        ),
        ListTile(
          title: Text('security').tr(),
          leading: Container(
            height: 30,
            width: 30,
            decoration: BoxDecoration(
                color: Colors.red, borderRadius: BorderRadius.circular(5)),
            child: Icon(Feather.lock, size: 20, color: Colors.white),
          ),
          trailing: Icon(
            Feather.chevron_right,
            size: 20,
          ),
          onTap: () => _openDeleteDialog(context),
        ),
      ],
    );
  }

  _openDeleteDialog(context) {
    return showDialog(
        barrierDismissible: true,
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Delete data').tr(),
            content: Text('Are you sure?').tr(),
            actions: [
              TextButton(
                onPressed: () async {
                  Navigator.pop(context);
                  await context  //This line is problematic! 
                      .read<SignInBloc>()
                      .deleteDatafromDatabase()
                      .then((_) async =>
                          await context.read<SignInBloc>().userSignout())
                      .then(
                          (_) => context.read<SignInBloc>().afterUserSignOut())
                      .then((_) {
                    Future.delayed(Duration(seconds: 1)).then((value) =>
                        nextScreenCloseOthers(context, WelcomePage()));
                  });
                },
                child: Text('YES').tr(),
              ),
              TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text('cancel').tr())
            ],
          );
        });
  }


}

SignInBloc 类:

class SignInBloc extends ChangeNotifier {

...
...
...


  Future deleteDatafromDatabase () async{
    FirebaseFirestore _db = FirebaseFirestore.instance;
    await _db.collection('x').doc('y').delete();
  }


}

当我运行应用程序并单击按钮时,它会删除数据但给出错误。

发生异常。

FlutterError(查找已停用的小部件的祖先是不安全的。在 此时小部件的元素树的状态不再稳定。 要在其 dispose() 方法中安全地引用小部件的祖先,请保存 通过调用来引用祖先 窗口小部件中的 dependentOnInheritedWidgetOfExactType() didChangeDependency() 方法。)

此外,应用程序冻结(未关闭)。点击、滑动等不起作用...

我该如何解决我的问题?

ios flutter dart flutter-layout flutter-widget
2个回答
7
投票

您已正确识别出有问题的代码。 问题是弹出对话框后,对话框的上下文不再存在,但您正在尝试访问它。 最好不要使用对话框构建器提供的上下文,因为它可以随时关闭。即使您将 Navigator.pop 移到末尾,您也会看到

barrierDismissible: true
,因此可以关闭该对话框,并且您将无法访问上下文。 您可以通过以下方式解决此问题:

  1. 更改对话框生成器提供的上下文名称为
    dialogContext
    并使用它来弹出
    Navigator.pop(dialogContext)
    并使用传递给
    context
    _openDeleteDialog
    进行下一步操作。
_openDeleteDialog(context) {
    return showDialog(
        barrierDismissible: true,
        context: context,
        builder: (dialogContext) {  <------- Change this
          return AlertDialog(
            title: Text('Delete data').tr(),
            content: Text('Are you sure?').tr(),
            actions: [
              TextButton(
                onPressed: () async {
                  Navigator.pop(dialogContext); <----- Use here
                  await context  <--- Keep as is
                      .read<SignInBloc>()
                      .deleteDatafromDatabase()
                      .then((_) async =>
                          await context.read<SignInBloc>().userSignout())
                      .then(
                          (_) => context.read<SignInBloc>().afterUserSignOut())
                      .then((_) {
                    Future.delayed(Duration(seconds: 1)).then((value) =>
                        nextScreenCloseOthers(context, WelcomePage()));
                  });
                },
                child: Text('YES').tr(),
              ),
              TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text('cancel').tr())
            ],
          );
        });
  }
  1. 将函数回调 (VoidCallback) 传递给
    _openDeleteDialog
    并在
    Navigator.pop
    之后调用它。
onTap: () => _openDeleteDialog(context, ()async {
             await context  
                      .read<SignInBloc>()
                      .deleteDatafromDatabase()
                      .then((_) async =>
                          await context.read<SignInBloc>().userSignout())
                      .then(
                          (_) => context.read<SignInBloc>().afterUserSignOut())
                      .then((_) {
                    Future.delayed(Duration(seconds: 1)).then((value) =>
                        nextScreenCloseOthers(context, WelcomePage()));
                  });
              } 
         ),

_openDeleteDialog(context, VoidCallback onDelete) {
    return showDialog(
        barrierDismissible: true,
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Delete data').tr(),
            content: Text('Are you sure?').tr(),
            actions: [
              TextButton(
                onPressed: ()  {
                  Navigator.pop(context);
                 onDelete();
                },
                child: Text('YES').tr(),
              ),
              TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text('cancel').tr())
            ],
          );
        });
  }

0
投票

当您在

context
方法中使用
showDailog
时,这是
showDailog
的参数,因此当您使用
Navigator.pop(context);
时,
context
不再存在,因此要解决此问题,您需要使用通过使用
context
而不是
build
 实现的 
this.context
 方法的 
context

像这样

TextButton(
  onPressed: () async {
    Navigator.pop(context);
      await this.context  //This line is problematic! 
            read<SignInBloc>()
            .deleteDatafromDatabase()
            .then((_) async =>
               await this.context.read<SignInBloc>().userSignout())
               .then(
                (_) => this.context.read<SignInBloc>().afterUserSignOut())
                 .then((_) {
                  Future.delayed(Duration(seconds: 1)).then((value) =>
                        nextScreenCloseOthers(context, WelcomePage()));
        },
      );
     },
     child: Text('YES').tr(),
),

我遇到了类似的问题,这就是我解决它的方法,希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.