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 @override3 Widget build(BuildContext context) => Row(4 mainAxisAlignment: .center,5 mainAxisSize: .min,6 spacing: 10,7 children: [8 FButton(9 variant: .outline,10 size: .sm,11 mainAxisSize: .min,12 child: const Text('Left'),13 onPress: () => showFSheet(14 context: context,15 side: .ltr,16 builder: (context) => const Form(side: .ltr),17 ),18 ),19 FButton(20 variant: .outline,21 size: .sm,22 mainAxisSize: .min,23 child: const Text('Top'),24 onPress: () => showFSheet(25 context: context,26 side: .ttb,27 builder: (context) => const Form(side: .ttb),28 ),29 ),30 FButton(31 variant: .outline,32 size: .sm,33 mainAxisSize: .min,34 child: const Text('Bottom'),35 onPress: () => showFSheet(36 context: context,37 side: .btt,38 builder: (context) => const Form(side: .btt),39 ),40 ),41 FButton(42 variant: .outline,43 size: .sm,44 mainAxisSize: .min,45 child: const Text('Right'),46 onPress: () => showFSheet(47 context: context,48 side: .rtl,49 builder: (context) => const Form(side: .rtl),50 ),51 ),52 ],53 );54}5556class Form extends StatelessWidget {57 final FLayout side;58 const Form({required this.side, super.key});59 @override60 Widget build(BuildContext context) => Container(61 height: .infinity,62 width: .infinity,63 decoration: BoxDecoration(64 color: context.theme.colors.background,65 border: side.vertical66 ? .symmetric(67 horizontal: BorderSide(color: context.theme.colors.border),68 )69 : .symmetric(70 vertical: BorderSide(color: context.theme.colors.border),71 ),72 ),73 child: Padding(74 padding: const .symmetric(horizontal: 15, vertical: 8.0),75 child: Center(76 child: Column(77 mainAxisSize: .min,78 crossAxisAlignment: .start,79 children: [80 Text(81 'Account',82 style: context.theme.typography.display.xl2.copyWith(83 fontWeight: .w600,84 color: context.theme.colors.foreground,85 height: 1.5,86 ),87 ),88 Text(89 'Make changes to your account here. Click save when you are done.',90 style: context.theme.typography.body.sm.copyWith(91 color: context.theme.colors.mutedForeground,92 ),93 ),94 const SizedBox(height: 12),95 SizedBox(96 width: 450,97 child: Column(98 children: [99 const FTextField(label: Text('Name'), hint: 'John Renalo'),100 const SizedBox(height: 12),101 const FTextField(label: Text('Email'), hint: 'john@doe.com'),102 const SizedBox(height: 20),103 Align(104 alignment: .centerRight,105 child: FButton(106 size: .sm,107 mainAxisSize: .min,108 child: const Text('Save'),109 onPress: () => Navigator.of(context).pop(),110 ),111 ),112 ],113 ),114 ),115 ],116 ),117 ),118 ),119 );120}121CLI
To generate a specific style for customization:
dart run forui style create modal-sheetUsage
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 @override3 Widget build(BuildContext context) => Row(4 mainAxisAlignment: .end,5 spacing: 20,6 children: [7 FButton(8 variant: .outline,9 size: .sm,10 mainAxisSize: .min,11 child: const Text('Open'),12 onPress: () => showFSheet(13 style: .delta(14 barrierFilter: (animation) => .compose(15 outer: ImageFilter.blur(16 sigmaX: animation * 5,17 sigmaY: animation * 5,18 ),19 inner: ColorFilter.mode(context.theme.colors.barrier, .srcOver),20 ),21 ),22 context: context,23 side: .rtl,24 builder: (context) => const Form(side: .rtl),25 ),26 ),27 Expanded(28 child: Column(29 mainAxisSize: .min,30 crossAxisAlignment: .start,31 spacing: 8,32 children: [33 Text(34 'Account Settings',35 style: context.theme.typography.display.lg.copyWith(36 fontWeight: .w600,37 ),38 ),39 Text(40 'Manage your preferences and profile details.',41 style: context.theme.typography.body.sm,42 ),43 const FDivider(),44 Row(45 spacing: 8,46 children: [47 FAvatar(48 image: const NetworkImage('https://picsum.photos/200'),49 fallback: const Text('JR'),50 ),51 Column(52 crossAxisAlignment: .start,53 children: [54 Text(55 'John Renalo',56 style: context.theme.typography.body.sm.copyWith(57 fontWeight: .w600,58 ),59 ),60 Text(61 'john@doe.com',62 style: context.theme.typography.body.xs,63 ),64 ],65 ),66 ],67 ),68 ],69 ),70 ),71 ],72 );73}7475class Form extends StatelessWidget {76 final FLayout side;77 const Form({required this.side, super.key});78 @override79 Widget build(BuildContext context) => Container(80 height: .infinity,81 width: .infinity,82 decoration: BoxDecoration(83 color: context.theme.colors.background,84 border: side.vertical85 ? .symmetric(86 horizontal: BorderSide(color: context.theme.colors.border),87 )88 : .symmetric(89 vertical: BorderSide(color: context.theme.colors.border),90 ),91 ),92 child: Padding(93 padding: const .symmetric(horizontal: 15, vertical: 8.0),94 child: Center(95 child: Column(96 mainAxisSize: .min,97 crossAxisAlignment: .start,98 children: [99 Text(100 'Account',101 style: context.theme.typography.display.xl2.copyWith(102 fontWeight: .w600,103 color: context.theme.colors.foreground,104 height: 1.5,105 ),106 ),107 Text(108 'Make changes to your account here. Click save when you are done.',109 style: context.theme.typography.body.sm.copyWith(110 color: context.theme.colors.mutedForeground,111 ),112 ),113 const SizedBox(height: 12),114 SizedBox(115 width: 450,116 child: Column(117 children: [118 const FTextField(label: Text('Name'), hint: 'John Renalo'),119 const SizedBox(height: 12),120 const FTextField(label: Text('Email'), hint: 'john@doe.com'),121 const SizedBox(height: 20),122 Align(123 alignment: .centerRight,124 child: FButton(125 size: .sm,126 mainAxisSize: .min,127 child: const Text('Save'),128 onPress: () => Navigator.of(context).pop(),129 ),130 ),131 ],132 ),133 ),134 ],135 ),136 ),137 ),138 );139}140With DraggableScrollableSheet
1@override2Widget build(BuildContext context) => FButton(3 variant: .outline,4 size: .sm,5 mainAxisSize: .min,6 child: const Text('Click me'),7 onPress: () => showFSheet(8 context: context,9 side: .btt,10 mainAxisMaxRatio: null,11 builder: (context) => DraggableScrollableSheet(12 expand: false,13 builder: (context, controller) => ScrollConfiguration(14 // This is required to enable dragging on desktop.15 // See https://github.com/flutter/flutter/issues/101903 for more information.16 behavior: ScrollConfiguration.of(17 context,18 ).copyWith(dragDevices: {.touch, .mouse, .trackpad}),19 child: FTileGroup.builder(20 count: 25,21 scrollController: controller,22 tileBuilder: (context, index) => FTile(title: Text('Tile $index')),23 ),24 ),25 ),26 ),27);28