Skip to content

0.1.1#83

Merged
Erengun merged 15 commits into
DevOpen-io:mainfrom
Erengun:main
Jul 15, 2026
Merged

0.1.1#83
Erengun merged 15 commits into
DevOpen-io:mainfrom
Erengun:main

Conversation

@Erengun

@Erengun Erengun commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added home-screen widgets for monthly spend, next due items, and upcoming subscriptions.
    • Added live activity support on iPhone for showing subscription dues on the lock screen and Dynamic Island.
    • Added richer analytics views, including switching between amount and share charts.
    • Added yearly/monthly views, sorting, and drag reordering on the home screen.
    • Expanded subscription details and editing with reminders, trial end, status, and payment method info.
  • Bug Fixes

    • Improved date handling, widget refreshes, and startup behavior.
    • Updated translations and visual polish across multiple screens.

Erengun added 13 commits June 5, 2026 17:32
…droid Glance widgets

- iOS SubsWidget extension: MonthlySpend, NextDue, Upcoming widgets (all sizes)
- iOS Live Activity / Dynamic Island via ActivityKit + MethodChannel
- Android Glance widgets: MonthlySpend, NextDue, Upcoming with receivers
- Flutter WidgetUpdateService wires SubsController + SettingsController → home_widget
- LiveActivityService starts activity for subs due today on app launch
- Debug tile in Settings to force-start Live Activity for testing
- Fix SubsDataReader key: flutter_subs_data → subs_data
- Add ReminderMode (none/onDay/dayBefore/both) to SubSlice, gating notification scheduling
- Add cardLastFour field with an input sheet on the detail screen
- Add sort sheet (name/amount asc/desc) and drag-to-reorder on the home list
- Add monthly/yearly toggle with a liquid-glass segmented control
- Add SubStatus (active/freeTrial/paused/cancelled), note, and trialEndDate to SubSlice
- Add status picker widget and badge; filter paused/cancelled subs out of
  home totals, analytics, and home-screen widget data via activeOnly
- Schedule trial-end reminders instead of recurring ones when status is freeTrial
- Refactor SubsBar/SubsPie to take slices/currency as params instead of watching providers directly
- Use locale-aware DateFormat instead of hardcoded "MMM d" / "EEE" patterns
- Add CLAUDE.md lint guidelines (no shrinkWrap, no inline .map() in Column, etc.)
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Erengun, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 23 minutes and 19 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 64402ef8-e0d8-40c4-98fc-76f1592cb593

📥 Commits

Reviewing files that changed from the base of the PR and between 6b9289a and 6a64790.

📒 Files selected for processing (15)
  • CLAUDE.md
  • analysis_options.yaml
  • android/app/src/main/AndroidManifest.xml
  • android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt
  • ios/LiveActivityChannel.swift
  • ios/Runner/AppDelegate.swift
  • ios/SubsWidget/MonthlySpendWidget.swift
  • ios/SubsWidget/SubsDueWidget.swift
  • ios/SubsWidget/SubsWidgetBundle.swift
  • ios/SubsWidget/UpcomingWidget.swift
  • lib/screens/app_startup.dart
  • lib/screens/home_screen.dart
  • lib/screens/sub_detail_screen.dart
  • lib/services/live_activity_service.dart
  • lib/widgets/edit_subs_dialog.dart
📝 Walkthrough

Walkthrough

This PR updates subscription data models and serialization, adds widget/live-activity services and platform integrations, rewires analytics/home/detail/settings screens, and refreshes translations, lint config, and tests across Flutter, Android, and iOS.

Changes

Subscription Experience Refresh

Layer / File(s) Summary
Project setup and linting
.metadata, CLAUDE.md, all_lint_rules.yaml, analysis_options.yaml, analysis_options_without_plugins.yaml, packages/simple_icons/analysis_options.yaml, pubspec.yaml
Project metadata, lint rules, analyzer settings, and package versions are updated together.
Shared models
lib/models/..., test/models/sub_slice_test.dart
SettingsViewModel and SubSlice gain the reminder/status/trial schema, generated JSON/copy helpers, and model tests for defaults and filtering.
State, services, and startup
lib/providers/..., lib/services/..., lib/utils/..., lib/main.dart, lib/screens/app_startup.dart, test/{providers/subs_controller_test.dart,services/widget_update_service_test.dart,widget_test.dart}
Keep-alive providers, notification/update helpers, subscription/widget/live-activity services, app startup, and related tests are updated to persist state and trigger refreshes.
Dashboard and shell UI
lib/layout/root_layout.dart, lib/screens/{analytics_screen.dart,home_screen.dart,calendar_screen.dart}, lib/widgets/{bar_chart.dart,pie_chart.dart,sub_zilla_app_bar.dart,glass_nav_bar.dart,bottom_controls.dart,menu_bar.dart,brand_logo.dart}, assets/translations/{en.json,tr.json}, test/{screens/calendar_screen_test.dart,utils/date_locale_test.dart}, .impeccable/critique/...
Analytics, home, calendar, and shell widgets change together, with chart mode switching, yearly totals, shell navigation, translation updates, tests, and the analytics critique report.
Subscription editing flow
lib/screens/sub_detail_screen.dart, lib/widgets/{add_subs_dialog.dart,edit_subs_dialog.dart,edit_user_profile.dart,action_text_form_field.dart,status_picker.dart}, assets/translations/{en.json,tr.json}, test/widgets/add_subs_dialog_test.dart
Subscription detail, add/edit dialogs, status/reminder pickers, and form helpers gain reminder, status, note, card-last-four, and trial-end fields with matching labels and tests.
Settings and onboarding flow
lib/screens/{settings_screen.dart,profile_page.dart,user_name_page.dart,popular_subs_page.dart,onboarding_screen.dart,intro_page.dart,color_scheme_page.dart}, lib/widgets/{floating_sub_card.dart,color_scheme_picker.dart}, assets/translations/{en.json,tr.json}, test/widgets/{color_scheme_settings_test.dart,intro_page_test.dart}
Settings, profile, onboarding, intro, and color-scheme pages switch to relative imports and update layout, localization, and live-activity action wiring.
Android widgets
android/app/...
Compose/Glance/WorkManager setup, widget receivers/provider XMLs, and the Kotlin data, date, widget, and refresh code are added for Android home widgets.
iOS live activity and widgets
ios/LiveActivity/..., ios/Runner/..., ios/Runner.xcodeproj/..., ios/SubsWidget/..., ios/SubsWidgetExtension.entitlements
The iOS live activity bridge, Runner target wiring, widget extension code, assets, and entitlements are added for live activities and WidgetKit widgets.

Sequence Diagram(s)

Widget refresh flow

sequenceDiagram
  participant SubsController
  participant WidgetUpdateService
  participant HomeWidget
  participant MonthlySpendWidget
  participant NextDueWidget
  participant UpcomingWidget

  SubsController->>WidgetUpdateService: update(subs, currencySymbol)
  WidgetUpdateService->>HomeWidget: saveWidgetData("subs_data")
  WidgetUpdateService->>HomeWidget: updateWidget("MonthlySpendWidget")
  WidgetUpdateService->>HomeWidget: updateWidget("NextDueWidget")
  WidgetUpdateService->>HomeWidget: updateWidget("UpcomingWidget")
  HomeWidget-->>MonthlySpendWidget: refresh
  HomeWidget-->>NextDueWidget: refresh
  HomeWidget-->>UpcomingWidget: refresh
Loading

Live activity start flow

sequenceDiagram
  participant AppStartup
  participant SettingsController
  participant SubsController
  participant LiveActivityService
  participant LiveActivityChannel
  participant ActivityKit

  AppStartup->>SettingsController: resolve settings
  AppStartup->>SubsController: load subscriptions
  AppStartup->>LiveActivityService: startIfDueToday(subs, currencySymbol)
  LiveActivityService->>LiveActivityChannel: invokeMethod("startActivity")
  LiveActivityChannel->>ActivityKit: request Activity<SubsDueAttributes>
  ActivityKit-->>LiveActivityChannel: currentActivity
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 I hopped through models, widgets, and code so bright,
With charts that hop in circles and live activities in flight.
I tucked reminders in a burrow neat and small,
Then polished tabs and widgets so they answer every call.
Hop-hop—SubZilla shines tonight!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is only a version number, so it doesn’t clearly describe the main change in the pull request. Use a concise descriptive title, e.g. mention the app version bump and key feature additions.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Nitpick comments (14)
android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt (1)

140-161: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Widget copy is hardcoded English; not localized for the tr locale.

Strings like "Add a subscription", "No subscriptions", "THIS MONTH", "NEXT DUE", "UPCOMING", "Today", and the "subscription"/"subscriptions" pluralization are inlined. Since the app ships an tr locale, these home-screen widgets stay English for Turkish users. Consider sourcing them from Android string resources (selected via the user's locale) so the widget matches the in-app language.

As per coding guidelines: supported locales are en and tr.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt`
around lines 140 - 161, The widget copy in SubsGlanceWidget is hardcoded in
English, so Turkish users won’t see localized text; move all visible labels and
pluralized subscription text to Android string resources and read them from the
widget using the current locale. Update the strings used in SubsGlanceWidget’s
EmptyState and main content (including “THIS MONTH”, “NEXT DUE”, “UPCOMING”,
“Today”, and singular/plural “subscription(s)”) to source from resources for the
supported en and tr locales.

Source: Coding guidelines

lib/widgets/glass_nav_bar.dart (1)

75-87: 🩺 Stability & Availability | 🔵 Trivial

Update liquid_glass_renderer usage for Impeller-only support and API verification.

The liquid_glass_renderer package version 0.2.0-dev.4 relies strictly on the Impeller rendering engine and currently does not support Web, Windows, or Linux platforms. Ensure your target devices are compatible (iOS/Android with Impeller enabled) or plan to implement a fallback. Additionally, confirm that parameters glassColor, ambientStrength, and saturation are still valid in this specific pre-release build, as parameter names and validation logic frequently shift between milestones.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/widgets/glass_nav_bar.dart` around lines 75 - 87, The
LiquidGlassLayer/LiquidGlass setup here must be verified against
liquid_glass_renderer 0.2.0-dev.4, which is Impeller-only and not supported on
Web, Windows, or Linux. Update the GlassNavBar implementation to either guard
this widget behind an iOS/Android Impeller-only check or provide a fallback path
for unsupported platforms, and confirm the LiquidGlassSettings fields used in
LiquidGlassLayer—especially glassColor, ambientStrength, and saturation—still
match the current pre-release API.
lib/screens/settings_screen.dart (1)

511-531: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: add a const constructor to _TestLiveActivityTile.

Other tiles are instantiated with const; this one (line 115) is not, and the class lacks a const constructor. Debug-only, so the hardcoded strings are acceptable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/screens/settings_screen.dart` around lines 511 - 531, The
_TestLiveActivityTile widget should match the other const-instantiated tiles by
adding a const constructor to the _TestLiveActivityTile class and updating its
usage so it can be created with const. Locate the StatelessWidget named
_TestLiveActivityTile in settings_screen.dart and make it constructible at
compile time without changing its existing hardcoded debug strings or onTap
behavior.
lib/screens/sub_detail_screen.dart (1)

476-521: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Dispose the local TextEditingControllers.

_showCardInput and _showNoteInput allocate a TextEditingController that is never disposed when the sheet closes, leaking the controller (and its listeners) each time the sheet opens. Wrap the body in a StatefulWidget/hook or dispose in the sheet's lifecycle.

Also applies to: 523-563

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/screens/sub_detail_screen.dart` around lines 476 - 521, The local
TextEditingController created in _showCardInput is never disposed, so the bottom
sheet leaks controller resources each time it opens. Update _showCardInput (and
the matching _showNoteInput referenced in the comment) to manage the controller
lifecycle by using a StatefulWidget/hook or otherwise disposing it when the
sheet is dismissed, while keeping the existing submit/save behavior tied to the
controller and onSelected callback.
CLAUDE.md (1)

18-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate directive. dont use git commands. appears on both Line 18 and Line 20. Drop one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` around lines 18 - 20, The CLAUDE.md guidance contains a duplicated
“dont use git commands.” directive, so remove one of the repeated entries and
keep only a single instance in the instruction list. Update the surrounding
bullet text in the document so the guidance remains clean and non-redundant,
preserving the unique “UI/UX Excellence” directive and the single git
restriction.
lib/main.dart (1)

39-39: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

TODO: merge light/dark theme providers. Tracked inline only.

Want me to open an issue to track consolidating lightThemeProvider/darkThemeProvider into a single provider?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/main.dart` at line 39, The theme setup still uses separate
lightThemeProvider and darkThemeProvider providers; consolidate them into one
shared provider in main.dart that returns both themes together as a single value
(tuple/class) and update any consumers to read from that combined source. Keep
the existing theme-building logic in the new unified provider so the app still
exposes both light and dark themes through one entry point.
lib/services/live_activity_service.dart (1)

46-54: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Unbounded day-by-day loop for old daily subscriptions.

For Frequency.daily the loop advances one day at a time from startDate, so a daily sub started years ago iterates hundreds/thousands of times on every startup call. Bounded but wasteful on the startup path; consider normalizing daily/weekly directly to the first candidate ≥ today via arithmetic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/services/live_activity_service.dart` around lines 46 - 54, The
_isDueToday method in LiveActivityService uses an unbounded day-by-day loop to
reach today, which is too expensive for old daily subscriptions on startup.
Update the candidate normalization logic in _isDueToday (and, if needed,
_advance) to compute the first occurrence on or after today directly with date
arithmetic for Frequency.daily and Frequency.weekly instead of iterating one day
at a time. Keep the behavior equivalent for determining whether the normalized
candidate matches todayDate.
lib/screens/app_startup.dart (1)

24-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fire-and-forget async in initState.

UpdateService.instance.checkAndUpdate() returns a Future that is discarded; wrap with unawaited(...) (already imported via dart:async) to make the intent explicit and satisfy lint rules.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/screens/app_startup.dart` around lines 24 - 28, The initState method in
AppStartup is calling UpdateService.instance.checkAndUpdate() without handling
its returned Future, so make the fire-and-forget intent explicit by wrapping
that call with unawaited(...) in initState; use the existing dart:async import
and keep the change limited to the AppStartup lifecycle method.
lib/providers/subs_controller.dart (3)

59-59: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

scheduleNotification() is fire-and-forget without error handling.

In build() it is awaited, but in addSlice/removeAt/updateAt/reorderSlices/sortSlices/clear the returned Future is discarded bare. If scheduling throws (plugin/permission failure), it surfaces as an unhandled async error rather than being contained, unlike the adjacent widget update which is wrapped in unawaited(...) and already swallows errors internally. Wrap consistently (and ideally guard the call) to avoid unobserved failures.

♻️ Suggested change
-    scheduleNotification();
+    unawaited(scheduleNotification());

Applies to the other mutation methods (removeAt, updateAt, reorderSlices, sortSlices, clear) as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/providers/subs_controller.dart` at line 59, The fire-and-forget call to
scheduleNotification() in SubsController mutations is not safely handled, so
async failures can surface as unhandled errors. Update the mutation methods that
call scheduleNotification() (addSlice, removeAt, updateAt, reorderSlices,
sortSlices, clear) to treat it consistently with the build() path by awaiting or
wrapping it in unawaited with proper internal error containment, and ensure the
scheduling call is guarded so failures do not escape.

52-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated currency-read + widget-update block.

The identical three-line pattern (read currency from settings, unawaited(WidgetUpdateService.instance.update(...))) is duplicated across six methods. Consolidate into a private helper to reduce drift risk.

♻️ Proposed helper
+  void _refreshWidgets() {
+    final currency =
+        ref.read(settingsControllerProvider).value?.currency.symbol ?? '';
+    unawaited(WidgetUpdateService.instance.update(state.value ?? [], currency));
+  }

Then replace each duplicated block with _refreshWidgets();.

Also applies to: 60-61, 195-196, 202-203, 213-214, 221-222

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/providers/subs_controller.dart` around lines 52 - 53, The currency-read
plus widget update sequence is duplicated in SubsController across multiple
methods, so extract it into a private helper to avoid drift. Create a helper in
SubsController that reads the currency from settings and calls
WidgetUpdateService.instance.update with the current state list, then replace
each repeated block in the affected methods with a single _refreshWidgets()
call.

62-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Review-prompt cadence may exceed store throttling.

Requesting an in-app review on every third subscription (count % 3 == 0 → 3, 6, 9, …) fires repeatedly. Both App Store and Play throttle prompts (iOS caps at ~3/year and silently drops extras), so triggering this often is wasted at best and can feel spammy at the boundary. Consider gating on a one-time milestone or persisting a "review requested" flag instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/providers/subs_controller.dart` around lines 62 - 67, The review prompt
logic in subs_controller.dart is firing on every multiple of three subscriptions
via the count check, which can repeatedly trigger
InAppReview.instance.requestReview(). Update the subscription milestone logic so
SubsController only requests a review once per intended milestone, or persist a
“review requested” flag and check it before calling
InAppReview.instance.isAvailable/requestReview, using the existing state.value
count path to locate the change.
ios/SubsWidget/NextDueDateHelper.swift (1)

8-36: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Logic is correct; consider guarding the force unwraps.

The next-due computation is sound (future startDate returns as-is; past advances to the next occurrence with month-end clamping handled). The forced unwraps on dateComponents(...).day!/.weekOfYear!/.month!/.year! and cal.date(byAdding:)! are safe for the Gregorian calendar but will crash if a component request returns nil. Optional fallbacks (?? 0 / early return) would harden this widget path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ios/SubsWidget/NextDueDateHelper.swift` around lines 8 - 36, The next-due
calculation in NextDueDateHelper.nextDueDate is fine, but the forced unwraps on
Calendar.dateComponents(...).day/weekOfYear/month/year and
Calendar.date(byAdding:) can crash if a component is unavailable. Update the
logic to use safe optional handling or early returns around those Calendar
calls, keeping the existing daily/weekly/monthly/yearly behavior and
month-end/year-end clamping intact.
ios/SubsWidget/SubsWidgetBundle.swift (1)

17-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the leftover // add this scaffolding comment.

Proposed tweak
-        SubsDueWidgetLiveActivity()  // add this
+        SubsDueWidgetLiveActivity()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ios/SubsWidget/SubsWidgetBundle.swift` at line 17, Remove the leftover
scaffolding comment from SubsWidgetBundle so the SubsDueWidgetLiveActivity()
entry is kept clean and only the actual widget declaration remains. Update the
SubsWidgetBundle.swift snippet to eliminate the inline “// add this” text and
leave the SubsDueWidgetLiveActivity() call unchanged.
ios/SubsWidget/WidgetBackground.swift (1)

9-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Double padding on the pre-iOS 17 path.

Callers (e.g. upcomingList Line 63-64) already apply .padding() before .widgetBackground(); the additional .padding() here compounds it only on iOS < 16/17, producing inconsistent insets vs. the containerBackground branch. Consider dropping the inner .padding() so spacing is owned by the caller.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ios/SubsWidget/WidgetBackground.swift` around lines 9 - 12, The pre-iOS 17
fallback in WidgetBackground is applying extra padding on top of padding already
added by callers like upcomingList, causing inconsistent spacing compared with
the containerBackground branch. Update WidgetBackground so the fallback path
only sets the background and does not add its own padding, leaving spacing to
the caller and keeping both branches aligned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@analysis_options.yaml`:
- Around line 12-14: The dependency pin for riverpod_lint is still on the
prerelease channel, so update the version in analysis_options.yaml from the dev
build to the stable release. Change the existing riverpod_lint entry under
plugins to use ^3.1.4, keeping the surrounding plugin configuration intact.

In `@android/app/src/main/AndroidManifest.xml`:
- Line 43: The widget receiver declarations are currently exported, which allows
other apps to send APPWIDGET_UPDATE broadcasts to them. Update the receiver
entries in AndroidManifest.xml for the widget components to be non-exported, or
protect them with an explicit permission, and make sure the same fix is applied
to each affected receiver declaration. Locate the relevant receiver tags by
their android:exported setting and adjust the manifest so only the system can
invoke them.

In `@android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt`:
- Around line 114-115: The amount formatting in SubsGlanceWidget uses
String.format without an explicit Locale, so the decimal separator can change
with the device locale; update all four formatting sites to pass a pinned Locale
explicitly. Fix the Text render paths in SubsGlanceWidget by choosing a
consistent locale (for example Locale.US) in each String.format("%.2f", ...)
call so the currency amount displays uniformly regardless of device language.

In `@ios/LiveActivityChannel.swift`:
- Around line 34-41: startActivity(subsJson:) in LiveActivityChannel is too
permissive and can crash or silently fail; update it to safely compute the
staleDate without any force unwrap, explicitly guard against starting when
currentActivity is already set, and replace the try? Activity.request flow with
explicit error handling so failures can be reported back to Flutter. If you need
to replace an existing activity, do it in a coordinated way with endActivity()
before requesting a new one, and avoid overwriting currentActivity until the old
activity has been cleaned up.

In `@ios/Runner.xcodeproj/project.pbxproj`:
- Around line 16-18: Remove the redundant PBXBuildFile entries for
SubsWidgetExtension that are already covered by fileSystemSynchronizedGroups in
the Xcode project. In project.pbxproj, delete the orphaned build file references
associated with SubsDueAttributes.swift, SubsDueWidget.swift, ColorHelper.swift,
and the other duplicate entry mentioned in the review, and ensure the
SubsWidgetExtension target relies only on the synchronized group configuration.

In `@ios/Runner/AppDelegate.swift`:
- Around line 8-18: `AppDelegate.swift` is missing the
`UNUserNotificationCenterDelegate` setup needed by
`flutter_local_notifications`, so foreground notifications and tap callbacks
won’t be handled. Update `AppDelegate` to conform to
`UNUserNotificationCenterDelegate`, assign
`UNUserNotificationCenter.current().delegate = self` inside
`application(_:didFinishLaunchingWithOptions:)` after
`GeneratedPluginRegistrant.register(with:)`, and add the
`userNotificationCenter(_:willPresent:withCompletionHandler:)` handler on
`AppDelegate` so notifications are presented while the app is in the foreground.

In `@ios/SubsWidget/MonthlySpendWidget.swift`:
- Line 8: The small widget’s empty-state logic in MonthlySpendSmallView is
missing the same non-empty subscription check used by MonthlySpendMediumView, so
it can render a zero-state summary instead of emptyState. Update the
MonthlySpendSmallView condition around entry.widgetData to also require that
data.subs is not empty, matching the guard used in MonthlySpendMediumView so
both views handle empty data consistently.

In `@ios/SubsWidget/SubsDueWidget.swift`:
- Line 43: The ForEach identity in SubsDueWidget is using \.name, which can
collide when multiple subscriptions share the same name and cause unstable row
rendering; update the ForEach in SubsDueWidget (including the other matching
ForEach mentioned in the review) to use a stable unique identifier such as an
existing id field, or key by Array.enumerated() offset if no unique model id
exists, so each row has a consistent identity.

In `@ios/SubsWidget/SubsTimelineProvider.swift`:
- Around line 27-36: The parseDate helper currently uses a brittle DateFormatter
fallback with a fixed millisecond pattern, which is unnecessary for the ISO 8601
strings being produced upstream. Simplify SubsTimelineProvider.parseDate to rely
only on ISO8601DateFormatter configured with .withInternetDateTime and
.withFractionalSeconds, and remove the DateFormatter branch entirely so all date
parsing is handled consistently with timezone support.

In `@ios/SubsWidget/UpcomingWidget.swift`:
- Line 35: The UpcomingWidget ForEach is using sub.name as the identity, which
can merge duplicate subscription names and collapse rows. Update the ForEach in
UpcomingWidget.swift to use a stable unique key for each item, or switch to
iterating Array(items.enumerated()) and key by the offset since the order is
fixed. Make sure the identity used in ForEach uniquely distinguishes each
subscription row.

In `@lib/screens/app_startup.dart`:
- Around line 34-47: The live activity startup guard in the settings provider
branch is being flipped too early in app_startup.dart, which can block startup
if subscriptions are not yet loaded. Update the logic around
_liveActivityStarted, ref.read(subsControllerProvider), and the whenData
callback so the flag is set only inside the whenData callback after subs is
available and LiveActivityService.instance.startIfDueToday is actually
scheduled. Keep the post-frame callback and settings load flow the same, but
ensure the one-time guard is applied only once both settings and subscription
data are ready.

In `@lib/screens/home_screen.dart`:
- Line 136: The row key in the reorderable list is using the item index, which
makes identities change after reordering and can mix up row state. Update the
key in the home screen row builder to use a stable identifier from the slice
itself in the widget that currently uses ValueKey('${slice.name}-$index'), so
each row keeps the same identity regardless of position changes.
- Line 431: The sorting in home_screen is using raw amount in
notifier.sortSlices, but the screen’s ranking/totals are based on monthlyAmount,
so mixed-frequency slices can be ordered incorrectly. Update the sort logic in
the relevant home screen sorting paths (including the notifier.sortSlices calls
used for the amount ordering) to compare normalized monthlyAmount instead of
amount, and keep the existing comparator structure around the affected slice
ranking code.

In `@lib/screens/settings_screen.dart`:
- Around line 98-101: The version shown in the settings/about item is hardcoded
to an outdated value, so update the SettingsScreen about entry to source the app
version dynamically instead of using a literal. Use the existing settings item
in settings_screen.dart and replace the fixed subtitle in that widget with a
value from package_info_plus or another pubspec-derived version source so it
stays in sync with the app release.

In `@lib/screens/sub_detail_screen.dart`:
- Around line 453-474: The trial-end picker in _showTrialEndPicker currently
saves on any dismissal because onSelected(selected) is invoked from the modal
future, so cancel/tap-outside also commits a value. Update this flow to be
confirm-driven like the other pickers in sub_detail_screen.dart: add an explicit
Done/Cancel action (or another confirmation mechanism) and only call onSelected
from the confirm action, keeping dismissal from writing DateTime.now() when
current is null.

In `@lib/services/live_activity_service.dart`:
- Around line 62-66: The monthly branch in `_advance` is drifting because it
clamps against the current `date.day` instead of the original subscription
anchor day. Update `_advance` in `live_activity_service.dart` so the monthly
case re-clamps from the original `startDate.day` (or an equivalent preserved
anchor) on every cycle, matching `SubsController._clampDay` behavior and keeping
dates like the 31st from getting stuck on shorter months. Ensure the fix
preserves the existing `Frequency.monthly` flow and uses the same day-of-month
source consistently across notifications and live activity calculations.

In `@lib/widgets/edit_subs_dialog.dart`:
- Line 340: The frequency label lookup in the widget that renders subscription
frequencies uses the inconsistent frequency_names namespace; update the
translation key in edit_subs_dialog.dart’s frequency display logic to match the
same frequency pattern used by add_subs_dialog.dart and sub_detail_screen.dart,
using the lowercased enum name via f.name so the app shows consistent
sentence-case labels.

In `@lib/widgets/sub_zilla_app_bar.dart`:
- Around line 17-25: The AppBar leading logic in SubZillaAppBar is tied to
rootScaffoldKey.currentState, which can be null during the first build and hide
the drawer button even when a drawer exists. Update the leading condition to
depend on a stable drawer-availability signal instead of currentState timing,
and keep the IconButton/openDrawer behavior in the SubZillaAppBar widget so the
menu icon still renders when the Scaffold state is not yet ready.

---

Nitpick comments:
In `@android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt`:
- Around line 140-161: The widget copy in SubsGlanceWidget is hardcoded in
English, so Turkish users won’t see localized text; move all visible labels and
pluralized subscription text to Android string resources and read them from the
widget using the current locale. Update the strings used in SubsGlanceWidget’s
EmptyState and main content (including “THIS MONTH”, “NEXT DUE”, “UPCOMING”,
“Today”, and singular/plural “subscription(s)”) to source from resources for the
supported en and tr locales.

In `@CLAUDE.md`:
- Around line 18-20: The CLAUDE.md guidance contains a duplicated “dont use git
commands.” directive, so remove one of the repeated entries and keep only a
single instance in the instruction list. Update the surrounding bullet text in
the document so the guidance remains clean and non-redundant, preserving the
unique “UI/UX Excellence” directive and the single git restriction.

In `@ios/SubsWidget/NextDueDateHelper.swift`:
- Around line 8-36: The next-due calculation in NextDueDateHelper.nextDueDate is
fine, but the forced unwraps on
Calendar.dateComponents(...).day/weekOfYear/month/year and
Calendar.date(byAdding:) can crash if a component is unavailable. Update the
logic to use safe optional handling or early returns around those Calendar
calls, keeping the existing daily/weekly/monthly/yearly behavior and
month-end/year-end clamping intact.

In `@ios/SubsWidget/SubsWidgetBundle.swift`:
- Line 17: Remove the leftover scaffolding comment from SubsWidgetBundle so the
SubsDueWidgetLiveActivity() entry is kept clean and only the actual widget
declaration remains. Update the SubsWidgetBundle.swift snippet to eliminate the
inline “// add this” text and leave the SubsDueWidgetLiveActivity() call
unchanged.

In `@ios/SubsWidget/WidgetBackground.swift`:
- Around line 9-12: The pre-iOS 17 fallback in WidgetBackground is applying
extra padding on top of padding already added by callers like upcomingList,
causing inconsistent spacing compared with the containerBackground branch.
Update WidgetBackground so the fallback path only sets the background and does
not add its own padding, leaving spacing to the caller and keeping both branches
aligned.

In `@lib/main.dart`:
- Line 39: The theme setup still uses separate lightThemeProvider and
darkThemeProvider providers; consolidate them into one shared provider in
main.dart that returns both themes together as a single value (tuple/class) and
update any consumers to read from that combined source. Keep the existing
theme-building logic in the new unified provider so the app still exposes both
light and dark themes through one entry point.

In `@lib/providers/subs_controller.dart`:
- Line 59: The fire-and-forget call to scheduleNotification() in SubsController
mutations is not safely handled, so async failures can surface as unhandled
errors. Update the mutation methods that call scheduleNotification() (addSlice,
removeAt, updateAt, reorderSlices, sortSlices, clear) to treat it consistently
with the build() path by awaiting or wrapping it in unawaited with proper
internal error containment, and ensure the scheduling call is guarded so
failures do not escape.
- Around line 52-53: The currency-read plus widget update sequence is duplicated
in SubsController across multiple methods, so extract it into a private helper
to avoid drift. Create a helper in SubsController that reads the currency from
settings and calls WidgetUpdateService.instance.update with the current state
list, then replace each repeated block in the affected methods with a single
_refreshWidgets() call.
- Around line 62-67: The review prompt logic in subs_controller.dart is firing
on every multiple of three subscriptions via the count check, which can
repeatedly trigger InAppReview.instance.requestReview(). Update the subscription
milestone logic so SubsController only requests a review once per intended
milestone, or persist a “review requested” flag and check it before calling
InAppReview.instance.isAvailable/requestReview, using the existing state.value
count path to locate the change.

In `@lib/screens/app_startup.dart`:
- Around line 24-28: The initState method in AppStartup is calling
UpdateService.instance.checkAndUpdate() without handling its returned Future, so
make the fire-and-forget intent explicit by wrapping that call with
unawaited(...) in initState; use the existing dart:async import and keep the
change limited to the AppStartup lifecycle method.

In `@lib/screens/settings_screen.dart`:
- Around line 511-531: The _TestLiveActivityTile widget should match the other
const-instantiated tiles by adding a const constructor to the
_TestLiveActivityTile class and updating its usage so it can be created with
const. Locate the StatelessWidget named _TestLiveActivityTile in
settings_screen.dart and make it constructible at compile time without changing
its existing hardcoded debug strings or onTap behavior.

In `@lib/screens/sub_detail_screen.dart`:
- Around line 476-521: The local TextEditingController created in _showCardInput
is never disposed, so the bottom sheet leaks controller resources each time it
opens. Update _showCardInput (and the matching _showNoteInput referenced in the
comment) to manage the controller lifecycle by using a StatefulWidget/hook or
otherwise disposing it when the sheet is dismissed, while keeping the existing
submit/save behavior tied to the controller and onSelected callback.

In `@lib/services/live_activity_service.dart`:
- Around line 46-54: The _isDueToday method in LiveActivityService uses an
unbounded day-by-day loop to reach today, which is too expensive for old daily
subscriptions on startup. Update the candidate normalization logic in
_isDueToday (and, if needed, _advance) to compute the first occurrence on or
after today directly with date arithmetic for Frequency.daily and
Frequency.weekly instead of iterating one day at a time. Keep the behavior
equivalent for determining whether the normalized candidate matches todayDate.

In `@lib/widgets/glass_nav_bar.dart`:
- Around line 75-87: The LiquidGlassLayer/LiquidGlass setup here must be
verified against liquid_glass_renderer 0.2.0-dev.4, which is Impeller-only and
not supported on Web, Windows, or Linux. Update the GlassNavBar implementation
to either guard this widget behind an iOS/Android Impeller-only check or provide
a fallback path for unsupported platforms, and confirm the LiquidGlassSettings
fields used in LiquidGlassLayer—especially glassColor, ambientStrength, and
saturation—still match the current pre-release API.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 914be5d3-c86c-4b75-b0b0-bb37a06e7ab1

📥 Commits

Reviewing files that changed from the base of the PR and between 0a325fb and 6b9289a.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (106)
  • .impeccable/critique/2026-06-07T13-10-40Z__lib-screens-analytics-screen-dart.md
  • .metadata
  • CLAUDE.md
  • all_lint_rules.yaml
  • analysis_options.yaml
  • analysis_options_without_plugins.yaml
  • android/app/build.gradle.kts
  • android/app/proguard-rules.pro
  • android/app/src/main/AndroidManifest.xml
  • android/app/src/main/kotlin/io/devopen/subzilla/widget/NextDueDateHelper.kt
  • android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsDataReader.kt
  • android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt
  • android/app/src/main/kotlin/io/devopen/subzilla/widget/WidgetRefreshWorker.kt
  • android/app/src/main/res/xml/monthly_spend_widget_info.xml
  • android/app/src/main/res/xml/next_due_widget_info.xml
  • android/app/src/main/res/xml/upcoming_widget_info.xml
  • assets/translations/en.json
  • assets/translations/tr.json
  • ios/LiveActivity/ColorHelper.swift
  • ios/LiveActivity/SubsDueAttributes.swift
  • ios/LiveActivity/SubsDueWidget.swift
  • ios/LiveActivityChannel.swift
  • ios/Runner.xcodeproj/project.pbxproj
  • ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  • ios/Runner/AppDelegate.swift
  • ios/Runner/Info.plist
  • ios/Runner/Runner.entitlements
  • ios/Runner/SceneDelegate.swift
  • ios/SubsWidget/Assets.xcassets/AccentColor.colorset/Contents.json
  • ios/SubsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/SubsWidget/Assets.xcassets/Contents.json
  • ios/SubsWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json
  • ios/SubsWidget/ColorHelper.swift
  • ios/SubsWidget/Info.plist
  • ios/SubsWidget/MonthlySpendWidget.swift
  • ios/SubsWidget/NextDueDateHelper.swift
  • ios/SubsWidget/NextDueWidget.swift
  • ios/SubsWidget/SubsDataReader.swift
  • ios/SubsWidget/SubsDueAttributes.swift
  • ios/SubsWidget/SubsDueWidget.swift
  • ios/SubsWidget/SubsEntry.swift
  • ios/SubsWidget/SubsModels.swift
  • ios/SubsWidget/SubsTimelineProvider.swift
  • ios/SubsWidget/SubsWidgetBundle.swift
  • ios/SubsWidget/UpcomingWidget.swift
  • ios/SubsWidget/WidgetBackground.swift
  • ios/SubsWidgetExtension.entitlements
  • lib/config/fade_extension.dart
  • lib/config/router_config.dart
  • lib/config/router_config.g.dart
  • lib/layout/root_layout.dart
  • lib/main.dart
  • lib/models/settings_view_model.dart
  • lib/models/sub_slice.dart
  • lib/models/sub_slice.freezed.dart
  • lib/models/sub_slice.g.dart
  • lib/providers/brands_provider.dart
  • lib/providers/brands_provider.g.dart
  • lib/providers/settings_controller.dart
  • lib/providers/settings_controller.g.dart
  • lib/providers/subs_controller.dart
  • lib/providers/subs_controller.g.dart
  • lib/providers/theme_provider.dart
  • lib/screens/analytics_screen.dart
  • lib/screens/app_startup.dart
  • lib/screens/calendar_screen.dart
  • lib/screens/color_scheme_page.dart
  • lib/screens/home_screen.dart
  • lib/screens/intro_page.dart
  • lib/screens/onboarding_screen.dart
  • lib/screens/popular_subs_page.dart
  • lib/screens/profile_page.dart
  • lib/screens/settings_screen.dart
  • lib/screens/sub_detail_screen.dart
  • lib/screens/user_name_page.dart
  • lib/services/live_activity_service.dart
  • lib/services/widget_update_service.dart
  • lib/utils/brand_utils.dart
  • lib/utils/color_utils.dart
  • lib/utils/notification_service.dart
  • lib/utils/update_service.dart
  • lib/widgets/action_text_form_field.dart
  • lib/widgets/add_subs_dialog.dart
  • lib/widgets/bar_chart.dart
  • lib/widgets/bottom_controls.dart
  • lib/widgets/brand_logo.dart
  • lib/widgets/color_scheme_picker.dart
  • lib/widgets/edit_subs_dialog.dart
  • lib/widgets/edit_user_profile.dart
  • lib/widgets/floating_sub_card.dart
  • lib/widgets/glass_nav_bar.dart
  • lib/widgets/menu_bar.dart
  • lib/widgets/pie_chart.dart
  • lib/widgets/status_picker.dart
  • lib/widgets/sub_zilla_app_bar.dart
  • packages/simple_icons/analysis_options.yaml
  • pubspec.yaml
  • test/models/sub_slice_test.dart
  • test/providers/subs_controller_test.dart
  • test/screens/calendar_screen_test.dart
  • test/services/widget_update_service_test.dart
  • test/utils/date_locale_test.dart
  • test/widget_test.dart
  • test/widgets/add_subs_dialog_test.dart
  • test/widgets/color_scheme_settings_test.dart
  • test/widgets/intro_page_test.dart
💤 Files with no reviewable changes (2)
  • packages/simple_icons/analysis_options.yaml
  • lib/widgets/floating_sub_card.dart

Comment thread analysis_options.yaml Outdated
Comment thread android/app/src/main/AndroidManifest.xml Outdated
Comment thread android/app/src/main/kotlin/io/devopen/subzilla/widget/SubsGlanceWidget.kt Outdated
Comment thread ios/LiveActivityChannel.swift
Comment on lines +16 to +18
1265D5522FD6B5EA003DE5DE /* SubsDueAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1265D54C2FD6B5EA003DE5DE /* SubsDueAttributes.swift */; };
1265D5532FD6B5EA003DE5DE /* SubsDueWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1265D54D2FD6B5EA003DE5DE /* SubsDueWidget.swift */; };
1265D5542FD6B5EA003DE5DE /* ColorHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1265D54B2FD6B5EA003DE5DE /* ColorHelper.swift */; };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm widget-extension Sources phase membership and locate duplicate Swift sources.
fd -t f 'SubsDueWidget.swift|SubsDueAttributes.swift|ColorHelper.swift|LiveActivityChannel.swift' ios
echo '--- pbxproj Sources phases ---'
rg -n 'in Sources' ios/Runner.xcodeproj/project.pbxproj

Repository: DevOpen-io/SubZilla

Length of output: 2847


Remove redundant manual BuildFile entries for SubsWidgetExtension.

The SubsWidgetExtension target utilizes fileSystemSynchronizedGroups to automatically include source files from the SubsWidget directory. The static PBXBuildFile entries at lines 16–18 and 20 (1265D552, 1265D553, 1265D554, 1265D557) effectively duplicate the definitions already resolved by the synchronization mechanism. Delete these orphaned entries to clean up the project configuration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ios/Runner.xcodeproj/project.pbxproj` around lines 16 - 18, Remove the
redundant PBXBuildFile entries for SubsWidgetExtension that are already covered
by fileSystemSynchronizedGroups in the Xcode project. In project.pbxproj, delete
the orphaned build file references associated with SubsDueAttributes.swift,
SubsDueWidget.swift, ColorHelper.swift, and the other duplicate entry mentioned
in the review, and ensure the SubsWidgetExtension target relies only on the
synchronized group configuration.

Comment on lines +98 to 101
title: 'settings.version'.tr(),
subtitle: '1.0.0',
onTap: null,
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcoded version 1.0.0 is stale.

This PR targets 0.1.1, but the About section shows 1.0.0. Consider sourcing it from package_info_plus/pubspec instead of a literal to avoid drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/screens/settings_screen.dart` around lines 98 - 101, The version shown in
the settings/about item is hardcoded to an outdated value, so update the
SettingsScreen about entry to source the app version dynamically instead of
using a literal. Use the existing settings item in settings_screen.dart and
replace the fixed subtitle in that widget with a value from package_info_plus or
another pubspec-derived version source so it stays in sync with the app release.

Comment thread lib/screens/sub_detail_screen.dart
Comment thread lib/services/live_activity_service.dart Outdated
Comment thread lib/widgets/edit_subs_dialog.dart Outdated
Comment on lines +17 to +25
leading: rootScaffoldKey.currentState?.hasDrawer ?? false
? IconButton(
icon: Icon(CupertinoIcons.bars),
icon: const Icon(CupertinoIcons.bars),
onPressed: () {
rootScaffoldKey.currentState?.openDrawer();
},
)
: null,
middle: Text("settings.app_name".tr()),
middle: Text('settings.app_name'.tr()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Drawer leading can disappear due to nullable currentState timing.

Line 17 gates leading on rootScaffoldKey.currentState, which can be null during initial build. That can leave the menu button missing even when a drawer exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/widgets/sub_zilla_app_bar.dart` around lines 17 - 25, The AppBar leading
logic in SubZillaAppBar is tied to rootScaffoldKey.currentState, which can be
null during the first build and hide the drawer button even when a drawer
exists. Update the leading condition to depend on a stable drawer-availability
signal instead of currentState timing, and keep the IconButton/openDrawer
behavior in the SubZillaAppBar widget so the menu icon still renders when the
Scaffold state is not yet ready.

Erengun added 2 commits June 25, 2026 18:50
- Android: protect widget receivers with BIND_APPWIDGET permission; pin Locale.US in String.format calls
- iOS: remove force-unwrap in LiveActivityChannel staleDate; guard against double-starting activity; add UNUserNotificationCenterDelegate for foreground notifications; fix MonthlySpendSmallView empty-state guard; use enumerated ForEach ids in SubsDueWidget and UpcomingWidget; remove stale comment from SubsWidgetBundle
- Dart: fix monthly drift in live_activity_service by anchoring to startDate.day; sort slices by monthlyAmount not raw amount; use stable ValueKey(slice.name) for reorderable list; trial-end picker now requires explicit Save to commit; dispose TextEditingControllers in card/note inputs; fix frequency label namespace in edit_subs_dialog (ALLCAPS → sentence-case); mark fire-and-forget calls with unawaited; bump riverpod_lint to stable ^3.1.4
@Erengun
Erengun merged commit 8b64cfa into DevOpen-io:main Jul 15, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant