Skip to content

Commit

Permalink
Update sheets documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Pante committed Dec 5, 2024
1 parent a4a87f0 commit 0cc19b1
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 56 deletions.
3 changes: 3 additions & 0 deletions docs/pages/docs/overlay/modal-sheet.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import LinkBadgeGroup from "../../../components/link-badge/link-badge-group.tsx"

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

A closely related widget is a [sheet](/docs/overlay/sheet), which shows information that supplements the primary content
of the app without preventing the user from interacting with the app.

<LinkBadgeGroup>
<LinkBadge label="API Reference" href="https://pub.dev/documentation/forui/latest/forui.widgets.sheet/forui.widgets.sheet-library.html"/>
</LinkBadgeGroup>
Expand Down
224 changes: 196 additions & 28 deletions docs/pages/docs/overlay/sheet.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import LinkBadgeGroup from "../../../components/link-badge/link-badge-group.tsx"
A sheet that are displayed above another widget. It is part of [FScaffold](/docs/layout/scaffold), which should be
preferred in most cases.

A closely related widget is a [modal sheet](/docs/overlay/modal-sheet) which prevents the user from interacting with the
rest of the app.

<LinkBadgeGroup>
<LinkBadge label="API Reference" href="https://pub.dev/documentation/forui/latest/forui.widgets.sheet/forui.widgets.sheet-library.html"/>
</LinkBadgeGroup>
Expand Down Expand Up @@ -38,7 +41,7 @@ preferred in most cases.
controller = _controllers[side] ??= showFSheet(
context: context,
side: Layout.ltr,
builder: (context) => Form(side: side),
builder: (context, controller) => Form(side: side, controller: controller),
);
} else {
controller.toggle();
Expand Down Expand Up @@ -77,8 +80,9 @@ preferred in most cases.
class Form extends StatelessWidget {
final Layout side;
final FSheetController controller;
const Form({required this.side, super.key});
const Form({required this.side, required this.controller, super.key});
@override
Widget build(BuildContext context) => Container(
Expand Down Expand Up @@ -171,6 +175,145 @@ showFSheet(

## Examples

### With `KeepAliveOffstage`

<Tabs items={['Preview', 'Code']}>
<Tabs.Tab>
<Widget name='sheets' query={{keepAliveOffstage: true}} height={500}/>
</Tabs.Tab>
<Tabs.Tab>
```dart {17}
class Sheets extends StatefulWidget {
@override
State<Sheets> createState() => _State();
}
class _State extends State<Sheets> {
final Map<Layout, FSheetController> _controllers = {};
@override
Widget build(BuildContext context) {
VoidCallback onPress(Layout side) => () {
var controller = _controllers[side];
if (controller == null) {
controller = _controllers[side] ??= showFSheet(
context: context,
side: Layout.ltr,
keepAliveOffstage: true,
builder: (context, controller) => Form(side: side, controller: controller),
);
} else {
controller.toggle();
}
};
return FScaffold( // This can be replaced with FSheets
content: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
FButton(
label: const Text('Left'),
onPress: onPress(Layout.ltr),
),
const SizedBox(height: 5),
FButton(
label: const Text('Top'),
onPress: onPress(Layout.ttb),
),
const SizedBox(height: 5),
FButton(
label: const Text('Right'),
onPress: onPress(Layout.rtl),
),
const SizedBox(height: 5),
FButton(
label: const Text('Bottom'),
onPress: onPress(Layout.btt),
),
],
),
);
}
}
class Form extends StatelessWidget {
final Layout side;
final FSheetController controller;
const Form({required this.side, required this.controller, super.key});
@override
Widget build(BuildContext context) => Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(
color: context.theme.colorScheme.background,
border: side.vertical
? Border.symmetric(horizontal: BorderSide(color: context.theme.colorScheme.border))
: Border.symmetric(vertical: BorderSide(color: context.theme.colorScheme.border)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Account',
style: context.theme.typography.xl2.copyWith(
fontWeight: FontWeight.w600,
color: context.theme.colorScheme.foreground,
height: 1.5,
),
),
Text(
'Make changes to your account here. Click save when you are done.',
style: context.theme.typography.sm.copyWith(
color: context.theme.colorScheme.mutedForeground,
),
),
const SizedBox(height: 8),
SizedBox(
width: 450,
child: Column(
children: [
const FTextField(
label: Text('Name'),
hint: 'John Renalo',
),
const SizedBox(height: 10),
const FTextField(
label: Text('Email'),
hint: '[email protected]',
),
const SizedBox(height: 16),
FButton(
label: const Text('Save'),
onPress: () => Navigator.of(context).pop(),
),
],
),
),
],
),
),
),
);
@override
void dispose() {
for (final controller in _controllers.values) {
controller.dispose();
}
super.dispose();
}
}
```
</Tabs.Tab>
</Tabs>

### With `DraggableScrollableSheet`

<Tabs items={['Preview', 'Code']}>
Expand All @@ -179,33 +322,58 @@ showFSheet(
</Tabs.Tab>
<Tabs.Tab>
```dart
FScaffold( // This can be replaced with FSheets
content: FButton(
label: const Text('Click me'),
onPress: () => showFSheet(
context: context,
side: Layout.btt,
mainAxisMaxRatio: null,
builder: (context) => DraggableScrollableSheet(
expand: false,
builder: (context, controller) => ScrollConfiguration(
// This is required to enable dragging on desktop.
// See https://github.com/flutter/flutter/issues/101903 for more information.
behavior: ScrollConfiguration.of(context).copyWith(dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
}),
child: FTileGroup.builder(
count: 25,
controller: controller,
tileBuilder: (context, index) => FTile(title: Text('Tile $index')),
),
class DraggableSheets extends StatefulWidget {
@override
State<DraggableSheets> createState() => _State();
}
class _State extends State<DraggableSheets> {
FSheetController? controller;
@override
Widget build(BuildContext context) => FScaffold(
content: FButton(
label: const Text('Click me'),
onPress: () {
if (controller != null) {
controller!.toggle();
return;
}
controller = showFSheet(
context: context,
side: Layout.btt,
mainAxisMaxRatio: null,
builder: (context, _) => DraggableScrollableSheet(
expand: false,
builder: (context, controller) => ScrollConfiguration(
// This is required to enable dragging on desktop.
// See https://github.com/flutter/flutter/issues/101903 for more information.
behavior: ScrollConfiguration.of(context).copyWith(
dragDevices: {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
},
),
child: FTileGroup.builder(
count: 25,
controller: controller,
tileBuilder: (context, index) => FTile(title: Text('Tile $index')),
),
),
),
);
},
),
),
),
),
);
);
@override
void dispose() {
controller?.dispose();
super.dispose();
}
}
```
</Tabs.Tab>
</Tabs>
Expand Down
14 changes: 12 additions & 2 deletions forui/lib/src/widgets/sheet/sheets.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:math';

import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import 'package:forui/forui.dart';
import 'package:forui/src/widgets/sheet/sheet.dart';
Expand Down Expand Up @@ -229,9 +230,18 @@ class FSheetsState extends State<FSheets> with TickerProviderStateMixin {
setState(() => sheets[controller.key] = (controller, sheet));
}

void _remove(Key key) {
if (mounted) {
Future<void> _remove(Key key) async {
// This checks if the method was called during the build phase, and schedules the removal for the next frame.
// This done as _remove is called in FSheetController.dispose, and subsequently StatefulWidget.dispose, which is
// part of the build phase.
if (mounted && SchedulerBinding.instance.schedulerPhase == SchedulerPhase.idle) {
setState(() => sheets.remove(key));
} else {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() => sheets.remove(key));
}
});
}
}

Expand Down
Loading

0 comments on commit 0cc19b1

Please sign in to comment.