Skip to content
Open
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
22 changes: 22 additions & 0 deletions example/lib/app_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.')),
);
}
}
1 change: 1 addition & 0 deletions lib/solidui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR’s description is focused on adding a customizable settingsWidget to SolidScaffold, but this change also exports a new public utility (path_utils.dart). If this export is intentional, it should be called out in the PR description/release notes; otherwise consider removing it from this PR to keep the feature-focused scope and avoid an untracked public API surface change.

Copilot uses AI. Check for mistakes.
export 'src/utils/solid_pod_helpers.dart'
show loginIfRequired, getKeyFromUserIfRequired;

Expand Down
11 changes: 11 additions & 0 deletions lib/src/widgets/solid_nav_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -87,6 +91,7 @@ class SolidNavBar extends StatelessWidget {
this.groupAlignment,
this.iconSize,
this.labelFontSize,
this.settingsWidget,
});

@override
Expand Down Expand Up @@ -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,
),
),
),
Expand Down
10 changes: 10 additions & 0 deletions lib/src/widgets/solid_nav_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class SolidNavDrawer extends StatefulWidget {

final ShapeBorder? drawerShape;

/// Optional custom Settings widget.

final Widget? settingsWidget;

const SolidNavDrawer({
super.key,
this.userInfo,
Expand All @@ -93,6 +97,7 @@ class SolidNavDrawer extends StatefulWidget {
this.showLogout = true,
this.additionalMenuItems,
this.drawerShape,
this.settingsWidget,
});

@override
Expand Down Expand Up @@ -175,6 +180,11 @@ class _SolidNavDrawerState extends State<SolidNavDrawer> {
}),
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),
],
Expand Down
5 changes: 5 additions & 0 deletions lib/src/widgets/solid_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -278,6 +282,7 @@ class SolidScaffold extends StatefulWidget {
this.selectedIndex,
this.themeToggle,
this.aboutConfig,
this.settingsWidget,
this.hideNavRail = false,
});

Expand Down
32 changes: 31 additions & 1 deletion lib/src/widgets/solid_scaffold_appbar_actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -182,6 +205,7 @@ class SolidAppBarActionsManager {
SolidAppBarConfig config,
SolidThemeToggleConfig? themeToggle,
bool hasLogout,
bool hasSettings,
) {
final existingIds = actions.map((a) => a.id).toSet();

Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions lib/src/widgets/solid_scaffold_appbar_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -65,6 +66,7 @@ class SolidScaffoldAppBarBuilder {
config,
themeToggle,
hasLogout: true,
hasSettings: settingsWidget != null,
);

final isWideScreen = !hideNavRail &&
Expand Down Expand Up @@ -100,6 +102,7 @@ class SolidScaffoldAppBarBuilder {
currentThemeMode: currentThemeMode,
themeToggleCallback: themeToggleCallback,
aboutConfig: aboutConfig,
settingsWidget: settingsWidget,
context: context,
onLogout: onLogout,
onLogin: onLogin,
Expand Down
23 changes: 23 additions & 0 deletions lib/src/widgets/solid_scaffold_appbar_ordered_actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}) {
Expand All @@ -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,
Expand Down Expand Up @@ -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)) {
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the Settings action is configured to show in overflow (showInOverflow = true) on narrow screens, this code hides the settingsWidget from the AppBar but there is no corresponding overflow-menu entry for it, so the Settings control effectively disappears. Consider either forcing Settings to always remain in the AppBar (ignore showInOverflow for this ID), or extend the overflow menu handler to render a Settings item (which may require changing the API from a raw Widget to something that can be represented in a PopupMenuItem).

Suggested change
if (isVisible && (!isNarrowScreen || !isInOverflow)) {
if (isVisible) {

Copilot uses AI. Check for mistakes.
orderedActions.add(
_OrderedAction(order: order, widget: settingsWidget),
);
}
}

static void _addAboutButton(
List<_OrderedAction> orderedActions,
SolidAboutConfig aboutConfig,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/widgets/solid_scaffold_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -269,6 +270,7 @@ class SolidScaffoldHelpers {
themeToggleCallback,
aboutConfig,
narrowScreenThreshold,
settingsWidget: settingsWidget,
hideNavRail: hideNavRail,
onLogout: onLogout,
onLogin: onLogin,
Expand Down
6 changes: 4 additions & 2 deletions lib/src/widgets/solid_scaffold_layout_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -73,6 +74,7 @@ class SolidScaffoldLayoutBuilder {
selectedIndex: selectedIndex,
onTabSelected: onTabSelected,
onShowAlert: onShowAlert,
settingsWidget: settingsWidget,
),
VerticalDivider(width: 1, color: theme.dividerColor),
Expanded(child: effectiveChild),
Expand Down
1 change: 1 addition & 0 deletions lib/src/widgets/solid_scaffold_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class SolidScaffoldState extends State<SolidScaffold> {
),
_onMenuSelected,
widget.onShowAlert,
settingsWidget: widget.settingsWidget,
);

return NotificationListener<SecurityKeyStatusChangedNotification>(
Expand Down
2 changes: 2 additions & 0 deletions lib/src/widgets/solid_scaffold_widget_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class SolidScaffoldWidgetBuilder {
widget.narrowScreenThreshold,
shouldShowVersion,
getVersionToDisplay,
settingsWidget: widget.settingsWidget,
hideNavRail: widget.hideNavRail,
onLogout: effectiveLogout,
onLogin: effectiveLogin,
Expand All @@ -158,6 +159,7 @@ class SolidScaffoldWidgetBuilder {
onTabSelected: onMenuSelected,
onLogout: effectiveLogout,
showLogout: effectiveLogout != null,
settingsWidget: widget.settingsWidget,
);
},
endDrawer: widget.endDrawer,
Expand Down