Add avatar-menu navigation style and inline Android search bar

- New Settings > Appearance > "Navigation menu" toggle
  (SettingsController.navMenuStyle, NooNavMenuStyle.drawer/avatarMenu):
  the hamburger-opens-a-Drawer pattern stays the default, or the avatar
  button opens a full-width dropdown (showAvatarMenu) instead, holding
  the same hidden-tabs + Settings content the drawer does. Removes one
  of the two top-corner targets the shell asks a thumb to reach, with
  no permanent chrome added.
- NooTopBar gains androidTitleReplacement so Android's top bar can swap
  its title for an inline search-field-styled launcher
  (ShellSearchLauncher) when search isn't in the bottom bar, instead of
  a bare search icon beside it.
- ShellAvatarButton's onTap/label are now overridable so AppTopBar can
  repoint it per the new setting without this file depending on
  avatar_menu.dart (which already depends on it for shared helpers).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 22:25:04 -04:00
co-authored by Claude Sonnet 5
parent 840ec4c301
commit e6d96a6f9f
11 changed files with 550 additions and 25 deletions
@@ -51,6 +51,7 @@ class SettingsAppearanceSection extends StatelessWidget {
children: [
_ThemeRow(settings: settings),
_BottomBarStyleRow(settings: settings),
_NavMenuStyleRow(settings: settings),
NooSettingsRow(
icon: LucideIcons.search,
label: const Text('Search in bottom bar'),
@@ -230,6 +231,66 @@ class _BottomBarStyleRow extends StatelessWidget {
}
}
/// "Navigation menu" (Settings → Appearance): which widget opens hidden
/// tabs + Settings - the original `menu` icon that opens a left `Drawer`,
/// or the avatar button instead (opens `showAvatarMenu`, a dropdown
/// anchored under it) - see [NooNavMenuStyle]'s own doc comment for the
/// full reachability rationale (dropping the hamburger removes one of the
/// two top-corner targets the shell asks a thumb to reach, rather than
/// adding a third kind of chrome like a side rail would).
class _NavMenuStyleRow extends StatelessWidget {
final SettingsController settings;
const _NavMenuStyleRow({required this.settings});
@override
Widget build(BuildContext context) {
final colors = context.nooColors;
return Material(
color: colors.surface,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: NooSpace.md,
vertical: 12,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(LucideIcons.panelLeft, size: 20, color: colors.fg2),
const SizedBox(width: 14),
Text(
'Navigation menu',
style: NooText.bodyL.copyWith(color: colors.fg1),
),
],
),
const SizedBox(height: 12),
NooSegmentedControl<NooNavMenuStyle>(
fill: true,
value: settings.navMenuStyle,
onChanged: settings.setNavMenuStyle,
options: const [
NooSegmentOption(
value: NooNavMenuStyle.drawer,
icon: LucideIcons.menu,
label: 'Hamburger',
),
NooSegmentOption(
value: NooNavMenuStyle.avatarMenu,
icon: LucideIcons.userRound,
label: 'Avatar',
),
],
),
],
),
),
);
}
}
/// The current accent-color choice, shown as a small filled circle in the
/// "Accent color" row - a wallpaper icon when following the OS's dynamic
/// color instead of a fixed swatch.