Skip to content
Back to skills

Flutter Pageview Url Routing Reorder Loop

ASecurity

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...

  • 265 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
developmentgoreactapi

Works with

  • claude code
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add divinevideo/divine-mobile --skill flutter-pageview-url-routing-reorder-loop --agent claude-code

Installs 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 with every re-scan.

Security grade badge for Flutter Pageview Url Routing Reorder Loop
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/divinevideo-flutter-pageview-url-routing-reorder-loop/badge)](https://www.skillsdirectory.com/skills/divinevideo-flutter-pageview-url-routing-reorder-loop)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…