skip to Main Content

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 : –

3

Answers


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

    Login or Signup to reply.
  2. 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>();
    
    Login or Signup to reply.
  3. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search