From 2af92546cffe4f14d1a01a7cd6534901fedf0e23 Mon Sep 17 00:00:00 2001 From: Matthias Ngeo <matthiasngeo@gmail.com> Date: Mon, 19 Aug 2024 17:40:39 +0800 Subject: [PATCH] Refactor bottom navigation bar --- docs/pages/docs/bottom-navigation-bar.mdx | 4 +- forui/CHANGELOG.md | 8 +++- forui/example/lib/main.dart | 2 +- .../bottom_navigation_bar.dart | 41 ++++++++++------ .../bottom_navigation_bar_item.dart | 48 +++++++++++-------- .../lib/widgets/bottom_navigation_bar.dart | 2 +- samples/lib/widgets/scaffold.dart | 2 +- 7 files changed, 65 insertions(+), 42 deletions(-) diff --git a/docs/pages/docs/bottom-navigation-bar.mdx b/docs/pages/docs/bottom-navigation-bar.mdx index fd55ade8e..04f9b9756 100644 --- a/docs/pages/docs/bottom-navigation-bar.mdx +++ b/docs/pages/docs/bottom-navigation-bar.mdx @@ -32,7 +32,7 @@ It is used to navigate between a small number of views, typically between three Widget build(BuildContext context) => FBottomNavigationBar( index: index, onChange: (index) => setState(() => this.index = index), - items: [ + children: [ FBottomNavigationBarItem( icon: FAssets.icons.home, label: 'Home', @@ -72,7 +72,7 @@ It is used to navigate between a small number of views, typically between three FBottomNavigationBar( index: 0, onChange: (index) => {}, - items: [ + children: [ FBottomNavigationBarItem( icon: FAssets.icons.home, label: 'Home', diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index aa7d8a359..da3ff2aa6 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -4,6 +4,8 @@ * Add `FButton.icon(...)`. +* Add `FBottomNavigationBar.of(...)` + * Add `FFormFieldStyle`. * Add `FResizable.semanticFormatterCallback`. @@ -16,6 +18,10 @@ * **Breaking:** Change `FAlertIconStyle.height` to `FAlertIconStyle.size`. +* **Breaking:** Rename `FBottomNavigationBar.items` to `FBottomNavigationBar.children`. + +* **Breaking:** Remove `FBottomNavigationBar.raw(...)` - use the default constructor instead. + * Change `FResizable` to resize by `FResizable.resizePercentage` when using a keyboard. * **Breaking:** Change `FResiableDividerStyle.thickness` to `FResizableDividerStyle.width`. @@ -28,7 +34,7 @@ * **Breaking:** Change how `FTextFieldStyle` stores various state-dependent styles. -* **Breaking:** Remove `FTextField.error`. +* **Breaking:** Remove `FTextField.error` - use `FTextField.forceErrorText` instead. ### Fixes diff --git a/forui/example/lib/main.dart b/forui/example/lib/main.dart index 4f98061d2..eb7037ad7 100644 --- a/forui/example/lib/main.dart +++ b/forui/example/lib/main.dart @@ -44,7 +44,7 @@ class _ApplicationState extends State<Application> { footer: FBottomNavigationBar( index: index, onChange: (index) => setState(() => this.index = index), - items: [ + children: [ FBottomNavigationBarItem( icon: FAssets.icons.home, label: 'Home', diff --git a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart index 8b6861484..b26550b75 100644 --- a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart +++ b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar.dart @@ -1,7 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:forui/forui.dart'; @@ -15,16 +14,18 @@ import 'package:forui/src/foundation/tappable.dart'; /// See: /// * https://forui.dev/docs/bottom-navigation-bar for working examples. /// * [FBottomNavigationBarStyle] for customizing a bottom navigation bar's appearance. +/// * [FBottomNavigationBarItem] for the items in a bottom navigation bar. class FBottomNavigationBar extends StatelessWidget { /// Returns the [FBottomNavigationBarItemStyle] and currently selected index of the [FBottomNavigationBar] in the /// given [context]. /// /// ## Contract /// Throws [AssertionError] if there is no ancestor [FBottomNavigationBar] in the given [context]. - static ({FBottomNavigationBarItemStyle itemStyle, bool current}) of(BuildContext context) { - final _InheritedData? result = context.dependOnInheritedWidgetOfExactType<_InheritedData>(); + @useResult + static FBottomNavigationBarData of(BuildContext context) { + final result = context.dependOnInheritedWidgetOfExactType<FBottomNavigationBarData>(); assert(result != null, 'No _InheritedData found in context'); - return (itemStyle: result!.itemStyle, current: result.current); + return result!; } /// The style. @@ -34,19 +35,24 @@ class FBottomNavigationBar extends StatelessWidget { final ValueChanged<int>? onChange; /// The index. + /// + /// ## Contract + /// Throws [AssertionError] if [index] is not null and is negative. final int? index; /// The children. final List<Widget> children; /// Creates a [FBottomNavigationBar] with [FBottomNavigationBarItem]s. + /// + /// See [FBottomNavigationBarItem] for the items in a bottom navigation bar. const FBottomNavigationBar({ required this.children, this.style, this.onChange, this.index, super.key, - }); + }) : assert(index == null || 0 <= index, 'index must be null or non-negative.'); @override Widget build(BuildContext context) { @@ -69,9 +75,9 @@ class FBottomNavigationBar extends StatelessWidget { onPress: () { onChange?.call(i); }, - child: _InheritedData( + child: FBottomNavigationBarData( itemStyle: style.itemStyle, - current: index == i, + selected: index == i, child: child, ), ), @@ -93,25 +99,30 @@ class FBottomNavigationBar extends StatelessWidget { } } -class _InheritedData extends InheritedWidget { +/// AFBottomNavigationBar]'s data. +class FBottomNavigationBarData extends InheritedWidget { + /// The item's style. final FBottomNavigationBarItemStyle itemStyle; - final bool current; + /// Whether the item is currently selected. + final bool selected; - const _InheritedData({ + /// Creates a [FBottomNavigationBarData]. + const FBottomNavigationBarData({ required this.itemStyle, - required this.current, + required this.selected, required super.child, + super.key, }); @override - bool updateShouldNotify(_InheritedData old) => old.itemStyle != itemStyle || old.current != current; + bool updateShouldNotify(FBottomNavigationBarData old) => old.itemStyle != itemStyle || old.selected != selected; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties ..add(DiagnosticsProperty('style', itemStyle)) - ..add(FlagProperty('current', value: current, ifTrue: 'current')); + ..add(FlagProperty('selected', value: selected, ifTrue: 'selected')); } } @@ -150,12 +161,12 @@ class FBottomNavigationBarStyle with Diagnosticable { FBottomNavigationBarStyle copyWith({ BoxDecoration? decoration, EdgeInsets? padding, - FBottomNavigationBarItemStyle? item, + FBottomNavigationBarItemStyle? itemStyle, }) => FBottomNavigationBarStyle( decoration: decoration ?? this.decoration, padding: padding ?? this.padding, - itemStyle: item ?? this.itemStyle, + itemStyle: itemStyle ?? this.itemStyle, ); @override diff --git a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart index 809c1d30e..44f7de777 100644 --- a/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart +++ b/forui/lib/src/widgets/bottom_navigation_bar/bottom_navigation_bar_item.dart @@ -24,30 +24,36 @@ class FBottomNavigationBarItem extends StatelessWidget { @override Widget build(BuildContext context) { - final (:itemStyle, :current) = FBottomNavigationBar.of(context); + final FBottomNavigationBarData(:itemStyle, :selected) = FBottomNavigationBar.of(context); final style = this.style ?? itemStyle; - return MouseRegion( - cursor: SystemMouseCursors.click, - child: Padding( - padding: style.padding, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - icon( - height: style.iconSize, - colorFilter: ColorFilter.mode( - current ? style.activeIconColor : style.inactiveIconColor, - BlendMode.srcIn, + return Semantics( + button: true, + selected: selected, + label: label, + excludeSemantics: true, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Padding( + padding: style.padding, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + icon( + height: style.iconSize, + colorFilter: ColorFilter.mode( + selected ? style.activeIconColor : style.inactiveIconColor, + BlendMode.srcIn, + ), ), - ), - const SizedBox(height: 2), - Text( - label, - overflow: TextOverflow.ellipsis, - style: current ? style.activeTextStyle : style.inactiveTextStyle, - ), - ], + const SizedBox(height: 2), + Text( + label, + overflow: TextOverflow.ellipsis, + style: selected ? style.activeTextStyle : style.inactiveTextStyle, + ), + ], + ), ), ), ); diff --git a/samples/lib/widgets/bottom_navigation_bar.dart b/samples/lib/widgets/bottom_navigation_bar.dart index 6ff466622..927f0f0ef 100644 --- a/samples/lib/widgets/bottom_navigation_bar.dart +++ b/samples/lib/widgets/bottom_navigation_bar.dart @@ -32,7 +32,7 @@ class _DemoState extends State<_Demo> { Widget build(BuildContext context) => FBottomNavigationBar( index: index, onChange: (index) => setState(() => this.index = index), - items: [ + children: [ FBottomNavigationBarItem( icon: FAssets.icons.home, label: 'Home', diff --git a/samples/lib/widgets/scaffold.dart b/samples/lib/widgets/scaffold.dart index 2ee384203..20455a5a5 100644 --- a/samples/lib/widgets/scaffold.dart +++ b/samples/lib/widgets/scaffold.dart @@ -97,7 +97,7 @@ class _DemoState extends State<_Demo> { footer: FBottomNavigationBar( index: index, onChange: (index) => setState(() => this.index = index), - items: [ + children: [ FBottomNavigationBarItem( icon: FAssets.icons.home, label: 'Home',