Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b7df833f6 |
@@ -275,7 +275,9 @@ bare system prompt gives no context on its own.
|
||||
|
||||
`MainShellView` builds its chrome from the Noo nav kit
|
||||
(`widgets/noo/nav/`) and switches between two layouts on
|
||||
`NooLayout.isDesktop`:
|
||||
`NooLayout.isDesktop` (width >= 900dp *and* shortest side >= 600dp, so a
|
||||
phone in landscape keeps the mobile layout and only tablet-class windows get
|
||||
the sidebar):
|
||||
|
||||
- **Mobile:** `AppTopBar` (`widgets/app_top_bar.dart`) wraps `NooTopBar`
|
||||
for *every* tab (previously only Files had shell-level top chrome, with
|
||||
|
||||
@@ -80,10 +80,10 @@ Catalog:
|
||||
| Folder | Components |
|
||||
|---|---|
|
||||
| `core/` | `NooButton`, `NooFab`, `NooChip`, `NooSegmentedControl`, `NooToggle`, `NooSearchField`, `NooAvatar`, `NooBadge`, `NooProgressBar` |
|
||||
| `lists/` | `NooGroupedList`, `NooSettingsRow`, `NooTabOrderRow`, `NooBanner`, `NooSummaryCard`, `NooSelectionBar` |
|
||||
| `lists/` | `NooGroupedList`, `NooSettingsRow`, `NooTabOrderRow`, `NooBanner`, `NooInfoNote` (tinted info callout; `SettingsSection(notice:)`), `NooSummaryCard`, `NooSelectionBar` |
|
||||
| `files/` | `NooFileKind` (spec §1.2 tiles; `NooFileKind.from(name:, mimeType:, isDirectory:)`), `NooFileTile`, `NooStatusIcon`/`NooSyncStatus`, `NooFileRow` (mobile 64px), `NooFileTableHeader`/`NooFileTableRow` (desktop), `NooSwipeAction` |
|
||||
| `media/` | `NooGridCard`, `NooPhotoTile` (video badge, selection), `NooPhotoGroupHeader`/`NooPhotoGrid` (sliver, or `.box`), `NooActivityItem`, `NooStatCard` |
|
||||
| `nav/` | `NooBottomBar`, `NooTopBar` (a `PreferredSizeWidget`) with `NooTopBarButton`/`NooTopBarBack`, `NooDrawer` with its `Account`/`Storage`/`Item`/`Link` parts, `NooSidebar` with `NooSidebarItem`/`Divider`/`Account`/`Storage`, `NooToolbar` |
|
||||
| `nav/` | `NooBottomBar`, `NooTopBar` (a `PreferredSizeWidget`) with `NooTopBarButton`/`NooTopBarBack`, `NooDrawer` with its `Account`/`Storage`/`Item`/`Link` parts, `NooSidebar` with `NooSidebarItem`/`Divider`/`Account`/`Storage`, `NooToolbar` (floating rounded card with margin; `NooToolbar.outerHeight` for `appBar` sizing) |
|
||||
| `overlays/` | `showNooSheet`, `showNooDialog`/`NooDialog`, `NooOverlayHeader`, `NooTextField`, and the share parts `NooShareSection`, `NooPersonAccessRow`, `NooPermissionPill` |
|
||||
|
||||
Gotchas:
|
||||
@@ -336,3 +336,17 @@ blocks are noted where they matter:
|
||||
Markdown files (`.md`/`.markdown`) in `MediaTextPreview` open rendered via `flutter_markdown_plus` (`Markdown`, styled from Noo tokens in `_markdownStyle`), with a top-right Edit/Preview toggle (hidden for read-only offline copies). Other text files go straight to the editor.
|
||||
|
||||
`ShareSheet`: focusing the people search field does not scroll; once the user types, `_revealPeopleSection` animates the "Share with people" section (keyed by `_peopleKey` on the `NooShareSection`, not the inner column) to just below the sheet's top edge with a small gap.
|
||||
|
||||
## Desktop/tablet layout notes
|
||||
|
||||
- The desktop branch in `main.dart` is wrapped in `SafeArea` so tablets keep
|
||||
system bars clear of the sidebar; the sidebar has no divider between pinned
|
||||
and hidden tabs.
|
||||
- Desktop Settings is the same two-level menu as mobile (centered 640px column, back button in the floating toolbar); the
|
||||
pushed category screens render the desktop card layout.
|
||||
- Settings that don't apply in wide-tablet (Appearance nav options,
|
||||
Swipe on a file) show a `NooInfoNote` via `SettingsSection.notice`.
|
||||
- Mobile-layout grids (Files, Favorites, move/copy picker, share upload,
|
||||
Photos) take their column count from `NooLayout.gridColumns` (min tile
|
||||
width, never below the phone count), so a tablet in portrait gets more
|
||||
columns rather than a few huge tiles.
|
||||
|
||||
@@ -1 +1 @@
|
||||
Redesigned Noo interface, multi-account support, app locks, offline sync, appearance customization, and a markdown viewer and editor.
|
||||
Improved tablet layout: sidebar navigation, floating title bars, and adaptive grids. Long-press selection and Settings submenus now work on tablets.
|
||||
+5
-2
@@ -444,7 +444,10 @@ class _MainShellViewState extends State<MainShellView> {
|
||||
if (isDesktop) {
|
||||
scaffold = Scaffold(
|
||||
backgroundColor: colors.bg,
|
||||
body: Row(
|
||||
// Tablets keep the system bars, so inset for them (desktop's
|
||||
// insets are zero).
|
||||
body: SafeArea(
|
||||
child: Row(
|
||||
children: [
|
||||
NooSidebar(
|
||||
account: NooSidebarAccount(
|
||||
@@ -466,7 +469,6 @@ class _MainShellViewState extends State<MainShellView> {
|
||||
selected: tab == selectedTab,
|
||||
onTap: () => selectTab(tab),
|
||||
),
|
||||
if (hiddenTabs.isNotEmpty) const NooSidebarDivider(),
|
||||
for (final tab in hiddenTabs)
|
||||
NooSidebarItem(
|
||||
icon: tab.icon,
|
||||
@@ -509,6 +511,7 @@ class _MainShellViewState extends State<MainShellView> {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final bottomBarStyle = settings.bottomBarStyle;
|
||||
|
||||
+37
-64
@@ -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,16 +197,24 @@ 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(
|
||||
appBar: desktop
|
||||
? _desktopBar(context, category.title)
|
||||
: NooTopBar(
|
||||
style: NooLayout.navStyle(context),
|
||||
title: category.title,
|
||||
leading: const NooTopBarBack(),
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: desktop ? 640 : double.infinity,
|
||||
),
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
NooSpace.sm,
|
||||
@@ -228,57 +226,32 @@ class _SettingsCategoryScreen extends StatelessWidget {
|
||||
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,
|
||||
children: [
|
||||
Expanded(child: _Column(children: left)),
|
||||
const SizedBox(width: NooSpace.xl),
|
||||
Expanded(child: _Column(children: right)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Column extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
const _Column({required this.children});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
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: [
|
||||
for (final child in children) ...[
|
||||
child,
|
||||
const SizedBox(height: NooSpace.xl),
|
||||
],
|
||||
IconButton(
|
||||
icon: const Icon(LucideIcons.arrowLeft),
|
||||
tooltip: 'Back',
|
||||
onPressed: () => Navigator.maybePop(context),
|
||||
),
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -154,6 +154,7 @@ class NooFileTableRow extends StatelessWidget {
|
||||
final VoidCallback? onTap;
|
||||
final VoidCallback? onDoubleTap;
|
||||
final VoidCallback? onSecondaryTap;
|
||||
final VoidCallback? onLongPress;
|
||||
|
||||
/// Overflow menu tap. The button is hidden when null.
|
||||
final VoidCallback? onMore;
|
||||
@@ -171,6 +172,7 @@ class NooFileTableRow extends StatelessWidget {
|
||||
this.onTap,
|
||||
this.onDoubleTap,
|
||||
this.onSecondaryTap,
|
||||
this.onLongPress,
|
||||
this.onMore,
|
||||
});
|
||||
|
||||
@@ -193,6 +195,7 @@ class NooFileTableRow extends StatelessWidget {
|
||||
onTap: onTap,
|
||||
onDoubleTap: onDoubleTap,
|
||||
onSecondaryTap: onSecondaryTap,
|
||||
onLongPress: onLongPress,
|
||||
child: SizedBox(
|
||||
height: NooSizes.rowDesktop,
|
||||
child: _TableColumns(
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||
import '../../../theme/design_tokens.dart';
|
||||
|
||||
/// Tinted info callout (info-soft fill, info icon) for a short notice that
|
||||
/// must be noticed immediately, unlike a muted footer/subtitle line.
|
||||
class NooInfoNote extends StatelessWidget {
|
||||
final String message;
|
||||
|
||||
const NooInfoNote({super.key, required this.message});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = context.nooColors;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: colors.infoSoft,
|
||||
borderRadius: BorderRadius.circular(NooRadii.input),
|
||||
border: Border.all(color: colors.info.withValues(alpha: 0.35)),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 1),
|
||||
child: Icon(LucideIcons.info, size: 18, color: colors.info),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
message,
|
||||
style: NooText.body.copyWith(color: colors.fg1, height: 1.35),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -48,21 +48,30 @@ class NooToolbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
);
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(NooSizes.toolbar);
|
||||
Size get preferredSize => const Size.fromHeight(outerHeight);
|
||||
|
||||
static const _margin = EdgeInsets.fromLTRB(12, 8, 12, 4);
|
||||
|
||||
/// Bar height plus the margin that makes it float.
|
||||
static const double outerHeight = NooSizes.toolbar + 12;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colors = context.nooColors;
|
||||
final titleStyle = NooText.title.copyWith(height: 1, color: colors.fg1);
|
||||
|
||||
return Material(
|
||||
return Padding(
|
||||
padding: _margin,
|
||||
child: Material(
|
||||
color: backgroundColor ?? colors.surface,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(NooRadii.card),
|
||||
side: BorderSide(color: colors.line),
|
||||
),
|
||||
child: Container(
|
||||
height: NooSizes.toolbar,
|
||||
padding: const EdgeInsetsDirectional.only(start: 24, end: 20),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: colors.line)),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
children: [
|
||||
@@ -102,6 +111,7 @@ class NooToolbar extends StatelessWidget implements PreferredSizeWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,16 @@ class NooLayout {
|
||||
/// (table rows, 5-column grids, 8-column photos, 24px gutter).
|
||||
static const double desktopBreakpoint = 900;
|
||||
|
||||
static bool isDesktop(BuildContext context) =>
|
||||
MediaQuery.sizeOf(context).width >= desktopBreakpoint;
|
||||
/// Shortest window side that counts as a tablet-class window (the usual
|
||||
/// 600dp rule). A phone in landscape is wide but short, so it stays on the
|
||||
/// mobile layout.
|
||||
static const double tabletShortestSide = 600;
|
||||
|
||||
static bool isDesktop(BuildContext context) {
|
||||
final size = MediaQuery.sizeOf(context);
|
||||
return size.width >= desktopBreakpoint &&
|
||||
size.shortestSide >= tabletShortestSide;
|
||||
}
|
||||
|
||||
static NooNavStyle navStyle(BuildContext context) =>
|
||||
NooNavStyle.fromPlatform(Theme.of(context).platform);
|
||||
@@ -24,4 +32,16 @@ class NooLayout {
|
||||
|
||||
/// Horizontal content gutter: 12 on mobile (cards), 24 on desktop.
|
||||
static double gutter(BuildContext context) => isDesktop(context) ? 24 : 12;
|
||||
|
||||
/// Grid column count for [minTile]-wide tiles, never fewer than [phone]
|
||||
/// (so phones keep their usual count while wide non-sidebar windows, e.g. a
|
||||
/// tablet in portrait, get more instead of a few huge tiles).
|
||||
static int gridColumns(
|
||||
BuildContext context, {
|
||||
required int phone,
|
||||
required double minTile,
|
||||
}) {
|
||||
final width = MediaQuery.sizeOf(context).width;
|
||||
return (width / minTile).floor().clamp(phone, 12);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ class SettingsAppearanceSection extends StatelessWidget {
|
||||
children: [
|
||||
SettingsSection(
|
||||
title: 'Appearance',
|
||||
notice:
|
||||
'Bottom bar style, menu style and search in bottom bar have no effect on wide tablets, which use a sidebar.',
|
||||
children: [
|
||||
_ThemeRow(settings: settings),
|
||||
_BottomBarStyleRow(settings: settings),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../theme/design_tokens.dart';
|
||||
import '../noo/lists/noo_grouped_list.dart';
|
||||
import '../noo/lists/noo_info_note.dart';
|
||||
import '../noo/noo_layout.dart';
|
||||
import '../noo/overlays/noo_dialog.dart';
|
||||
import '../noo/overlays/noo_sheet.dart';
|
||||
@@ -17,10 +18,14 @@ class SettingsSection extends StatelessWidget {
|
||||
final String? subtitle;
|
||||
final List<Widget> children;
|
||||
|
||||
/// Shown as a prominent [NooInfoNote] above the rows.
|
||||
final String? notice;
|
||||
|
||||
const SettingsSection({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.subtitle,
|
||||
this.notice,
|
||||
required this.children,
|
||||
});
|
||||
|
||||
@@ -28,7 +33,7 @@ class SettingsSection extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final colors = context.nooColors;
|
||||
if (!NooLayout.isDesktop(context)) {
|
||||
return NooGroupedList(
|
||||
final list = NooGroupedList(
|
||||
label: title,
|
||||
footer: subtitle != null ? Text(subtitle!) : null,
|
||||
// Not collapsible: each mobile Settings section now renders on its
|
||||
@@ -40,6 +45,15 @@ class SettingsSection extends StatelessWidget {
|
||||
// one is already isolated on its own screen.
|
||||
children: children,
|
||||
);
|
||||
if (notice == null) return list;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
NooInfoNote(message: notice!),
|
||||
const SizedBox(height: NooSpace.md),
|
||||
list,
|
||||
],
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
@@ -57,6 +71,10 @@ class SettingsSection extends StatelessWidget {
|
||||
Text(subtitle!, style: NooText.meta.copyWith(color: colors.fg3)),
|
||||
],
|
||||
const SizedBox(height: NooSpace.md),
|
||||
if (notice != null) ...[
|
||||
NooInfoNote(message: notice!),
|
||||
const SizedBox(height: NooSpace.md),
|
||||
],
|
||||
FlatRowGroup(children: children),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -37,7 +37,11 @@ class SettingsSwipeSection extends StatelessWidget {
|
||||
NooSettingsRow(
|
||||
label: Text(_swipeActionLabel(action)),
|
||||
trailing: action == current
|
||||
? Icon(LucideIcons.check, size: 18, color: context.nooColors.accentText)
|
||||
? Icon(
|
||||
LucideIcons.check,
|
||||
size: 18,
|
||||
color: context.nooColors.accentText,
|
||||
)
|
||||
: null,
|
||||
onTap: () {
|
||||
onChanged(action);
|
||||
@@ -55,6 +59,7 @@ class SettingsSwipeSection extends StatelessWidget {
|
||||
return SettingsSection(
|
||||
title: 'Swipe on a file',
|
||||
subtitle: 'Choose what swiping a file left or right does in list view',
|
||||
notice: 'Swipe actions have no effect on wide tablets.',
|
||||
children: [
|
||||
NooSettingsRow(
|
||||
icon: LucideIcons.chevronsRight,
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0+3
|
||||
version: 1.0.1+4
|
||||
|
||||
environment:
|
||||
sdk: ^3.12.2
|
||||
|
||||
Reference in New Issue
Block a user