Tablet layout polish: tablet-only sidebar layout, floating toolbar, Settings submenus, info notes, adaptive grids; bump to 1.0.1+4
Build App Bundle / build (push) Successful in 6m0s
Build App Bundle / build (push) Successful in 6m0s
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+50
-77
@@ -20,11 +20,8 @@ import '../widgets/settings/settings_tabs.dart';
|
||||
/// order: account card, accounts, security, file sync, files cache,
|
||||
/// appearance, tabs, action bar, swipe on a file).
|
||||
///
|
||||
/// Desktop is unchanged: a 2-column grid of cards ([_DesktopGrid]) wide
|
||||
/// enough to see every section at once, so it has no scroll-depth problem
|
||||
/// and needs no menu.
|
||||
///
|
||||
/// Mobile is a two-level menu, the way native iOS/Android Settings apps
|
||||
/// 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,
|
||||
@@ -47,18 +44,6 @@ import '../widgets/settings/settings_tabs.dart';
|
||||
class AccountView extends StatelessWidget {
|
||||
const AccountView({super.key});
|
||||
|
||||
static const _sections = <Widget>[
|
||||
SettingsAccountCard(),
|
||||
SettingsAccountsSection(),
|
||||
SettingsSecuritySection(),
|
||||
SettingsFileSyncSection(),
|
||||
SettingsFilesCacheSection(),
|
||||
SettingsAppearanceSection(),
|
||||
SettingsTabsSection(),
|
||||
SettingsActionBarSection(),
|
||||
SettingsSwipeSection(),
|
||||
];
|
||||
|
||||
static final _categories = <_SettingsCategory>[
|
||||
_SettingsCategory(
|
||||
title: 'Accounts',
|
||||
@@ -110,7 +95,7 @@ class AccountView extends StatelessWidget {
|
||||
return Scaffold(
|
||||
backgroundColor: colors.bg,
|
||||
appBar: desktop
|
||||
? const NooToolbar(title: 'Settings')
|
||||
? _desktopBar(context, 'Settings')
|
||||
: NooTopBar(
|
||||
style: NooLayout.navStyle(context),
|
||||
title: 'Settings',
|
||||
@@ -119,7 +104,12 @@ class AccountView extends StatelessWidget {
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: desktop
|
||||
? _DesktopGrid(sections: _sections)
|
||||
? Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
child: _MobileMenu(categories: _categories),
|
||||
),
|
||||
)
|
||||
: _MobileMenu(categories: _categories),
|
||||
),
|
||||
);
|
||||
@@ -207,78 +197,61 @@ class _SettingsCategoryScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = context.nooColors;
|
||||
final desktop = NooLayout.isDesktop(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colors.bg,
|
||||
appBar: NooTopBar(
|
||||
style: NooLayout.navStyle(context),
|
||||
title: category.title,
|
||||
leading: const NooTopBarBack(),
|
||||
),
|
||||
appBar: desktop
|
||||
? _desktopBar(context, category.title)
|
||||
: NooTopBar(
|
||||
style: NooLayout.navStyle(context),
|
||||
title: category.title,
|
||||
leading: const NooTopBarBack(),
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
NooSpace.sm,
|
||||
NooSpace.sm,
|
||||
NooSpace.sm,
|
||||
NooSpace.xxl,
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: desktop ? 640 : double.infinity,
|
||||
),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
NooSpace.sm,
|
||||
NooSpace.sm,
|
||||
NooSpace.sm,
|
||||
NooSpace.xxl,
|
||||
),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [category.builder(context)],
|
||||
),
|
||||
),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
children: [category.builder(context)],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits the sections between two columns rather than a strict grid, since
|
||||
/// each card's content height varies a lot (the tab reorder list and the
|
||||
/// accounts list can both run much taller than, say, Security) - a fixed
|
||||
/// grid would either clip content or leave large gaps.
|
||||
class _DesktopGrid extends StatelessWidget {
|
||||
final List<Widget> sections;
|
||||
const _DesktopGrid({required this.sections});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final left = <Widget>[];
|
||||
final right = <Widget>[];
|
||||
for (var i = 0; i < sections.length; i++) {
|
||||
(i.isEven ? left : right).add(sections[i]);
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(NooSpace.xl),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 1040),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
PreferredSizeWidget _desktopBar(BuildContext context, String title) {
|
||||
final top = MediaQuery.paddingOf(context).top;
|
||||
return PreferredSize(
|
||||
preferredSize: Size.fromHeight(NooToolbar.outerHeight + top),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: top),
|
||||
child: NooToolbar(
|
||||
titleWidget: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: NooSpace.sm,
|
||||
children: [
|
||||
Expanded(child: _Column(children: left)),
|
||||
const SizedBox(width: NooSpace.xl),
|
||||
Expanded(child: _Column(children: right)),
|
||||
IconButton(
|
||||
icon: const Icon(LucideIcons.arrowLeft),
|
||||
tooltip: 'Back',
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
),
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Column extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
const _Column({required this.children});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final child in children) ...[
|
||||
child,
|
||||
const SizedBox(height: NooSpace.xl),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -586,7 +586,9 @@ class _FavoritesViewState extends State<FavoritesView> {
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isDesktop ? 5 : 2,
|
||||
crossAxisCount: isDesktop
|
||||
? 5
|
||||
: NooLayout.gridColumns(context, phone: 2, minTile: 180),
|
||||
crossAxisSpacing: isDesktop ? 16 : 10,
|
||||
mainAxisSpacing: isDesktop ? 16 : 10,
|
||||
childAspectRatio: isDesktop ? 0.92 : 0.85,
|
||||
|
||||
@@ -754,7 +754,9 @@ class _FilesViewState extends State<FilesView> {
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isDesktop ? 5 : 2,
|
||||
crossAxisCount: isDesktop
|
||||
? 5
|
||||
: NooLayout.gridColumns(context, phone: 2, minTile: 180),
|
||||
childAspectRatio: isDesktop ? 1.05 : 0.92,
|
||||
crossAxisSpacing: isDesktop ? 16 : 10,
|
||||
mainAxisSpacing: isDesktop ? 16 : 10,
|
||||
@@ -1323,6 +1325,9 @@ class _FilesViewState extends State<FilesView> {
|
||||
selected: isSelected,
|
||||
thumbnail: _rowThumbnail(context, item, session, NooFileTileSize.desktop),
|
||||
onTap: () => _handleItemTap(context, item, picking: picking),
|
||||
onLongPress: _offline || (picking && !pick.pickRequest!.allowMultiple)
|
||||
? null
|
||||
: () => _toggleSelection(item),
|
||||
onMore: !_isSelecting && !picking
|
||||
? () => _showItemActions(context, item)
|
||||
: null,
|
||||
|
||||
@@ -284,7 +284,9 @@ class _MoveCopyDestinationPickerState extends State<MoveCopyDestinationPicker> {
|
||||
padding: EdgeInsets.symmetric(horizontal: gutter),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: desktop ? 5 : 2,
|
||||
crossAxisCount: desktop
|
||||
? 5
|
||||
: NooLayout.gridColumns(context, phone: 2, minTile: 180),
|
||||
childAspectRatio: desktop ? 1.05 : 0.92,
|
||||
crossAxisSpacing: desktop ? 16 : 10,
|
||||
mainAxisSpacing: desktop ? 16 : 10,
|
||||
|
||||
@@ -342,7 +342,11 @@ class _PhotosViewState extends State<PhotosView> {
|
||||
itemCount: group.items.length,
|
||||
columns: isDesktop
|
||||
? NooPhotoGrid.desktopColumns
|
||||
: NooPhotoGrid.mobileColumns,
|
||||
: NooLayout.gridColumns(
|
||||
context,
|
||||
phone: NooPhotoGrid.mobileColumns,
|
||||
minTile: 130,
|
||||
),
|
||||
gap: isDesktop ? NooPhotoGrid.desktopGap : NooPhotoGrid.mobileGap,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: isDesktop ? NooLayout.gutter(context) : 0,
|
||||
|
||||
@@ -209,7 +209,9 @@ class _ShareUploadViewState extends State<ShareUploadView> {
|
||||
padding: EdgeInsets.symmetric(horizontal: gutter),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: desktop ? 5 : 2,
|
||||
crossAxisCount: desktop
|
||||
? 5
|
||||
: NooLayout.gridColumns(context, phone: 2, minTile: 180),
|
||||
childAspectRatio: desktop ? 1.05 : 0.92,
|
||||
crossAxisSpacing: desktop ? 16 : 10,
|
||||
mainAxisSpacing: desktop ? 16 : 10,
|
||||
|
||||
Reference in New Issue
Block a user