Overlay

Dialog

A modal dialog interrupts the user with important content and expects a response.

dart run forui snippet create adaptive-dialog
1class DialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: AdaptiveDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 body: const Text(
16 'This action cannot be undone. This will permanently delete your account and '
17 'remove your data from our servers.',
18 ),
19 actions: [
20 FButton(
21 size: .sm,
22 child: const Text('Continue'),
23 onPress: () => Navigator.of(context).pop(),
24 ),
25 FButton(
26 variant: .outline,
27 size: .sm,
28 child: const Text('Cancel'),
29 onPress: () => Navigator.of(context).pop(),
30 ),
31 ],
32 ),
33 ),
34 ),
35 child: const Text('Show Dialog'),
36 );
37}
38
39class AdaptiveDialog extends StatelessWidget {
40 final FDialogStyleDelta style;
41 final Animation<double>? animation;
42 final Widget title;
43 final Widget body;
44 final List<Widget> actions;
45 const AdaptiveDialog({
46 required this.title,
47 required this.body,
48 required this.actions,
49 this.style = const .context(),
50 this.animation,
51 super.key,
52 });
53 @override
54 Widget build(BuildContext context) => FDialog.adaptive(
55 style: style,
56 animation: animation,
57 horizontalBuilder: (context, style) {
58 final touch = context.platformVariant.touch;
59 return Padding(
60 padding: touch
61 ? const .symmetric(horizontal: 16, vertical: 18)
62 : const .symmetric(horizontal: 16, vertical: 14),
63 child: Column(
64 crossAxisAlignment: .start,
65 mainAxisSize: .min,
66 children: [
67 Padding(
68 padding: touch
69 ? const .only(left: 8, right: 8, bottom: 9)
70 : const .only(bottom: 5),
71 child: DefaultTextStyle.merge(
72 style: style.titleTextStyle,
73 child: title,
74 ),
75 ),
76 Flexible(
77 child: Padding(
78 padding: touch
79 ? const .only(left: 8, right: 8, bottom: 20)
80 : const .only(bottom: 16),
81 child: DefaultTextStyle.merge(
82 style: style.bodyTextStyle,
83 child: body,
84 ),
85 ),
86 ),
87 Row(
88 mainAxisAlignment: .end,
89 spacing: touch ? 10 : 8,
90 children: touch
91 ? [for (final action in actions) Expanded(child: action)]
92 : actions,
93 ),
94 ],
95 ),
96 );
97 },
98 verticalBuilder: (context, style) {
99 final touch = context.platformVariant.touch;
100 return Padding(
101 padding: touch
102 ? const .symmetric(horizontal: 16, vertical: 18)
103 : const .symmetric(horizontal: 16, vertical: 14),
104 child: Column(
105 crossAxisAlignment: .start,
106 mainAxisSize: .min,
107 children: [
108 Padding(
109 padding: touch
110 ? const .only(left: 8, right: 8, bottom: 9)
111 : const .only(left: 8, right: 8, bottom: 5),
112 child: DefaultTextStyle.merge(
113 style: style.titleTextStyle,
114 child: title,
115 ),
116 ),
117 Flexible(
118 child: Padding(
119 padding: touch
120 ? const .only(left: 8, right: 8, bottom: 20)
121 : const .only(left: 8, right: 8, bottom: 16),
122 child: DefaultTextStyle.merge(
123 style: style.bodyTextStyle,
124 child: body,
125 ),
126 ),
127 ),
128 Column(
129 mainAxisSize: .min,
130 spacing: touch ? 10 : 8,
131 children: actions,
132 ),
133 ],
134 ),
135 );
136 },
137 );
138}
139

FDialog does not provide a built-in content layout. Generate one with the CLI or copy it from an example below, then customize the widget directly.

CLI

To generate a specific style for customization:

dart run forui style create dialog
dart run forui style create dialog-route

Usage

showFDialog(...)

1showFDialog(
2 context: context,
3 style: const .delta(insetPadding: .value(.zero)),
4 routeStyle: const .delta(motion: .delta()),
5 builder: (context, style, animation) => FDialog.adaptive(
6 style: style,
7 animation: animation,
8 horizontalBuilder: (context, style) => const Text('Horizontal content'),
9 verticalBuilder: (context, style) => const Text('Vertical content'),
10 ),
11)

FDialog(...)

1FDialog(
2 style: const .delta(insetPadding: .value(.zero)),
3 clipBehavior: .none,
4 builder: (context, style) => const Text('Custom content'),
5)

FDialog.adaptive(...)

1FDialog.adaptive(
2 style: const .delta(insetPadding: .value(.zero)),
3 clipBehavior: .none,
4 horizontalBuilder: (context, style) => const Text('Horizontal content'),
5 verticalBuilder: (context, style) => const Text('Vertical content'),
6)

Examples

Adaptive

Lays out the actions horizontally on wider devices and vertically on smaller ones.

Default

dart run forui snippet create adaptive-dialog
1class DialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: AdaptiveDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 body: const Text(
16 'This action cannot be undone. This will permanently delete your account and '
17 'remove your data from our servers.',
18 ),
19 actions: [
20 FButton(
21 size: .sm,
22 child: const Text('Continue'),
23 onPress: () => Navigator.of(context).pop(),
24 ),
25 FButton(
26 variant: .outline,
27 size: .sm,
28 child: const Text('Cancel'),
29 onPress: () => Navigator.of(context).pop(),
30 ),
31 ],
32 ),
33 ),
34 ),
35 child: const Text('Show Dialog'),
36 );
37}
38
39class AdaptiveDialog extends StatelessWidget {
40 final FDialogStyleDelta style;
41 final Animation<double>? animation;
42 final Widget title;
43 final Widget body;
44 final List<Widget> actions;
45 const AdaptiveDialog({
46 required this.title,
47 required this.body,
48 required this.actions,
49 this.style = const .context(),
50 this.animation,
51 super.key,
52 });
53 @override
54 Widget build(BuildContext context) => FDialog.adaptive(
55 style: style,
56 animation: animation,
57 horizontalBuilder: (context, style) {
58 final touch = context.platformVariant.touch;
59 return Padding(
60 padding: touch
61 ? const .symmetric(horizontal: 16, vertical: 18)
62 : const .symmetric(horizontal: 16, vertical: 14),
63 child: Column(
64 crossAxisAlignment: .start,
65 mainAxisSize: .min,
66 children: [
67 Padding(
68 padding: touch
69 ? const .only(left: 8, right: 8, bottom: 9)
70 : const .only(bottom: 5),
71 child: DefaultTextStyle.merge(
72 style: style.titleTextStyle,
73 child: title,
74 ),
75 ),
76 Flexible(
77 child: Padding(
78 padding: touch
79 ? const .only(left: 8, right: 8, bottom: 20)
80 : const .only(bottom: 16),
81 child: DefaultTextStyle.merge(
82 style: style.bodyTextStyle,
83 child: body,
84 ),
85 ),
86 ),
87 Row(
88 mainAxisAlignment: .end,
89 spacing: touch ? 10 : 8,
90 children: touch
91 ? [for (final action in actions) Expanded(child: action)]
92 : actions,
93 ),
94 ],
95 ),
96 );
97 },
98 verticalBuilder: (context, style) {
99 final touch = context.platformVariant.touch;
100 return Padding(
101 padding: touch
102 ? const .symmetric(horizontal: 16, vertical: 18)
103 : const .symmetric(horizontal: 16, vertical: 14),
104 child: Column(
105 crossAxisAlignment: .start,
106 mainAxisSize: .min,
107 children: [
108 Padding(
109 padding: touch
110 ? const .only(left: 8, right: 8, bottom: 9)
111 : const .only(left: 8, right: 8, bottom: 5),
112 child: DefaultTextStyle.merge(
113 style: style.titleTextStyle,
114 child: title,
115 ),
116 ),
117 Flexible(
118 child: Padding(
119 padding: touch
120 ? const .only(left: 8, right: 8, bottom: 20)
121 : const .only(left: 8, right: 8, bottom: 16),
122 child: DefaultTextStyle.merge(
123 style: style.bodyTextStyle,
124 child: body,
125 ),
126 ),
127 ),
128 Column(
129 mainAxisSize: .min,
130 spacing: touch ? 10 : 8,
131 children: actions,
132 ),
133 ],
134 ),
135 );
136 },
137 );
138}
139

Title

dart run forui snippet create adaptive-title-dialog
1class AdaptiveTitleDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: AdaptiveTitleDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 actions: [
16 FButton(
17 size: .sm,
18 child: const Text('Continue'),
19 onPress: () => Navigator.of(context).pop(),
20 ),
21 FButton(
22 variant: .outline,
23 size: .sm,
24 child: const Text('Cancel'),
25 onPress: () => Navigator.of(context).pop(),
26 ),
27 ],
28 ),
29 ),
30 ),
31 child: const Text('Show Dialog'),
32 );
33}
34
35class AdaptiveTitleDialog extends StatelessWidget {
36 final FDialogStyleDelta style;
37 final Animation<double>? animation;
38 final Widget title;
39 final List<Widget> actions;
40 const AdaptiveTitleDialog({
41 required this.title,
42 required this.actions,
43 this.style = const .context(),
44 this.animation,
45 super.key,
46 });
47 @override
48 Widget build(BuildContext context) => FDialog.adaptive(
49 style: style,
50 animation: animation,
51 horizontalBuilder: (context, style) {
52 final touch = context.platformVariant.touch;
53 return Padding(
54 padding: touch
55 ? const .symmetric(horizontal: 16, vertical: 18)
56 : const .symmetric(horizontal: 16, vertical: 14),
57 child: Column(
58 crossAxisAlignment: .start,
59 mainAxisSize: .min,
60 children: [
61 Padding(
62 padding: touch
63 ? const .only(left: 8, right: 8, bottom: 20)
64 : const .only(bottom: 16),
65 child: DefaultTextStyle.merge(
66 style: style.titleTextStyle,
67 child: title,
68 ),
69 ),
70 Row(
71 mainAxisAlignment: .end,
72 spacing: touch ? 10 : 8,
73 children: touch
74 ? [for (final action in actions) Expanded(child: action)]
75 : actions,
76 ),
77 ],
78 ),
79 );
80 },
81 verticalBuilder: (context, style) {
82 final touch = context.platformVariant.touch;
83 return Padding(
84 padding: touch
85 ? const .symmetric(horizontal: 16, vertical: 18)
86 : const .symmetric(horizontal: 16, vertical: 14),
87 child: Column(
88 crossAxisAlignment: .start,
89 mainAxisSize: .min,
90 children: [
91 Padding(
92 padding: touch
93 ? const .only(left: 8, right: 8, bottom: 20)
94 : const .only(left: 8, right: 8, bottom: 16),
95 child: DefaultTextStyle.merge(
96 style: style.titleTextStyle,
97 child: title,
98 ),
99 ),
100 Column(
101 mainAxisSize: .min,
102 spacing: touch ? 10 : 8,
103 children: actions,
104 ),
105 ],
106 ),
107 );
108 },
109 );
110}
111

Media

dart run forui snippet create adaptive-media-dialog
1class AdaptiveMediaDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: AdaptiveMediaDialog(
12 style: style,
13 animation: animation,
14 image: ClipRRect(
15 borderRadius: context.theme.style.borderRadius.sm,
16 child: Container(
17 decoration: BoxDecoration(
18 image: DecorationImage(
19 image: AssetImage(path('avatar.png')),
20 fit: .cover,
21 ),
22 ),
23 height: 140,
24 ),
25 ),
26 title: const Text('Gratitude'),
27 body: const Text(
28 'The quality of being thankful; readiness to show appreciation for and to return kindness.',
29 ),
30 actions: [
31 FButton(
32 size: .sm,
33 child: const Text('Continue'),
34 onPress: () => Navigator.of(context).pop(),
35 ),
36 FButton(
37 variant: .outline,
38 size: .sm,
39 child: const Text('Cancel'),
40 onPress: () => Navigator.of(context).pop(),
41 ),
42 ],
43 ),
44 ),
45 ),
46 child: const Text('Show Dialog'),
47 );
48}
49
50class AdaptiveMediaDialog extends StatelessWidget {
51 final FDialogStyleDelta style;
52 final Animation<double>? animation;
53 final Widget image;
54 final Widget title;
55 final Widget body;
56 final List<Widget> actions;
57 const AdaptiveMediaDialog({
58 required this.image,
59 required this.title,
60 required this.body,
61 required this.actions,
62 this.style = const .context(),
63 this.animation,
64 super.key,
65 });
66 @override
67 Widget build(BuildContext context) => FDialog.adaptive(
68 style: style,
69 animation: animation,
70 horizontalBuilder: (context, style) {
71 final touch = context.platformVariant.touch;
72 return Padding(
73 padding: touch
74 ? const .symmetric(horizontal: 16, vertical: 18)
75 : const .symmetric(horizontal: 16, vertical: 14),
76 child: Column(
77 crossAxisAlignment: .start,
78 mainAxisSize: .min,
79 children: [
80 image,
81 Padding(
82 padding: touch
83 ? const .only(left: 8, right: 8, top: 9, bottom: 9)
84 : const .only(top: 9, bottom: 5),
85 child: DefaultTextStyle.merge(
86 style: style.titleTextStyle,
87 child: title,
88 ),
89 ),
90 Flexible(
91 child: Padding(
92 padding: touch
93 ? const .only(left: 8, right: 8, bottom: 20)
94 : const .only(bottom: 16),
95 child: DefaultTextStyle.merge(
96 style: style.bodyTextStyle,
97 child: body,
98 ),
99 ),
100 ),
101 Row(
102 mainAxisAlignment: .end,
103 spacing: touch ? 10 : 8,
104 children: touch
105 ? [for (final action in actions) Expanded(child: action)]
106 : actions,
107 ),
108 ],
109 ),
110 );
111 },
112 verticalBuilder: (context, style) {
113 final touch = context.platformVariant.touch;
114 return Padding(
115 padding: touch
116 ? const .symmetric(horizontal: 16, vertical: 18)
117 : const .symmetric(horizontal: 16, vertical: 14),
118 child: Column(
119 crossAxisAlignment: .start,
120 mainAxisSize: .min,
121 children: [
122 image,
123 Padding(
124 padding: touch
125 ? const .only(left: 8, right: 8, top: 9, bottom: 9)
126 : const .only(left: 8, right: 8, top: 9, bottom: 5),
127 child: DefaultTextStyle.merge(
128 style: style.titleTextStyle,
129 child: title,
130 ),
131 ),
132 Flexible(
133 child: Padding(
134 padding: touch
135 ? const .only(left: 8, right: 8, bottom: 20)
136 : const .only(left: 8, right: 8, bottom: 16),
137 child: DefaultTextStyle.merge(
138 style: style.bodyTextStyle,
139 child: body,
140 ),
141 ),
142 ),
143 Column(
144 mainAxisSize: .min,
145 spacing: touch ? 10 : 8,
146 children: actions,
147 ),
148 ],
149 ),
150 );
151 },
152 );
153}
154

Horizontal

Lays out the actions horizontally. Recommended for desktop and wider devices

default

dart run forui snippet create horizontal-dialog
1class HorizontalDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: HorizontalDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 body: const Text(
16 'This action cannot be undone. This will permanently delete your account and '
17 'remove your data from our servers.',
18 ),
19 actions: [
20 FButton(
21 size: .sm,
22 child: const Text('Continue'),
23 onPress: () => Navigator.of(context).pop(),
24 ),
25 FButton(
26 variant: .outline,
27 size: .sm,
28 child: const Text('Cancel'),
29 onPress: () => Navigator.of(context).pop(),
30 ),
31 ],
32 ),
33 ),
34 ),
35 child: const Text('Show Dialog'),
36 );
37}
38
39class HorizontalDialog extends StatelessWidget {
40 final FDialogStyleDelta style;
41 final Animation<double>? animation;
42 final Widget title;
43 final Widget body;
44 final List<Widget> actions;
45 const HorizontalDialog({
46 required this.title,
47 required this.body,
48 required this.actions,
49 this.style = const .context(),
50 this.animation,
51 super.key,
52 });
53 @override
54 Widget build(BuildContext context) => FDialog(
55 style: style,
56 animation: animation,
57 builder: (context, style) {
58 final touch = context.platformVariant.touch;
59 return Padding(
60 padding: touch
61 ? const .symmetric(horizontal: 16, vertical: 18)
62 : const .symmetric(horizontal: 16, vertical: 14),
63 child: Column(
64 crossAxisAlignment: .start,
65 mainAxisSize: .min,
66 children: [
67 Padding(
68 padding: touch
69 ? const .only(left: 8, right: 8, bottom: 9)
70 : const .only(bottom: 5),
71 child: DefaultTextStyle.merge(
72 style: style.titleTextStyle,
73 child: title,
74 ),
75 ),
76 Flexible(
77 child: Padding(
78 padding: touch
79 ? const .only(left: 8, right: 8, bottom: 20)
80 : const .only(bottom: 16),
81 child: DefaultTextStyle.merge(
82 style: style.bodyTextStyle,
83 child: body,
84 ),
85 ),
86 ),
87 Row(
88 mainAxisAlignment: .end,
89 spacing: touch ? 10 : 8,
90 children: touch
91 ? [for (final action in actions) Expanded(child: action)]
92 : actions,
93 ),
94 ],
95 ),
96 );
97 },
98 );
99}
100

Title

dart run forui snippet create horizontal-title-dialog
1class HorizontalTitleDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: HorizontalTitleDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 actions: [
16 FButton(
17 size: .sm,
18 child: const Text('Continue'),
19 onPress: () => Navigator.of(context).pop(),
20 ),
21 FButton(
22 variant: .outline,
23 size: .sm,
24 child: const Text('Cancel'),
25 onPress: () => Navigator.of(context).pop(),
26 ),
27 ],
28 ),
29 ),
30 ),
31 child: const Text('Show Dialog'),
32 );
33}
34
35class HorizontalTitleDialog extends StatelessWidget {
36 final FDialogStyleDelta style;
37 final Animation<double>? animation;
38 final Widget title;
39 final List<Widget> actions;
40 const HorizontalTitleDialog({
41 required this.title,
42 required this.actions,
43 this.style = const .context(),
44 this.animation,
45 super.key,
46 });
47 @override
48 Widget build(BuildContext context) => FDialog(
49 style: style,
50 animation: animation,
51 builder: (context, style) {
52 final touch = context.platformVariant.touch;
53 return Padding(
54 padding: touch
55 ? const .symmetric(horizontal: 16, vertical: 18)
56 : const .symmetric(horizontal: 16, vertical: 14),
57 child: Column(
58 crossAxisAlignment: .start,
59 mainAxisSize: .min,
60 children: [
61 Padding(
62 padding: touch
63 ? const .only(left: 8, right: 8, bottom: 20)
64 : const .only(bottom: 16),
65 child: DefaultTextStyle.merge(
66 style: style.titleTextStyle,
67 child: title,
68 ),
69 ),
70 Row(
71 mainAxisAlignment: .end,
72 spacing: touch ? 10 : 8,
73 children: touch
74 ? [for (final action in actions) Expanded(child: action)]
75 : actions,
76 ),
77 ],
78 ),
79 );
80 },
81 );
82}
83

Media

dart run forui snippet create horizontal-media-dialog
1class HorizontalMediaDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: HorizontalMediaDialog(
12 style: style,
13 animation: animation,
14 image: ClipRRect(
15 borderRadius: context.theme.style.borderRadius.sm,
16 child: Container(
17 decoration: BoxDecoration(
18 image: DecorationImage(
19 image: AssetImage(path('avatar.png')),
20 fit: .cover,
21 ),
22 ),
23 height: 140,
24 ),
25 ),
26 title: const Text('Gratitude'),
27 body: const Text(
28 'The quality of being thankful; readiness to show appreciation for and to return kindness.',
29 ),
30 actions: [
31 FButton(
32 size: .sm,
33 child: const Text('Continue'),
34 onPress: () => Navigator.of(context).pop(),
35 ),
36 FButton(
37 variant: .outline,
38 size: .sm,
39 child: const Text('Cancel'),
40 onPress: () => Navigator.of(context).pop(),
41 ),
42 ],
43 ),
44 ),
45 ),
46 child: const Text('Show Dialog'),
47 );
48}
49
50class HorizontalMediaDialog extends StatelessWidget {
51 final FDialogStyleDelta style;
52 final Animation<double>? animation;
53 final Widget image;
54 final Widget title;
55 final Widget body;
56 final List<Widget> actions;
57 const HorizontalMediaDialog({
58 required this.image,
59 required this.title,
60 required this.body,
61 required this.actions,
62 this.style = const .context(),
63 this.animation,
64 super.key,
65 });
66 @override
67 Widget build(BuildContext context) => FDialog(
68 style: style,
69 animation: animation,
70 builder: (context, style) {
71 final touch = context.platformVariant.touch;
72 return Padding(
73 padding: touch
74 ? const .symmetric(horizontal: 16, vertical: 18)
75 : const .symmetric(horizontal: 16, vertical: 14),
76 child: Column(
77 crossAxisAlignment: .start,
78 mainAxisSize: .min,
79 children: [
80 image,
81 Padding(
82 padding: touch
83 ? const .only(left: 8, right: 8, top: 9, bottom: 9)
84 : const .only(top: 9, bottom: 5),
85 child: DefaultTextStyle.merge(
86 style: style.titleTextStyle,
87 child: title,
88 ),
89 ),
90 Flexible(
91 child: Padding(
92 padding: touch
93 ? const .only(left: 8, right: 8, bottom: 20)
94 : const .only(bottom: 16),
95 child: DefaultTextStyle.merge(
96 style: style.bodyTextStyle,
97 child: body,
98 ),
99 ),
100 ),
101 Row(
102 mainAxisAlignment: .end,
103 spacing: touch ? 10 : 8,
104 children: touch
105 ? [for (final action in actions) Expanded(child: action)]
106 : actions,
107 ),
108 ],
109 ),
110 );
111 },
112 );
113}
114

Vertical

Lays out the actions vertically. Recommended for mobile devices.

Default

dart run forui snippet create vertical-dialog
1class VerticalDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: VerticalDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 body: const Text(
16 'This action cannot be undone. This will permanently delete your account and '
17 'remove your data from our servers.',
18 ),
19 actions: [
20 FButton(
21 size: .sm,
22 child: const Text('Continue'),
23 onPress: () => Navigator.of(context).pop(),
24 ),
25 FButton(
26 variant: .outline,
27 size: .sm,
28 child: const Text('Cancel'),
29 onPress: () => Navigator.of(context).pop(),
30 ),
31 ],
32 ),
33 ),
34 ),
35 child: const Text('Show Dialog'),
36 );
37}
38
39class VerticalDialog extends StatelessWidget {
40 final FDialogStyleDelta style;
41 final Animation<double>? animation;
42 final Widget title;
43 final Widget body;
44 final List<Widget> actions;
45 const VerticalDialog({
46 required this.title,
47 required this.body,
48 required this.actions,
49 this.style = const .context(),
50 this.animation,
51 super.key,
52 });
53 @override
54 Widget build(BuildContext context) => FDialog(
55 style: style,
56 animation: animation,
57 builder: (context, style) {
58 final touch = context.platformVariant.touch;
59 return Padding(
60 padding: touch
61 ? const .symmetric(horizontal: 16, vertical: 18)
62 : const .symmetric(horizontal: 16, vertical: 14),
63 child: Column(
64 crossAxisAlignment: .start,
65 mainAxisSize: .min,
66 children: [
67 Padding(
68 padding: touch
69 ? const .only(left: 8, right: 8, bottom: 9)
70 : const .only(left: 8, right: 8, bottom: 5),
71 child: DefaultTextStyle.merge(
72 style: style.titleTextStyle,
73 child: title,
74 ),
75 ),
76 Flexible(
77 child: Padding(
78 padding: touch
79 ? const .only(left: 8, right: 8, bottom: 20)
80 : const .only(left: 8, right: 8, bottom: 16),
81 child: DefaultTextStyle.merge(
82 style: style.bodyTextStyle,
83 child: body,
84 ),
85 ),
86 ),
87 Column(
88 mainAxisSize: .min,
89 spacing: touch ? 10 : 8,
90 children: actions,
91 ),
92 ],
93 ),
94 );
95 },
96 );
97}
98

Title

dart run forui snippet create vertical-title-dialog
1class VerticalTitleDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: VerticalTitleDialog(
12 style: style,
13 animation: animation,
14 title: const Text('Are you absolutely sure?'),
15 actions: [
16 FButton(
17 size: .sm,
18 child: const Text('Continue'),
19 onPress: () => Navigator.of(context).pop(),
20 ),
21 FButton(
22 variant: .outline,
23 size: .sm,
24 child: const Text('Cancel'),
25 onPress: () => Navigator.of(context).pop(),
26 ),
27 ],
28 ),
29 ),
30 ),
31 child: const Text('Show Dialog'),
32 );
33}
34
35class VerticalTitleDialog extends StatelessWidget {
36 final FDialogStyleDelta style;
37 final Animation<double>? animation;
38 final Widget title;
39 final List<Widget> actions;
40 const VerticalTitleDialog({
41 required this.title,
42 required this.actions,
43 this.style = const .context(),
44 this.animation,
45 super.key,
46 });
47 @override
48 Widget build(BuildContext context) => FDialog(
49 style: style,
50 animation: animation,
51 builder: (context, style) {
52 final touch = context.platformVariant.touch;
53 return Padding(
54 padding: touch
55 ? const .symmetric(horizontal: 16, vertical: 18)
56 : const .symmetric(horizontal: 16, vertical: 14),
57 child: Column(
58 crossAxisAlignment: .start,
59 mainAxisSize: .min,
60 children: [
61 Padding(
62 padding: touch
63 ? const .only(left: 8, right: 8, bottom: 20)
64 : const .only(left: 8, right: 8, bottom: 16),
65 child: DefaultTextStyle.merge(
66 style: style.titleTextStyle,
67 child: title,
68 ),
69 ),
70 Column(
71 mainAxisSize: .min,
72 spacing: touch ? 10 : 8,
73 children: actions,
74 ),
75 ],
76 ),
77 );
78 },
79 );
80}
81

Media

dart run forui snippet create vertical-media-dialog
1class VerticalMediaDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 builder: (context, style, animation) => FTheme(
10 data: theme,
11 child: VerticalMediaDialog(
12 style: style,
13 animation: animation,
14 image: ClipRRect(
15 borderRadius: context.theme.style.borderRadius.sm,
16 child: Container(
17 decoration: BoxDecoration(
18 image: DecorationImage(
19 image: AssetImage(path('avatar.png')),
20 fit: .cover,
21 ),
22 ),
23 height: 140,
24 ),
25 ),
26 title: const Text('Gratitude'),
27 body: const Text(
28 'The quality of being thankful; readiness to show appreciation for and to return kindness.',
29 ),
30 actions: [
31 FButton(
32 size: .sm,
33 child: const Text('Continue'),
34 onPress: () => Navigator.of(context).pop(),
35 ),
36 FButton(
37 variant: .outline,
38 size: .sm,
39 child: const Text('Cancel'),
40 onPress: () => Navigator.of(context).pop(),
41 ),
42 ],
43 ),
44 ),
45 ),
46 child: const Text('Show Dialog'),
47 );
48}
49
50class VerticalMediaDialog extends StatelessWidget {
51 final FDialogStyleDelta style;
52 final Animation<double>? animation;
53 final Widget image;
54 final Widget title;
55 final Widget body;
56 final List<Widget> actions;
57 const VerticalMediaDialog({
58 required this.image,
59 required this.title,
60 required this.body,
61 required this.actions,
62 this.style = const .context(),
63 this.animation,
64 super.key,
65 });
66 @override
67 Widget build(BuildContext context) => FDialog(
68 style: style,
69 animation: animation,
70 builder: (context, style) {
71 final touch = context.platformVariant.touch;
72 return Padding(
73 padding: touch
74 ? const .symmetric(horizontal: 16, vertical: 18)
75 : const .symmetric(horizontal: 16, vertical: 14),
76 child: Column(
77 crossAxisAlignment: .start,
78 mainAxisSize: .min,
79 children: [
80 image,
81 Padding(
82 padding: touch
83 ? const .only(left: 8, right: 8, top: 9, bottom: 9)
84 : const .only(left: 8, right: 8, top: 9, bottom: 5),
85 child: DefaultTextStyle.merge(
86 style: style.titleTextStyle,
87 child: title,
88 ),
89 ),
90 Flexible(
91 child: Padding(
92 padding: touch
93 ? const .only(left: 8, right: 8, bottom: 20)
94 : const .only(left: 8, right: 8, bottom: 16),
95 child: DefaultTextStyle.merge(
96 style: style.bodyTextStyle,
97 child: body,
98 ),
99 ),
100 ),
101 Column(
102 mainAxisSize: .min,
103 spacing: touch ? 10 : 8,
104 children: actions,
105 ),
106 ],
107 ),
108 );
109 },
110 );
111}
112

Blurred Barrier

1class BlurredDialogExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 variant: .outline,
5 size: .sm,
6 mainAxisSize: .min,
7 onPress: () => showFDialog(
8 context: context,
9 routeStyle: .delta(
10 barrierFilter: () =>
11 (animation) => ImageFilter.compose(
12 outer: ImageFilter.blur(
13 sigmaX: animation * 5,
14 sigmaY: animation * 5,
15 ),
16 inner: ColorFilter.mode(context.theme.colors.barrier, .srcOver),
17 ),
18 ),
19 builder: (context, style, animation) => FTheme(
20 data: theme,
21 child: VerticalDialog(
22 style: style,
23 animation: animation,
24 title: const Text('Are you absolutely sure?'),
25 body: const Text(
26 'This action cannot be undone. This will permanently delete your account and '
27 'remove your data from our servers.',
28 ),
29 actions: [
30 FButton(
31 size: .sm,
32 child: const Text('Continue'),
33 onPress: () => Navigator.of(context).pop(),
34 ),
35 FButton(
36 size: .sm,
37 variant: .outline,
38 child: const Text('Cancel'),
39 onPress: () => Navigator.of(context).pop(),
40 ),
41 ],
42 ),
43 ),
44 ),
45 child: const Text('Show Dialog'),
46 );
47}
48
49class VerticalDialog extends StatelessWidget {
50 final FDialogStyleDelta style;
51 final Animation<double>? animation;
52 final Widget title;
53 final Widget body;
54 final List<Widget> actions;
55 const VerticalDialog({
56 required this.title,
57 required this.body,
58 required this.actions,
59 this.style = const .context(),
60 this.animation,
61 super.key,
62 });
63 @override
64 Widget build(BuildContext context) => FDialog(
65 style: style,
66 animation: animation,
67 builder: (context, style) {
68 final touch = context.platformVariant.touch;
69 return Padding(
70 padding: touch
71 ? const .symmetric(horizontal: 16, vertical: 18)
72 : const .symmetric(horizontal: 16, vertical: 14),
73 child: Column(
74 crossAxisAlignment: .start,
75 mainAxisSize: .min,
76 children: [
77 Padding(
78 padding: touch
79 ? const .only(left: 8, right: 8, bottom: 9)
80 : const .only(left: 8, right: 8, bottom: 5),
81 child: DefaultTextStyle.merge(
82 style: style.titleTextStyle,
83 child: title,
84 ),
85 ),
86 Flexible(
87 child: Padding(
88 padding: touch
89 ? const .only(left: 8, right: 8, bottom: 20)
90 : const .only(left: 8, right: 8, bottom: 16),
91 child: DefaultTextStyle.merge(
92 style: style.bodyTextStyle,
93 child: body,
94 ),
95 ),
96 ),
97 Column(
98 mainAxisSize: .min,
99 spacing: touch ? 10 : 8,
100 children: actions,
101 ),
102 ],
103 ),
104 );
105 },
106 );
107}
108

On this page