From 7d677e23cb5a35b467e9a8a893b3a6aab81b3226 Mon Sep 17 00:00:00 2001 From: Amoghhosamane Date: Fri, 20 Feb 2026 00:08:02 +0530 Subject: [PATCH] feat: add customized settings widget on AppBar #27 This change adds a settingsWidget parameter to SolidScaffold, allowing developers to define their own settings widget (e.g., an IconButton) which is then displayed in the AppBar next to the About button. It also integrates this widget into the Navigation Rail (trailing) and the Navigation Drawer. Closes #27 --- example/lib/app_scaffold.dart | 22 +++++++++++++ lib/solidui.dart | 1 + lib/src/widgets/solid_nav_bar.dart | 11 +++++++ lib/src/widgets/solid_nav_drawer.dart | 10 ++++++ lib/src/widgets/solid_scaffold.dart | 5 +++ .../solid_scaffold_appbar_actions.dart | 32 ++++++++++++++++++- .../solid_scaffold_appbar_builder.dart | 3 ++ ...solid_scaffold_appbar_ordered_actions.dart | 23 +++++++++++++ lib/src/widgets/solid_scaffold_helpers.dart | 2 ++ .../solid_scaffold_layout_builder.dart | 6 ++-- lib/src/widgets/solid_scaffold_state.dart | 1 + .../solid_scaffold_widget_builder.dart | 2 ++ 12 files changed, 115 insertions(+), 3 deletions(-) diff --git a/example/lib/app_scaffold.dart b/example/lib/app_scaffold.dart index bb5aa4e..f61c341 100644 --- a/example/lib/app_scaffold.dart +++ b/example/lib/app_scaffold.dart @@ -181,7 +181,29 @@ class AppScaffold extends StatelessWidget { onLogout: (context) => SolidAuthHandler.instance.handleLogout(context), + // SETTINGS. + + settingsWidget: IconButton( + icon: const Icon(Icons.settings), + onPressed: () => _scaffoldController.navigateToSubpage( + const _MySettings(), + ), + tooltip: 'Settings', + ), + child: const Home(title: appTitle), ); } } + +class _MySettings extends StatelessWidget { + const _MySettings(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Custom Settings')), + body: const Center(child: Text('This is a custom settings page.')), + ); + } +} diff --git a/lib/solidui.dart b/lib/solidui.dart index 50e80af..cd19c2a 100644 --- a/lib/solidui.dart +++ b/lib/solidui.dart @@ -102,6 +102,7 @@ export 'src/utils/solid_file_operations.dart'; export 'src/utils/is_phone.dart'; export 'src/utils/solid_alert.dart'; export 'src/utils/solid_notifications.dart'; +export 'src/utils/path_utils.dart'; export 'src/utils/solid_pod_helpers.dart' show loginIfRequired, getKeyFromUserIfRequired; diff --git a/lib/src/widgets/solid_nav_bar.dart b/lib/src/widgets/solid_nav_bar.dart index 9d42f20..af95bbb 100644 --- a/lib/src/widgets/solid_nav_bar.dart +++ b/lib/src/widgets/solid_nav_bar.dart @@ -75,6 +75,10 @@ class SolidNavBar extends StatelessWidget { final double? labelFontSize; + /// Optional custom Settings widget. + + final Widget? settingsWidget; + /// Creates a [SolidNavBar] with the specified configuration. const SolidNavBar({ @@ -87,6 +91,7 @@ class SolidNavBar extends StatelessWidget { this.groupAlignment, this.iconSize, this.labelFontSize, + this.settingsWidget, }); @override @@ -165,6 +170,12 @@ class SolidNavBar extends StatelessWidget { letterSpacing: NavigationConstants.navLabelLetterSpacing, color: theme.colorScheme.onSurface.withValues(alpha: 0.6), ), + trailing: settingsWidget != null + ? Padding( + padding: const EdgeInsets.only(bottom: 20), + child: settingsWidget, + ) + : null, ), ), ), diff --git a/lib/src/widgets/solid_nav_drawer.dart b/lib/src/widgets/solid_nav_drawer.dart index 53ea331..0631da8 100644 --- a/lib/src/widgets/solid_nav_drawer.dart +++ b/lib/src/widgets/solid_nav_drawer.dart @@ -81,6 +81,10 @@ class SolidNavDrawer extends StatefulWidget { final ShapeBorder? drawerShape; + /// Optional custom Settings widget. + + final Widget? settingsWidget; + const SolidNavDrawer({ super.key, this.userInfo, @@ -93,6 +97,7 @@ class SolidNavDrawer extends StatefulWidget { this.showLogout = true, this.additionalMenuItems, this.drawerShape, + this.settingsWidget, }); @override @@ -175,6 +180,11 @@ class _SolidNavDrawerState extends State { }), if (widget.additionalMenuItems != null) ...widget.additionalMenuItems!, + if (widget.settingsWidget != null) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: widget.settingsWidget, + ), if (widget.showLogout && widget.onLogout != null) ..._buildLogoutSection(context, theme), ], diff --git a/lib/src/widgets/solid_scaffold.dart b/lib/src/widgets/solid_scaffold.dart index 1fa0c65..1ce67d6 100644 --- a/lib/src/widgets/solid_scaffold.dart +++ b/lib/src/widgets/solid_scaffold.dart @@ -231,6 +231,10 @@ class SolidScaffold extends StatefulWidget { final SolidAboutConfig? aboutConfig; + /// Optional custom Settings widget. + + final Widget? settingsWidget; + /// Option to force the navigation rail to be hidden. final bool hideNavRail; @@ -278,6 +282,7 @@ class SolidScaffold extends StatefulWidget { this.selectedIndex, this.themeToggle, this.aboutConfig, + this.settingsWidget, this.hideNavRail = false, }); diff --git a/lib/src/widgets/solid_scaffold_appbar_actions.dart b/lib/src/widgets/solid_scaffold_appbar_actions.dart index 604c0a0..118d8de 100644 --- a/lib/src/widgets/solid_scaffold_appbar_actions.dart +++ b/lib/src/widgets/solid_scaffold_appbar_actions.dart @@ -50,13 +50,20 @@ class SolidAppBarActionsManager { SolidAppBarConfig config, SolidThemeToggleConfig? themeToggle, { bool hasLogout = false, + bool hasSettings = false, }) { // Check if we need to add missing buttons (standard or custom). final existingActions = solidPreferencesNotifier.appBarActions; final needsInit = existingActions.isEmpty; final needsMerge = !needsInit && - _hasMissingButtons(existingActions, config, themeToggle, hasLogout); + _hasMissingButtons( + existingActions, + config, + themeToggle, + hasLogout, + hasSettings, + ); if (!needsInit && !needsMerge) return; @@ -141,6 +148,22 @@ class SolidAppBarActionsManager { ); } + // Add Settings button if provided. + + if (hasSettings) { + actionEntries.add( + _ActionEntry( + item: const SolidAppBarActionItem( + id: SolidAppBarActionIds.preferences, + label: 'Settings', + icon: Icons.settings, + showInOverflow: false, // Show in AppBar by default. + ), + initialIndex: 850, // Settings button just before About. + ), + ); + } + // Add About button. // Default: show in AppBar, rightmost position. @@ -182,6 +205,7 @@ class SolidAppBarActionsManager { SolidAppBarConfig config, SolidThemeToggleConfig? themeToggle, bool hasLogout, + bool hasSettings, ) { final existingIds = actions.map((a) => a.id).toSet(); @@ -214,6 +238,12 @@ class SolidAppBarActionsManager { expectedIds.add(SolidAppBarActionIds.logout); } + // Settings button (if provided). + + if (hasSettings) { + expectedIds.add(SolidAppBarActionIds.preferences); + } + // About button should always exist. expectedIds.add(SolidAppBarActionIds.about); diff --git a/lib/src/widgets/solid_scaffold_appbar_builder.dart b/lib/src/widgets/solid_scaffold_appbar_builder.dart index 44569e2..2bc002c 100644 --- a/lib/src/widgets/solid_scaffold_appbar_builder.dart +++ b/lib/src/widgets/solid_scaffold_appbar_builder.dart @@ -55,6 +55,7 @@ class SolidScaffoldAppBarBuilder { VoidCallback? themeToggleCallback, SolidAboutConfig aboutConfig, double narrowScreenThreshold, { + Widget? settingsWidget, bool hideNavRail = false, void Function(BuildContext)? onLogout, void Function(BuildContext)? onLogin, @@ -65,6 +66,7 @@ class SolidScaffoldAppBarBuilder { config, themeToggle, hasLogout: true, + hasSettings: settingsWidget != null, ); final isWideScreen = !hideNavRail && @@ -100,6 +102,7 @@ class SolidScaffoldAppBarBuilder { currentThemeMode: currentThemeMode, themeToggleCallback: themeToggleCallback, aboutConfig: aboutConfig, + settingsWidget: settingsWidget, context: context, onLogout: onLogout, onLogin: onLogin, diff --git a/lib/src/widgets/solid_scaffold_appbar_ordered_actions.dart b/lib/src/widgets/solid_scaffold_appbar_ordered_actions.dart index e4b2aff..7ebf2c4 100644 --- a/lib/src/widgets/solid_scaffold_appbar_ordered_actions.dart +++ b/lib/src/widgets/solid_scaffold_appbar_ordered_actions.dart @@ -58,6 +58,7 @@ class SolidAppBarOrderedActionsBuilder { required VoidCallback? themeToggleCallback, required SolidAboutConfig aboutConfig, required BuildContext context, + Widget? settingsWidget, void Function(BuildContext)? onLogout, void Function(BuildContext)? onLogin, }) { @@ -76,6 +77,7 @@ class SolidAppBarOrderedActionsBuilder { _addCustomActions(orderedActions, config, screenWidth, isNarrowScreen); _addOverflowItems(orderedActions, config, isNarrowScreen); _addAuthButton(orderedActions, onLogout, onLogin, isNarrowScreen, context); + _addSettingsButton(orderedActions, settingsWidget, isNarrowScreen); _addAboutButton( orderedActions, aboutConfig, @@ -220,6 +222,27 @@ class SolidAppBarOrderedActionsBuilder { } } + static void _addSettingsButton( + List<_OrderedAction> orderedActions, + Widget? settingsWidget, + bool isNarrowScreen, + ) { + if (settingsWidget == null) return; + + final actionConfig = SolidAppBarActionsManager.getActionConfig( + SolidAppBarActionIds.preferences, + ); + final isVisible = actionConfig?.isVisible ?? true; + final isInOverflow = actionConfig?.showInOverflow ?? false; + final order = actionConfig?.order ?? 850; + + if (isVisible && (!isNarrowScreen || !isInOverflow)) { + orderedActions.add( + _OrderedAction(order: order, widget: settingsWidget), + ); + } + } + static void _addAboutButton( List<_OrderedAction> orderedActions, SolidAboutConfig aboutConfig, diff --git a/lib/src/widgets/solid_scaffold_helpers.dart b/lib/src/widgets/solid_scaffold_helpers.dart index d044786..7f6763b 100644 --- a/lib/src/widgets/solid_scaffold_helpers.dart +++ b/lib/src/widgets/solid_scaffold_helpers.dart @@ -253,6 +253,7 @@ class SolidScaffoldHelpers { double narrowScreenThreshold, bool Function() shouldShowVersion, String Function() getVersionToDisplay, { + Widget? settingsWidget, bool hideNavRail = false, void Function(BuildContext)? onLogout, void Function(BuildContext)? onLogin, @@ -269,6 +270,7 @@ class SolidScaffoldHelpers { themeToggleCallback, aboutConfig, narrowScreenThreshold, + settingsWidget: settingsWidget, hideNavRail: hideNavRail, onLogout: onLogout, onLogin: onLogin, diff --git a/lib/src/widgets/solid_scaffold_layout_builder.dart b/lib/src/widgets/solid_scaffold_layout_builder.dart index 8b356c1..d29a611 100644 --- a/lib/src/widgets/solid_scaffold_layout_builder.dart +++ b/lib/src/widgets/solid_scaffold_layout_builder.dart @@ -51,8 +51,9 @@ class SolidScaffoldLayoutBuilder { int? selectedIndex, Widget? effectiveChild, Function(int) onTabSelected, - Function(BuildContext, String, String?)? onShowAlert, - ) { + Function(BuildContext, String, String?)? onShowAlert, { + Widget? settingsWidget, + }) { final theme = Theme.of(context); if (effectiveChild == null) { @@ -73,6 +74,7 @@ class SolidScaffoldLayoutBuilder { selectedIndex: selectedIndex, onTabSelected: onTabSelected, onShowAlert: onShowAlert, + settingsWidget: settingsWidget, ), VerticalDivider(width: 1, color: theme.dividerColor), Expanded(child: effectiveChild), diff --git a/lib/src/widgets/solid_scaffold_state.dart b/lib/src/widgets/solid_scaffold_state.dart index aba9363..f1962d4 100644 --- a/lib/src/widgets/solid_scaffold_state.dart +++ b/lib/src/widgets/solid_scaffold_state.dart @@ -206,6 +206,7 @@ class SolidScaffoldState extends State { ), _onMenuSelected, widget.onShowAlert, + settingsWidget: widget.settingsWidget, ); return NotificationListener( diff --git a/lib/src/widgets/solid_scaffold_widget_builder.dart b/lib/src/widgets/solid_scaffold_widget_builder.dart index a73c345..2cfe853 100644 --- a/lib/src/widgets/solid_scaffold_widget_builder.dart +++ b/lib/src/widgets/solid_scaffold_widget_builder.dart @@ -140,6 +140,7 @@ class SolidScaffoldWidgetBuilder { widget.narrowScreenThreshold, shouldShowVersion, getVersionToDisplay, + settingsWidget: widget.settingsWidget, hideNavRail: widget.hideNavRail, onLogout: effectiveLogout, onLogin: effectiveLogin, @@ -158,6 +159,7 @@ class SolidScaffoldWidgetBuilder { onTabSelected: onMenuSelected, onLogout: effectiveLogout, showLogout: effectiveLogout != null, + settingsWidget: widget.settingsWidget, ); }, endDrawer: widget.endDrawer,