Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

refactor ForEachFunction to use the existing registry instead of crea…#419

Open
oscar-penelo wants to merge 16 commits into
peiffer-innovations:mainfrom
RiiotLabs:v12-with-json-theme-11
Open

refactor ForEachFunction to use the existing registry instead of crea…#419
oscar-penelo wants to merge 16 commits into
peiffer-innovations:mainfrom
RiiotLabs:v12-with-json-theme-11

Conversation

@oscar-penelo
Copy link
Copy Markdown

@oscar-penelo oscar-penelo commented Nov 15, 2025

Fixes #420

Description

  • 'for_each' function now uses the existing registry instead of creating new instances. This ensures that variables set during iteration are accessible in the broader context and resolves issues related to variable scope during iteration.

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Code Style Guide and followed the process outlined there for submitting PRs.
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (flutter analyze or dart analyze) does not report any problems on my PR.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I am authorized to release this code under the MIT License and agree to do so

UI Change

Does your PR affect any UI screens?

  • Yes, the relevant screens are included below.
  • No, there are no UI impacts with this PR.

Summary by CodeRabbit

  • Bug Fixes

    • Iteration templates now reuse a shared registry so per-item variables are properly scoped, removed when no longer needed, and visible to surrounding widgets.
    • Placeholder replacement now skips text inside string literals to avoid unintended substitutions.
    • Deferred item resolution triggers cleanup when rendered widgets are disposed to prevent stale temporary values.
  • New Features

    • Added an example page demonstrating dynamic list rendering with interactive template buttons and live value binding.
  • Chores

    • Package version bumped to v12.0.1.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 15, 2025

Walkthrough

for_each now reuses the shared JsonWidgetRegistry by namespacing per-item keys and replacing identifiers only outside string literals. DeferredJsonWidgetData accepts a dynamic key and optional onResolved cleanup callback and resolves via JsonWidgetData.fromDynamic(...). Package version bumped to 12.0.1; example Issue420 added.

Changes

Cohort / File(s) Summary
for_each implementation
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart
Reworks for_each to reuse the shared registry with per-invocation unique suffixes; registers per-item value/index/key under composite keys; constructs DeferredJsonWidgetData for each item; adds unique key generation, registry cleanup, and identifier-replacement that skips string literals.
Deferred resolution & cleanup
packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart
Changes DeferredJsonWidgetData key type to dynamic, adds optional onResolved callback, resolves via JsonWidgetData.fromDynamic(...).copyWith(jsonWidgetRegistry: ...), and wraps built widgets to run cleanup on dispose (including PreferredSizeWidget support). Removes legacy registry-based resolution path.
Package metadata & changelog
packages/json_dynamic_widget/pubspec.yaml, packages/json_dynamic_widget/CHANGELOG.md
Bumps package version to 12.0.1 and adds changelog entry describing registry reuse for for_each and variable scope fix.
Example & launcher
examples/json_dynamic_widget_example/lib/src/issue_420_page.dart, examples/json_dynamic_widget_example/lib/src/launcher.dart
Adds Issue420Page demonstrating for_each + template interactions and registers a new issue_420 navigation entry in the example launcher.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant ForEach as for_each
  participant Registry as JsonWidgetRegistry
  participant Template as Template JSON
  participant Decoder as JSON→JsonWidgetData
  participant Deferred as DeferredJsonWidgetData

  Note over ForEach,Registry: New flow — reuse shared registry and namespace per-item keys
  ForEach->>Registry: generate uniqueSuffix
  loop for each item
    ForEach->>Registry: set(compositeValueKey, itemValue)
    ForEach->>Registry: set(compositeIndexKey, indexOrKey)
    ForEach->>Template: replace identifiers outside string literals with composite keys
    Template->>Decoder: decode replaced JSON
    Decoder->>Deferred: create DeferredJsonWidgetData(decoded, registry=Registry, onResolved=cleanup)
    ForEach->>Deferred: collect deferred widget
  end
  Deferred->>Registry: onResolved callback triggers cleanup of namespaced keys
Loading
sequenceDiagram
  autonumber
  participant ForEachOld as for_each (old)
  participant RegistryOld as JsonWidgetRegistry (per-item)
  participant TemplateOld as Template JSON
  participant DecoderOld as JSON→JsonWidgetData
  participant DeferredOld as DeferredJsonWidgetData

  Note over ForEachOld,RegistryOld: Old flow — created per-item registries
  loop for each item
    ForEachOld->>RegistryOld: create new JsonWidgetRegistry()
    ForEachOld->>RegistryOld: set("value", itemValue)
    ForEachOld->>TemplateOld: replace placeholders referencing "value"
    TemplateOld->>DecoderOld: decode replaced JSON
    DecoderOld->>DeferredOld: create DeferredJsonWidgetData(decoded, registry=RegistryOld)
    ForEachOld->>DeferredOld: collect deferred widget
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring ForEachFunction to use the existing registry instead of creating new instances.
Linked Issues check ✅ Passed The code changes directly address issue #420 by enabling for_each templates to share the global registry, allowing registry-dependent actions like set_value to work correctly in templates.
Out of Scope Changes check ✅ Passed All changes are in scope: for_each registry refactoring, DeferredJsonWidgetData enhancements for cleanup, version bump, changelog entry, and example page demonstrating the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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



📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f845a40 and e9beaaa.

📒 Files selected for processing (1)
  • packages/json_dynamic_widget/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/json_dynamic_widget/CHANGELOG.md

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 096bf4e and 7aa7f9b.

📒 Files selected for processing (3)
  • packages/json_dynamic_widget/CHANGELOG.md (1 hunks)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (1 hunks)
  • packages/json_dynamic_widget/pubspec.yaml (1 hunks)
🔇 Additional comments (2)
packages/json_dynamic_widget/pubspec.yaml (1)

4-4: LGTM!

The patch version bump to 12.0.1 is appropriate for this refactoring change.

packages/json_dynamic_widget/CHANGELOG.md (1)

1-4: LGTM!

The changelog entry clearly documents the registry reuse change and its impact on variable scope during iteration.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7aa7f9b and 8f99995.

📒 Files selected for processing (4)
  • examples/json_dynamic_widget_example/lib/src/issue_420_page.dart (1 hunks)
  • examples/json_dynamic_widget_example/lib/src/launcher.dart (2 hunks)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
  • packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart (2 hunks)
🔇 Additional comments (2)
examples/json_dynamic_widget_example/lib/src/launcher.dart (1)

17-18: Issue420Page wiring matches existing patterns

The new import and 'issue_420' route follow the same pattern as other issue pages and are consistent with how the example navigator is structured.

Also applies to: 262-266

packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart (1)

8-15: The review's primary concern about breaking changes is unsupported by actual usage patterns.

Verification shows:

  1. String key risk is invalid: All actual calls to DeferredJsonWidgetData (in for_each.dart at lines 81 and 119) pass json.decode(replacedTemplate) — fully decoded JSON objects, not string variable names. The change aligns with existing usage.

  2. Optimization suggestion is valid: JsonWidgetData.fromDynamic() does accept a registry parameter (confirmed in json_widget_data.dart), so the suggestion to avoid copyWith is technically sound but optional—both approaches work.

  3. Properly scoped: DeferredJsonWidgetData is internal-only for deferred widget parsing. The semantic change is intentional and supported by actual call sites.

The core risk assessment is unfounded. The code change is correct as written.

Likely an incorrect or invalid review comment.

Comment thread examples/json_dynamic_widget_example/lib/src/issue_420_page.dart Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3)

7-8: Unique key generation is reasonable; implementation looks good

Using a shared _random and _random10Digits() to generate a per‑invocation uniqueKey is a simple and effective way to namespace per‑item variables and avoid the previous “last iteration wins” issue. Collision probability with 10 random digits is negligible for typical usage, so this looks fine as implemented.

If you ever expect extremely high volumes or long‑lived registries, you could document the collision assumptions or switch to something like an incrementing counter/UUID, but it’s not strictly necessary here.

Also applies to: 20-26, 32-32


34-37: Remove unused template local for clarity

final template = '\${${args[1]}}'; is no longer used now that you work directly from templateObjectString. Keeping it around is slightly confusing and may mislead future readers about how the template is resolved.

You can safely drop it:

-    final template = '\${${args[1]}}';
-    final templateObjectString = json.encode(registry.getValue(args[1]));
+    final templateObjectString = json.encode(
+      registry.getValue(args[1]),
+    );

47-102: Placeholder rewriting strategy is sound; apply optional DRY/perf improvements

The overall approach correctly avoids the "shared mutable state" bug by using per-item names like value_<uniqueKey>_<index> / key_<uniqueKey>_<index>, and restricting replacements to ${…} and whole-word varName/keyName occurrences. Registry keys are treated as opaque strings with no path parsing, so special characters in Map keys pose no structural risks.

Consider these micro-optimizations in both branches (lines 47–102 and 104–153):

  • placeholderPattern is identical and recomputed per iteration; hoist to static final or compute once per _body call.
  • varNamePattern / keyNamePattern depend only on varName/keyName, not on the index or entry.key; compute once per branch and reuse inside the callback to avoid per-item regex allocations.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f99995 and 14f38df.

📒 Files selected for processing (2)
  • examples/json_dynamic_widget_example/lib/src/issue_420_page.dart (1 hunks)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/json_dynamic_widget_example/lib/src/issue_420_page.dart

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (1)

114-116: Remove redundant regex creation.

The keyNamePattern is already created at line 46 and can be reused here. Recreating it on each map iteration is inefficient and unnecessary.

Apply this diff to use the existing pattern:

             inside = inside.replaceAllMapped(
               varNamePattern,
               (_) => '${varName}_${uniqueKey}_${entry.key}',
             );

-            final keyNamePattern = RegExp(
-              r'\b' + RegExp.escape(keyName) + r'\b',
-            );
             inside = inside.replaceAllMapped(
               keyNamePattern,
               (_) => '${keyName}_${uniqueKey}_${entry.key}',
             );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14f38df and 1c14f15.

📒 Files selected for processing (2)
  • examples/json_dynamic_widget_example/lib/src/issue_420_page.dart (1 hunks)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
🔇 Additional comments (3)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (2)

1-2: LGTM: Imports support the unique key generation strategy.

The addition of dart:convert and dart:math along with the global _random instance appropriately supports the new approach of generating unique keys for per-item registry values.

Also applies to: 7-7


48-90: LGTM: Iterable branch correctly implements scoped replacement strategy.

The implementation properly:

  1. Stores per-item values in the shared registry with unique keys
  2. Scopes variable/key name replacements to placeholder expressions only (via placeholderPattern), preventing corruption of property names
  3. Creates deferred widgets that reference the correct registry values via the modified template

This addresses the concerns from previous reviews about both registry state capture and overly broad replacements.

examples/json_dynamic_widget_example/lib/src/issue_420_page.dart (1)

1-54: LGTM: Example effectively demonstrates the registry sharing behavior.

This example appropriately exercises the refactored for_each functionality:

  • The for_each(array, 'template') call renders multiple buttons using shared registry
  • Each button's onPressed updates the parent context's myvalue variable via set_value
  • The listening text widget reflects these changes, confirming that variables set during iteration are now accessible in the broader context

This serves as both a regression test and documentation for issue 420.

Comment thread packages/json_dynamic_widget/lib/src/components/functions/for_each.dart Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c14f15 and 099517c.

📒 Files selected for processing (2)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
  • packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart (4 hunks)
🔇 Additional comments (9)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (5)

17-23: LGTM: Unique key generation eliminates collision risk.

The combination of microsecond timestamp and incrementing counter provides a robust unique identifier for each for_each invocation, effectively addressing the collision concerns from previous reviews.


25-32: LGTM: Cleanup function is straightforward.

The helper correctly removes per-item registry keys, preventing memory leaks from accumulated iteration variables.


50-52: LGTM: Efficient regex pattern creation.

The patterns are correctly created once per invocation rather than per iteration, and the use of RegExp.escape() ensures special characters in variable names are handled safely.


73-89: LGTM: Template replacement approach is pragmatic and safe.

The current implementation correctly limits replacement to within ${...} placeholders only, avoiding the broader string-wide corruption issues flagged in earlier reviews. The word-boundary regex (\b) properly isolates variable names in most expression contexts.

Note: Theoretical edge case exists where variable names appear in string literals within expressions (e.g., ${"text with value in it" + value}), but this would require full expression parsing and is unlikely in typical usage.


91-100: LGTM: Cleanup callback correctly tied to widget lifecycle.

The onResolved callback ensures per-item registry values are removed when the deferred widget is disposed, completing the cleanup strategy and preventing memory leaks.

packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart (4)

8-18: LGTM: Constructor changes enable flexible key types and cleanup.

The change from String to dynamic key type accommodates the new template replacement approach in for_each, and the optional onResolved callback provides the hook needed for registry cleanup.


33-35: LGTM: Data resolution correctly handles dynamic keys.

Using JsonWidgetData.fromDynamic() properly handles the dynamic key type introduced by the template replacement mechanism in for_each.


52-78: LGTM: Build method correctly implements cleanup lifecycle.

The conditional wrapping strategy is well-designed:

  • Returns the built widget directly when cleanup is not needed
  • Handles the PreferredSizeWidget special case to preserve its interface
  • Wraps in a cleanup widget that invokes onResolved on disposal

This ensures registry values are cleaned up when the widget is removed from the tree.


111-163: LGTM: Cleanup widget implementations follow standard Flutter patterns.

Both _CleanupWidget and _PreferredSizeCleanupWidget are implemented correctly:

  • Stateful widgets that invoke callbacks in dispose()
  • Simple pass-through rendering that forwards the child
  • _PreferredSizeCleanupWidget correctly implements PreferredSizeWidget interface

Comment thread packages/json_dynamic_widget/lib/src/components/functions/for_each.dart Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (1)

53-143: Logic is sound; consider extracting shared iteration logic.

The implementation correctly:

  • Generates unique per-item keys scoped by the invocation
  • Stores values in the shared registry with proper namespacing
  • Replaces identifiers safely within placeholder expressions
  • Sets up cleanup callbacks to prevent memory leaks

Both the Iterable and Map branches follow the same pattern with only minor variations (index vs. entry.key). While the current duplication is localized and manageable, extracting a helper function for the shared logic could improve maintainability.

Optional refactor to reduce duplication:

Consider extracting the common pattern into a helper:

static JsonWidgetData _createDeferredWidget({
  required String valueKey,
  required String keyKey,
  required dynamic value,
  required dynamic key,
  required String templateObjectString,
  required String varName,
  required String keyName,
  required RegExp placeholderPattern,
  required JsonWidgetRegistry registry,
}) {
  registry.setValue(valueKey, value, originator: null);
  registry.setValue(keyKey, key, originator: null);

  final replacedTemplate = templateObjectString.replaceAllMapped(
    placeholderPattern,
    (match) {
      var inside = match.group(1)!;
      inside = _replaceIdentifiersOutsideStringLiterals(
        inside,
        {varName: valueKey, keyName: keyKey},
      );
      return '\${$inside}';
    },
  );

  return DeferredJsonWidgetData(
    key: json.decode(replacedTemplate),
    registry: registry,
    onResolved: () => _cleanupRegistryValues(registry, [valueKey, keyKey]),
  );
}

Then both branches would simplify to:

// Iterable branch
results.add(_createDeferredWidget(
  valueKey: '${varName}_${uniqueKey}_$index',
  keyKey: '${keyName}_${uniqueKey}_$index',
  value: value,
  key: index,
  // ... other params
));

// Map branch
results.add(_createDeferredWidget(
  valueKey: '${varName}_${uniqueKey}_${entry.key}',
  keyKey: '${keyName}_${uniqueKey}_${entry.key}',
  value: entry.value,
  key: entry.key,
  // ... other params
));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da6c9bb and ea70dc3.

📒 Files selected for processing (2)
  • examples/json_dynamic_widget_example/lib/src/issue_420_page.dart (1 hunks)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/json_dynamic_widget_example/lib/src/issue_420_page.dart
🔇 Additional comments (4)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (4)

1-18: Clean setup with appropriate dependencies.

The addition of dart:convert for JSON manipulation and the static members for unique key generation are well-structured. The use of a static counter is safe in Dart's single-threaded execution model for typical use cases.


20-24: Excellent unique key generation strategy.

Combining timestamp with an incrementing counter provides strong uniqueness guarantees while keeping keys compact via base-36 encoding. This effectively eliminates the collision risk present in random-only approaches.


26-33: LGTM: Proper cleanup mechanism.

The cleanup function correctly removes per-item registry entries, preventing memory leaks in long-running applications. This relies on the onResolved callback being invoked by DeferredJsonWidgetData, which was confirmed in previous review iterations.


149-217: Robust identifier replacement with proper quote handling.

This method correctly addresses the previous concern about corrupting string literals by:

  • Tracking when the parser is inside quoted strings (single or double quotes)
  • Properly handling escaped quotes through backslash counting (even count = unescaped)
  • Only replacing identifiers outside strings and at word boundaries
  • Supporting multiple simultaneous replacements

The character-by-character approach is necessary for correctness and the performance trade-off is acceptable for typical template sizes.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (1)

91-91: Optional: Note limitation with nested braces in expressions.

The placeholder pattern r'\$\{([^}]*)\}' uses [^}]* which captures content until the first } character. This means expressions with nested braces (e.g., ${someFunction({key: value})}) would be incorrectly parsed, as the capture would stop at the first closing brace.

However, this is likely an acceptable limitation since:

  • Such complex expressions may not be supported by the library's expression parser anyway
  • The current pattern handles all examples and typical use cases correctly
  • Fixing this would require a more complex parser (bracket matching/counting)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea70dc3 and 29a1b56.

📒 Files selected for processing (2)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
  • packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart (4 hunks)
🔇 Additional comments (2)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (1)

1-209: Excellent refactor addressing the registry sharing issue!

The solution is well-designed and correctly fixes the original problem. The approach of using unique per-item keys with the shared registry, combined with the cleanup mechanism, ensures that:

  1. Actions inside templates can interact with the global registry (fixing issue #420)
  2. Each iteration maintains its own variable scope via unique keys
  3. Registry entries are properly cleaned up when widgets are disposed
  4. All past review concerns (collisions, string replacement, cleanup) have been addressed

The _replaceIdentifiersOutsideStringLiterals function is particularly well-implemented with correct handling of quote tracking, escape sequences, and word boundaries.

packages/json_dynamic_widget/lib/src/models/deferred_json_widget_data.dart (1)

1-164: Well-implemented cleanup mechanism with proper widget lifecycle integration.

The changes cleanly support the new for_each design:

  1. The dynamic key type provides flexibility for passing decoded template objects
  2. The onResolved callback enables proper registry cleanup when widgets are disposed
  3. The cleanup wrapper widgets correctly implement the Flutter lifecycle (dispose) and handle the PreferredSizeWidget special case
  4. The resolution path via JsonWidgetData.fromDynamic is cleaner than the previous approach

The implementation correctly ensures that registry entries are cleaned up when widgets are removed from the tree, preventing memory leaks in normal usage scenarios.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (2)

17-17: Thread-safety consideration for the counter.

The static _uniqueKeyCounter is not synchronized, but this is acceptable for typical Flutter/Dart usage where code runs on a single isolate. If the library is used in a multi-isolate environment, consider using atomic operations or per-isolate counters to guarantee uniqueness across isolates.


203-271: LGTM! The identifier replacement logic is correct and safe.

The implementation carefully:

  • Only replaces outside string literals via quote state tracking
  • Ensures word boundaries to avoid partial matches (e.g., won't replace "value" in "myvalue")
  • Handles escaped quotes properly

The O(n × m) complexity (where m = number of replacements, typically 2) is acceptable for the expected usage patterns. If performance becomes a concern with very large templates, consider using a more efficient approach like regex compilation or a trie-based matcher.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29a1b56 and ab609a4.

📒 Files selected for processing (1)
  • packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (3 hunks)
🔇 Additional comments (4)
packages/json_dynamic_widget/lib/src/components/functions/for_each.dart (4)

20-24: LGTM! Unique key generation eliminates collision risk.

The timestamp + counter approach provides guaranteed uniqueness and addresses the previous collision concerns mentioned in past reviews.


66-127: LGTM! The registry sharing approach correctly addresses the PR objectives.

The refactored logic successfully:

  • Uses the shared registry instead of creating per-iteration instances, fixing the global registry access issue
  • Creates unique keys per invocation and per item to prevent collisions
  • Maintains iteration-specific values through namespaced registry keys
  • Delegates cleanup to the deferred widget lifecycle

The approach elegantly solves the problem described in issue #420 where actions inside templates couldn't access the global registry.


129-201: LGTM! The placeholder replacement logic is robust.

The implementation correctly handles complex edge cases:

  • Nested ${} expressions via brace depth tracking
  • String literals inside expressions via quote state tracking
  • Escaped quotes via backslash counting (even number of backslashes = not escaped)
  • Malformed templates via early break when braces don't match

This addresses the previous review concern about broad string replacement corrupting templates by only replacing identifiers inside interpolation expressions and outside string literals.


35-64: Address potential memory leak from unused DeferredJsonWidgetData instances in lazy-rendered lists.

The onResolved cleanup callback only fires when DeferredJsonWidgetData.build() is invoked. However, when for_each results are passed to widgets using lazy item builders (ListView, GridView, SliverList), items that never reach the viewport are never built, leaving their registry values (valueKey and keyKey) indefinitely orphaned.

In long-running applications with large datasets (e.g., a ListView with 10,000 items where only 10 are visible), this causes unbounded registry growth and memory leaks.

Mitigation options:

  • Wrap results in a lifecycle-aware container that cleans up offscreen items
  • Use weak references for registry values to allow garbage collection
  • Implement explicit cleanup when the parent container is disposed
  • Document this limitation for users creating large for_each lists

@jpeiffer
Copy link
Copy Markdown
Contributor

Great report and thanks for the PR. I'll try to get to it within the week.

@jpeiffer
Copy link
Copy Markdown
Contributor

Apologies... Vacations and travel got in the way. It looks like this now has merge conflicts. :(

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/json_dynamic_widget/CHANGELOG.md (2)

1-29: Remove duplicate changelog entry and fix chronological ordering.

The version 12.0.1 entry appears twice (lines 1-4 and lines 26-29) with identical content. Additionally, if the date of November 15th, 2025 is correct, this entry should appear after versions dated in January 2026 (lines 6-14), not before them. CHANGELOGs should be in reverse chronological order (newest first).

Please:

  1. Remove one of the duplicate entries
  2. Verify the release date and reorder entries chronologically
📝 Proposed fix assuming November 15th, 2025 date is correct
-## [12.0.1] - November 15th, 2025
-
-* 'for_each' function now uses the existing registry instead of creating new instances. This ensures that variables set during iteration are accessible in the broader context and resolves issues related to variable scope during iteration.
-
-
 ## [12.0.0+5] - January 13, 2026
 
 * Automated dependency updates

431-431: Remove git merge conflict marker.

A git merge conflict marker was left in the file, indicating incomplete conflict resolution. This must be removed before merging.

🔧 Proposed fix
->>>>>>> fabcc57c6772a1015dbb260dc2cb5a28bfade292:CHANGELOG.md
-
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d18ea05 and 2532374.

📒 Files selected for processing (2)
  • packages/json_dynamic_widget/CHANGELOG.md
  • packages/json_dynamic_widget/pubspec.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/json_dynamic_widget/pubspec.yaml
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
packages/json_dynamic_widget/CHANGELOG.md

26-26: Multiple headings with the same content

(MD024, no-duplicate-heading)

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/json_dynamic_widget/CHANGELOG.md (1)

426-426: Remove the merge conflict marker.

A Git merge conflict marker has been left in the file. This must be removed before merging.

🔧 Proposed fix
->>>>>>> fabcc57c6772a1015dbb260dc2cb5a28bfade292:CHANGELOG.md
🤖 Fix all issues with AI agents
In `@packages/json_dynamic_widget/CHANGELOG.md`:
- Around line 1-5: Update the release header for "## [12.0.1] - November 15th,
2025" in packages/json_dynamic_widget/CHANGELOG.md to the correct release date
in January 2026 (e.g., "## [12.0.1] - January 15th, 2026") so the 12.0.1 entry
is chronologically after "12.0.0+5" dated January 13, 2026; edit that exact
header line to reflect the correct date.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2532374 and 828611c.

📒 Files selected for processing (1)
  • packages/json_dynamic_widget/CHANGELOG.md

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread packages/json_dynamic_widget/CHANGELOG.md Outdated
@oscar-penelo
Copy link
Copy Markdown
Author

Hi @jpeiffer , all Changelog conflicts are solved. You can merge it 😄

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

'for_each' function is not sharing global registry

2 participants