Release 1.0.3: refine tablet layouts, pointer selection and account sync cleanup
Build iOS / build (push) Successful in 4m45s
Build App Bundle / build (push) Successful in 7m7s

This commit is contained in:
Ayushya Amitabh
2026-10-09 00:36:42 -04:00
parent 79085874a1
commit 0a4608e6c3
67 changed files with 1740 additions and 451 deletions
+120 -33
View File
@@ -5,9 +5,11 @@ import '../widgets/noo/lists/noo_grouped_list.dart';
import '../widgets/noo/lists/noo_settings_row.dart';
import '../widgets/noo/nav/noo_top_bar.dart';
import '../widgets/noo/nav/noo_toolbar.dart';
import '../widgets/noo/nav/noo_sidebar.dart';
import '../widgets/noo/noo_layout.dart';
import '../widgets/settings/settings_account_card.dart';
import '../widgets/settings/settings_accounts.dart';
import '../widgets/settings/settings_about.dart';
import '../widgets/settings/settings_action_bar.dart';
import '../widgets/settings/settings_appearance.dart';
import '../widgets/settings/settings_file_sync.dart';
@@ -15,35 +17,20 @@ import '../widgets/settings/settings_files_cache.dart';
import '../widgets/settings/settings_security.dart';
import '../widgets/settings/settings_swipe.dart';
import '../widgets/settings/settings_tabs.dart';
import '../widgets/settings/settings_section.dart';
/// Settings, pushed on top of the shell (DESIGN_SYSTEM.md 4's 9-section
/// order: account card, accounts, security, file sync, files cache,
/// appearance, tabs, action bar, swipe on a file).
///
/// Every layout (including wide tablet, in a centered 640px column) is
/// a two-level menu, the way native iOS/Android Settings apps
/// work: [SettingsAccountCard] (the account summary, not a settings picker)
/// stays pinned at the top of a single top-level list ([_MobileMenu]), and
/// every other section becomes one tappable [NooSettingsRow] - icon, title,
/// chevron - in a [NooGroupedList] below it. Tapping a row pushes a new
/// [_SettingsCategoryScreen] with its own [NooTopBar]/[NooTopBarBack],
/// containing just that section's content full-screen. This replaces an
/// earlier design where every section rendered inline in one long
/// collapsible-sections column (`_MobileList`, since removed) - and before
/// that, a trailing jump rail (an even earlier version) that scrolled that
/// *same* page to an anchor. Both were rejected: the jump rail added a
/// second, redundant way to navigate on top of plain scrolling without
/// shortening the page, and the collapsible-sections column still left a
/// long page to scroll past even collapsed. A genuinely separate pushed
/// screen per category removes the scroll-depth problem outright, so
/// neither a jump rail nor per-section collapsing is needed any more - see
/// [SettingsSection]'s doc comment for how that reflects in its mobile
/// layout. See each `widgets/settings/*.dart` file for a section's own
/// content and any setting that had to be slotted in or grouped under
/// "Advanced appearance".
class AccountView extends StatelessWidget {
/// Settings uses pushed category screens on phones and an inline category
/// sidebar with a rounded content pane on tablets.
class AccountView extends StatefulWidget {
const AccountView({super.key});
@override
State<AccountView> createState() => _AccountViewState();
}
class _AccountViewState extends State<AccountView> {
int selected = 0;
static final _categories = <_SettingsCategory>[
_SettingsCategory(
title: 'Accounts',
@@ -85,6 +72,11 @@ class AccountView extends StatelessWidget {
icon: LucideIcons.chevronsLeftRight,
builder: (_) => const SettingsSwipeSection(),
),
_SettingsCategory(
title: 'About',
icon: LucideIcons.info,
builder: (_) => const SettingsAboutSection(),
),
];
@override
@@ -92,6 +84,101 @@ class AccountView extends StatelessWidget {
final colors = context.nooColors;
final desktop = NooLayout.isDesktop(context);
if (desktop) {
final category = _categories[selected];
final duration = MediaQuery.disableAnimationsOf(context)
? Duration.zero
: const Duration(milliseconds: 260);
return Scaffold(
backgroundColor: colors.bg,
body: SafeArea(
child: Row(
children: [
NooSidebar(
windowControls: Row(
children: [
IconButton(
icon: const Icon(LucideIcons.arrowLeft),
tooltip: 'Back',
onPressed: () => Navigator.maybePop(context),
),
Expanded(
child: Text(
'Settings',
style: NooText.title.copyWith(color: colors.fg1),
),
),
],
),
account: SettingsAccountCard(
compact: true,
onManage: () => setState(() => selected = 0),
),
groupItems: true,
items: [
for (var index = 0; index < _categories.length; index++)
if (_categories[index].title != 'Swipe on a file')
NooSidebarItem(
icon: _categories[index].icon,
label: _categories[index].title,
selected: selected == index,
onTap: () => setState(() => selected = index),
),
],
),
Expanded(
child: Column(
children: [
NooToolbar(
title: category.title,
backgroundColor: colors.bg,
framed: false,
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 8, 12, 16),
child: Material(
color: colors.surface2,
borderRadius: BorderRadius.circular(NooRadii.card),
clipBehavior: Clip.antiAlias,
child: AnimatedSwitcher(
duration: duration,
switchInCurve: Curves.easeOutCubic,
switchOutCurve: Curves.easeInCubic,
transitionBuilder: (child, animation) =>
FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween(
begin: const Offset(0, .025),
end: Offset.zero,
).animate(animation),
child: child,
),
),
child: ListView(
key: ValueKey(category.title),
padding: const EdgeInsets.all(12),
children: [
SettingsCategoryHeading(
title: category.title,
child: category.builder(context),
),
],
),
),
),
),
),
],
),
),
],
),
),
);
}
return Scaffold(
backgroundColor: colors.bg,
appBar: desktop
@@ -184,12 +271,7 @@ class _MobileMenu extends StatelessWidget {
}
}
/// A pushed, single-category Settings screen: [NooTopBar] titled with the
/// category, a back button, and just that section's own content - the
/// pushed half of [AccountView]'s mobile menu-then-screen pattern. Always
/// built in a mobile-width context (desktop never opens this screen; it
/// shows every section inline in its own grid instead), so the section
/// widgets inside render their normal mobile layout unchanged.
/// A pushed category screen used by phone navigation.
class _SettingsCategoryScreen extends StatelessWidget {
final _SettingsCategory category;
const _SettingsCategoryScreen({required this.category});
@@ -223,7 +305,12 @@ class _SettingsCategoryScreen extends StatelessWidget {
NooSpace.xxl,
),
physics: const BouncingScrollPhysics(),
children: [category.builder(context)],
children: [
SettingsCategoryHeading(
title: category.title,
child: category.builder(context),
),
],
),
),
),