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,
);
}
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
Screenshots or Video
flutter_reorderable_list_issue.webm
Logs
No response
Flutter Doctor output
Doctor output