Skip to content
19 changes: 14 additions & 5 deletions packages/flet/lib/src/controls/alert_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,27 @@ class AlertDialogControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? dialogRoute;
showDialog(
barrierDismissible: !modal,
// Render the barrier in the dialog widget so it updates live.
barrierColor: Colors.transparent,
useSafeArea: false,
useRootNavigator: false,
context: context,
builder: (context) => _createAlertDialog(context)).then((value) {
debugPrint("Dismissing AlertDialog(${control.id})");
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
builder: (context) {
dialogRoute ??= ModalRoute.of(context);
return _createAlertDialog(context);
}).then((value) {
// showDialog future completes on pop() — before the exit animation
// finishes. Wait for the route's transition to fully complete so
// the dismiss event fires after the closing animation ends.
(dialogRoute?.completed ?? Future.value()).then((_) {
debugPrint("Dismissing AlertDialog(${control.id})");
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
});
});
});
} else if (!open && lastOpen) {
Expand Down
10 changes: 7 additions & 3 deletions packages/flet/lib/src/controls/bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ class BottomSheetControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? sheetRoute;
showModalBottomSheet<void>(
context: context,
builder: (context) {
sheetRoute ??= ModalRoute.of(context);
var content = control.buildWidget("content");

if (content == null) {
Expand Down Expand Up @@ -73,9 +75,11 @@ class BottomSheetControl extends StatelessWidget {
shape: control.getOutlinedBorder("shape", Theme.of(context)),
useSafeArea: control.getBool("use_safe_area", true)!)
.then((value) {
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
(sheetRoute?.completed ?? Future.value()).then((_) {
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
});
});
});
} else if (open != lastOpen && lastOpen) {
Expand Down
16 changes: 11 additions & 5 deletions packages/flet/lib/src/controls/cupertino_alert_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,24 @@ class CupertinoAlertDialogControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? dialogRoute;
showDialog(
barrierDismissible: !modal,
// Render the barrier in the dialog widget so it updates live.
barrierColor: Colors.transparent,
useSafeArea: false,
useRootNavigator: false,
context: context,
builder: (context) => _createCupertinoAlertDialog()).then((value) {
debugPrint("Dismissing CupertinoAlertDialog(${control.id})");
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
builder: (context) {
dialogRoute ??= ModalRoute.of(context);
return _createCupertinoAlertDialog();
}).then((value) {
(dialogRoute?.completed ?? Future.value()).then((_) {
debugPrint("Dismissing CupertinoAlertDialog(${control.id})");
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
});
});
});
} else if (!open && lastOpen) {
Expand Down
14 changes: 10 additions & 4 deletions packages/flet/lib/src/controls/cupertino_bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,20 @@ class CupertinoBottomSheetControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? popupRoute;
showCupertinoModalPopup(
barrierDismissible: !control.getBool("modal", false)!,
useRootNavigator: false,
context: context,
builder: (context) => dialog).then((value) {
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
builder: (context) {
popupRoute ??= ModalRoute.of(context);
return dialog;
}).then((value) {
(popupRoute?.completed ?? Future.value()).then((_) {
control.updateProperties({"_open": false}, python: false);
control.updateProperties({"open": false});
control.triggerEvent("dismiss");
});
});
});
} else if (open != lastOpen && lastOpen && Navigator.of(context).canPop()) {
Expand Down
12 changes: 9 additions & 3 deletions packages/flet/lib/src/controls/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,20 @@ class DatePickerControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? dialogRoute;
showDialog<DateTime>(
barrierDismissible: !control.getBool("modal", false)!,
barrierColor: control.getColor("barrier_color", context),
useRootNavigator: false,
context: context,
builder: (context) => createSelectDateDialog()).then((result) {
debugPrint("pickDate() completed");
onClosed(result);
builder: (context) {
dialogRoute ??= ModalRoute.of(context);
return createSelectDateDialog();
}).then((result) {
(dialogRoute?.completed ?? Future.value()).then((_) {
debugPrint("pickDate() completed");
onClosed(result);
});
});
});
}
Expand Down
12 changes: 9 additions & 3 deletions packages/flet/lib/src/controls/date_range_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,20 @@ class DateRangePickerControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? dialogRoute;
showDialog<DateTimeRange<DateTime>>(
barrierDismissible: !control.getBool("modal", false)!,
barrierColor: control.getColor("barrier_color", context),
useRootNavigator: false,
context: context,
builder: (context) => createSelectDateDialog()).then((result) {
debugPrint("pickDate() completed");
onClosed(result);
builder: (context) {
dialogRoute ??= ModalRoute.of(context);
return createSelectDateDialog();
}).then((result) {
(dialogRoute?.completed ?? Future.value()).then((_) {
debugPrint("pickDate() completed");
onClosed(result);
});
});
});
}
Expand Down
10 changes: 8 additions & 2 deletions packages/flet/lib/src/controls/time_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,19 @@ class TimePickerControl extends StatelessWidget {
control.updateProperties({"_open": open}, python: false);

WidgetsBinding.instance.addPostFrameCallback((_) {
ModalRoute? dialogRoute;
showDialog<TimeOfDay>(
barrierColor: control.getColor("barrier_color", context),
barrierDismissible: !control.getBool("modal", false)!,
useRootNavigator: false,
context: context,
builder: (context) => createSelectTimeDialog()).then((result) {
onClosed(result);
builder: (context) {
dialogRoute ??= ModalRoute.of(context);
return createSelectTimeDialog();
}).then((result) {
(dialogRoute?.completed ?? Future.value()).then((_) {
onClosed(result);
});
});
});
}
Expand Down
56 changes: 56 additions & 0 deletions sdk/python/examples/apps/declarative/use_dialog_basic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import asyncio

import flet as ft


@ft.component
def App():
show, set_show = ft.use_state(False)
deleting, set_deleting = ft.use_state(False)

async def handle_delete():
set_deleting(True)
# Simulate async operation
await asyncio.sleep(2)
set_deleting(False)
set_show(False)

ft.use_dialog(
ft.AlertDialog(
modal=True,
title=ft.Text("Delete report.pdf?"),
content=ft.Text(
"Deleting, please wait..." if deleting else "This cannot be undone."
),
actions=[
ft.Button(
"Deleting..." if deleting else "Delete",
disabled=deleting,
on_click=handle_delete,
),
ft.TextButton(
"Cancel",
on_click=lambda: set_show(False),
disabled=deleting,
),
],
on_dismiss=lambda: set_show(False),
)
if show
else None
)

return ft.Column(
controls=[
ft.Text("Declarative Dialog Example", size=24, weight=ft.FontWeight.BOLD),
ft.Text("Click the button to open a confirmation dialog."),
ft.Button(
"Delete File",
icon=ft.Icons.DELETE,
on_click=lambda: set_show(True),
),
],
)


ft.run(lambda page: page.render(App))
81 changes: 81 additions & 0 deletions sdk/python/examples/apps/declarative/use_dialog_chained.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import asyncio

import flet as ft


@ft.component
def App():
show_confirm, set_show_confirm = ft.use_state(False)
show_success, set_show_success = ft.use_state(False)
deleting, set_deleting = ft.use_state(False)
deleted, set_deleted = ft.use_state(False)
# Ref avoids stale closure — always holds the current value
should_chain = ft.use_ref(False)

async def handle_delete():
set_deleting(True)
await asyncio.sleep(2)
set_deleting(False)
set_deleted(True)
should_chain.current = True
set_show_confirm(False)

def on_confirm_dismiss():
if should_chain.current:
should_chain.current = False
set_show_success(True)

ft.use_dialog(
ft.AlertDialog(
modal=True,
title=ft.Text("Delete report.pdf?"),
content=ft.Text(
"Deleting, please wait..." if deleting else "This cannot be undone."
),
actions=[
ft.Button(
"Deleting..." if deleting else "Delete",
disabled=deleting,
on_click=handle_delete,
),
ft.TextButton(
"Cancel",
on_click=lambda: set_show_confirm(False),
disabled=deleting,
),
],
on_dismiss=on_confirm_dismiss,
)
if show_confirm
else None
)

ft.use_dialog(
ft.AlertDialog(
title=ft.Text("Done!"),
content=ft.Text("report.pdf has been deleted."),
actions=[
ft.FilledButton("OK", on_click=lambda: set_show_success(False)),
],
)
if show_success
else None
)

return ft.Column(
controls=[
ft.Text("Chained Dialogs Example", size=24, weight=ft.FontWeight.BOLD),
ft.Text(
"File deleted." if deleted else "Click the button to delete the file."
),
ft.Button(
"Delete File",
icon=ft.Icons.DELETE,
on_click=lambda: set_show_confirm(True),
disabled=deleted,
),
],
)


ft.run(lambda page: page.render(App))
Loading
Loading