Overlay

Sheet

A modal sheet is an alternative to a menu or a dialog and prevents the user from interacting with the rest of the app.

It navigates to a new page each time.

A closely related widget is a persistent sheet, which shows information that supplements the primary content of the app without preventing the user from interacting with the app.

1class ModalSheetExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => Column(
4 mainAxisAlignment: .center,
5 mainAxisSize: .min,
6 spacing: 5,
7 children: [
8 FButton(
9 child: const Text('Left'),
10 onPress: () => showFSheet(
11 context: context,
12 side: .ltr,
13 builder: (context) => const Form(side: .ltr),
14 ),
15 ),
16 FButton(
17 child: const Text('Top'),
18 onPress: () => showFSheet(
19 context: context,
20 side: .ttb,
21 builder: (context) => const Form(side: .ttb),
22 ),
23 ),
24 FButton(
25 child: const Text('Right'),
26 onPress: () => showFSheet(
27 context: context,
28 side: .rtl,
29 builder: (context) => const Form(side: .rtl),
30 ),
31 ),
32 FButton(
33 child: const Text('Bottom'),
34 onPress: () => showFSheet(
35 context: context,
36 side: .btt,
37 builder: (context) => const Form(side: .btt),
38 ),
39 ),
40 ],
41 );
42}
43
44class Form extends StatelessWidget {
45 final FLayout side;
46 const Form({required this.side, super.key});
47 @override
48 Widget build(BuildContext context) => Container(
49 height: .infinity,
50 width: .infinity,
51 decoration: BoxDecoration(
52 color: context.theme.colors.background,
53 border: side.vertical
54 ? .symmetric(
55 horizontal: BorderSide(color: context.theme.colors.border),
56 )
57 : .symmetric(
58 vertical: BorderSide(color: context.theme.colors.border),
59 ),
60 ),
61 child: Padding(
62 padding: const .symmetric(horizontal: 15, vertical: 8.0),
63 child: Center(
64 child: Column(
65 mainAxisSize: .min,
66 crossAxisAlignment: .start,
67 children: [
68 Text(
69 'Account',
70 style: context.theme.typography.xl2.copyWith(
71 fontWeight: .w600,
72 color: context.theme.colors.foreground,
73 height: 1.5,
74 ),
75 ),
76 Text(
77 'Make changes to your account here. Click save when you are done.',
78 style: context.theme.typography.sm.copyWith(
79 color: context.theme.colors.mutedForeground,
80 ),
81 ),
82 const SizedBox(height: 8),
83 SizedBox(
84 width: 450,
85 child: Column(
86 children: [
87 const FTextField(label: Text('Name'), hint: 'John Renalo'),
88 const SizedBox(height: 10),
89 const FTextField(label: Text('Email'), hint: 'john@doe.com'),
90 const SizedBox(height: 16),
91 FButton(
92 child: const Text('Save'),
93 onPress: () => Navigator.of(context).pop(),
94 ),
95 ],
96 ),
97 ),
98 ],
99 ),
100 ),
101 ),
102 );
103}
104

CLI

To generate and customize this style:

dart run forui style create modal-sheet

Usage

showFSheet(...)

1showFSheet(
2 context: context,
3 style: const .delta(flingVelocity: 700),
4 side: .btt,
5 builder: (context) =>
6 const Padding(padding: .all(16), child: Text('Sheet content')),
7)

FModalSheetRoute(...)

1FModalSheetRoute<void>(
2 style: const FModalSheetStyle(),
3 side: .btt,
4 builder: (context) =>
5 const Padding(padding: .all(16), child: Text('Sheet content')),
6)

Examples

Blurred Barrier

1class BlurredModalSheetExample extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) => FButton(
4 child: const Text('Open'),
5 onPress: () => showFSheet(
6 style: .delta(
7 barrierFilter: (animation) => .compose(
8 outer: ImageFilter.blur(sigmaX: animation * 5, sigmaY: animation * 5),
9 inner: ColorFilter.mode(context.theme.colors.barrier, .srcOver),
10 ),
11 ),
12 context: context,
13 side: .ltr,
14 builder: (context) => const Form(side: .ltr),
15 ),
16 );
17}
18
19class Form extends StatelessWidget {
20 final FLayout side;
21 const Form({required this.side, super.key});
22 @override
23 Widget build(BuildContext context) => Container(
24 height: .infinity,
25 width: .infinity,
26 decoration: BoxDecoration(
27 color: context.theme.colors.background,
28 border: side.vertical
29 ? .symmetric(
30 horizontal: BorderSide(color: context.theme.colors.border),
31 )
32 : .symmetric(
33 vertical: BorderSide(color: context.theme.colors.border),
34 ),
35 ),
36 child: Padding(
37 padding: const .symmetric(horizontal: 15, vertical: 8.0),
38 child: Center(
39 child: Column(
40 mainAxisSize: .min,
41 crossAxisAlignment: .start,
42 children: [
43 Text(
44 'Account',
45 style: context.theme.typography.xl2.copyWith(
46 fontWeight: .w600,
47 color: context.theme.colors.foreground,
48 height: 1.5,
49 ),
50 ),
51 Text(
52 'Make changes to your account here. Click save when you are done.',
53 style: context.theme.typography.sm.copyWith(
54 color: context.theme.colors.mutedForeground,
55 ),
56 ),
57 const SizedBox(height: 8),
58 SizedBox(
59 width: 450,
60 child: Column(
61 children: [
62 const FTextField(label: Text('Name'), hint: 'John Renalo'),
63 const SizedBox(height: 10),
64 const FTextField(label: Text('Email'), hint: 'john@doe.com'),
65 const SizedBox(height: 16),
66 FButton(
67 child: const Text('Save'),
68 onPress: () => Navigator.of(context).pop(),
69 ),
70 ],
71 ),
72 ),
73 ],
74 ),
75 ),
76 ),
77 );
78}
79

With DraggableScrollableSheet

1@override
2Widget build(BuildContext context) => FButton(
3 child: const Text('Click me'),
4 onPress: () => showFSheet(
5 context: context,
6 side: .btt,
7 mainAxisMaxRatio: null,
8 builder: (context) => DraggableScrollableSheet(
9 expand: false,
10 builder: (context, controller) => ScrollConfiguration(
11 // This is required to enable dragging on desktop.
12 // See https://github.com/flutter/flutter/issues/101903 for more information.
13 behavior: ScrollConfiguration.of(
14 context,
15 ).copyWith(dragDevices: {.touch, .mouse, .trackpad}),
16 child: FTileGroup.builder(
17 count: 25,
18 scrollController: controller,
19 tileBuilder: (context, index) => FTile(title: Text('Tile $index')),
20 ),
21 ),
22 ),
23 ),
24);
25

On this page