how to implement a drag and drop in flutter using reorderable packages ?

Spread the love

implementing drag and roup in flutter
implementing drag and roup in flutter
implementing drag and droup in flutter
implementing drag and droup in flutter
impelmenting drag and droup in flutter
impelmenting drag and droup in flutter

how to implement a drag and drop in flutter using reorderable packages ?

Introduction:

Implementing drag and drop functionality in Flutter allows users to interact with items in a list or grid by dragging them to a new position. The reorderables package provides a convenient way to implement drag and drop functionality in Flutter. This tutorial will guide you through implementing drag and drop in Flutter using the reorderables package.

Content:

Step 1: Add the dependency:

Add the reorderables package to your pubspec.yaml file:

Save the file and run flutter pub get to install the package.

Step 2: Import the package:

Import the reorderables package in your Dart file:


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

Step 3: Create a list of items:

Create a list of items that you want to make draggable. Each item should be a widget that can be dragged and dropped.


List<String> items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];

Step 4: Create a draggable list:

Create a ReorderableWrap widget to display the draggable list of items. Use the Reorderable widget to make each item draggable.


ReorderableWrap(
  spacing: 8.0,
  runSpacing: 8.0,
  padding: EdgeInsets.all(8.0),
  children: items.map((item) {
    return Reorderable(
      key: ValueKey(item),
      child: Container(
        width: 100.0,
        height: 100.0,
        color: Colors.blue,
        child: Center(
          child: Text(
            item,
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    );
  }).toList(),
  onReorder: (int oldIndex, int newIndex) {
    setState(() {
      if (oldIndex < newIndex) {
        newIndex -= 1;
      }
      final String item = items.removeAt(oldIndex);
      items.insert(newIndex, item);
    });
  },
),

Step 5: Run the app:

Run your Flutter app to see the draggable list in action. You should be able to drag and drop items to reorder them.

Sample Code:


import 'package:flutter/material.dart';
import 'package:reorderables/reorderables.dart';
// import './column_example1.dart';
// import './column_example2.dart';
// import './nested_wrap_example.dart';
// import './row_example.dart';
// import './sliver_example.dart';
// import './table_example.dart';
// import './wrap_example.dart';
import 'dart:math' as math;

class Reorderbalepage extends StatefulWidget {
  Reorderbalepage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

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

class _ReorderbalepageState extends State<Reorderbalepage> {
  int _currentIndex = 0;
  final List<Widget> _examples = [
    TableExample(),
    WrapExample(),
    NestedWrapExample(),
    ColumnExample1(),
    ColumnExample2(),
    RowExample(),
    SliverExample(),
  ];
  final _bottomNavigationColor = Colors.blue;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: _examples[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        fixedColor: _bottomNavigationColor,
        showSelectedLabels: true,
        showUnselectedLabels: false,
        currentIndex: _currentIndex,
        // this will be set when a new tab is tapped
//        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.grid_on, color: _bottomNavigationColor), tooltip: "ReorderableTable", label: "Table"),
          BottomNavigationBarItem(icon: Icon(Icons.apps, color: _bottomNavigationColor), tooltip: "ReorderableWrap", label: "Wrap"),
          BottomNavigationBarItem(icon: Icon(Icons.view_quilt, color: _bottomNavigationColor), tooltip: 'Nested ReroderableWrap', label: "Nested"),
          BottomNavigationBarItem(icon: Icon(Icons.more_vert, color: _bottomNavigationColor), tooltip: "ReorderableColumn 1", label: "Column 1"),
          BottomNavigationBarItem(icon: Icon(Icons.more_vert, color: _bottomNavigationColor), tooltip: "ReroderableColumn 2", label: "Column 2"),
          BottomNavigationBarItem(icon: Icon(Icons.more_horiz, color: _bottomNavigationColor), tooltip: "ReorderableRow", label: "Row"),
          BottomNavigationBarItem(
              icon: Icon(Icons.calendar_view_day, color: _bottomNavigationColor), tooltip: "ReroderableSliverList", label: "SliverList"),
        ],
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}

class ColumnExample2 extends StatefulWidget {
  @override
  _ColumnExample2State createState() => _ColumnExample2State();
}

class _ColumnExample2State extends State<ColumnExample2> {
  late List<Widget> _rows;

  @override
  void initState() {
    super.initState();
    _rows = List<Widget>.generate(
        25,
        (int index) => Container(
              key: ValueKey(index),
              width: double.infinity,
              child: Container(
                margin: EdgeInsets.symmetric(vertical: 5),
                padding: EdgeInsets.symmetric(vertical: 15),
                decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Colors.grey),
                child: Center(
                  child: Text(
                    'This is row $index' " hold drag and droup",
                    textScaleFactor: 1.3,
                    style: TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
                  ),
                ),
              ),
            ));
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _rows.removeAt(oldIndex);
        _rows.insert(newIndex, row);
      });
    }

    Widget reorderableColumn = IntrinsicWidth(
        child: ReorderableColumn(
      header: Text('List-like view but supports IntrinsicWidth'),
//        crossAxisAlignment: CrossAxisAlignment.start,
      children: _rows,
      onReorder: _onReorder,
      onNoReorder: (int index) {
        //this callback is optional
        debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
      },
    ));

    return Container(
      width: double.infinity,
      height: double.maxFinite,
      child: Transform(
        transform: Matrix4.rotationZ(0),
        alignment: FractionalOffset.topLeft,
        child: Material(
          child: Card(child: reorderableColumn),
          elevation: 6.0,
          color: Colors.transparent,
          borderRadius: BorderRadius.zero,
        ),
      ),
    );
  }
}

class TableExample extends StatefulWidget {
  @override
  _TableExampleState createState() => _TableExampleState();
}

class _TableExampleState extends State<TableExample> {
  //The children of ReorderableTableRow must be of type ReorderableTableRow
  late List<ReorderableTableRow> _itemRows;

  @override
  void initState() {
    super.initState();
    var data = [
      ['Alex', 'D', 'B+', 'AA', ''],
      ['Bob', 'AAAAA+', '', 'B', ''],
      ['Cindy', '', 'To Be Confirmed', '', ''],
      ['Duke', 'C-', '', 'Failed', ''],
      ['Ellenina', 'C', 'B', 'A', 'A'],
      ['Floral', '', 'BBB', 'A', 'A'],
      ['Duke', 'C-', '', 'Failed', ''],
      ['Floral', '', 'BBB', 'A', 'A'],
      ['Alex', 'D', 'B+', 'AA', ''],
    ];

    Widget _textWithPadding(String text) {
      return Padding(
        padding: EdgeInsets.symmetric(vertical: 4),
        child: Text(text, textScaleFactor: 1.1),
      );
    }

    _itemRows = data.map((row) {
      return ReorderableTableRow(
        //a key must be specified for each row
        key: ObjectKey(row),
        mainAxisSize: MainAxisSize.max,
        decoration: BoxDecoration(border: Border.all()),
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
          _textWithPadding('${row[0]}'),
          _textWithPadding('${row[1]}'),
          _textWithPadding('${row[2]}'),
          _textWithPadding('${row[3]}'),
//          Text('${row[4]}'),
        ],
      );
    }).toList();
  }

  @override
  Widget build(BuildContext context) {
    var headerRow = ReorderableTableRow(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
      Text('Name', textScaleFactor: 1.5),
      Text('Math', textScaleFactor: 1.5),
      Text('Science', textScaleFactor: 1.5),
      Text('Physics', textScaleFactor: 1.5),
      Text('Sports', textScaleFactor: 1.5)
    ]);

    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        ReorderableTableRow row = _itemRows.removeAt(oldIndex);
        _itemRows.insert(newIndex, row);
      });
    }

    return Container(
      padding: EdgeInsets.symmetric(vertical: 10, horizontal: 0),
      decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), color: Colors.greenAccent),
      child: ReorderableTable(
        header: headerRow,
        children: _itemRows,
        borderColor: const Color(0xFFE0E0E0),
        onReorder: _onReorder,
        onNoReorder: (int index) {
          //this callback is optional
          debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
        },
      ),
    );
  }
}

class WrapExample extends StatefulWidget {
  @override
  _WrapExampleState createState() => _WrapExampleState();
}

class _WrapExampleState extends State<WrapExample> {
  final double _iconSize = 90;
  late List<Widget> _tiles;

  @override
  void initState() {
    super.initState();
    _tiles = <Widget>[
      Icon(Icons.filter_1, size: _iconSize),
      Icon(Icons.filter_2, size: _iconSize),
      Icon(Icons.filter_3, size: _iconSize),
      Icon(Icons.filter_4, size: _iconSize),
      Icon(Icons.filter_5, size: _iconSize),
      Icon(Icons.filter_6, size: _iconSize),
      Icon(Icons.filter_7, size: _iconSize),
      Icon(Icons.filter_8, size: _iconSize),
      Icon(Icons.filter_9, size: _iconSize),
    ];
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _tiles.removeAt(oldIndex);
        _tiles.insert(newIndex, row);
      });
    }

    var wrap = ReorderableWrap(
        spacing: 8.0,
        runSpacing: 4.0,
        padding: const EdgeInsets.all(8),
        children: _tiles,
        onReorder: _onReorder,
        onNoReorder: (int index) {
          //this callback is optional
          debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
        },
        onReorderStarted: (int index) {
          //this callback is optional
          debugPrint('${DateTime.now().toString().substring(5, 22)} reorder started: index:$index');
        });

    var column = Column(
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        wrap,
        ButtonBar(
          alignment: MainAxisAlignment.center,
          children: <Widget>[
            IconButton(
              iconSize: 50,
              icon: Icon(Icons.add_circle),
              color: Colors.deepOrange,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                var newTile = Icon(Icons.filter_9_plus, size: _iconSize);
                setState(() {
                  _tiles.add(newTile);
                });
              },
            ),
            IconButton(
              iconSize: 50,
              icon: Icon(Icons.remove_circle),
              color: Colors.teal,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                setState(() {
                  _tiles.removeAt(0);
                });
              },
            ),
          ],
        ),
      ],
    );

    return SingleChildScrollView(
      child: column,
    );
  }
}

class ColumnExample1 extends StatefulWidget {
  @override
  _ColumnExample1State createState() => _ColumnExample1State();
}

class _ColumnExample1State extends State<ColumnExample1> {
  late List<Widget> _rows;

  @override
  void initState() {
    super.initState();

    _rows = List<ReorderableWidget>.generate(
        10,
        (int index) => ReorderableWidget(
              reorderable: true,
              key: ValueKey(index),
              child: Container(
                  width: double.infinity, child: Align(alignment: Alignment.centerLeft, child: Text('This is row $index', textScaleFactor: 1.5))),
            ));

    _rows += <ReorderableWidget>[
      ReorderableWidget(
        reorderable: false,
        key: ValueKey(10),
        child: Text('This row is not reorderable', textScaleFactor: 2),
      )
    ];

    _rows += List<ReorderableWidget>.generate(
        40,
        (int index) => ReorderableWidget(
              reorderable: true,
              key: ValueKey(11 + index),
              child: Container(
                width: double.infinity,
                child: Align(alignment: Alignment.centerLeft, child: Text('This is row $index', textScaleFactor: 1.5)),
              ),
            ));
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _rows.removeAt(oldIndex);
        _rows.insert(newIndex, row);
      });
    }

    return ReorderableColumn(
      header: Text('THIS IS THE HEADER ROW'),
      footer: Text('THIS IS THE FOOTER ROW'),
      crossAxisAlignment: CrossAxisAlignment.start,
      children: _rows,
      onReorder: _onReorder,
      onNoReorder: (int index) {
        //this callback is optional
        debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
      },
    );
  }
}

class NestedWrapExample extends StatefulWidget {
  NestedWrapExample({
    Key? key,
    this.depth = 0,
    this.valuePrefix = '',
    this.color,
  }) : super(key: key);
  final int depth;
  final String valuePrefix;
  final Color? color;
  final List<Widget> _tiles = [];

  @override
  State createState() => _NestedWrapExampleState();
}

class _NestedWrapExampleState extends State<NestedWrapExample> {
//  List<Widget> _tiles;
  Color? _color;
  Color? _colorBrighter;

  @override
  void initState() {
    super.initState();
    _color = widget.color ?? Colors.primaries[widget.depth % Colors.primaries.length];
    _colorBrighter = Color.lerp(_color, Colors.white, 0.6);
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        widget._tiles.insert(newIndex, widget._tiles.removeAt(oldIndex));
      });
    }

    var wrap = ReorderableWrap(
      spacing: 8.0,
      runSpacing: 4.0,
      padding: const EdgeInsets.all(8),
      children: widget._tiles,
      onReorder: _onReorder,
      onNoReorder: (int index) {
        //this callback is optional
        debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
      },
    );

    var buttonBar = Container(
        color: _colorBrighter,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            IconButton(
              iconSize: 42,
              icon: Icon(Icons.add_circle),
              color: Colors.deepOrange,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                setState(() {
                  widget._tiles.add(Card(
                    child: Container(
                      child: Text('${widget.valuePrefix}${widget._tiles.length}', textScaleFactor: 3 / math.sqrt(widget.depth + 1)),
                      padding: EdgeInsets.all((24.0 / math.sqrt(widget.depth + 1)).roundToDouble()),
                    ),
                    color: _colorBrighter,
                    elevation: 3,
                  ));
                });
              },
            ),
            IconButton(
              iconSize: 42,
              icon: Icon(Icons.remove_circle),
              color: Colors.teal,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                setState(() {
                  widget._tiles.removeAt(0);
                });
              },
            ),
            IconButton(
              iconSize: 42,
              icon: Icon(Icons.add_to_photos),
              color: Colors.pink,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                setState(() {
                  widget._tiles.add(NestedWrapExample(
                    depth: widget.depth + 1,
                    valuePrefix: '${widget.valuePrefix}${widget._tiles.length}.',
                  ));
                });
              },
            ),
            Text('Level ${widget.depth} / ${widget.valuePrefix}'),
          ],
        ));

    var column = Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
      buttonBar,
      wrap,
    ]);

    return SingleChildScrollView(
      child: Container(
        child: column,
        color: _color,
      ),
    );
  }
}

//main() => runApp(MaterialApp(
//  home: Scaffold(
//    appBar: AppBar(),
//    body: NestedWrapExample(),
//  ),
//));import 'package:flutter/material.dart';

class RowExample extends StatefulWidget {
  @override
  _RowExampleState createState() => _RowExampleState();
}

class _RowExampleState extends State<RowExample> {
  late List<Widget> _columns;

  @override
  void initState() {
    super.initState();
    _columns = <Widget>[
      Image.network(
          'https://st.depositphotos.com/1384048/2724/v/950/depositphotos_27248023-stock-illustration-background-internet-vertical.jpg                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          ',
          key: ValueKey('river1.jpg')),
      Image.network(
          'https://c8.alamy.com/comp/2K4EFB4/technology-vertical-wallpaper-with-lines-simulating-internet-data-poster-or-background-of-screen-2K4EFB4.jpg',
          key: ValueKey('river2.jpg')),
      Image.network(
          'https://c8.alamy.com/comp/2K4EFB4/technology-vertical-wallpaper-with-lines-simulating-internet-data-poster-or-background-of-screen-2K4EFB4.jpg',
          key: ValueKey('river3.jpg')),
    ];
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget col = _columns.removeAt(oldIndex);
        _columns.insert(newIndex, col);
      });
    }

    return ReorderableRow(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: _columns,
      onReorder: _onReorder,
      onNoReorder: (int index) {
        //this callback is optional
        debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
      },
    );
  }
}

class SliverExample extends StatefulWidget {
  @override
  _SliverExampleState createState() => _SliverExampleState();
}

class _SliverExampleState extends State<SliverExample> {
  late List<Widget> _rows;

  @override
  void initState() {
    super.initState();
    _rows = List<Widget>.generate(
        50,
        (int index) => Container(
            width: double.infinity, child: Align(alignment: Alignment.centerLeft, child: Text('This is sliver child $index', textScaleFactor: 2))));
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _rows.removeAt(oldIndex);
        _rows.insert(newIndex, row);
      });
    }

    // Make sure there is a scroll controller attached to the scroll view that contains ReorderableSliverList.
    // Otherwise an error will be thrown.
    ScrollController _scrollController = PrimaryScrollController.maybeOf(context) ?? ScrollController();

    return CustomScrollView(
      // A ScrollController must be included in CustomScrollView, otherwise
      // ReorderableSliverList wouldn't work
      controller: _scrollController,
      slivers: <Widget>[
        SliverAppBar(
          expandedHeight: 210.0,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('ReorderableSliverList'),
            background: Image.network('https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Yushan'
                '_main_east_peak%2BHuang_Chung_Yu%E9%BB%83%E4%B8%AD%E4%BD%91%2B'
                '9030.png/640px-Yushan_main_east_peak%2BHuang_Chung_Yu%E9%BB%83'
                '%E4%B8%AD%E4%BD%91%2B9030.png'),
          ),
        ),
        ReorderableSliverList(
          delegate: ReorderableSliverChildListDelegate(_rows),
          // or use ReorderableSliverChildBuilderDelegate if needed
//          delegate: ReorderableSliverChildBuilderDelegate(
//            (BuildContext context, int index) => _rows[index],
//            childCount: _rows.length
//          ),
          onReorder: _onReorder,
          onNoReorder: (int index) {
            //this callback is optional
            debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
          },
          onReorderStarted: (int index) {
            debugPrint('${DateTime.now().toString().substring(5, 22)} reorder started. index:$index');
          },
        )
      ],
    );
  }
}

Output:

implementing drag and roup in flutter
implementing drag and roup in flutter
implementing drag and droup in flutter
implementing drag and droup in flutter
impelmenting drag and droup in flutter
impelmenting drag and droup in flutter

Conclusion:

 

By following these steps, you can easily implement drag and drop functionality in Flutter using the reorderables package. This allows you to create interactive lists and grids that users can rearrange by dragging and dropping items.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *