feat: DI - Added first-class Dependency Injection support to FlutterInit with a new wizard option and generated code wiring.#21
Conversation
|
@noeljabraham is attempting to deploy a commit to the arjun544's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 46 minutes and 24 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughIntroduces GetIt dependency injection support to the Flutter scaffold generator. Adds Changes
Sequence DiagramsequenceDiagram
participant App as App Startup
participant Config as AppConfig
participant DI as initDependencies()
participant SL as ServiceLocator
participant Auth as AuthService
participant Repo as AuthRepository
App->>Config: await AppConfig.init()
Config-->>App: ✓ Config ready
alt usesGetItDi enabled
App->>DI: await initDependencies()
DI->>SL: GetIt.instance
SL->>Auth: Register AuthService singleton
Auth-->>SL: ✓ Registered
SL->>Repo: Register AuthRepository singleton<br/>(with AuthService dependency)
Repo-->>SL: ✓ Registered
DI-->>App: ✓ DI initialized
end
App->>App: runApp(MyApp)
App->>Repo: createAuthRepository()
alt usesGetItDi
Repo->>SL: sl<AuthRepository>()
SL-->>Repo: Return singleton instance
else
Repo->>Repo: AuthRepositoryImpl(authService: AuthService.instance)
end
Repo-->>App: AuthRepository ready
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
templates/flutter/base/lib/main.dart.hbs (1)
26-26: Coupling note: unconditional call requires the function to always exist.
await initDependencies()is invoked unconditionally, which assumesservice_locator.dart.hbsis always generated and always exposesinitDependencies()(with the body gated byflags.usesGetItDi). That matches the current template, but ifservice_locator.dart.hbsis ever made conditional onflags.usesGetItDi, this call site will break the build for thenonepath. Consider either keeping the template unconditional (current design) or gating this call with{{#ifflags.usesGetItDi}}to make the coupling explicit.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@templates/flutter/base/lib/main.dart.hbs` at line 26, The unconditional call to initDependencies() couples this template to service_locator.dart.hbs; guard the invocation with the feature flag by wrapping the call in the template conditional {{`#if` flags.usesGetItDi}}...{{/if}} so the call to initDependencies() only exists when service_locator.dart.hbs (and its exported initDependencies function) is generated; reference initDependencies() and service_locator.dart.hbs when making the change.templates/flutter/base/lib/src/di/service_locator.dart.hbs (1)
10-22: Minor: emptyinitDependencies()when DI is disabled.When
flags.usesGetItDiis false the body collapses to an emptyasyncfunction thatmain.dartstill awaits. It's harmless, but you could either:
- only emit
initDependencies()(and itsawaitcall inmain.dart) when DI is enabled, or- keep it unconditional as an intentional future-proof hook and add a one-line comment noting that.
Also consider registering
AuthServiceby constructing the type directly (e.g.,() => AuthService()) rather than() => AuthService.instance, otherwise you're just wrapping an existing singleton in GetIt and bothAuthService.instanceandsl<AuthService>()remain live references — not incorrect, just redundant.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@templates/flutter/base/lib/src/di/service_locator.dart.hbs` around lines 10 - 22, The generated initDependencies() becomes an empty async function when flags.usesGetItDi is false; either stop emitting initDependencies() (and its await in main.dart) when flags.usesGetItDi is false, or keep it always emitted but add a one-line comment inside to clarify it is an intentional no-op hook; additionally, when registering AuthService in the block guarded by flags.usesGetItDi prefer registering a new instance (use () => AuthService()) instead of wrapping AuthService.instance to avoid keeping two live singleton references—update the registration lines for AuthService and keep the AuthRepository registration using AuthRepositoryImpl(authService: sl<AuthService>()) as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/components/wizard/steps/MiscStep.tsx`:
- Around line 249-279: The info <button> is nested inside the <label>, which is
invalid and breaks a11y; refactor the map so each option is a wrapper <div> (or
other non-interactive container) that contains a <label> scoped only to the
RadioGroupItem and its text (use dependencyInjectionOptions and
RadioGroupItem/option.label/option.description), and place the info button
(HugeiconsIcon with InformationCircleIcon) as a sibling element outside the
<label>; keep the existing onClick handler that calls setSelectedItem but ensure
the button still calls e.preventDefault()/e.stopPropagation() so it doesn't
toggle the radio, and preserve the original conditional className logic that
uses config.dependencyInjection to maintain the visual selected state; move the
key={option.value} to the outer wrapper so list keys remain stable.
In `@templates/flutter/base/pubspec.yaml.hbs`:
- Around line 28-33: The get_it dependency is incorrectly listed under the
"State Management" section and uses an outdated constraint; when
flags.usesGetItDi is true, remove the get_it: ^8.0.3 entry from under the State
Management block and instead add a new commented section "# Dependency
Injection" (or similar) where you include get_it with an updated constraint (at
least ^9.0.0, e.g., ^9.0.0 or ^9.2.1); update the Handlebars template around the
conditional (flags.usesGetItDi) so the get_it line is emitted under that new
section rather than next to fpdart/equatable.
In `@templates/flutter/partials/features/auth/auth_logic.hbs`:
- Around line 7-9: The top comment is incorrect: it claims the provider returns
a single instance of AuthRepositoryImpl, but authRepositoryProvider actually
returns createAuthRepository() which may resolve via GetIt and not necessarily
AuthRepositoryImpl; update the comment above authRepositoryProvider to remove
the guarantee of AuthRepositoryImpl and instead state that the provider exposes
an AuthRepository resolved by createAuthRepository() (which may use GetIt).
In `@templates/flutter/partials/features/auth/forgot_password_screen.hbs`:
- Around line 46-47: The template currently sets authStore to null when
flags.usesFlutterHooks is false which causes a null dereference on
authStore.isLoading; change the conditional so you emit two distinct
declarations: when flags.usesFlutterHooks is true generate a memoized store
(useMemoized(() => AuthStore(repository: createAuthRepository()), [])) and when
false generate a plain instance (AuthStore(repository: createAuthRepository()));
ensure the variable name authStore remains the same so subsequent references
like authStore.isLoading compile.
In `@templates/flutter/partials/features/auth/login_screen.hbs`:
- Around line 51-52: The template currently renders authStore as null when
flags.usesFlutterHooks is false because the inline comment breaks the
expression; change the conditional so authStore is always initialized: if
flags.usesFlutterHooks render useMemoized(() => AuthStore(repository:
createAuthRepository()), []), else render AuthStore(repository:
createAuthRepository()); remove the inline comment that caused the null and
ensure usages like isLoading and handleLogin (which reference authStore) work
with the non-null AuthStore instance.
In `@templates/flutter/partials/features/auth/signup_screen.hbs`:
- Around line 56-57: The template currently emits invalid Dart when MobX is
enabled but Flutter Hooks are disabled by assigning authStore to null; instead,
change the template logic around authStore so that when flags.usesFlutterHooks
is true you keep the existing useMemoized invocation, and when false you
instantiate AuthStore directly (AuthStore(repository: createAuthRepository()));
ensure subsequent usages like authStore.isLoading and other authStore references
work with the instantiated AuthStore; update the conditional in
signup_screen.hbs (and mirror the same change in login_screen.hbs and
forgot_password_screen.hbs) to branch between useMemoized(...) and new
AuthStore(...) rather than null.
---
Nitpick comments:
In `@templates/flutter/base/lib/main.dart.hbs`:
- Line 26: The unconditional call to initDependencies() couples this template to
service_locator.dart.hbs; guard the invocation with the feature flag by wrapping
the call in the template conditional {{`#if` flags.usesGetItDi}}...{{/if}} so the
call to initDependencies() only exists when service_locator.dart.hbs (and its
exported initDependencies function) is generated; reference initDependencies()
and service_locator.dart.hbs when making the change.
In `@templates/flutter/base/lib/src/di/service_locator.dart.hbs`:
- Around line 10-22: The generated initDependencies() becomes an empty async
function when flags.usesGetItDi is false; either stop emitting
initDependencies() (and its await in main.dart) when flags.usesGetItDi is false,
or keep it always emitted but add a one-line comment inside to clarify it is an
intentional no-op hook; additionally, when registering AuthService in the block
guarded by flags.usesGetItDi prefer registering a new instance (use () =>
AuthService()) instead of wrapping AuthService.instance to avoid keeping two
live singleton references—update the registration lines for AuthService and keep
the AuthRepository registration using AuthRepositoryImpl(authService:
sl<AuthService>()) as-is.
🪄 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: 2c5a91b9-2e99-401d-9922-fef41fe8dc88
📒 Files selected for processing (19)
app/components/wizard/PackageInfoPanel.tsxapp/components/wizard/steps/GenerateStep.tsxapp/components/wizard/steps/MiscStep.tsxapp/lib/config/schema.tsapp/lib/generator/index.tsdocs/configuration.mdtemplates/flutter/base/README.md.hbstemplates/flutter/base/lib/main.dart.hbstemplates/flutter/base/lib/src/di/service_locator.dart.hbstemplates/flutter/base/lib/src/imports/core_imports.dart.hbstemplates/flutter/base/lib/src/routing/(isGetX)@app_bindings.dart.hbstemplates/flutter/base/lib/src/shared/wrappers/state_wrapper.dart.hbstemplates/flutter/base/pubspec.yaml.hbstemplates/flutter/partials/features/auth/auth_logic.hbstemplates/flutter/partials/features/auth/auth_repository_impl.hbstemplates/flutter/partials/features/auth/forgot_password_screen.hbstemplates/flutter/partials/features/auth/login_screen.hbstemplates/flutter/partials/features/auth/session_provider.hbstemplates/flutter/partials/features/auth/signup_screen.hbs
| {dependencyInjectionOptions.map((option) => ( | ||
| <label | ||
| key={option.value} | ||
| className={cn( | ||
| "flex items-center justify-between rounded-xl border p-3 cursor-pointer transition-all duration-200", | ||
| config.dependencyInjection === option.value | ||
| ? "border-primary/50 bg-primary/5 shadow-md shadow-primary/5 ring-1 ring-primary/20" | ||
| : "border-border/40 bg-card/30 hover:bg-card/50 hover:border-primary/20" | ||
| )} | ||
| > | ||
| <div className="flex items-center gap-3"> | ||
| <RadioGroupItem value={option.value} className="text-primary border-primary" /> | ||
| <div className="space-y-1"> | ||
| <span className="text-sm font-semibold text-foreground/90">{option.label}</span> | ||
| <p className="text-xs text-muted-foreground">{option.description}</p> | ||
| </div> | ||
| </div> | ||
| <button | ||
| type="button" | ||
| onClick={(e) => { | ||
| e.preventDefault() | ||
| e.stopPropagation() | ||
| setSelectedItem(option.value) | ||
| }} | ||
| className="p-1.5 rounded-full hover:bg-primary/20 text-muted-foreground hover:text-primary transition-colors focus:outline-hidden cursor-pointer" | ||
| title="View details" | ||
| > | ||
| <HugeiconsIcon icon={InformationCircleIcon} size={18} /> | ||
| </button> | ||
| </label> | ||
| ))} |
There was a problem hiding this comment.
Avoid nesting a <button> inside a <label> (invalid HTML / a11y).
The HTML spec disallows interactive descendants other than a single labelable control inside <label>. Nesting the info <button> inside the label can trigger accessibility warnings and inconsistent click behavior across browsers (some will still activate the radio even with preventDefault/stopPropagation). Prefer a sibling layout: wrap the row in a <div> and keep the <label> scoped to the radio + text only.
♻️ Proposed refactor
- {dependencyInjectionOptions.map((option) => (
- <label
- key={option.value}
- className={cn(
- "flex items-center justify-between rounded-xl border p-3 cursor-pointer transition-all duration-200",
- config.dependencyInjection === option.value
- ? "border-primary/50 bg-primary/5 shadow-md shadow-primary/5 ring-1 ring-primary/20"
- : "border-border/40 bg-card/30 hover:bg-card/50 hover:border-primary/20"
- )}
- >
- <div className="flex items-center gap-3">
- <RadioGroupItem value={option.value} className="text-primary border-primary" />
- <div className="space-y-1">
- <span className="text-sm font-semibold text-foreground/90">{option.label}</span>
- <p className="text-xs text-muted-foreground">{option.description}</p>
- </div>
- </div>
- <button
- type="button"
- onClick={(e) => {
- e.preventDefault()
- e.stopPropagation()
- setSelectedItem(option.value)
- }}
- className="p-1.5 rounded-full hover:bg-primary/20 text-muted-foreground hover:text-primary transition-colors focus:outline-hidden cursor-pointer"
- title="View details"
- >
- <HugeiconsIcon icon={InformationCircleIcon} size={18} />
- </button>
- </label>
- ))}
+ {dependencyInjectionOptions.map((option) => {
+ const id = `di-${option.value}`
+ const selected = config.dependencyInjection === option.value
+ return (
+ <div
+ key={option.value}
+ className={cn(
+ "flex items-center justify-between rounded-xl border p-3 transition-all duration-200",
+ selected
+ ? "border-primary/50 bg-primary/5 shadow-md shadow-primary/5 ring-1 ring-primary/20"
+ : "border-border/40 bg-card/30 hover:bg-card/50 hover:border-primary/20"
+ )}
+ >
+ <label htmlFor={id} className="flex items-center gap-3 cursor-pointer flex-1">
+ <RadioGroupItem id={id} value={option.value} className="text-primary border-primary" />
+ <div className="space-y-1">
+ <span className="text-sm font-semibold text-foreground/90">{option.label}</span>
+ <p className="text-xs text-muted-foreground">{option.description}</p>
+ </div>
+ </label>
+ <button
+ type="button"
+ aria-label={`View details for ${option.label}`}
+ onClick={() => setSelectedItem(option.value)}
+ className="p-1.5 rounded-full hover:bg-primary/20 text-muted-foreground hover:text-primary transition-colors focus:outline-hidden cursor-pointer"
+ title="View details"
+ >
+ <HugeiconsIcon icon={InformationCircleIcon} size={18} />
+ </button>
+ </div>
+ )
+ })}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {dependencyInjectionOptions.map((option) => ( | |
| <label | |
| key={option.value} | |
| className={cn( | |
| "flex items-center justify-between rounded-xl border p-3 cursor-pointer transition-all duration-200", | |
| config.dependencyInjection === option.value | |
| ? "border-primary/50 bg-primary/5 shadow-md shadow-primary/5 ring-1 ring-primary/20" | |
| : "border-border/40 bg-card/30 hover:bg-card/50 hover:border-primary/20" | |
| )} | |
| > | |
| <div className="flex items-center gap-3"> | |
| <RadioGroupItem value={option.value} className="text-primary border-primary" /> | |
| <div className="space-y-1"> | |
| <span className="text-sm font-semibold text-foreground/90">{option.label}</span> | |
| <p className="text-xs text-muted-foreground">{option.description}</p> | |
| </div> | |
| </div> | |
| <button | |
| type="button" | |
| onClick={(e) => { | |
| e.preventDefault() | |
| e.stopPropagation() | |
| setSelectedItem(option.value) | |
| }} | |
| className="p-1.5 rounded-full hover:bg-primary/20 text-muted-foreground hover:text-primary transition-colors focus:outline-hidden cursor-pointer" | |
| title="View details" | |
| > | |
| <HugeiconsIcon icon={InformationCircleIcon} size={18} /> | |
| </button> | |
| </label> | |
| ))} | |
| {dependencyInjectionOptions.map((option) => { | |
| const id = `di-${option.value}` | |
| const selected = config.dependencyInjection === option.value | |
| return ( | |
| <div | |
| key={option.value} | |
| className={cn( | |
| "flex items-center justify-between rounded-xl border p-3 transition-all duration-200", | |
| selected | |
| ? "border-primary/50 bg-primary/5 shadow-md shadow-primary/5 ring-1 ring-primary/20" | |
| : "border-border/40 bg-card/30 hover:bg-card/50 hover:border-primary/20" | |
| )} | |
| > | |
| <label htmlFor={id} className="flex items-center gap-3 cursor-pointer flex-1"> | |
| <RadioGroupItem id={id} value={option.value} className="text-primary border-primary" /> | |
| <div className="space-y-1"> | |
| <span className="text-sm font-semibold text-foreground/90">{option.label}</span> | |
| <p className="text-xs text-muted-foreground">{option.description}</p> | |
| </div> | |
| </label> | |
| <button | |
| type="button" | |
| aria-label={`View details for ${option.label}`} | |
| onClick={() => setSelectedItem(option.value)} | |
| className="p-1.5 rounded-full hover:bg-primary/20 text-muted-foreground hover:text-primary transition-colors focus:outline-hidden cursor-pointer" | |
| title="View details" | |
| > | |
| <HugeiconsIcon icon={InformationCircleIcon} size={18} /> | |
| </button> | |
| </div> | |
| ) | |
| })} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/components/wizard/steps/MiscStep.tsx` around lines 249 - 279, The info
<button> is nested inside the <label>, which is invalid and breaks a11y;
refactor the map so each option is a wrapper <div> (or other non-interactive
container) that contains a <label> scoped only to the RadioGroupItem and its
text (use dependencyInjectionOptions and
RadioGroupItem/option.label/option.description), and place the info button
(HugeiconsIcon with InformationCircleIcon) as a sibling element outside the
<label>; keep the existing onClick handler that calls setSelectedItem but ensure
the button still calls e.preventDefault()/e.stopPropagation() so it doesn't
toggle the radio, and preserve the original conditional className logic that
uses config.dependencyInjection to maintain the visual selected state; move the
key={option.value} to the outer wrapper so list keys remain stable.
| # State Management | ||
| fpdart: ^1.2.0 | ||
| equatable: ^2.0.7 | ||
| {{#if flags.usesGetItDi}} | ||
| get_it: ^8.0.3 | ||
| {{/if}} |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🌐 Web query:
What is the latest stable version of the get_it Dart/Flutter package on pub.dev?
💡 Result:
The latest stable version of the get_it Dart/Flutter package on pub.dev is 9.2.1.
Citations:
- 1: https://pub.dev/packages/get_it
- 2: https://pub.dev/packages/get_it/versions
- 3: https://github.com/flutter-it/get_it/blob/main/CHANGELOG.md
- 4: https://pub.dev/packages/get_it/changelog
Move get_it to a dedicated "Dependency Injection" section and update version constraint.
get_it is a DI/service-locator package, not a state management library. Grouping it under # State Management is misleading. Additionally, the version constraint ^8.0.3 is outdated; the latest stable version is 9.2.1. Update to at least ^9.0.0.
♻️ Proposed fix
# State Management
fpdart: ^1.2.0
equatable: ^2.0.7
- {{`#if` flags.usesGetItDi}}
- get_it: ^8.0.3
- {{/if}}
{{`#if` flags.usesFlutterHooks}}
flutter_hooks: ^0.20.5
{{/if}}
@@
{{`#if` flags.isMobX}}
mobx: ^2.6.0
flutter_mobx: ^2.3.0
{{/if}}
+
+ # Dependency Injection
+ {{`#if` flags.usesGetItDi}}
+ get_it: ^9.0.0
+ {{/if}}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@templates/flutter/base/pubspec.yaml.hbs` around lines 28 - 33, The get_it
dependency is incorrectly listed under the "State Management" section and uses
an outdated constraint; when flags.usesGetItDi is true, remove the get_it:
^8.0.3 entry from under the State Management block and instead add a new
commented section "# Dependency Injection" (or similar) where you include get_it
with an updated constraint (at least ^9.0.0, e.g., ^9.0.0 or ^9.2.1); update the
Handlebars template around the conditional (flags.usesGetItDi) so the get_it
line is emitted under that new section rather than next to fpdart/equatable.
| // Provides the single instance of AuthRepositoryImpl | ||
| final authRepositoryProvider = Provider<AuthRepository>((ref) { | ||
| return AuthRepositoryImpl(); | ||
| return createAuthRepository(); |
There was a problem hiding this comment.
Update the stale repository comment.
This provider no longer guarantees an AuthRepositoryImpl; with GetIt enabled it resolves through createAuthRepository().
📝 Proposed comment update
-// Provides the single instance of AuthRepositoryImpl
+// Provides the configured AuthRepository instance.
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return createAuthRepository();
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Provides the single instance of AuthRepositoryImpl | |
| final authRepositoryProvider = Provider<AuthRepository>((ref) { | |
| return AuthRepositoryImpl(); | |
| return createAuthRepository(); | |
| // Provides the configured AuthRepository instance. | |
| final authRepositoryProvider = Provider<AuthRepository>((ref) { | |
| return createAuthRepository(); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@templates/flutter/partials/features/auth/auth_logic.hbs` around lines 7 - 9,
The top comment is incorrect: it claims the provider returns a single instance
of AuthRepositoryImpl, but authRepositoryProvider actually returns
createAuthRepository() which may resolve via GetIt and not necessarily
AuthRepositoryImpl; update the comment above authRepositoryProvider to remove
the guarantee of AuthRepositoryImpl and instead state that the provider exposes
an AuthRepository resolved by createAuthRepository() (which may use GetIt).
| final authStore = {{#if flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []); | ||
| final isLoading = authStore.isLoading; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "forgot_password_screen.hbs" -type fRepository: Arjun544/flutter_init
Length of output: 134
🏁 Script executed:
cat -n ./templates/flutter/partials/features/auth/forgot_password_screen.hbsRepository: Arjun544/flutter_init
Length of output: 16534
Fix MobX code generation when Flutter Hooks are disabled.
The non-hooks MobX code path has a critical bug: the template assigns null to authStore on line 46, then attempts to dereference it on line 47 with authStore.isLoading. This generates invalid Dart that fails at compile time.
When flags.usesFlutterHooks is false, line 46 currently renders as:
final authStore = null; // Add your MobX store here(...);
The template should separate the conditional to generate valid code for both paths:
Template fix
{{else if flags.isMobX}}
- final authStore = {{`#if` flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []);
+ {{`#if` flags.usesFlutterHooks}}
+ final authStore = useMemoized(() => AuthStore(repository: createAuthRepository()), []);
+ {{else}}
+ final authStore = AuthStore(repository: createAuthRepository());
+ {{/if}}
final isLoading = authStore.isLoading;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| final authStore = {{#if flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []); | |
| final isLoading = authStore.isLoading; | |
| {{else if flags.isMobX}} | |
| {{`#if` flags.usesFlutterHooks}} | |
| final authStore = useMemoized(() => AuthStore(repository: createAuthRepository()), []); | |
| {{else}} | |
| final authStore = AuthStore(repository: createAuthRepository()); | |
| {{/if}} | |
| final isLoading = authStore.isLoading; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@templates/flutter/partials/features/auth/forgot_password_screen.hbs` around
lines 46 - 47, The template currently sets authStore to null when
flags.usesFlutterHooks is false which causes a null dereference on
authStore.isLoading; change the conditional so you emit two distinct
declarations: when flags.usesFlutterHooks is true generate a memoized store
(useMemoized(() => AuthStore(repository: createAuthRepository()), [])) and when
false generate a plain instance (AuthStore(repository: createAuthRepository()));
ensure the variable name authStore remains the same so subsequent references
like authStore.isLoading compile.
| final authStore = {{#if flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []); | ||
| final isLoading = authStore.isLoading; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "login_screen.hbs" -type fRepository: Arjun544/flutter_init
Length of output: 124
🏁 Script executed:
cat -n ./templates/flutter/partials/features/auth/login_screen.hbsRepository: Arjun544/flutter_init
Length of output: 35200
Fix MobX generation when Flutter Hooks are disabled.
When MobX is used without Flutter Hooks, the template renders authStore as null due to an inline comment that consumes the function initialization. This causes a null dereference on line 52 when accessing authStore.isLoading and again at line 105 in the handleLogin function.
🐛 Proposed template fix
{{else if flags.isMobX}}
// In a real app, MobX stores might be provided via Provider/GetIt.
// Here we use a locally created memoized instance for the template.
- final authStore = {{`#if` flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []);
+ {{`#if` flags.usesFlutterHooks}}
+ final authStore = useMemoized(() => AuthStore(repository: createAuthRepository()), []);
+ {{else}}
+ final authStore = AuthStore(repository: createAuthRepository());
+ {{/if}}
final isLoading = authStore.isLoading;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| final authStore = {{#if flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []); | |
| final isLoading = authStore.isLoading; | |
| {{`#if` flags.usesFlutterHooks}} | |
| final authStore = useMemoized(() => AuthStore(repository: createAuthRepository()), []); | |
| {{else}} | |
| final authStore = AuthStore(repository: createAuthRepository()); | |
| {{/if}} | |
| final isLoading = authStore.isLoading; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@templates/flutter/partials/features/auth/login_screen.hbs` around lines 51 -
52, The template currently renders authStore as null when flags.usesFlutterHooks
is false because the inline comment breaks the expression; change the
conditional so authStore is always initialized: if flags.usesFlutterHooks render
useMemoized(() => AuthStore(repository: createAuthRepository()), []), else
render AuthStore(repository: createAuthRepository()); remove the inline comment
that caused the null and ensure usages like isLoading and handleLogin (which
reference authStore) work with the non-null AuthStore instance.
| final authStore = {{#if flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []); | ||
| final isLoading = authStore.isLoading; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find MobX template branches that render `authStore` as null when hooks are disabled.
rg -n -C2 '\{\{`#if` flags\.usesFlutterHooks\}\}useMemoized\{\{else\}\}null; // Add your MobX store here\{\{/if\}\}' templates/flutter/partials/features/authRepository: Arjun544/flutter_init
Length of output: 2084
🏁 Script executed:
# Read the signup_screen.hbs file around the problematic lines to understand context
cat -n templates/flutter/partials/features/auth/signup_screen.hbs | sed -n '50,65p'Repository: Arjun544/flutter_init
Length of output: 972
🏁 Script executed:
# Read the login_screen.hbs file around the problematic lines
cat -n templates/flutter/partials/features/auth/login_screen.hbs | sed -n '45,60p'Repository: Arjun544/flutter_init
Length of output: 1118
🏁 Script executed:
# Read the forgot_password_screen.hbs file around the problematic lines
cat -n templates/flutter/partials/features/auth/forgot_password_screen.hbs | sed -n '40,55p'Repository: Arjun544/flutter_init
Length of output: 972
🏁 Script executed:
# Check if there are any other similar patterns with null assignments in auth templates
rg -n 'null; //' templates/flutter/partials/features/authRepository: Arjun544/flutter_init
Length of output: 768
Fix MobX generation when Flutter Hooks are disabled in all auth screens.
For isMobX without hooks, the template renders invalid syntax: final authStore = null; followed by authStore.isLoading, causing compilation failure. This affects signup_screen.hbs (line 56), login_screen.hbs (line 51), and forgot_password_screen.hbs (line 46).
Restructure these templates to properly instantiate AuthStore when hooks are disabled, following the pattern already used in the NoneState branches:
🐛 Proposed template fix (apply to all 3 auth screens)
{{else if flags.isMobX}}
- final authStore = {{`#if` flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []);
+ {{`#if` flags.usesFlutterHooks}}
+ final authStore = useMemoized(() => AuthStore(repository: createAuthRepository()), []);
+ {{else}}
+ final authStore = AuthStore(repository: createAuthRepository());
+ {{/if}}
final isLoading = authStore.isLoading;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| final authStore = {{#if flags.usesFlutterHooks}}useMemoized{{else}}null; // Add your MobX store here{{/if}}(() => AuthStore(repository: createAuthRepository()), []); | |
| final isLoading = authStore.isLoading; | |
| {{`#if` flags.usesFlutterHooks}} | |
| final authStore = useMemoized(() => AuthStore(repository: createAuthRepository()), []); | |
| {{else}} | |
| final authStore = AuthStore(repository: createAuthRepository()); | |
| {{/if}} | |
| final isLoading = authStore.isLoading; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@templates/flutter/partials/features/auth/signup_screen.hbs` around lines 56 -
57, The template currently emits invalid Dart when MobX is enabled but Flutter
Hooks are disabled by assigning authStore to null; instead, change the template
logic around authStore so that when flags.usesFlutterHooks is true you keep the
existing useMemoized invocation, and when false you instantiate AuthStore
directly (AuthStore(repository: createAuthRepository())); ensure subsequent
usages like authStore.isLoading and other authStore references work with the
instantiated AuthStore; update the conditional in signup_screen.hbs (and mirror
the same change in login_screen.hbs and forgot_password_screen.hbs) to branch
between useMemoized(...) and new AuthStore(...) rather than null.
[. ] - Introduced dependencyInjection config option with values: none, get_it.
[. ] - Added DI selection UI in the Misc step and surfaced it in Review & Generate.
[ .] - Wired DI flag through generator context (usesGetItDi) so templates can branch correctly.
[. ] - Added generated DI bootstrap file: lib/src/di/service_locator.dart.
[ .] - App startup now initializes DI via initDependencies() in main.dart.
[. ] - Added conditional get_it dependency in generated pubspec.yaml.
[. ] - Refactored templates to use createAuthRepository() instead of direct AuthRepositoryImpl() construction, enabling centralized DI wiring.
[. ] - Updated docs/config reference and generated README to include DI strategy.
[. ] - Added package info metadata for get_it in the wizard info panel.
Summary by CodeRabbit
New Features
Documentation