ren
Flutter Feature Gravity Analyzer
ren detects performance risk in your Flutter features before you ship — analyzing costly AST patterns, widget combinations, lifecycle misuse, and resource leaks to report a gravity score per feature, without running the app.
ren is not a replacement for
flutter analyzeor DevTools. It occupies a different space:
Tool Answers flutter analyzeQuality and errors DevTools Runtime performance renWhich features carry the most performance risk — before you run anything
Installation
dart pub global activate ren
Windows (PowerShell):
renconflicts with the built-inRename-Itemalias. RunRemove-Item Alias:ren -Forceonce per session to use ren directly.
Usage
# Analyze the current project
ren
# Generate ren.yaml from your project structure
ren --init
# Analyze a specific path
ren --project ./my_app
# Custom feature root (for projects not using lib/features/)
ren --project ./my_app --features lib/ui/screens
# Exclude generated or internal paths
ren --project ./my_app --exclude lib/generated,lib/_tools
# JSON output for CI/CD
ren --format json > ren-report.json
# Fail pipeline if any feature reaches HIGH or above
ren --fail-on high
Example output
◈ ren · Flutter Feature Gravity Analyzer
────────────────────────────────────────────────────────────
◦ checkout ●●●●● CRITICAL 100%
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
🔴 BackdropFilter [inside ListView] line 17
BackdropFilter inside ListView is one of the worst Flutter performance patterns.
→ Move BackdropFilter outside the ListView. Apply blur to a static background instead.
🔴 Opacity [inside ListView] line 36
Opacity inside ListView creates an offscreen layer per visible item.
→ Remove Opacity from list items. Use a colored Container or Image with opacity baked in.
Top contributors:
BackdropFilter ················ +100
Opacity ······················· +70
◦ profile ●●●○○ HIGH 56%
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
🔴 StreamController leak line 18
_controller (StreamController) is created but close() is never called in dispose().
→ Add _controller.close() inside dispose() to prevent memory leaks.
🔴 AnimationController leak line 22
_animation (AnimationController) is created but dispose() is never called in dispose().
→ Add _animation.dispose() inside dispose() to prevent memory leaks.
Top contributors:
StreamController leak ········· +40
AnimationController leak ······ +30
────────────────────────────────────────────────────────────
How scores work:
⚪ Presence — pattern detected individually
🟡 Context — inside a costly parent widget (×1.5)
🔴 Risk — inside a critical parent widget (×2.5)
────────────────────────────────────────────────────────────
2 feature(s) · 4 pattern(s) detected
ren.yaml
Place a ren.yaml at your project root — or run ren --init to generate one:
# Custom feature root
features: lib/ui/screens
# Fail CI/CD if any feature reaches this level
fail_on: high
# Paths to exclude
ignore:
- lib/generated
- lib/l10n
- lib/_tools
# Override built-in weights (0 = disable rule)
weights:
Opacity: 0 # our team uses Opacity correctly
ListView: 0 # not a concern for our use case
BackdropFilter: 60 # we want to be extra strict
# Team-specific patterns
custom_rules:
- name: MyHeavyWidget
reason: Internal widget known to cause jank in production.
weight: 45
fix: Replace with LightweightWidget from our design system.
CLI flags always take priority over ren.yaml.
How it works
ren scans your project's Dart source files using the analyzer package,
visits the AST of each file, and detects patterns known to cause performance
issues in Flutter apps.
Each pattern carries a base weight. When a costly pattern is found inside another costly widget, the weight is multiplied:
| Level | Example | Multiplier |
|---|---|---|
| ⚪ Presence | Opacity found |
×1.0 |
| 🟡 Context | NetworkImage inside ListView |
×1.5 |
| 🔴 Risk | BackdropFilter inside ListView |
×2.5 |
ren also runs a leak detector at the class level — verifying that
controllers, streams, timers, and notifiers declared in initState are
properly closed in dispose().
If no feature root is found (lib/features/, lib/modules/), ren
auto-discovers candidate folders or falls back to lib/ as a single
feature — so it always produces output regardless of your project structure.
Detected patterns
Base patterns
| Category | Pattern | Weight |
|---|---|---|
| GPU | saveLayer |
50 |
| GPU | BackdropFilter |
40 |
| GPU | ShaderMask |
35 |
| GPU | ImageFiltered |
35 |
| GPU | ClipPath |
25 |
| GPU | Opacity |
20 |
| GPU | ColorFiltered |
20 |
| GPU | CustomPaint |
20 |
| GPU | ClipRRect |
15 |
| Lists | SingleChildScrollView |
20 |
| Lists | ListView |
15 |
| Lists | GridView |
15 |
| Lists | Wrap |
15 |
| Images | Image.network |
25 |
| Images | NetworkImage |
25 |
| Images | FadeInImage |
15 |
| Rebuilds | MediaQuery.of |
15 |
| Rebuilds | Hero |
20 |
| Rebuilds | RepaintBoundary |
5 |
| Memory leaks | Timer.periodic |
30 |
| Memory leaks | StreamController |
25 |
| Memory leaks | StreamSubscription |
25 |
| Memory leaks | Timer |
20 |
| Lifecycle | setState in dispose |
60 |
| Lifecycle | setState in build |
50 |
| Lifecycle | setState in initState |
35 |
Resource leak detection
ren verifies that the following resources are closed in dispose():
StreamController · StreamSubscription · Timer · AnimationController ·
TextEditingController · ScrollController · FocusNode · PageController ·
TabController · ValueNotifier · ChangeNotifier
Compound patterns (elevated weight)
| Level | Combination | Weight |
|---|---|---|
| 🔴 Risk | BackdropFilter inside ListView / GridView |
+100 |
| 🔴 Risk | ShaderMask / ImageFiltered inside ListView / GridView |
+87 |
| 🔴 Risk | Opacity inside AnimatedBuilder / ListView / GridView |
+50 |
| 🔴 Risk | ClipPath inside AnimatedBuilder |
+62 |
| 🔴 Risk | ColorFiltered inside ListView / GridView |
+50 |
| 🟡 Context | BackdropFilter inside Stack / PageView |
+60 |
| 🟡 Context | NetworkImage inside ListView / GridView |
+37 |
| 🟡 Context | CustomPaint inside AnimatedBuilder / ListView / GridView |
+30 |
| 🟡 Context | ClipPath inside ListView / GridView |
+37 |
| 🟡 Context | Hero inside ListView / GridView |
+30 |
| 🟡 Context | ShaderMask inside AnimatedBuilder |
+52 |
Gravity levels
| Score | Level |
|---|---|
| 0–20 | 🟢 LOW — safe to ship |
| 21–45 | 🟡 MEDIUM — review before release |
| 46–70 | 🟠 HIGH — refactor soon |
| 71–100 | 🔴 CRITICAL — immediate action needed |
CI/CD
- name: Ren — Feature Gravity
run: |
dart pub global activate ren
ren --format json > ren-report.json
ren --fail-on high
License
Apache 2.0 — see LICENSE.
Libraries
- ren
- Ren — Flutter Feature Gravity Analyzer