- Revert the scrollbar-thumb feature entirely (per-view Scrollbar wrappers and the ScrollbarThemeData override) - back to plain scrollables with no visible thumb - Settings: move the logout button into the profile card (next to the name/email), and move the ONLINE/OFFLINE badge + "Connected to..." text out of the top row into a combined line at the bottom of the card, instead of split between the AppBar and the top of the screen - Update architecture.md/styling.md to drop the stale scrollbar-theme documentation Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
84 lines
4.8 KiB
Markdown
84 lines
4.8 KiB
Markdown
# Styling
|
|
|
|
## Theme
|
|
|
|
All theming goes through [`AppTheme`](../../lib/theme/app_theme.dart)
|
|
(`AppTheme.light`/`AppTheme.dark`) — don't set colors/fonts ad hoc in
|
|
widgets. Key points:
|
|
|
|
- **Material 3**, seed-color based. `AppTheme.seedColors` is 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`'s `DynamicColorBuilder` wrapping the
|
|
whole app in `main.dart`. When available and `useDynamicColor` is on, the
|
|
OS-provided `ColorScheme` wins over the seed color — always thread both
|
|
`dynamicScheme` and `useDynamicColor` through when adding a theme knob.
|
|
- **Font**: Inter via `google_fonts`, applied through
|
|
`GoogleFonts.interTextTheme(...)`. In tests, set
|
|
`GoogleFonts.config.allowRuntimeFetching = false` in `setUpAll` — without
|
|
it, the font-fetch call to Google's CDN can stall `pumpAndSettle`
|
|
indefinitely (see `standards.md`).
|
|
- **Cards**: flat (`elevation: 0`), 20px rounded corners,
|
|
`surfaceContainerLow`.
|
|
- **App bars**: flat, not centered, `surface` background.
|
|
- 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-2023 `SliderTheme`.
|
|
Follow that pattern (a themed default + a comment) instead of overriding
|
|
per-instance if you need the same behavior elsewhere.
|
|
- Dark theme supports an `amoled` flag that flattens every surface tone to
|
|
pure black — extend `colorScheme.copyWith(...)` there if a new surface
|
|
role needs the same treatment, don't hardcode `Colors.black` at call sites.
|
|
- **Scrollbars are deliberately not shown** — every scrollable list in the
|
|
app is a plain `ListView`/`CustomScrollView` with no `Scrollbar` wrapper
|
|
and no `scrollbarTheme` override (tried once, explicitly reverted). Don't
|
|
reintroduce one without being asked.
|
|
|
|
## Reusable chrome
|
|
|
|
- [`FrostedGlassContainer`](../../lib/widgets/frosted_glass_container.dart) —
|
|
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`](../../lib/widgets/floating_bottom_bar.dart) — 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`](../../lib/widgets/synced_header_scaffold.dart) —
|
|
the pull-to-sync `CustomScrollView` header shared by 5 of the 6 tabs (see
|
|
`architecture.md`); also where the pull-to-refresh gesture thresholds and
|
|
the classic Material refresh spinner live.
|
|
- [`SeekBarPainter`/`SeekBarPreview`](../../lib/widgets/seek_bar_painter.dart)
|
|
— the four `MediaProgressBarStyle` presets (Default/Wavy/Slim/Squiggly)
|
|
for the video player's seek bar, plus a perpetually-animated
|
|
`SeekBarPreview` wrapper used by the Settings style picker so every
|
|
preview always matches the real widget exactly (same painter, just fed
|
|
demo `progress`/`phase` values). 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` + `Icon` at a fixed 22px, colored from
|
|
`colorScheme.onSurface` unless overridden) rather than plain `IconButton`s
|
|
— match this instead of adding a bare `IconButton` in 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 with
|
|
`TextPainter` first and only switch to a scrolling `Marquee` when the text
|
|
actually doesn't fit, rather than marqueeing unconditionally.
|
|
- Icons: prefer `Icons.*_rounded` (matches the rest of the app) or
|
|
`material_symbols_icons` where 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`, not `Colors.blue` etc. (per-file-type
|
|
icon tinting in `files_view.dart`'s `_getIconColor` is the one deliberate
|
|
exception, since those colors are content-identity cues, not theme).
|
|
- Use `colorScheme.surfaceContainer*`/`onSurfaceVariant` tokens for
|
|
elevation/secondary text rather than manual opacity on black/white.
|