Skip to content

SliverReorderableList newIndex arg off by one on drag down list #169878

Description

Steps to reproduce

1 - Create new project using flutter create command
2 - Replace code w/ sample code and run
3 - Drag list tiles using the drag icon up and down
4 - Observe index values

Expected results

Dragging from position x to y should return value y as new index
Dragging from position y to x should return value x as new index

Actual results

Dragging from position x to y returns y+1 as new index (INCORRECT RESULT)
Dragging from position y to x returns x as new index (EXPECTED RESULT)

Code sample

Code sample
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const MyHomePage(title: 'Flutter Reorderable List Index Bug'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int newestIndex = 0;
  int expectedIndex = 0;
  final ScrollController c = ScrollController();

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;
    final padding = MediaQuery.of(context).viewPadding;
    final height = (size.height - padding.top - kToolbarHeight) / 2;
    final items = Iterable.generate(10, (index) => 'Draggable Tile');

    final list = SliverReorderableList(
      proxyDecorator: _proxyDecorator,
      itemBuilder: (context, index) {
        final item = items.elementAt(index);

        return Container(
          key: Key('$item-$index'),

          decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
          padding: EdgeInsets.only(bottom: 20),
          margin: EdgeInsets.only(bottom: 5),
          child: ListTile(
            title: Text(item),
            leading: ReorderableDragStartListener(
              enabled: true,
              index: index,
              child: Icon(Icons.reorder),
            ),
          ),
        );
      },

      itemCount: items.length,
      onReorder: (oldIndex, newIndex) {
        setState(() {
          //BUG HERE
          //Dragging down an object shows newest index as 1 greater than expected
          newestIndex = newIndex;
          expectedIndex = oldIndex > newIndex ? newIndex: newIndex - 1 ;
        });
      },
    );

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Newest index:'),
            Text(
              '$newestIndex',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const Text('Expected index:'),
            Text(
              '$expectedIndex',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            SizedBox(
              height: height,
              child: RawScrollbar(
                controller: c,
                child: CustomScrollView(
                  controller: c,
                  scrollDirection: Axis.vertical,
                  shrinkWrap: true,
                  slivers: [list],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Widget _proxyDecorator(Widget child, int index, Animation<double> animation) {
  return AnimatedBuilder(
    animation: animation,
    builder: (BuildContext context, Widget? child) {
      final double animValue = Curves.easeInOut.transform(animation.value);
      final double elevation = lerpDouble(0, 6, animValue)!;
      return Material(elevation: elevation, child: child);
    },
    child: child,
  );
}

Screenshots or Video

flutter_reorderable_list_issue.webm

Logs

No response

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.32.0, on Debian GNU/Linux 12 (bookworm)
    6.1.0-34-amd64, locale en_US.UTF-8) [220ms]
    • Flutter version 3.32.0 on channel stable at /home/k/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision be698c48a6 (2 weeks ago), 2025-05-19 12:59:14 -0700
    • Engine revision 1881800949
    • Dart version 3.8.0
    • DevTools version 2.45.1

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
    [1,701ms]
    • Android SDK at /home/k/Android/Sdk
    • Platform android-35, build-tools 35.0.1
    • Java binary at:
      /home/k/.local/share/JetBrains/Toolbox/apps/android-studio/jbr/bin/java
      This is the JDK bundled with the latest Android Studio installation on
      this machine.
      To manually set the JDK path, use: `flutter config
      --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)
    • All Android licenses accepted.

[✓] Chrome - develop for the web [133ms]
    • Chrome at google-chrome

[✓] Linux toolchain - develop for Linux desktop [558ms]
    • Debian clang version 14.0.6
    • cmake version 3.25.1
    • ninja version 1.11.1
    • pkg-config version 1.8.1
    • GL_EXT_framebuffer_blit: no
    • GL_EXT_texture_format_BGRA8888: no

[✓] Android Studio (version 2024.3.2) [131ms]
    • Android Studio at
      /home/k/.local/share/JetBrains/Toolbox/apps/android-studio
    • Flutter plugin version 85.3.1
    • Dart plugin version 243.26753.1
    • Java version OpenJDK Runtime Environment (build 21.0.6+-13368085-b895.109)

[✓] IntelliJ IDEA Ultimate Edition (version 2024.3) [10ms]
    • IntelliJ at
      /home/k/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart

[✓] VS Code (version unknown) [10ms]
    • VS Code at /usr/share/code
    • Flutter extension can be installed from:
      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
    ✗ Unable to determine VS Code version.

[✓] Connected device (3 available) [106ms]
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 16
      (API 36) (emulator)
    • Linux (desktop)              • linux         • linux-x64      • Debian
      GNU/Linux 12 (bookworm) 6.1.0-34-amd64
    • Chrome (web)                 • chrome        • web-javascript • Google
      Chrome 136.0.7103.92

[✓] Network resources [616ms]
    • All expected network resources are available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: scrollingViewports, list views, slivers, etc.found in release: 3.32Found to occur in 3.32found in release: 3.33Found to occur in 3.33frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer versionteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions