Skip to content

Commit b9bc62e

Browse files
authored
Add FSelectMenuTile.fromMap(...) constructor & fix FPopover & FSidebar focus traversal edge behavior (#614)
* Add `FSelectMenuTile.fromMap` * Fix focus traversal edge behavior for FSidebar & FPopover * Prepare samples for review * Prepare Forui for review * Update windows-latest goldens * Move toaster alignment to FToasterStyle * whoops * Prepare Forui for review --------- Co-authored-by: Pante <[email protected]>
1 parent 549fa2b commit b9bc62e

File tree

19 files changed

+621
-76
lines changed

19 files changed

+621
-76
lines changed

docs/app/docs/tile/select-menu-tile/page.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,14 @@ A tile that, when triggered, displays a list of options for the user to pick fro
2626
2727
class _SelectMenuTile extends StatelessWidget {
2828
@override
29-
Widget build(BuildContext context) => FSelectMenuTile(
29+
Widget build(BuildContext context) => FSelectMenuTile.fromMap(
30+
const {
31+
'All': Notification.all,
32+
'Direct Messages': Notification.direct,
33+
'None': Notification.nothing,
34+
},
3035
initialValue: Notification.all,
36+
autoHide: autoHide,
3137
validator: (value) => value == null ? 'Select an item' : null,
3238
prefix: const Icon(FIcons.bell),
3339
title: const Text('Notifications'),
@@ -36,13 +42,7 @@ A tile that, when triggered, displays a list of options for the user to pick fro
3642
Notification.direct => 'Direct Messages',
3743
_ => 'None',
3844
}),
39-
menu: const [
40-
FSelectTile(title: Text('All'), value: Notification.all),
41-
FSelectTile(title: Text('Direct Messages'), value: Notification.direct),
42-
FSelectTile(title: Text('None'), value: Notification.nothing),
43-
],
4445
);
45-
}
4646
```
4747

4848
</Tabs.Tab>

forui/CHANGELOG.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,28 @@
1212
* Add `FHeaderAction.shortcuts`.
1313

1414

15+
### `FPopover`
16+
* **Breaking** Change `FPopoverController.shown` to `FPopoverController.status`.
17+
18+
* Fix `FPopover` not respecting `FPopover.traversalEdgeBehavior`.
19+
20+
1521
### `FSelect`
1622
* Fix `FSelect` flickering when selecting an item on desktop.
1723
* Fix `FSelect` not scrolling to selected item when opened if the item is really far down the list.
1824
* Fix `FSelect` layout shifting when scrolling through a long list of items.
1925

2026

27+
### `FSidebar`
28+
* Add `FSidebar.focusNode`.
29+
* Add `FSidebar.traversalEdgeBehavior`.
30+
31+
* Change `FSidebarItemStyle.focusedOutlineStyle.spacing` from 3 to 0.
32+
* Convert `FSidebar` from a stateless to a stateful widget.
33+
34+
* Fix `FSidebar`'s focus traversal behavior.
35+
36+
2137
### `FTappable`
2238
* Add `FTappable.onSecondaryPress`.
2339
* Add `FTappable.onSecondaryLongPress`.
@@ -29,15 +45,17 @@
2945

3046

3147
### Others
48+
* Add `FSelectMenuTile.fromMap(...)`.
49+
* Add `FToasterStyle.toastAlignment`.
50+
3251
* **Breaking** Change `FPersistentSheetController.shown` to `FPersistentSheetController.status`.
33-
* **Breaking** Change `FPopoverController.shown` to `FPopoverController.status`.
3452
* **Breaking** Change `FTooltipController.shown` to `FTooltipController.status`.
3553

3654

3755
## 0.13.1
3856

3957
* Fix `FSelectTile` mixing in `FItemMixin` instead of `FTileMixin`.
40-
* Fix `SelectMenuTile` mixing in `FItemMixin` instead of `FTileMixin`.
58+
* Fix `FSelectMenuTile` mixing in `FItemMixin` instead of `FTileMixin`.
4159

4260

4361
## 0.13.0

forui/bin/commands/style/style.dart

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

forui/example/lib/sandbox.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ class Sandbox extends StatefulWidget {
88
State<Sandbox> createState() => _SandboxState();
99
}
1010

11+
enum Notification { all, direct, nothing, limitedTime, timeSensitive, selectedApps }
12+
1113
class _SandboxState extends State<Sandbox> with SingleTickerProviderStateMixin {
1214
late final FPopoverController controller;
1315

forui/lib/src/widgets/breadcrumb.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ class _CollapsedCrumb extends StatefulWidget implements FBreadcrumbItem {
246246
final ValueChanged<bool>? onFocusChange;
247247
final ValueChanged<bool>? onHoverChange;
248248
final ValueChanged<Set<WidgetState>>? onStateChange;
249-
final TraversalEdgeBehavior traversalEdgeBehavior;
249+
final TraversalEdgeBehavior? traversalEdgeBehavior;
250250
final String? semanticsLabel;
251251

252252
const _CollapsedCrumb({
@@ -270,7 +270,7 @@ class _CollapsedCrumb extends StatefulWidget implements FBreadcrumbItem {
270270
this.onFocusChange,
271271
this.onHoverChange,
272272
this.onStateChange,
273-
this.traversalEdgeBehavior = TraversalEdgeBehavior.closedLoop,
273+
this.traversalEdgeBehavior,
274274
super.key,
275275
}) : itemMenu = menu,
276276
tileMenu = null;

forui/lib/src/widgets/popover.dart

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -197,9 +197,10 @@ class FPopover extends StatefulWidget {
197197
/// Controls the transfer of focus beyond the first and the last items in a popover. Defaults to
198198
/// [TraversalEdgeBehavior.closedLoop].
199199
///
200-
/// Changing this field value has no immediate effect on the UI.
200+
/// ## Contract
201+
/// Throws [AssertionError] if both [focusNode] and [traversalEdgeBehavior] are not null.
201202
/// {@endtemplate}
202-
final TraversalEdgeBehavior traversalEdgeBehavior;
203+
final TraversalEdgeBehavior? traversalEdgeBehavior;
203204

204205
/// {@template forui.widgets.FPopover.barrierSemanticsLabel}
205206
/// The popover's barrier label used by accessibility frameworks.
@@ -257,7 +258,7 @@ class FPopover extends StatefulWidget {
257258
this.autofocus,
258259
this.focusNode,
259260
this.onFocusChange,
260-
this.traversalEdgeBehavior = TraversalEdgeBehavior.closedLoop,
261+
this.traversalEdgeBehavior,
261262
this.barrierSemanticsLabel,
262263
this.barrierSemanticsDismissible = true,
263264
this.semanticsLabel,
@@ -271,6 +272,10 @@ class FPopover extends StatefulWidget {
271272
groupId == null || hideOnTapOutside == FHidePopoverRegion.excludeTarget,
272273
'groupId can only be used with FHidePopoverRegion.excludeTarget',
273274
),
275+
assert(
276+
focusNode == null || traversalEdgeBehavior == null,
277+
'focusNode and traversalEdgeBehavior cannot both be set.',
278+
),
274279
assert(builder != _builder || child != null, 'Either builder or child must be provided.'),
275280
popoverAnchor = popoverAnchor ?? defaultPlatform.popover,
276281
childAnchor = childAnchor ?? defaultPlatform.child;
@@ -314,6 +319,15 @@ class FPopover extends StatefulWidget {
314319
class _State extends State<FPopover> with SingleTickerProviderStateMixin {
315320
late Object? _groupId = widget.groupId ?? UniqueKey();
316321
late FPopoverController _controller = widget.controller ?? FPopoverController(vsync: this);
322+
FocusScopeNode? _focusNode;
323+
324+
@override
325+
void initState() {
326+
super.initState();
327+
_focusNode =
328+
widget.focusNode ??
329+
FocusScopeNode(traversalEdgeBehavior: widget.traversalEdgeBehavior ?? TraversalEdgeBehavior.closedLoop);
330+
}
317331

318332
@override
319333
void didUpdateWidget(covariant FPopover old) {
@@ -322,6 +336,16 @@ class _State extends State<FPopover> with SingleTickerProviderStateMixin {
322336
_groupId = widget.groupId ?? UniqueKey();
323337
}
324338

339+
if (widget.focusNode != old.focusNode || widget.traversalEdgeBehavior != old.traversalEdgeBehavior) {
340+
if (old.focusNode == null) {
341+
_focusNode?.dispose();
342+
}
343+
344+
_focusNode =
345+
widget.focusNode ??
346+
FocusScopeNode(traversalEdgeBehavior: widget.traversalEdgeBehavior ?? TraversalEdgeBehavior.closedLoop);
347+
}
348+
325349
if (widget.controller != old.controller) {
326350
if (old.controller == null) {
327351
_controller.dispose();
@@ -330,6 +354,18 @@ class _State extends State<FPopover> with SingleTickerProviderStateMixin {
330354
}
331355
}
332356

357+
@override
358+
void dispose() {
359+
if (widget.focusNode == null) {
360+
_focusNode?.dispose();
361+
}
362+
363+
if (widget.controller == null) {
364+
_controller.dispose();
365+
}
366+
super.dispose();
367+
}
368+
333369
@override
334370
Widget build(BuildContext context) {
335371
final style = widget.style?.call(context.theme.popoverStyle) ?? context.theme.popoverStyle;
@@ -374,7 +410,7 @@ class _State extends State<FPopover> with SingleTickerProviderStateMixin {
374410
container: true,
375411
child: FocusScope(
376412
autofocus: widget.autofocus ?? (style.barrierFilter != null),
377-
node: widget.focusNode,
413+
node: _focusNode,
378414
onFocusChange: widget.onFocusChange,
379415
child: TapRegion(
380416
groupId: _groupId,
@@ -423,14 +459,6 @@ class _State extends State<FPopover> with SingleTickerProviderStateMixin {
423459
_controller.hide();
424460
}
425461
}
426-
427-
@override
428-
void dispose() {
429-
if (widget.controller == null) {
430-
_controller.dispose();
431-
}
432-
super.dispose();
433-
}
434462
}
435463

436464
/// A [FPopover]'s style.

forui/lib/src/widgets/popover_menu.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class FPopoverMenu extends StatelessWidget {
103103
final ValueChanged<bool>? onFocusChange;
104104

105105
/// {@macro forui.widgets.FPopover.traversalEdgeBehavior}
106-
final TraversalEdgeBehavior traversalEdgeBehavior;
106+
final TraversalEdgeBehavior? traversalEdgeBehavior;
107107

108108
/// {@macro forui.widgets.FPopover.barrierSemanticsLabel}
109109
final String? barrierSemanticsLabel;
@@ -160,7 +160,7 @@ class FPopoverMenu extends StatelessWidget {
160160
this.autofocus,
161161
this.focusNode,
162162
this.onFocusChange,
163-
this.traversalEdgeBehavior = TraversalEdgeBehavior.closedLoop,
163+
this.traversalEdgeBehavior,
164164
List<FItemGroupMixin> Function(BuildContext, FPopoverController, List<FItemGroupMixin>?) menuBuilder =
165165
_defaultItemBuilder,
166166
List<FItemGroupMixin>? menu,
@@ -216,7 +216,7 @@ class FPopoverMenu extends StatelessWidget {
216216
this.autofocus,
217217
this.focusNode,
218218
this.onFocusChange,
219-
this.traversalEdgeBehavior = TraversalEdgeBehavior.closedLoop,
219+
this.traversalEdgeBehavior,
220220
List<FTileGroupMixin> Function(BuildContext, FPopoverController, List<FTileGroupMixin>?) menuBuilder =
221221
_defaultTileBuilder,
222222
List<FTileGroupMixin>? menu,

forui/lib/src/widgets/select/content/content.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class _ContentState<T> extends State<Content<T>> {
152152
padding: EdgeInsets.zero,
153153
physics: widget.physics,
154154
child: Column(
155+
mainAxisSize: MainAxisSize.min,
155156
children: [
156157
if (widget.children.firstOrNull case final first?)
157158
SelectContentData<T>(

0 commit comments

Comments
 (0)