legacy_shared was the junk drawer of the legacy app mode — 37 files
mixing device entities, command infrastructure, UI primitives, generic
formatters, and a duplicate of sf_infrastructure's dio_error_mapper.
Any module needing one piece pulled the whole bag into its graph.
Split it by responsibility, following Mandamiento 4 of Real-World Flutter:
- legacy_ui (new): 6 widget/layout primitives (PageLayout, MenuButton,
SectionButton, PulsingLocationMarker, RefreshableErrorState,
WeekDayChips) plus mapStyleProvider — shared UI state that was the
only reason two modules needed a common package.
- legacy_device_state (expanded 9 → 30): absorbed device entities,
commands infrastructure (datasource + repo + provider + guard),
device settings update flow, and the CSV exporter. Now one package
owns the device domain end-to-end.
- packages/utils: absorbed battery_utils and date_format_utils as pure
formatters that never belonged in a legacy-scoped package.
- legacy_shared: deleted entirely.
The duplicate dio_error_mapper in legacy_shared is gone; callers now use
the sf_infrastructure version (which was always the superset — it adds
ApiException and the dart:io socket handling).
DeviceEntity note: legacy_device_state keeps its own DeviceEntity (with
int timestamps and typed paymentOptions) separate from sf_shared's
DeviceEntity (String timestamps, untyped paymentOptions). The legacy
one is intentionally not exported from the barrel to avoid the
ambiguous_import collision that legacy_shared quietly hid by never
exporting it in the first place. Unifying the two is a domain-model
refactor out of scope here.
0 cross-module imports remain among legacy feature modules.
Spanish is the app default (SFLocalizations.testInit uses 'es',
localeResolutionCallback falls back to the first supported locale), so
make that explicit by pointing the code generator at es.json instead of
en.json. Regenerating picked up 12 activity-meter keys that were already
present in every locale file but had drifted out of I18n.
Add scripts/check_i18n_parity.dart: treats es.json as the template and
reports any missing or orphan keys in en/fr/de/it/pt. Exits non-zero so
it can gate CI or a pre-commit hook later.
Replace the ThemePort/ThemeCode abstraction (GetIt-registered adapter)
with a Riverpod-driven Material 3 ColorScheme, an SfColors ThemeExtension
for brand tokens, and a user-facing appearance selector for light/dark/
system modes. Persisted via SharedPreferences, reacts to system
brightness changes. Payments mode keeps the existing ThemePort API.
Highlights
- New legacy_theme package: LegacyAppTheme (light/dark), LegacyColorSchemes,
SfColors ThemeExtension, LegacyThemePreferences, LegacyThemeNotifier,
LegacyThemeSelector. Timeframe-based variants scaffolded but disabled.
- New /legacy/dashboard/control_panel/settings/appearance route + screen.
- MaterialApp.router picks the legacy theme only when isLegacyMode.
- ~90 ThemeCode.* usages migrated to colorScheme.* / context.sfColors.*.
- 25 widgets dropped the 'ThemePort theme' constructor param.
- ~145 hardcoded colors migrated (exact hex 1:1, grey.shade tiers,
destructive red -> colorScheme.error, background whites -> surface).
Content-over-color whites, transparents, and brand semantic reds/
oranges/greens intentionally preserved.
- sf_localizations updated with appearance / appearanceDescription keys
in all six locales.
These files are regenerated by flutter pub get and contain machine-
specific paths (FLUTTER_ROOT, FLUTTER_APPLICATION_PATH). They should
not be tracked — the file header itself states "do not check into
version control".
- Update .gitignore: prepend **/ to .flutter-plugins-dependencies so
it matches in sub-packages, add Generated.xcconfig and
flutter_export_environment.sh patterns.
- git rm --cached the 4 files that were already tracked (keeps them
on disk for local builds but removes from repo).
Prevent position polling timer from firing with frequency < 60s (protects
against 0s values that saturate the API). Also filter out 0 from location
capability options in the frequency selector.