1

I want to achieve the functionality along with animation of reorderableListView Widget when it is dragged and dropped but instead of dragging how could it be done explicitly by a button click?

Expected Output : -

This is what I have done , remove the desired item and added a new item on that index.

Code : -

import 'package:flutter/material.dart';

void main() => runApp(const ReorderableApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ReorderableListView Sample')),
        body: const ReorderableExample(),
      ),
    );
  }
}

class ReorderableExample extends StatefulWidget {
  const ReorderableExample({super.key});

  @override
  State<ReorderableExample> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<ReorderableExample> {
  final List<int> _items = List<int>.generate(4, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Container(
            height: 300,
            width: 300,
            color: Colors.blue[100],
            padding: const EdgeInsets.all(10),
            child: Center(
              child: ReorderableListView(
                children: <Widget>[
                  for (int index = 0; index < _items.length; index += 1)
                    Container(
                      key: Key('$index'),
                      height: 60,
                      margin: const EdgeInsets.all(5),
                      color: Colors.blue[400],
                      alignment: Alignment.center,
                      child: Text('Item ${_items[index]}'),
                    ),
                ],
                onReorder: (int oldIndex, int newIndex) {},
              ),
            ),
          ),
          const SizedBox(
            height: 20,
          ),
          FloatingActionButton(
            onPressed: () {
              setState(() {
                final int item = _items.removeAt(0);
                _items.insert(1, item);
              });
            },
            child: const Icon(Icons.arrow_downward_sharp),
          ),
        ],
      ),
    );
  }
}

Actual Output : -

Ramji
  • 904
  • 2
  • 5
  • 20

3 Answers3

1

You should create reorder method like this:

 void reorderData(int oldindex, int newindex){
  setState(() {
    if(newindex>oldindex){
     newindex-=1;
    }
    final items =widget.item.removeAt(oldindex);
    widget.item.insert(newindex, items);
  });
 } 

Then assign it to onReorder parameter in your ReorderListView like that

ReorderableListView(
    children: <Widget>[
      for (int index = 0; index < _items.length; index += 1)
         Container(
             key: Key('$index'),
             height: 60,
             margin: const EdgeInsets.all(5),
             color: Colors.blue[400],
             alignment: Alignment.center,
             child: Text('Item ${_items[index]}'),
          ),
   ],
   onReorder: reorderData,
 )  

This is an article about ReorderableListView

Ahmad Hasan
  • 101
  • 5
0

If you don't mind not having the animation, I would use a normal ListView:

ListView(
      children: [
        for(var i = 0; i < _items.length; i++) ItemWidget(_items[I])
       ],
     )

Note though that this won't be an optimal solution for many children.

Nick is tired
  • 6,860
  • 20
  • 39
  • 51
ben
  • 33
  • 4
0

You can use the great_list_view package.

An example of using this package:

import 'package:flutter/material.dart';
import 'package:great_list_view/great_list_view.dart';

void main() {
  Executor().warmUp();
  runApp(App());
}

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Test App',
        home: SafeArea(
            child: Scaffold(
          body: Body(key: gkey),
        )));
  }
}

class Body extends StatefulWidget {
  Body({Key? key}) : super(key: key);

  @override
  _BodyState createState() => _BodyState();
}

class _BodyState extends State<Body> {
  late List<ItemData> currentList;

  @override
  void initState() {
    super.initState();
    currentList = listA;
  }

  void swapList() {
    setState(() {
      if (currentList == listA) {
        currentList = listB;
      } else {
        currentList = listA;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scrollbar(
      child: AutomaticAnimatedListView<ItemData>(
        list: currentList,
        comparator: AnimatedListDiffListComparator<ItemData>(
            sameItem: (a, b) => a.id == b.id,
            sameContent: (a, b) =>
                a.color == b.color && a.fixedHeight == b.fixedHeight),
        itemBuilder: (context, item, data) => data.measuring
            ? Container(
                margin: EdgeInsets.all(5), height: item.fixedHeight ?? 60)
            : Item(data: item),
        listController: controller,
        addLongPressReorderable: true,
        reorderModel: AutomaticAnimatedListReorderModel(currentList),
        detectMoves: true,
      ),
    );
  }
}

class Item extends StatelessWidget {
  final ItemData data;

  const Item({Key? key, required this.data}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () => gkey.currentState?.swapList(),
        child: AnimatedContainer(
            height: data.fixedHeight ?? 60,
            duration: const Duration(milliseconds: 500),
            margin: EdgeInsets.all(5),
            padding: EdgeInsets.all(15),
            decoration: BoxDecoration(
                color: data.color,
                border: Border.all(color: Colors.black12, width: 0)),
            child: Center(
                child: Text(
              'Item ${data.id}',
              style: TextStyle(fontSize: 16),
            ))));
  }
}

class ItemData {
  final int id;
  final Color color;
  final double? fixedHeight;
  const ItemData(this.id, [this.color = Colors.blue, this.fixedHeight]);
}

List<ItemData> listA = [
  ItemData(1, Colors.orange),
  ItemData(2),
  ItemData(3),
  ItemData(4, Colors.cyan),
  ItemData(5),
  ItemData(8, Colors.green)
];
List<ItemData> listB = [
  ItemData(4, Colors.cyan),
  ItemData(2),
  ItemData(6),
  ItemData(5, Colors.pink, 100),
  ItemData(7),
  ItemData(8, Colors.yellowAccent),
];

final controller = AnimatedListController();
final gkey = GlobalKey<_BodyState>();
Just a Person
  • 1,276
  • 1
  • 5
  • 23
  • The animation effect is different, in this library, the items vanishes and reappears again. I don't want it to disappear. I checked its parameter for animation while reordering but I could not find any. – Ramji Apr 03 '23 at 10:01
  • You can add `onFadeTransaction` property to false. For details: see this issue: https://github.com/DavideBelsole/great_list_view/issues/29 – Just a Person Apr 03 '23 at 11:37
  • I set the property to false and used a custom animator too, still item disappears – Ramji Apr 05 '23 at 02:27
  • Can you edit your question to post what custom animator you used? – Just a Person Apr 05 '23 at 02:40
  • I have shared the full code on this note pad : - https://notepad.link/CRafj – Ramji Apr 05 '23 at 04:07
  • At last I found something useful which can be used by you without any external dependencies: https://stackoverflow.com/questions/69422633/move-an-item-from-one-list-to-another-with-animation-in-flutter – Just a Person Apr 06 '23 at 07:15