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

时间:2018-12-20 15:59:32

标签: dart flutter

我有一个应用程序,可以将json对象导出到json文件,并且在导出时,我想显示一个带有圆形进度指示器的警报对话框。但是由于某种原因,没有显示带有进度指示器的警报对话框。

这是我导出json之前应用程序的外观:

enter image description here

以下是用于激活导出部分的代码:

...

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",

...

在此警报按钮中单击“是”后,它应该显示进度对话框,但不显示,而是显示完成的对话框。

赞:

enter image description here

这是进度对话框的代码:

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)
                )
              )
          ]
          )
        )


      );
  }
  );
}

这是exportData回调:

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

我尝试添加Timer类以将完成的对话框延迟3秒钟显示,但是它不起作用。我可以确认我的json文件已成功导出,但是没有显示Timer的回调(即进度对话框)。

我将不胜感激。

更新

我根据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.");
},

此时,正在显示过程对话框,但是在打印“导出完成”并执行Navigator.pop(context);之后,出现了错误,并且过程对话框保留在屏幕中,未弹出。

赞:

enter image description here

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.

我将await Future.delayed(Duration(seconds: 5));注释掉后,效果很好。

我的问题是,为什么在使用Future.delayed时失败了?

这是完整的错误:

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)

更新

这是我的错。我需要研究更多有关上下文的信息。看来我在两个对话框中都弹出了相同的上下文。我更改了对话框的名称,它起作用了。

2 个答案:

答案 0 :(得分:2)

为什么不将其提取到自定义对话框窗口小部件并动态处理其状态?它更干净,更可自定义,而且还设置了计时器(就像您设置的5秒钟一样),因此这不是一个好习惯,因为您不确定要花多少时间来完成工作。

enter image description here

那么我可以建议例如创建一个具有3种状态的enum DialogState

enum DialogState {
  LOADING,
  COMPLETED,
  DISMISSED,
}

然后创建自己的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'),
                    ),
            ),
          );
  }
}

,然后在屏幕上将其插入到所需的任何位置。我更改了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,
            )
          ],
        ),
      ),
    );
  }
}

答案 1 :(得分:1)

您可以执行以下操作:

    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();
        },