- Add SavedAccount/AccountStore and rework ServerProvider around a list of saved accounts: switch/remove/add, swipe-to-cycle on the avatar button, per-account browsing prefs (sort/view/filters/cache), and a session-generation guard so a stale fetch from an abandoned account can't land in the newly active one - Migrate existing single-account installs into the new schema automatically so upgrading users stay logged in - Settings gains an Accounts section; LoginView gains an "add account" mode pushed on top of the main shell instead of replacing it - Give every Scrollbar in the app a thicker, draggable, Android-style thumb via a shared ScrollbarTheme instead of the default hairline - Splash screen: use the monochrome app icon (tinted per theme) and a smaller spinner - Update CLAUDE.md and .claude/context/*.md to reflect the accumulated multi-account, sharing, and UI changes across this session, and add a standing instruction to keep these docs current going forward Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.5 KiB
Code standards
Linting
analysis_options.yaml includes package:flutter_lints/flutter.yaml with
no rules added/relaxed. Run flutter analyze before considering a change
done; it should stay clean.
Comments
Comments are used sparingly and only for non-obvious why — a hidden
constraint, a workaround, or the rationale for overriding a framework
default. See AppTheme._pageTransitionsTheme/_sliderTheme,
NextcloudService._parseDavDate/_davPath, and LoginFlowService's class
doc comment for the house style: one short doc comment on the
class/function explaining why it exists, not what each line does. Don't
add comments that restate the code or describe what a well-named
class/method already makes obvious.
Widget structure
- Screens (
views/) are typicallyStatefulWidgetwhen they own controllers/local UI state (e.g.LoginView's form key + text controller); presentation is frequently split into small privateStatelessWidgets in the same file (_ServerForm,_WaitingForBrowserinlogin_view.dart) rather than inlined in one largebuild. Follow this split for any view complex enough to have more than one visual "mode". - Private helpers/widgets are prefixed with
_and live in the same file as their one caller; promote towidgets/only once something is reused across files. - Read provider state with
context.watch<ServerProvider>()inbuild, andcontext.read<ServerProvider>()for one-off calls from callbacks (matchesLoginView._handleContinue).
ServerProvider conventions
- Any method that fetches data and writes it into a shared field
(
refreshData,fetchAllMedia,fetchTrash,fetchShares,fetchRecent,_applyCredentialsForAccount) must guard against a stale write from an account the user has since switched away from: capturefinal gen = _sessionGeneration;at entry, and checkif (gen != _sessionGeneration) return;immediately after eachawaitbefore touching any field or callingnotifyListeners(). Follow this pattern for any new fetch method added to the provider. - New persisted state on
ServerProvidermust be classified global vs. per-account (seearchitecture.md/server.md) up front — global state uses a plain_prefsFuture.then((p) => p.setX(key, value)); per-account state goes through_persistAccountPref(key, (p, namespacedKey) => p.setX(namespacedKey, value))and must also be handled in_applyAccountPrefs(both the "reset to default when no account" and the "load for this account" branches) so it's correct immediately after a switch, not just at startup.
Testing
- Widget tests must mock platform channels that the app touches on startup
—
SharedPreferences.setMockInitialValues({})and a mockMethodChannel('plugins.it_nomads.com/flutter_secure_storage')handler — and disable Google Fonts network fetching (GoogleFonts.config.allowRuntimeFetching = false) insetUpAll. Without these,ServerProvider's session restore never resolves in the test sandbox (no plugin implementation is registered, so the read future just never completes) and the app stays on_SplashView's indeterminate spinner, which makespumpAndSettle()hang until its own timeout instead of failing fast. Seetest/widget_test.dartfor the reference setup. - Run with
flutter test.
Dependencies
Networking is deliberately split: package:http for simple JSON/XML
request-response calls, package:dio only where streaming/progress is
needed (downloads). Don't introduce a third HTTP client — extend the
existing split instead.