- Add optional parameter to ToggleTile (default false)
- Add optional parameter to GeneratedForm (default false)
- GeneratedFormModal now passes to its inner GeneratedForm
- Set on direct AlertDialog + GeneratedForm in import_export.dart
- Reduce non-tile mode row spacing from 24 to 8
- Improved update check UI performance (#3043)
- Improved app list loading UI (#2666)
- Other minor UI tweaks
- Improve install result detection for external installers in foreground
A. Tile wrapper: Replace inline Material in tileMode form loop with
SettingsTile — eliminates duplicate shape/clip/color wiring.
B. Haptic helper: Extract hapticSwitchOnChanged() shared between
_FormSwitchRow and SettingsToggleRow.
C. Tile joining: Use shapeSettingsTiles() in tileMode form loop instead
of inline isFirst/isLast/positionalTileShape logic.
D. ConnectedCard -> SettingsTile delegation: ConnectedCard now delegates
to SettingsTile internally, unifying the tile rendering path (switches
from standard rounded corners to RoundedSuperellipseBorder).
Dependency clean-up:
- Move showHelpDialog from ui_widgets to settings_widgets to break
circular import (ui_widgets now imports settings_widgets for D).
- Remove unused theme.dart and ui_widgets imports from form renderer.
- SettingsToggleRow: contentPadding horizontal 8 -> 12, matching default
SettingsTile padding so toggle labels align with other settings text
- ConnectedCard: default padding EdgeInsets.all(16) ->
EdgeInsets.symmetric(horizontal: 16, vertical: 20) for more breathing room
around text fields and buttons inside cards
- SettingsToggleRow: remove full-row tap ink, unify both paths to
non-tappable ListTile + Switch in trailing. Matches _FormSwitchRow
in generated forms — only the Switch itself is interactive.
- ActionListTile: add optional borderRadius param for ListTile.shape
to match container card corners for proper ink clipping.
- LinkText: add InkWell borderRadius (circular 4) to contain splash.
- Replace raw Material in add_app source note card with ConnectedCard.
- Replace raw Card in ExportSection with ConnectedCard.
- Add ListTile.shape (connectedTileBigRadius) to color picker and
external installer tiles for matching ink clipping.
- Standardize SettingsTile paddings: remove horizontal 8px overrides
on install method, external installer, group-by, and manual BG check
tiles — use default horizontal 12px. Theme/sort order keep 16px.
- GeneratedForm labels now always call tr() at render time (7 locations)
preventing stale/raw translation keys when the item cache is built
before locale initialization or persists across locale changes.
- Remove unused SwitchListTile import (now unused in SettingsToggleRow).
- Use an extended (icon + label) FAB for the primary Add action in the
bottom layout; keep a compact FAB in the navigation rail so it fits.
- Add tactile feedback (respecting the existing setting) to interactions
that lacked it: download cancel buttons, Add/more/view FABs, list-tile
update and swipe install/remove, settings segmented buttons, and all
settings toggles (centralized in SettingsToggleRow).
- Drop manual strokeWidth overrides so inline spinners inherit the themed
Material 3 look.
Add a cancel button for in-progress downloads (in the app list tile, the
detail page, and the download notification) and surface download sizes:
the expected size before starting (best-effort probe) and live
received/total progress in the UI and notification.
- Route notification Cancel taps via the FLN background isolate to the main
isolate (IsolateNameServer/SendPort); gated off for background-isolate
downloads whose cancel token isn't reachable.
- Share per-app download state (progress + bytes) across AppInMemory copies
so UI listeners keep updating after saveApps replaces the map entry.
- Treat cancellations silently and keep the .part file for later resume.
* architecture: consolidate and simplify
- Collapse 14 directories into 4 (app_sources, components, pages, providers)
- Replace DI container with inline construction in main.dart
- Fold page controllers back into their pages (one file per screen)
- Merge small files into their natural homes (removing 40 files)
- Standardize error handling across all 28 app sources (rethrowOrWrapError)
- Make App immutable with copyWith() replacing deepCopy()
- Replace globalNavigatorKey with go_router
- Add error boundaries, source health monitor, typed settings
- Eliminate all silent catch blocks
- Add tests and CI pipeline
- Extend lint rules
97 files → 56 files. 0 errors, 4/4 tests passing.
* fix: remove unused go_router, restore internal navigation
- Drop go_router (ShellRoute broke tab switching and disabled the
tablet/TV two-pane layout); revert to MaterialApp + navigatorKey
- Move appNavigatorKey into main.dart; delete router.dart
- Remove HomePage.child branch so the internal switcher drives the UI
- Fix dead 'Restart' button in the error fallback screen
- Remove TypedSettings []= mutation backdoor
- Persist clamped preferredApkIndex to the in-memory app map
- Use settings.getBool('trackOnly') instead of raw map access
* fix: remove dead rethrowOrWrapError, add missing import, update stale comments and docs
* fix: satisfy lint rules, wrap fire-and-forget futures, drop unused CI
- Fix use_build_context_synchronously via mounted/context.mounted guards
- Wrap deliberate fire-and-forget futures in unawaited()
- Document static app-lifetime event bus for close_sinks
- Apply dart format and dart fix across the codebase
- Drop avoid_catches_without_on_clauses (noisy in source scrapers)
- Remove test.yml CI workflow
- Rename test/services to test/providers
* fix: show real HTTP error messages, remove source health monitor and dead event-bus code
- Bypass deferred localization for HTTP_ERROR so the real reason phrase/status is shown instead of a generic 'unexpected error'
- Remove SourceHealthMonitor entirely (per-instance state made it ineffective for background checks)
- Simplify save-notification bus to StreamController<void>; drop unused AppRepositoryEvent/AppRepositoryEventType and events getter
- Scrub stale docs (SourceHealthMonitor, event type, deleted test.yml workflow)
* fix: preserve installedVersion on import, correct stale theme doc reference
* fix: type logger as Logger?, simplify ErrorWidget builder, document _sentinel, resolve TextDirection conflict
- Change dynamic logger to Logger? in AppsProvider for type safety (the
field was unused but the contract should be explicit).
- Replace MaterialApp wrapper in ErrorWidget.builder with a minimal
Directionality + Scaffold; MaterialApp is wasteful for a one-shot
error screen.
- Add a doc comment explaining the _sentinel pattern used by
App.copyWith to distinguish unset from explicitly-null nullable fields.
- Hide TextDirection from easy_localization (which re-exports intl's
conflicting TextDirection enum that lacks ltr/rtl) so the Flutter
dart:ui TextDirection.ltr resolves correctly.
* fix: register SourceForge, restore Palestine banner, drop dead code and tests
- Register the implemented SourceForge source in _buildSources() so the
README's supported-sources list is accurate.
- Restore the Support Palestine banner in README.md (unintentionally removed).
- Remove unused ConfigProvider, CredentialHealth, getCredentialHealth, and
the never-injected _configProvider path from settings_provider.
- Remove unit tests and their test-only dev dependencies.
App sources:
- Harden sources against malformed/changed responses (null, parse, index
and empty-version guards): rustore, tencent, apkmirror, gitlab, aptoide,
fdroid(repo), sourcehut, huaweiappgallery, itchio, izzyondroid,
telegramapp, uptodown, vivoappstore, etc.
- APKCombo: fix and enable (issue #341) - unwrap "/r2?u=" links to the
signed Cloudflare R2 URL, drop the Host header that 403'd the R2
download, take one build per variant, and parse the release date;
register the source.
- Remove orphaned Mullvad source (never registered).
- F-Droid version-regex filter selects the newest match; fix Jenkins
changelog link; GitLab reuses one GitHub helper instance; GitHubStars
reuses one GitHub instance.
UI / behaviour:
- Fix download progress bar never updating on the app detail page and the
apps list (provider select on an in-place-mutated AppInMemory never
fired a rebuild).
- Fix single-category gradient crash, dead "installing" tile state, silent
swipe-install errors, deep-link dedup, mounted/context guards in
import/export and add-app, and ExportSection future caching.
- generated_form: GeneratedFormSwitch.disabled is now applied; clone()
preserves disabled and autoCompleteOptions.
Performance:
- Cache SettingsProvider.categories (was rebuilding a Map + re-parsing JSON
on every access, over-rebuilding category selectors).
- Memoize the flattened combined form items used by the per-app JSON
migration at load.
Tooling / config:
- build.sh: set -euo pipefail; make the --build-id=none workaround
idempotent and glob-safe.
- pubspec: branch-track git dependencies.
- Remove unused config_keys.dart (consolidate on raw setting keys).
- Fix GitHub rate limit time calculation (was ignoring current time)
- Fix HTTP client leaks on redirect exhaustion and early download returns
- Drain StreamedResponse before closing client in checkETagHeader
- Fix null assertion crash in checkUpdate when app ID not found
- Remove duplicate null check in getObtainiumHttpError
- Replace empty catch blocks with typed exceptions and comments
- Fix potential null dereference in AppFilePicker and CategorySelector
- Fix category sort comparator transitivity violation for null values
- Call LogsProvider.close() on app dispose (was never called)
- Replace print() with debugPrint() for consistency
- Replace sync I/O with async variants (readAsString, writeAsString, rename, list, stat)
- Convert installedAppsData list scan to O(1) map lookup in loadApps
- Pre-group apps by category to avoid O(n*m) per-category scans
- Use Set for alreadyAddedUrls to enable O(1) contains lookups
- Add sourceType cache to AppInMemory to avoid re-detecting source on filter
- Use context.select instead of context.watch to reduce unnecessary rebuilds
- Extract interval slider to dedicated widget to avoid full settings page rebuilds
- Extract _FormSwitchRow to prevent switch rebuilds on text field changes
- Add cacheWidth/cacheHeight to AppIcon image decoding
- Batch URL import with concurrency limit of 4 instead of sequential
- Guard auto-export timer callback with _disposed check
- Defer _lastBackgroundSave clear until loadApps completes
- Simplify findExistingUpdates complex nested logic
- Refactor 7 app sources to use standardizeUrlWithRegex helper
- Replace 25+ magic numbers with named constants
- Clean up late keyword on immutable fields in App and APKDetails
- apps_provider_install: stream-decompress gzip tarballs >64MB to temp file
instead of loading into memory; async readAsBytes for APK sharing
- html.dart: null-guard res.request.url fallback; cap intermediate link
depth at 10
- github.dart: null-check regex matches before .start/.end assertions
- home.dart: use context.select for isTV to avoid full-shell rebuilds on
every settings change
- app_list_tile.dart: replace MediaQuery.sizeOf with LayoutBuilder;
guard category gradient division-by-zero for single-category apps
- add_app.dart: cache FutureBuilder future so it does not re-fetch on
every rebuild
- import_export.dart: add didUpdateWidget to SelectionModal to reset
stale entrySelections when parent rebuilds with new entries
- generated_form.dart: dispose old TextEditingControllers before re-init
- logs_provider.dart: make db connection static (shared across instances);
add close() method
- fdroid.dart: guard changelog substring against UTF-16 surrogate splits
- ui_widgets.dart: await Clipboard.setData; change showConfirmDialog
content from Object? to Widget?; use consistent TextButton type in
HighlightableButton; add semanticLabel to EmptyState icon
- app.dart: await _getUpdate in RefreshIndicator; show loading spinner
in WebView instead of empty Container
- analysis_options.yaml: enable stricter lint rules
- Settings: show a loading spinner instead of a blank screen while prefs load
- APK-origin warning dialog: add a "don't show again" checkbox (sets
hideAPKOriginWarning) so the choice isn't buried in settings
- Accessibility: announce LinkText as a link; exclude decorative AppIcon images
from semantics; mark category color swatches as selectable buttons with their
selected state; expose the category long-press-to-edit via a tooltip and a
semantic long-press action
- Settings: extract the ~85-line update-interval slider tile into
_buildIntervalSliderTile to shrink the build() method
- Extract the god build() methods in the apps/app/add_app pages into focused, named helper methods
- Memoize AppSource instances and SettingsProvider platform calls; drop a duplicate source-config lookup
- Batch loadApps notifications, replace the busy-wait with a Completer lock, and debounce auto-export
- Add a compatVersion stamp so already-migrated apps skip one-time legacy migrations
- Only rebuild on integer download-progress changes; harden list/detail cache signatures with Object.hashAll
- Fix dead select-all branches, add mounted guards, and clone source-owned form items to avoid mutation leaks
- Expose swipe install/remove and icon long-press to screen readers; add a list progress bar and URL copy tooltip
- Move flutter_lints to dev_dependencies and apply dart format across the tree
Extract the repeated about/help AlertDialog into showHelpDialog(context, title, content) and rewire the generated_form field-help and settings toggle-help buttons to it.
Extract a reusable showConfirmDialog(context, title, content, ...) -> Future<bool> and rewire the mass-mark and mark-updated yes/no dialogs to it. Behavior-preserving: the dialog is identical and the action runs on confirm.
Replaces ~11 hand-rolled Material(positionalTileShape/squircle) cards across the app detail page (sections + repo-rename) and apps page (category segments, update banner, filter dialog) with a single ConnectedCard widget; both pages no longer depend on ui_shapes directly.
- Move AppFilePicker/APKOriginWarningDialog out of apps_provider into components/app_dialogs.dart
- Add reusable EmptyState, LinkText, and copyToClipboard to ui_widgets; rewire apps/home/app pages
- SourceProvider.getSource: build the source list once per call instead of up to 3x