Created
February 17, 2022 07:03
-
-
Save comatory/cf49c56c3a37213127dedae7a34794c2 to your computer and use it in GitHub Desktop.
Flutter not handling closing of specific dialog
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/scheduler.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
// This widget is the root of your application. | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: const MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
const MyHomePage({Key? key, required this.title}) : super(key: key); | |
final String title; | |
@override | |
State<MyHomePage> createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
Completer<BuildContext>? _dialog1Completer; | |
Completer<BuildContext>? _dialog2Completer; | |
bool _opened1 = false; | |
bool _opened2 = false; | |
@override | |
void initState() { | |
super.initState(); | |
Timer(const Duration(seconds: 3), () { | |
_openDialog1(); | |
debugPrint('Opened dialog 1. Dialog should read: "Dialog 1"'); | |
Timer(const Duration(seconds: 2), () { | |
_openDialog2(); | |
debugPrint('Opened dialog 2. Dialog should read: "Dialog 2"'); | |
Timer(const Duration(seconds: 3), () { | |
_closeDialog1(); | |
debugPrint('Closed dialog 1. Dialog should read: "Dialog 2"'); | |
Timer(const Duration(seconds: 5), () { | |
_closeDialog2(); | |
debugPrint('Closed dialog 2. You should not see any dialog at all.'); | |
}); | |
}); | |
}); | |
}); | |
} | |
Future<void> _openDialog1() async { | |
setState(() { | |
_opened1 = true; | |
}); | |
_dialog1Completer = Completer<BuildContext>(); | |
await showDialog( | |
barrierDismissible: false, | |
context: context, | |
routeSettings: const RouteSettings(name: 'dialog1'), | |
builder: (dialogContext) { | |
if (_dialog1Completer?.isCompleted == false) { | |
_dialog1Completer?.complete(dialogContext); | |
} | |
return CustomDialog(title: 'Dialog 1', timeout: false, onClose: _closeDialog1); | |
}); | |
} | |
Future<void> _openDialog2() async { | |
setState(() { | |
_opened2 = true; | |
}); | |
_dialog2Completer = Completer<BuildContext>(); | |
await showDialog( | |
barrierDismissible: false, | |
context: context, | |
routeSettings: const RouteSettings(name: 'dialog1'), | |
builder: (dialogContext) { | |
if (_dialog2Completer?.isCompleted == false) { | |
_dialog2Completer?.complete(dialogContext); | |
} | |
return CustomDialog(title: 'Dialog 2', timeout: false, onClose: _closeDialog2); | |
}); | |
} | |
Future<void> _closeDialog1() async { | |
final ctx = await _dialog1Completer?.future; | |
if (ctx == null) { | |
debugPrint('Could not closed dialog 1, no context.'); | |
return; | |
} | |
Navigator.of(ctx, rootNavigator: true).pop(); | |
setState(() { | |
_dialog1Completer = null; | |
_opened1 = false; | |
}); | |
} | |
Future<void> _closeDialog2() async { | |
final ctx = await _dialog2Completer?.future; | |
if (ctx == null) { | |
debugPrint('Could not closed dialog 2, no context.'); | |
return; | |
} | |
Navigator.of(ctx, rootNavigator: true).pop(); | |
setState(() { | |
_dialog2Completer = null; | |
_opened2 = false; | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title), | |
), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
mainAxisSize: MainAxisSize.max, | |
children: <Widget>[ | |
TextButton(onPressed: _openDialog1, child: const Text('Open 1')), | |
TextButton(onPressed: _openDialog2, child: const Text('Open 2')), | |
const Spacer(), | |
Align( | |
alignment: Alignment.bottomCenter, | |
child: Text('Opened 1? $_opened1\nOpened 2? $_opened2'), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
class CustomDialog extends StatefulWidget { | |
const CustomDialog({ | |
Key? key, | |
required this.timeout, | |
required this.title, | |
required this.onClose, | |
}) : super(key: key); | |
final bool timeout; | |
final String title; | |
final void Function() onClose; | |
@override | |
createState() => _CustomDialogState(); | |
} | |
class _CustomDialogState extends State<CustomDialog> | |
with SingleTickerProviderStateMixin { | |
late final Ticker _ticker; | |
Duration? _elapsed; | |
final Duration _closeIn = const Duration(seconds: 5); | |
late final Timer? _timer; | |
@override | |
void initState() { | |
super.initState(); | |
_timer = widget.timeout ? Timer(_closeIn, widget.onClose) : null; | |
_ticker = createTicker((elapsed) { | |
setState(() { | |
_elapsed = elapsed; | |
}); | |
}); | |
_ticker.start(); | |
} | |
@override | |
void dispose() { | |
_ticker.dispose(); | |
_timer?.cancel(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AlertDialog( | |
title: Text(widget.title), | |
content: SizedBox( | |
height: MediaQuery.of(context).size.height / 3, | |
child: Center( | |
child: Text([ | |
'${_elapsed?.inMilliseconds ?? 0.0}', | |
if (widget.timeout) ' / ${_closeIn.inMilliseconds}', | |
].join('')))), | |
actions: [ | |
TextButton(onPressed: widget.onClose, child: const Text('Close')) | |
], | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment