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,