Build APK / build (push) Successful in 7m40s
- Let Noo be picked as a source (Files + Photos) by other apps' native file/photo pickers via Android's GET_CONTENT intent, mirroring the existing "Share to Noo" plumbing: MainActivity.kt/PickIntentService/ PickRequest, ServerProvider.pickRequest/confirmPick/cancelPick, and pick-mode tap handling in FilesView/PhotosView. While picking, the bottom nav and MoreTabsButton are restricted to Files/Photos only. - Fix two account bugs: testConnection() failures no longer delete a still-valid stored app password (only an actual 401 does), and switchAccount() now retries a nominally-active-but-logged-out account instead of no-op'ing - both were silently locking users out of "Continue as" with no way back in except removing and re-adding the account (now exposed via login_view.dart's inline remove button). - Fix the login WebView: clear cookies before every load so "Add Account" can't silently reuse an existing session, and push it as a non-animated route to avoid a known WebView-as-PlatformView black-screen- on-pop issue under Impeller. - Redesign the Files/Photos selection toolbar to take over the top app bar entirely (SyncedHeaderScaffold.selectionBar) instead of sitting as a second bar under the controls row, with a horizontally-scrollable, uniformly-colored actions row and the same one-shot scroll-hint nudge the controls row uses. - Redesign ShareUploadView to share the same SyncedHeaderScaffold chrome as every tab, with the uploading-file summary (marqueed via the new shared MarqueeTitle widget) and upload button grouped into one rounded/elevated bottom sheet. - Restyle ShareSheet to open on the same DetailsHeader every other per-item sheet uses, and fix a couple of small design-language drifts (hardcoded text style, a non-rounded icon). - Settings' account card: icon buttons with tooltips instead of text buttons for refresh/logout/remove, divider removed.
6.2 KiB
6.2 KiB
Styling
Theme
All theming goes through AppTheme
(AppTheme.light/AppTheme.dark) — don't set colors/fonts ad hoc in
widgets. Key points:
- Material 3, seed-color based.
AppTheme.seedColorsis the picker list users choose from;defaultNextcloudBlue(#0082C9) is the fallback. - Dynamic color (Android 12+ Material You / desktop accent color) is
supported via
package:dynamic_color'sDynamicColorBuilderwrapping the whole app inmain.dart. When available anduseDynamicColoris on, the OS-providedColorSchemewins over the seed color — always thread bothdynamicSchemeanduseDynamicColorthrough when adding a theme knob. - Font: Inter via
google_fonts, applied throughGoogleFonts.interTextTheme(...). In tests, setGoogleFonts.config.allowRuntimeFetching = falseinsetUpAll— without it, the font-fetch call to Google's CDN can stallpumpAndSettleindefinitely (seestandards.md). - Cards: flat (
elevation: 0), 20px rounded corners,surfaceContainerLow. - App bars: flat, not centered,
surfacebackground. - Two Flutter defaults are deliberately overridden app-wide rather than
per-widget, each with a comment explaining why in
app_theme.dart: Android predictive-back page transitions, and the non-2023SliderTheme. Follow that pattern (a themed default + a comment) instead of overriding per-instance if you need the same behavior elsewhere. - Dark theme supports an
amoledflag that flattens every surface tone to pure black — extendcolorScheme.copyWith(...)there if a new surface role needs the same treatment, don't hardcodeColors.blackat call sites. - Scrollbars are deliberately not shown — every scrollable list in the
app is a plain
ListView/CustomScrollViewwith noScrollbarwrapper and noscrollbarThemeoverride (tried once, explicitly reverted). Don't reintroduce one without being asked.
Reusable chrome
getItemIcon/getIconColor/ItemThumbnail— the icon/color/thumbnail treatment for a file or folder, shared by any screen that listsNextcloudItems the way the Files tab does (currentlyfiles_view.dartandshare_upload_view.dart's destination picker). Extend this rather than re-deriving per-type icons/colors at a new call site.DetailsHeader— the icon-box/name/meta row every per-item bottom sheet opens on (DetailsSheet, the media viewer's collapsed peek state, andShareSheet). Takes apaddingoverride for callers whose own scroll view already applies horizontal insets (ShareSheet'sListView), so it doesn't get doubled up. Reuse this instead of a bare titleTextfor any new per-item sheet - a plain title reads as under-designed next to the other sheets (a real instance:ShareSheetused to be just that).FrostedGlassContainer— the blurred/translucent pill background shared by all floating chrome (bottom nav bar, media-viewer top/bottom bars and video transport controls). Reuse this for any new floating overlay instead of building a new blur/shadow combo.FloatingBottomNavBar— the main tab bar; opacity/blur are user-adjustable settings (ServerProvider.bottomBarOpacity/bottomBarBlur), not constants — pull new adjustable visual knobs from the provider the same way rather than hardcoding them.SyncedHeaderScaffold— the pull-to-syncCustomScrollViewheader shared by 5 of the 6 tabs (seearchitecture.md); also where the pull-to-refresh gesture thresholds and the classic Material refresh spinner live.SeekBarPainter/SeekBarPreview— the fourMediaProgressBarStylepresets (Default/Wavy/Slim/Squiggly) for the video player's seek bar, plus a perpetually-animatedSeekBarPreviewwrapper used by the Settings style picker so every preview always matches the real widget exactly (same painter, just fed demoprogress/phasevalues). Add new seek-bar presets here, not by forking the painter.- Chrome inside the media viewer (
file_viewer_screen.dart— the top bar's back button + filename, the bottom action bar, the video transport controls) all share one small hand-rolled icon-button pattern (_ActionIconButton:InkWell+Iconat a fixed 22px, colored fromcolorScheme.onSurfaceunless overridden) rather than plainIconButtons — match this instead of adding a bareIconButtonin that screen, since a default-styled one visibly stands out against the rest (this was a real bug: an unstyled back button read as "too large" next to everything else). - A title/label that might overflow a fixed-width chrome bar (e.g. the media
viewer's filename) should use
_MarqueeTitle-style logic — measure withTextPainterfirst and only switch to a scrollingMarqueewhen the text actually doesn't fit, rather than marqueeing unconditionally. - Icons: prefer
Icons.*_rounded(matches the rest of the app) ormaterial_symbols_iconswhere Material Symbols are already in use; avoid mixing in the sharp/outlined default set.
Conventions
- No hardcoded colors for anything themeable — pull from
Theme.of(context).colorScheme, notColors.blueetc. (per-file-type icon tinting infiles_view.dart's_getIconColoris the one deliberate exception, since those colors are content-identity cues, not theme). - Use
colorScheme.surfaceContainer*/onSurfaceVarianttokens for elevation/secondary text rather than manual opacity on black/white. - Anywhere the app shows its own icon in-app (splash, lock screen,
login screen) uses
assets/icon/app_icon_monochrome.png— a plain white silhouette on transparent, tinted viaColorFiltered(colorFilter: ColorFilter.mode(colorScheme.onSurface, BlendMode.srcIn), ...)so it reads correctly in both light and dark mode. Never the full-colorapp_icon.png/adaptive-icon assets for in-app UI — those are for the launcher icon only (flutter_launcher_iconsinpubspec.yaml).