Skip to content

fix(settings): reset shared controllers without notifying during a route build - #162

Merged
anilcancakir merged 4 commits into
mainfrom
fix/settings-subpage-notify-during-build
Sep 24, 2026
Merged

anilcancakir merged 4 commits into
mainfrom
fix/settings-subpage-notify-during-build

Conversation

@anilcancakir

Copy link
Copy Markdown
Member

What

  • resetQuietly() on MagicStarterProfileController and MagicStarterTeamController: clears errors and returns to empty without notifying (setState(notify: false)).
  • The eight views sharing the profile controller call it in onInit, and the two that load sessions on mount do so under withoutNotifying.
  • Team create resets its controller the same way.
  • withoutNotifying's suppression is a count instead of a flag, so overlapping actions no longer un-suppress each other.

Why

onInit runs while the new route is being built. The settings hub and every sub-page bind one controller and the hub stays mounted under a stacked sub-page, so a notifying reset (and getSessions()'s synchronous setLoading()) marked the hub dirty mid-build: "setState() or markNeedsBuild() called during build" on every opening. Team create over team settings has the same shape. Closes #161.

Testing

  • New group in settings_hub_view_test.dart: each of 8 sub-pages pushed over the hub with stale errors set and the sessions feature on. All red before, green after; reverting one view or the sessions wrapper turns only its own case red.
  • magic_starter_team_create_view_test.dart: create pushed over settings; red before, green after.
  • Profile controller unit tests: overlapping withoutNotifying stays quiet (red with the old flag), resetQuietly clears without notifying.
  • flutter analyze clean, 1550 tests green.

Not in this PR

Team settings pushed over team create (after a successful create) still notifies through loadMembersAndInvitations()'s synchronous setLoading(). Fixing it needs a choice between a quiet-start option on the method and a framework-level answer in magic, so it is tracked separately.

The hub and every settings sub-page bind MagicStarterProfileController,
and the hub stays mounted under a stacked sub-page. Each sub-page reset
the controller in onInit with clearErrors() and setEmpty(), which
notify, while its route was being built, so the hub was marked dirty
mid-build and every opening reported "setState() or markNeedsBuild()
called during build".

The eight views sharing the controller now call resetQuietly(), which
clears and empties it with notifications suppressed. Nothing needed the
notification: the resetting view reads the state in its own first build
and the hub renders nothing from it.

Closes #161
…pressions

Review follow-up. The sessions pages load on mount, and getSessions()
sets loading before its first await, so opening Sessions still marked
the hub dirty mid-build; the test missed it because the sessions
feature defaults to off. Both session loads now run under
withoutNotifying, and the test turns the feature on.

Team create had the same shape over team settings, which opens it
through the team selector, and now resets its controller quietly too.

withoutNotifying cleared a flag, so the first of two overlapping
actions to finish un-suppressed the other. It is a count now, and
resetQuietly no longer touches it: it uses setState(notify: false).
@kodizm

kodizm Bot commented Sep 23, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

The approach is sound and matches the described fix, but CI is red on this commit, so it isn't ready to merge until that is explained.

Major

CI, Lint & Test: the check failed. The annotation only says "Process completed with exit code 1", so I can't tell whether it was analyze, format or a test. The PR description claims analyze is clean and 1550 tests are green, which contradicts this. CLAUDE.md says analyzer infos are fatal in CI, so a new lint is a possible cause. Please check the job log before merging.

Minor

lib/src/ui/views/settings/security/magic_starter_sessions_view.dart:86 (correctness): withoutNotifying(controller.getSessions) suppresses every notification from the shared controller until the whole request finishes. That includes the final success or error state. If another view bound to that controller is mounted, such as the hub under the sub-page, it never sees that state. Overlapping suppression is now handled by the counter. The intended trade-off is only that this view drives its own spinner, so it is worth confirming that nothing else depends on the session-load result.

lib/src/ui/views/profile/magic_starter_profile_settings_view.dart:349: _loadSessions calls setState after the await without a mounted check. The sessions view has that check and this one doesn't. This was already the case, but the PR now edits this exact line.

Tests

New widget tests cover each of the 8 sub-pages over the hub and team create over settings. Controller unit tests cover the overlapping withoutNotifying calls and resetQuietly. I did not read the test files, and I can't confirm they pass while CI is failing.

CI

  • Lint & Test: failure (https://github.com/fluttersdk/magic_starter/actions/runs/35930926907/job/107417033643)
  • Published graph: success
  • Auto-merge low-risk Dependabot PRs: skipped

@codecov

codecov Bot commented Sep 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@kodizm

kodizm Bot commented Sep 23, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

CI is now green; the earlier blocker is resolved.

Since my last review, the only change is a pure dartfmt reformat of test/http/controllers/magic_starter_profile_controller_test.dart (line-wrapping the withoutNotifying overlap test), no logic change. That reformat is what fixed the previously failing Lint & Test check.

The two minor points from my earlier review still apply and haven't been addressed or discussed since:

Minor

lib/src/ui/views/settings/security/magic_starter_sessions_view.dart:86 (correctness): withoutNotifying(controller.getSessions) suppresses every notification from the shared controller until the whole request finishes, including the final success/error state, so another view bound to the same controller (e.g. the hub under this sub-page) never sees it. This appears intentional per the PR description (the view drives its own spinner), but worth a final confirmation nothing else relies on the session-load result reaching listeners.

lib/src/ui/views/profile/magic_starter_profile_settings_view.dart:349: _loadSessions calls setState after the await without a mounted check, unlike the sessions view which has one. Pre-existing, but this PR touches this exact line.

Tests

Unchanged from last review: new widget tests cover the 8 sub-pages over the hub and team create over settings; controller unit tests cover overlapping withoutNotifying and resetQuietly. This round only reformatted one of those test files.

CI

  • Lint & Test: success
  • Published graph: success
  • codecov/patch: success
  • Auto-merge low-risk Dependabot PRs: skipped (not applicable)

@anilcancakir

Copy link
Copy Markdown
Member Author

Both minors addressed in 555793b.

  • Suppressed session-load notifications: checked who else listens. Nothing in the package binds MagicStarterProfileController besides the settings views, and the one that stays mounted under a sub-page, the hub, renders nothing from the controller (its only reference is the import). No view navigates from one sub-page to another, so no second sub-page is listening either. The loading view drives its own spinner and reads the returned list directly, so nothing depends on the final state reaching listeners.
  • Missing mounted check in magic_starter_profile_settings_view.dart: added, matching the sessions view.

@kodizm review

@kodizm

kodizm Bot commented Sep 23, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Both prior minors are resolved; nothing outstanding.

Verified directly: magic_starter_profile_settings_view.dart now has a mounted check before the post-await setState at line 350, matching the sessions view. The session-notification-suppression point was a design confirmation rather than a bug, and the explanation (no other view listens to the controller's load result while a sub-page is stacked over the hub, and the hub renders nothing from it) holds up given the hub only imports the controller without binding to it in the widget tree.

No new commits since the last round; this is the same 555793b head already reviewed.

Tests

Unchanged: widget tests cover the 8 sub-pages over the hub and team create over settings; controller unit tests cover overlapping withoutNotifying and resetQuietly.

CI

  • Lint & Test: success
  • Published graph: success
  • codecov/patch: success
  • Auto-merge low-risk Dependabot PRs: skipped (not applicable)

@anilcancakir
anilcancakir merged commit 4a8a82e into main Sep 24, 2026
5 checks passed
@anilcancakir
anilcancakir deleted the fix/settings-subpage-notify-during-build branch September 24, 2026 08:49
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.

Opening a settings sub-page calls setState during build on the settings hub

1 participant