diff --git a/CHANGELOG.md b/CHANGELOG.md index c58f03cfe..5886d6d89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,73 @@ Date format: DD/MM/YYYY - Tests ([#142](https://github.com/bdlukaa/fluent_ui/pull/142)) - Added Material Theme to Fluent Theme Builder ([#133](https://github.com/bdlukaa/fluent_ui/issues/133)) - Add more customization options to PaneItem ([#111](https://github.com/bdlukaa/fluent_ui/issues/111), [#144](https://github.com/bdlukaa/fluent_ui/issues/144)) +- Properly add item key to `PaneItem` in top mode ([#143](https://github.com/bdlukaa/fluent_ui/issues/143)) +- Added the helper functions `NavigationIndicator.end` and `NavigationIndicator.sticky` +- **BREAKING** Removed `NavigationPane.defaultNavigationIndicator` +- **BREAKING** Replaced `offsets` and `sizes` with `pane` in `NavigationPane` + + Before: + ```dart + pane: NavigationPane( + indicatorBuilder: ({ + required BuildContext context, + /// The navigation pane corresponding to this indicator + required NavigationPane pane, + /// Corresponds to the current display mode. If top, Axis.vertical + /// is passed, otherwise Axis.vertical + Axis? axis, + /// Corresponds to the pane itself as a widget. The indicator is + /// rendered over the whole pane. + required Widget child, + }) { + if (pane.selected == null) return child; + assert(debugCheckHasFluentTheme(context)); + final theme = NavigationPaneThemeData.of(context); + + axis??= Axis.horizontal; + + return EndNavigationIndicator( + index: pane.selected, + offsets: () => pane.effectiveItems.getPaneItemsOffsets (pane.paneKey), + sizes: pane.effectiveItems.getPaneItemsSizes, + child: child, + color: theme.highlightColor, + curve: theme.animationCurve ?? Curves.linear, + axis: axis, + ); + }, + ), + ``` + + Now: + ```dart + pane: NavigationPane( + indicatorBuilder: ({ + required BuildContext context, + /// The navigation pane corresponding to this indicator + required NavigationPane pane, + /// Corresponds to the current display mode. If top, Axis.vertical + /// is passed, otherwise Axis.vertical + required Axis axis, + /// Corresponds to the pane itself as a widget. The indicator is + /// rendered over the whole pane. + required Widget child, + }) { + if (pane.selected == null) return child; + assert(debugCheckHasFluentTheme(context)); + final theme = NavigationPaneThemeData.of(context); + + return EndNavigationIndicator( + index: pane.selected, + pane: pane, + child: child, + color: theme.highlightColor, + curve: theme.animationCurve ?? Curves.linear, + axis: axis, + ); + }, + ), + ``` ## [3.7.0] - Breaking changes - [21/01/2022] diff --git a/example/lib/main.dart b/example/lib/main.dart index 4cb3d8306..4363fe1f1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -165,48 +165,15 @@ class _MyHomePageState extends State { ), ), displayMode: appTheme.displayMode, - indicatorBuilder: ({ - required BuildContext context, - required NavigationPane pane, - Axis? axis, - required Widget child, - }) { - if (pane.selected == null) return child; - axis ??= Axis.horizontal; - assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneTheme.of(context); + indicatorBuilder: () { switch (appTheme.indicator) { case NavigationIndicators.end: - return EndNavigationIndicator( - index: pane.selected!, - offsets: () => - pane.effectiveItems.getPaneItemsOffsets(pane.paneKey), - sizes: pane.effectiveItems.getPaneItemsSizes, - child: child, - color: theme.highlightColor, - curve: theme.animationCurve ?? Curves.linear, - axis: axis, - ); + return NavigationIndicator.end; case NavigationIndicators.sticky: - return NavigationPane.defaultNavigationIndicator( - context: context, - axis: axis, - pane: pane, - child: child, - ); default: - return NavigationIndicator( - index: pane.selected!, - offsets: () => - pane.effectiveItems.getPaneItemsOffsets(pane.paneKey), - sizes: pane.effectiveItems.getPaneItemsSizes, - child: child, - color: theme.highlightColor, - curve: theme.animationCurve ?? Curves.linear, - axis: axis, - ); + return NavigationIndicator.sticky; } - }, + }(), items: [ // It doesn't look good when resizing from compact to open // PaneItemHeader(header: Text('User Interaction')), @@ -221,9 +188,7 @@ class _MyHomePageState extends State { PaneItemSeparator(), PaneItem( icon: const Icon(FluentIcons.color), - title: const Text( - 'Colors', - ), + title: const Text('Colors'), ), PaneItem( icon: const Icon(FluentIcons.icon_sets_flag), diff --git a/lib/src/controls/navigation/navigation_view/indicators.dart b/lib/src/controls/navigation/navigation_view/indicators.dart index 82376aa49..8807f1abf 100644 --- a/lib/src/controls/navigation/navigation_view/indicators.dart +++ b/lib/src/controls/navigation/navigation_view/indicators.dart @@ -8,7 +8,7 @@ part of 'view.dart'; typedef NavigationIndicatorBuilder = Widget Function({ required BuildContext context, required NavigationPane pane, - Axis? axis, + required Axis axis, required Widget child, }); @@ -19,27 +19,68 @@ class NavigationIndicator extends StatefulWidget { /// to render the selected indicator. const NavigationIndicator({ Key? key, - required this.offsets, - required this.sizes, required this.index, required this.child, + required this.pane, required this.axis, this.curve = Curves.linear, this.color, }) : super(key: key); + /// Creates a [StickyNavigationIndicator] + static Widget sticky({ + required BuildContext context, + required NavigationPane pane, + required Axis axis, + required Widget child, + }) { + if (pane.selected == null) return child; + assert(debugCheckHasFluentTheme(context)); + final theme = NavigationPaneTheme.of(context); + + final left = theme.iconPadding?.left ?? theme.labelPadding?.left ?? 0; + final right = theme.labelPadding?.right ?? theme.iconPadding?.right ?? 0; + + return StickyNavigationIndicator( + index: pane.selected!, + pane: pane, + child: child, + color: theme.highlightColor, + curve: theme.animationCurve ?? Curves.linear, + axis: axis, + topPadding: EdgeInsets.only(left: left, right: right), + ); + } + + /// Creates an [EndNavigationIndicator] + static Widget end({ + required BuildContext context, + required NavigationPane pane, + required Axis axis, + required Widget child, + }) { + if (pane.selected == null) return child; + assert(debugCheckHasFluentTheme(context)); + final theme = NavigationPaneTheme.of(context); + + return EndNavigationIndicator( + index: pane.selected!, + pane: pane, + child: child, + color: theme.highlightColor, + curve: theme.animationCurve ?? Curves.linear, + axis: axis, + ); + } + /// The [NavigationPane]. It can be open, compact, closed or top. final Widget child; /// The current selected index; final int index; - /// A function that tells the indicator the item offsets. - final List Function() offsets; - - /// A function that tells the indicator the item sizes. The sizes - /// must not be [Size.infinite] - final List Function() sizes; + /// The navigation pane + final NavigationPane pane; /// The axis corresponding to the current navigation pane. If it's /// a top pane, [Axis.vertical] will be provided, otherwise @@ -81,9 +122,11 @@ class NavigationIndicatorState extends State { void fetch() { WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { - final _offsets = widget.offsets(); - final _sizes = widget.sizes(); - if (mounted && offsets != _offsets && _sizes != sizes) { + final _offsets = widget.pane.effectiveItems.getPaneItemsOffsets( + widget.pane.paneKey, + ); + final _sizes = widget.pane.effectiveItems.getPaneItemsSizes(); + if (mounted && (offsets != _offsets || _sizes != sizes)) { offsets = _offsets; sizes = _sizes; } @@ -100,8 +143,7 @@ class NavigationIndicatorState extends State { class EndNavigationIndicator extends NavigationIndicator { const EndNavigationIndicator({ Key? key, - required List Function() offsets, - required List Function() sizes, + required NavigationPane pane, required int index, required Widget child, required Axis axis, @@ -110,10 +152,9 @@ class EndNavigationIndicator extends NavigationIndicator { }) : super( key: key, axis: axis, + pane: pane, child: child, index: index, - offsets: offsets, - sizes: sizes, curve: curve, color: color, ); @@ -132,7 +173,7 @@ class _EndNavigationIndicatorState widget.child, ...List.generate(offsets!.length, (index) { if (widget.index != index) return const SizedBox.shrink(); - final isTop = widget.axis != Axis.horizontal; + final isTop = widget.axis == Axis.vertical; final offset = offsets![index]; final size = sizes![index]; @@ -144,12 +185,12 @@ class _EndNavigationIndicatorState horizontal: isTop ? 10.0 : 0.0, ), width: isTop ? size.width : 6.0, - height: isTop ? 4 : size.height, + height: isTop ? 6.0 : size.height, color: widget.color, ), ); - // debugPrint('at $offset with $size'); + debugPrint('at $offset with $size'); if (isTop) { return Positioned( @@ -185,8 +226,7 @@ class StickyNavigationIndicator extends NavigationIndicator { /// Creates a sticky navigation indicator. const StickyNavigationIndicator({ Key? key, - required List Function() offsets, - required List Function() sizes, + required NavigationPane pane, required int index, required Widget child, required Axis axis, @@ -196,10 +236,9 @@ class StickyNavigationIndicator extends NavigationIndicator { }) : super( key: key, axis: axis, + pane: pane, child: child, index: index, - offsets: offsets, - sizes: sizes, curve: curve, color: color, ); @@ -428,7 +467,11 @@ class _StickyPainter extends CustomPainter { bool shouldRebuildSemantics(_StickyPainter oldDelegate) => false; } -extension OffsetExtension on Offset { +extension _OffsetExtension on Offset { + /// Gets the value based on [axis] + /// + /// If [Axis.horizontal], [dy] is going to be returned. Otherwise, [dx] is + /// returned. double fromAxis(Axis axis) { if (axis == Axis.horizontal) { return dy; @@ -437,12 +480,3 @@ extension OffsetExtension on Offset { } } } - -// extension _size on Size { -// double fromAxis(Axis axis) { -// if (axis == Axis.horizontal) -// return height; -// else -// return width; -// } -// } diff --git a/lib/src/controls/navigation/navigation_view/pane.dart b/lib/src/controls/navigation/navigation_view/pane.dart index e8ed8474b..47449daf5 100644 --- a/lib/src/controls/navigation/navigation_view/pane.dart +++ b/lib/src/controls/navigation/navigation_view/pane.dart @@ -81,7 +81,7 @@ class NavigationPane with Diagnosticable { this.customPane, this.menuButton, this.scrollController, - this.indicatorBuilder = defaultNavigationIndicator, + this.indicatorBuilder = NavigationIndicator.sticky, }) : assert(selected == null || selected >= 0); final Key? key; @@ -167,32 +167,6 @@ class NavigationPane with Diagnosticable { /// A function called when building the navigation indicator final NavigationIndicatorBuilder indicatorBuilder; - static Widget defaultNavigationIndicator({ - required BuildContext context, - required NavigationPane pane, - Axis? axis, - required Widget child, - }) { - if (pane.selected == null) return child; - assert(debugCheckHasFluentTheme(context)); - final theme = NavigationPaneTheme.of(context); - axis ??= Axis.horizontal; - - final left = theme.iconPadding?.left ?? theme.labelPadding?.left ?? 0; - final right = theme.labelPadding?.right ?? theme.iconPadding?.right ?? 0; - - return StickyNavigationIndicator( - index: pane.selected!, - offsets: () => pane.effectiveItems.getPaneItemsOffsets(pane.paneKey), - sizes: pane.effectiveItems.getPaneItemsSizes, - child: child, - color: theme.highlightColor, - curve: theme.animationCurve ?? Curves.linear, - axis: axis, - topPadding: EdgeInsets.only(left: left, right: right), - ); - } - @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); @@ -402,7 +376,9 @@ class _TopNavigationPane extends StatelessWidget { if (pane.header != null) Padding( padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 6.0), + horizontal: 8.0, + vertical: 6.0, + ), child: pane.header!, ), // TODO: A listview shouldn't be used here. Instead, if there are @@ -459,7 +435,7 @@ class _CompactNavigationPane extends StatelessWidget { Widget _buildItem(BuildContext context, NavigationPaneItem item) { assert(debugCheckHasFluentTheme(context)); if (item is PaneItemHeader) { - /// Item Header is not visible on compact pane + // Item Header is not visible on compact pane return const SizedBox(); } else if (item is PaneItemSeparator) { return item.build(context, Axis.horizontal); @@ -494,6 +470,7 @@ class _CompactNavigationPane extends StatelessWidget { child: pane.indicatorBuilder( context: context, pane: pane, + axis: Axis.horizontal, child: Align( key: pane.paneKey, alignment: Alignment.topCenter, @@ -665,6 +642,7 @@ class _OpenNavigationPaneState extends State<_OpenNavigationPane> child: widget.pane.indicatorBuilder( context: context, pane: widget.pane, + axis: Axis.horizontal, child: Column(key: widget.pane.paneKey, children: [ Container( margin: widget.pane.autoSuggestBox != null diff --git a/lib/src/controls/navigation/navigation_view/pane_items.dart b/lib/src/controls/navigation/navigation_view/pane_items.dart index a1a8d13f2..c10bbcb6d 100644 --- a/lib/src/controls/navigation/navigation_view/pane_items.dart +++ b/lib/src/controls/navigation/navigation_view/pane_items.dart @@ -279,7 +279,7 @@ class PaneItem extends NavigationPaneItem { ), ]); } - return Center(child: result); + return Center(key: itemKey, child: result); default: throw '$mode is not a supported type'; }