简体   繁体   English

如何修复我的代码以显示flutter上的警报对话框?

[英]How to fix my code to show alert dialog on flutter?

I have an app which exports a json object to a json file and while it's exporting, I wanted to show an alert dialog with a circular progress indicator on it. 我有一个应用程序将json对象导出到json文件,当它导出时,我想显示一个带有循环进度指示器的警告对话框。 But for some reason, the alert dialog with my progress indicator is not showing up. 但由于某种原因,我的进度指示器的警报对话框没有显示出来。

This is the look of my app before I export my json: 在我导出json之前,这是我的应用程序的外观:

在此输入图像描述

Here is the code for activating the exporting part: 以下是激活导出部分的代码:

...

child: FlatButton(
  onPressed: () async{
    //Popping the confirm dialog
    Navigator.pop(context);
    //Showing the progress dialog
    showProcessingDialog();
    //Buying some time
    _timer = Timer(Duration(seconds: 5), exportData);
    //Pops the progress dialog
    Navigator.pop(context);
    //Shows the finished dialog
    showFinishedDialog();
    },
  child: Text(
    "Yes",

... ...

After I click 'Yes' in this alert button, it should show the progress dialog but it doesn't show, instead it shows the finished dialog. 在此警报按钮中单击“是”后,它应显示进度对话框但不显示,而是显示已完成的对话框。

Like this: 像这样:

在此输入图像描述

Here is the code for progress dialog: 以下是进度对话框的代码:

void showProcessingDialog() async{
return showDialog(

  barrierDismissible: false,
  context: context,
  builder: (BuildContext context){

    return AlertDialog(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(10.0))),
      contentPadding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
      content: Container(
        width: 250.0,
        height: 100.0,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: [
            CircularProgressIndicator(),
            Text("Exporting...",
              style: TextStyle(
                fontFamily: "OpenSans",
                color:  Color(0xFF5B6978)
                )
              )
          ]
          )
        )


      );
  }
  );
}

Here is the exportData callback: 这是exportData回调:

void exportData() async{
  List<dynamic> _msgList = await _msgStorage._getList;
  await _expData._saveList(_msgList);
}

I have tried to add Timer class to delay showing finished dialog for 3 seconds but it doesn't work. 我试图添加Timer类来延迟显示已完成的对话3秒,但它不起作用。 I can confirm that my json file was exported successfully but the callback of Timer which is the progress dialog didn't show up. 我可以确认我的json文件已成功导出但是Timer的回调是进度对话框没有显示出来。

I would appreciate any kind of help. 我将不胜感激任何帮助。

UPDATE : 更新

I rewrote my code based on the answer of diegoveloper: 我根据diegoveloper的答案重写了我的代码:

onPressed: () async{

  Navigator.pop(context);
  print("confirm dialog has pop");

  print("showing processdialog");
  showProcessingDialog();
  print("processdialog is being shown.");

  print("buying some time");
  await Future.delayed(Duration(seconds: 5));
  print("done buying some time");

  print("exporting begin");
  await exportData();
  print("exporting done");

  Navigator.pop(context);
  print("processdialog has pop");

  print("showing finished dialog");
  showFinishedDialog();
  print("finished dialog is being shown.");
},

At this point, the process dialog is being shown but after printing the "exporting done" and executing the Navigator.pop(context); 此时,正在显示进程对话框,但在打印“导出完成”并执行Navigator.pop(context); it gave an error and the process dialog remains in the screen, unpopped. 它给出了一个错误,进程对话框保留在屏幕上,未打开。

Like this: 像这样:

在此输入图像描述

I/flutter ( 9767): confirm dialog has pop
I/flutter ( 9767): showing processdialog
I/flutter ( 9767): processdialog is being shown.
I/flutter ( 9767): buying some time
I/flutter ( 9767): done buying some time
I/flutter ( 9767): exporting begin
I/flutter ( 9767): exporting done
E/flutter ( 9767): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter ( 9767): Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9767): At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.

After I comment out the await Future.delayed(Duration(seconds: 5)); 在我注释掉await Future.delayed(Duration(seconds: 5)); it worked fine. 它工作得很好。

My question is why did it failed when using Future.delayed ? 我的问题是为什么在使用Future.delayed时它失败了?

Here is the full error: 这是完整的错误:

I/flutter ( 9767): confirm dialog has pop
I/flutter ( 9767): showing processingdialog
I/flutter ( 9767): processdialog is being shown.
I/flutter ( 9767): buying some time
I/flutter ( 9767): done buying some time
I/flutter ( 9767): exporting begin
I/flutter ( 9767): exporting done
E/flutter ( 9767): [ERROR:flutter/shell/common/shell.cc(184)] Dart Error: Unhandled exception:
E/flutter ( 9767): Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9767): At this point the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter ( 9767): 
E/flutter ( 9767): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:3246:9)
E/flutter ( 9767): #1      Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:3255:6)
E/flutter ( 9767): #2      Element.ancestorStateOfType (package:flutter/src/widgets/framework.dart:3303:12)
E/flutter ( 9767): #3      Navigator.of (package:flutter/src/widgets/navigator.dart:1288:19)
E/flutter ( 9767): #4      ChatWindow.showExportedDialog.<anonymous closure>.<anonymous closure> (package:msgdiary/main.dart:368:37)
E/flutter ( 9767): <asynchronous suspension>
E/flutter ( 9767): #5      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:507:14)
E/flutter ( 9767): #6      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:562:30)
E/flutter ( 9767): #7      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
E/flutter ( 9767): #8      TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:242:9)
E/flutter ( 9767): #9      TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:175:7)
E/flutter ( 9767): #10     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9)
E/flutter ( 9767): #11     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
E/flutter ( 9767): #12     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
E/flutter ( 9767): #13     _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:180:19)
E/flutter ( 9767): #14     _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:158:22)
E/flutter ( 9767): #15     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:138:7)
E/flutter ( 9767): #16     _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:101:7)
E/flutter ( 9767): #17     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:85:7)
E/flutter ( 9767): #18     _invoke1 (dart:ui/hooks.dart:168:13)
E/flutter ( 9767): #19     _dispatchPointerDataPacket (dart:ui/hooks.dart:122:5)

UPDATE : 更新

It was my fault. 这都是我的错。 I need to study more about context. 我需要更多地研究背景。 It seems that I was popping the same context for the two dialogs. 看来我正在弹出两个对话框的相同上下文。 I changed the name of the dialog and it worked. 我更改了对话框的名称并且它有效。

Why don't you extract it to a custom dialog widget and handle its states dynamically? 为什么不将它提取到自定义对话框小部件并动态处理其状态? It's cleaner and more customizable, also giving a timer (like you did of 5 seconds) it's not a good practice since you can't be sure how much time it will take to do its work. 它更干净,更可定制,也提供一个计时器(就像你做了5秒)这不是一个好习惯,因为你不能确定它需要花多少时间来完成它的工作。

在此输入图像描述

Then I can suggest, for example, to create an enum DialogState with 3 states 然后我可以建议,例如,创建一个包含3个状态的enum DialogState

enum DialogState {
  LOADING,
  COMPLETED,
  DISMISSED,
}

Then create your own Dialog widget that when built receives its current state 然后创建自己的Dialog小部件,在构建时接收其当前状态

class MyDialog extends StatelessWidget {
  final DialogState state;
  MyDialog({this.state});

  @override
  Widget build(BuildContext context) {
    return state == DialogState.DISMISSED
        ? Container()
        : AlertDialog(
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.all(
                Radius.circular(10.0),
              ),
            ),
            content: Container(
              width: 250.0,
              height: 100.0,
              child: state == DialogState.LOADING
                  ? Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        CircularProgressIndicator(),
                        Padding(
                          padding: const EdgeInsets.only(left: 10.0),
                          child: Text(
                            "Exporting...",
                            style: TextStyle(
                              fontFamily: "OpenSans",
                              color: Color(0xFF5B6978),
                            ),
                          ),
                        )
                      ],
                    )
                  : Center(
                      child: Text('Data loaded with success'),
                    ),
            ),
          );
  }
}

and then, in your screen, you can insert it anywhere you want. 然后,在您的屏幕中,您可以将其插入任何您想要的地方。 I changed my exportData function to dummy a request that takes 5 seconds. 我将我的exportData函数更改为虚拟请求需要5秒。

class MyScreen extends StatefulWidget {
  _MyScreenState createState() => _MyScreenState();
}

class _MyScreenState extends State<MyScreen> {
  DialogState _dialogState = DialogState.DISMISSED;

  void _exportData() {
    setState(() => _dialogState = DialogState.LOADING);
    Future.delayed(Duration(seconds: 5)).then((_) {
      setState(() => _dialogState = DialogState.COMPLETED);
      Timer(Duration(seconds: 3), () => setState(() => _dialogState = DialogState.DISMISSED));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Stack(
          alignment: Alignment.center,
          children: <Widget>[
            RaisedButton(
              child: Text('Show dialog'),
              onPressed: () => _exportData(),
            ),
            MyDialog(
              state: _dialogState,
            )
          ],
        ),
      ),
    );
  }
}

You can do the following: 您可以执行以下操作:

    onPressed: () async{
        //Popping the confirm dialog
        Navigator.pop(context);
        //Showing the progress dialog
        showProcessingDialog();
        //wait 5 seconds : just for testing purposes, you don't need to wait in a real scenario
        await Future.delayed(Duration(seconds: 5));
        //call export data
        await exportData();
        //Pops the progress dialog
        Navigator.pop(context);
        //Shows the finished dialog
        await showFinishedDialog();
        },

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM