Fix infinite rebuild loop in Flutter when using PageView with URL-based routing (GoRouter) and reactive state management (Riverpod/BLoC). Use when: (1) RAPID REBUILD warnings appear in console (#20+), (2) "setState() or markNeedsBuild() called during build" errors from ValueListenableBuilder, (3) Video feed or list-based PageView is frozen and won't swipe, (4) Overlay or UI elements flicker between visible/invisible rapidly, (5) Logs show item "moved from index X to Y" repeatedly. Caused by t...
Scanned 9/3/2026
Install to Claude Code
npx -y skills add divinevideo/divine-mobile --skill flutter-pageview-url-routing-reorder-loop --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Flutter Pageview Url Routing Reorder Loop?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/divinevideo-flutter-pageview-url-routing-reorder-loop-divine-mobile)More formats (shields.io, HTML) on the badges page.
---
name: flutter-pageview-url-routing-reorder-loop
description: |
Fix infinite rebuild loop in Flutter when using PageView with URL-based routing (GoRouter)
and reactive state management (Riverpod/BLoC). Use when: (1) RAPID REBUILD warnings appear
in console (#20+), (2) "setState() or markNeedsBuild() called during build" errors from
ValueListenableBuilder, (3) Video feed or list-based PageView is frozen and won't swipe,
(4) Overlay or UI elements flicker between visible/invisible rapidly, (5) Logs show item
"moved from index X to Y" repeatedly. Caused by tracking items by ID and updating URL
when reactive list reorders.
author: Claude Code
version: 1.0.0
date: 2026-02-06
---
# Flutter PageView + URL Routing Reorder Detection Loop
## Problem
When a PageView is synced bidirectionally with URL-based routing (e.g., GoRouter `/home/:index`)
and the data source is a reactive provider (Riverpod, BLoC stream), attempting to track the
currently-viewed item by its ID and update the URL when the item moves to a different index
in the list creates an infinite feedback loop.
## Context / Trigger Conditions
**Symptoms:**
- Console shows `RAPID REBUILD #42!` warnings (build count growing rapidly)
- `setState() or markNeedsBuild() called during build` errors from `ValueListenableBuilder`
- PageView is frozen - user swipes but page bounces back to same position
- UI overlay (social buttons, controls) flickers between visible and invisible
- Logs show repeated pattern: `Video X moved from index 4 -> 3, updating URL`
**Architecture that triggers this:**
- `PageView.builder` with `onPageChanged` updating URL via `context.go('/feed/$index')`
- URL-derived index synced back to PageController via `jumpToPage()` during build
- Reactive data source (Riverpod provider, stream) that can re-emit with reordered items
- Item tracking: `_currentItemId` compared against list to detect "moves"
**Example of the anti-pattern:**
```dart
// IN BUILD METHOD - creates feedback loop!
if (_currentVideoStableId != null && videos.isNotEmpty) {
final currentVideoIndex = videos.indexWhere(
(v) => v.stableId == _currentVideoStableId,
);
if (currentVideoIndex != -1 && currentVideoIndex != urlIndex) {
// This triggers URL update -> rebuild -> PageController sync ->
// onPageChanged -> URL update -> INFINITE LOOP
context.go('/home/$currentVideoIndex');
}
}
```
## Solution
**Remove item-tracking reorder detection entirely.** The PageController should be the sole
source of truth for which page the user is viewing.
### Step 1: Remove tracking state
```dart
// REMOVE these fields:
// String? _currentVideoStableId;
// bool _urlUpdateScheduled = false;
```
### Step 2: Remove reorder detection block
Remove any code in `build()` that:
- Searches for a tracked item ID in the current list
- Compares found index against URL index
- Schedules URL updates when indices differ
### Step 3: Keep only one-way sync patterns
**User swipe -> URL (one-way):**
```dart
onPageChanged: (newIndex) {
if (newIndex != urlIndex) {
context.go('/home/$newIndex');
}
}
```
**External navigation -> PageController (one-way):**
```dart
if (urlIndex != _lastUrlIndex) {
_lastUrlIndex = urlIndex;
controller.jumpToPage(urlIndex);
}
```
These two paths don't create loops because:
- User swipe: URL updates, next build sees matching urlIndex -> no sync needed
- External nav: URL changes, sync fires, onPageChanged fires but `newIndex == urlIndex` -> no URL update
## The Feedback Loop Explained
```
┌─ Reactive provider re-emits (new data from server) ─┐
│ │
▼ │
Build runs with new video list │
│ │
▼ │
Reorder detection: "Video X moved from idx 4 to 3" │
│ │
▼ │
Schedule URL update: context.go('/home/3') │
│ │
▼ │
Build runs: urlIndex=3, PageController at page 4 │
│ │
▼ │
Sync: jumpToPage(3) │
│ │
▼ │
onPageChanged(3) fires → context.go('/home/3') │
│ │
▼ │
But PageController was at 4, which showed different │
video → _currentVideoStableId mismatches → detects │
"move" again → URL update to /home/4 │
│ │
└───────── INFINITE LOOP ──────────────────────────────┘
```
## Verification
After removing reorder detection:
1. No more `RAPID REBUILD` warnings in console
2. No `setState() called during build` errors
3. Swiping between pages works smoothly
4. UI overlays stay visible on the active page
5. No "moved from index X to Y" log spam
## Notes
- **Why reorder detection seems needed:** When a reactive list reorders (e.g., new items
prepended), the user's current page index points to a different item. However, trying to
"follow" the item creates worse UX (infinite loop) than staying on the same page index.
- **Alternative if position preservation is critical:** Instead of URL-based reorder detection,
stabilize the list order. Either:
- Don't reorder while the user is actively viewing (batch updates)
- Use a stable sort that preserves relative positions of existing items
- Only prepend/append new items, never reorder existing ones
- **Related pattern:** `flutter-pageview-implicit-scrolling-snapback` addresses a different
PageView issue (snap-back on state rebuild) but can co-occur with this loop.
- **Framework-agnostic:** While this was discovered with GoRouter + Riverpod, the same
pattern applies to any PageView + URL routing + reactive state combination (Navigator 2.0,
auto_route, BLoC streams, etc.)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!