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) => 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}4344class Form extends StatelessWidget {45 final FLayout side;46 const Form({required this.side, super.key});47 @override48 Widget build(BuildContext context) => Container(49 height: .infinity,50 width: .infinity,51 decoration: BoxDecoration(52 color: context.theme.colors.background,53 border: side.vertical54 ? .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}104CLI
To generate and customize this style:
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) => 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}1819class Form extends StatelessWidget {20 final FLayout side;21 const Form({required this.side, super.key});22 @override23 Widget build(BuildContext context) => Container(24 height: .infinity,25 width: .infinity,26 decoration: BoxDecoration(27 color: context.theme.colors.background,28 border: side.vertical29 ? .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}79With DraggableScrollableSheet
1@override2Widget 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