Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Fixed `TreeView` selection state behavior for items that are not expanded ([#578](https://github.com/bdlukaa/fluent_ui/issues/578))
- Added support for Romanian language
- Ensure the body state in `NavigationView` is properly preserved ([#607](https://github.com/bdlukaa/fluent_ui/pull/607))

## 4.0.3+1

Expand Down
70 changes: 50 additions & 20 deletions lib/src/controls/navigation/navigation_view/body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class _NavigationBody extends StatefulWidget {
// ignore: unused_element
super.key,
required this.itemKey,
required this.child,
this.paneBodyBuilder,
this.transitionBuilder,
// ignore: unused_element
this.animationCurve,
Expand All @@ -24,7 +24,7 @@ class _NavigationBody extends StatefulWidget {

final ValueKey<int>? itemKey;

final Widget child;
final NavigationContentBuilder? paneBodyBuilder;

/// The transition builder.
///
Expand Down Expand Up @@ -77,11 +77,25 @@ class _NavigationBody extends StatefulWidget {
}

class _NavigationBodyState extends State<_NavigationBody> {
final _pageKey = GlobalKey();
final _pageController = PageController();

@override
void didChangeDependencies() {
super.didChangeDependencies();
final view = InheritedNavigationView.of(context);

if (_pageController.hasClients && view.oldIndex != view.pane?.selected) {
_pageController.jumpToPage(view.pane?.selected ?? 0);
}
}

@override
Widget build(BuildContext context) {
assert(debugCheckHasFluentTheme(context));
final view = InheritedNavigationView.maybeOf(context);
final view = InheritedNavigationView.of(context);
final theme = FluentTheme.of(context);

return Container(
color: theme.scaffoldBackgroundColor,
child: AnimatedSwitcher(
Expand All @@ -98,21 +112,19 @@ class _NavigationBodyState extends State<_NavigationBody> {
return widget.transitionBuilder!(child, animation);
}

if (view != null) {
bool isTop = view.displayMode == PaneDisplayMode.top;

if (isTop) {
// Other transtitions other than default is only applied to top nav
// when clicking overflow on topnav, transition is from bottom
// otherwise if prevItem is on left side of nextActualItem, transition is from left
// if prevItem is on right side of nextActualItem, transition is from right
// click on Settings item is considered Default
return HorizontalSlidePageTransition(
animation: animation,
fromLeft: view.oldIndex < (view.pane?.selected ?? 0),
child: child,
);
}
bool isTop = view.displayMode == PaneDisplayMode.top;

if (isTop) {
// Other transtitions other than default is only applied to top nav
// when clicking overflow on topnav, transition is from bottom
// otherwise if prevItem is on left side of nextActualItem, transition is from left
// if prevItem is on right side of nextActualItem, transition is from right
// click on Settings item is considered Default
return HorizontalSlidePageTransition(
animation: animation,
fromLeft: view.oldIndex < (view.pane?.selected ?? 0),
child: child,
);
}

return EntrancePageTransition(
Expand All @@ -121,9 +133,27 @@ class _NavigationBodyState extends State<_NavigationBody> {
child: child,
);
},
child: FocusTraversalGroup(
child: KeyedSubtree(
key: widget.itemKey,
child: widget.child,
child: PageView.builder(
key: _pageKey,
physics: const NeverScrollableScrollPhysics(),
allowImplicitScrolling: false,
controller: _pageController,
itemCount: view.pane!.effectiveItems.length,
itemBuilder: (context, index) {
final bool isSelected = view.pane!.selected == index;
return view.pane!.effectiveItems.map((item) {
return ExcludeFocus(
key: item.bodyKey,
excluding: !isSelected,
child: FocusTraversalGroup(
child: widget.paneBodyBuilder?.call(item.body) ?? item.body,
),
);
}).elementAt(index);
},
),
),
),
);
Expand Down
14 changes: 14 additions & 0 deletions lib/src/controls/navigation/navigation_view/pane_items.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
part of 'view.dart';

class NavigationPaneItem with Diagnosticable {
/// The key used for the item itself. Useful to find the position and size of
/// the pane item within the screen
///
/// See also:
///
/// * [PaneItem.build], which assigns
GlobalKey itemKey = GlobalKey();

NavigationPaneItem();
Expand All @@ -21,6 +27,14 @@ class NavigationPaneItem with Diagnosticable {
/// * [PaneItemAction], the item used for execute an action on click
/// * [PaneItemExpander], which creates hierhical navigation
class PaneItem extends NavigationPaneItem {
/// The key used for the body content
///
/// See also:
///
/// * [body], which this is assigned to
/// * [_NavigationBody], which assigns this to every pane body
GlobalKey bodyKey = GlobalKey();

/// Creates a pane item.
PaneItem({
required this.icon,
Expand Down
16 changes: 6 additions & 10 deletions lib/src/controls/navigation/navigation_view/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ part 'style.dart';
/// Value eyeballed from Windows 10 v10.0.19041.928
const double _kDefaultAppBarHeight = 50.0;

typedef NavigationContentBuilder = Widget Function(Widget? body);

/// The NavigationView control provides top-level navigation for your app. It
/// adapts to a variety of screen sizes and supports both top and left
/// navigation styles.
Expand Down Expand Up @@ -56,14 +58,13 @@ class NavigationView extends StatefulWidget {

/// Can be used to override the widget that is built from
/// the [PaneItem.body]. Only used if [pane] is provided.
/// If nothing is selected, `selectedPaneItemBody` will be
/// null.
/// If nothing is selected, `body` will be null.
///
/// This can be useful if you are using router-based navigation,
/// and the body of the navigation pane is dynamically determined or
/// affected by the current route rather than just by the currently
/// selected pane.
final Widget Function(Widget? selectedPaneItemBody)? paneBodyBuilder;
final NavigationContentBuilder? paneBodyBuilder;

/// The navigation pane, that can be displayed either on the
/// left, on the top, or above the body.
Expand Down Expand Up @@ -361,17 +362,12 @@ class NavigationViewState extends State<NavigationView> {
late Widget paneResult;
if (widget.pane != null) {
final pane = widget.pane!;
final paneBodyBuilder = widget.paneBodyBuilder;
final body = _NavigationBody(
itemKey: ValueKey(pane.selected ?? -1),
transitionBuilder: widget.transitionBuilder,
child: paneBodyBuilder != null
? paneBodyBuilder(
pane.selected != null ? pane.selectedItem.body : null)
: (pane.selected != null
? pane.selectedItem.body
: const SizedBox.shrink()),
paneBodyBuilder: widget.paneBodyBuilder,
);

if (pane.customPane != null) {
paneResult = Builder(builder: (context) {
return PaneScrollConfiguration(
Expand Down