Frontend usability and aesthetics pass across web and mobile:
- Add shared layout tokens (theme/dimens.dart: Insets + Breakpoints) and
replace magic-number spacing and per-file breakpoint consts.
- Route both themes through buildAppTheme (theme/theme_builder.dart): explicit
useMaterial3 and a shared branded textTheme (Barlow Condensed for
display/headline/title styles); logo now reads its style from the theme.
- Move screen titles into the shared AppBar (route-derived) and drop the
redundant in-body headers; upgrade Settings buttons to M3 FilledButton.
- Add reusable EmptyState and skeleton loaders; Devices empty state links to
scanner status, notifications get filter-aware messages.
- Add a first-run welcome intro and Save-disabled helper text in Settings.
- Make device Filter/Sort adaptive: bottom sheet on phones, dialog on wide.
- Collapse the device-list filter chips and Sort/Filter buttons into one row;
rename the home Scanners card title and align its style.
Tests: add EmptyState/skeleton widget tests and Settings first-run cases;
update tests for the FilledButton swap and relocated titles. 89 passing,
dart analyze clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Defense-in-depth: the persisted value is the (XOR-obfuscated) API key,
so a future SharedPreferences backend that surfaced the value in its
exception could leak it through the UI. Log the detail via debugPrint
and show a fixed message to the user instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap main in runZonedGuarded and install FlutterError.onError /
PlatformDispatcher.onError so background async failures get logged
instead of dying silently. Guard PrefUtil.init so a SharedPreferences
platform error no longer aborts startup. Recognize TypeError and
FormatException in dioErrorToUserMessage to give a clearer message
when the backend returns an unexpected JSON shape or bad timestamp.
Wrap Settings._save in try/catch so prefs write failures show a
snackbar instead of leaving the Save button silently dead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PrefUtil.setValue now returns the underlying SharedPreferences result
and throws on unsupported types; settings save reports failures rather
than always showing success. Device detail and event history report
the real Dio error message and skip cancellations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Settings._save wrote new base_url/api_key to prefs but the BackendAPI
singleton kept the Dio it built at first access, so URL/key changes only
took effect after an app restart. Expose reconfigureFromPrefs() on
BackendAPI and call it from Settings._save so subsequent requests pick up
the new config immediately.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Replace single NavigationRail with adaptive navigation: NavigationBar
(bottom) for compact <600dp, icon-only NavigationRail for medium
600–840dp, and extended NavigationRail for expanded ≥840dp
- Remove nested Scaffold/AppBar from About, Settings, DeviceList, and
DeviceDetail — all screens now render as plain widgets inside the
single shell Scaffold; page titles surface as inline headings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract inline widget closures into named StatelessWidgets (_DeviceCard,
_DeviceFilterSheet, _NotificationCard), replace magic ints and hardcoded
repeated widgets with enum-driven loops, fix the InputDecorator/DropdownButton
hack in device_actions, merge symmetric _markAsRead/_markAsNew into _setRead,
and remove dead _isLoading/_isSaving state in settings where operations are
synchronous.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a dropdown in the settings page to switch between available themes
(Catppuccin Mocha, Gruvbox Dark). The selected theme is applied immediately
on save and persisted across restarts via SharedPreferences.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>