[Flutter/dart] Create a gridview which can be drag and drop
Overview
We often place gridview in the app, but I wanted to be able to drag and drop them.
In that case, use drag_and_drop_gridview.
Method
Basic
first, install the library
drag_and_drop_gridview: ^1.0.8
import it at the top of dart file.
import 'package:drag_and_drop_gridview/devdrag.dart';
The part of grid view is as below.
Widget _myGrid(){
return DragAndDropGridView(
onWillAccept: (oldInd, newInd)=> true,
onReorder: (oldInd, newInd){
changeListOrder(
oldInd: oldInd, newInd: newInd, list: _indexes
);
},
itemCount: ITEM_COUNT,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: CARD_SPACE,
mainAxisSpacing: 24,
),
itemBuilder: (context, index)=> _itemBuilder(index),
);
}
int ITEM_COUNT=8;
List<int> _indexes=[];
static const double PADDING=32;
static const double CARD_SPACE=24;
bool _sizeCalced=false;
double _cardSize;
@override
Widget build(BuildContext context) {
if (!_sizeCalced) {
_cardSize = (MediaQuery.of(context).size.width - 2 * PADDING -
CARD_SPACE) / 2;
_sizeCalced=true;
}
_indexes=List.generate(ITEM_COUNT, (index) => index);
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Center(
child: Container(
padding: const EdgeInsets.all(32),
child: _myGrid(),
width: double.infinity,
),
)
),
);
}
About each property
onWillAccept: Whether to allow it if the order is changed by dragging. This time it is allowed unconditionally.
onReorder: Processing when the order is changed. Here, the elements of _indexes are replaced using the following method.
static void changeListOrder({int oldInd, int newInd, List<int> list}){
int oldVal=list[oldInd];
if (oldInd<newInd){
for (int i=oldInd; i<newInd; i++){
list[i]=list[i+1];
}
}
else{
for (int i=oldInd; i>newInd; i--){
list[i]=list[i-1];
}
}
list[newInd]=oldVal;
}
itemCount: Element count. Note that if you forget to set this, an index error will occur in itemBuilder.
gridDelegate: Set grid placement.
itemBuilder: Create individual views. The second argument means the (index)th element. Here, it is as follows.
Widget _itemBuilder(int index){
int _ind=_indexes[index];
return Container(
height: _cardSize,
width: _cardSize,
child: Center(
child: Text(
_ind.toString(),
style: const TextStyle(
fontSize: 24,
color: Colors.white,
decoration: TextDecoration.none
),
),
),
color: Colors.grey,
);
}
Without decoration: TextDecoration.none, you get a yellow line while dragging.
Also, specify the height and width of the Container explicitly. This is to keep the same size while dragging. This variable _cardSize is calculated at the beginning of build ().
The result is as below.
Change the appearance while dragging
At this rate, the card looks the same when you are dragging and when you are not dragging, so try to change the appearance while dragging.
Set isCustomFeedback to true and specify the widget being dragged in feedback :.
This time, let's change the background color while dragging.
Add an argument to _itemBuilder () to indicate whether it is being dragged.
Widget _itemBuilder(int index, bool onDrag){
int _ind=_indexes[index];
return Container(
height: _cardSize,
width: _cardSize,
child: Center(
child: Text(
_ind.toString(),
style: const TextStyle(
fontSize: 24,
color: Colors.white,
decoration: TextDecoration.none
),
),
),
color: onDrag? Colors.red: Colors.grey,
);
Widget _myGrid(){
return DragAndDropGridView(
onWillAccept: (oldInd, newInd)=> true,
onReorder: (oldInd, newInd){
changeListOrder(
oldInd: oldInd, newInd: newInd, list:_indexes
);
},
itemCount: 8,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: CARD_SPACE,
mainAxisSpacing: 24,
),
itemBuilder: (context, index)=> _itemBuilder(index, false),
isCustomFeedback: true, //here
feedback: (index)=> _itemBuilder(index, true), //here
);
}
The result is as below.
Change the appearance of the original place while dragging
Also, while dragging, change the appearance of the location where the card you are dragging originally located.
To do this, set isCustomChildWhenDragging to true and specify the widget to display in the original location in childWhenDragging.
Widget _myGrid(){
return DragAndDropGridView(
onWillAccept: (oldInd, newInd)=> true,
onReorder: (oldInd, newInd){
changeListOrder(
oldInd: oldInd, newInd: newInd, list: _indexes
);
},
itemCount: 8,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: CARD_SPACE,
mainAxisSpacing: 24,
),
itemBuilder: (context, index)=> _itemBuilder(index, false),
isCustomFeedback: true,
feedback: (index)=> _itemBuilder(index, true),
isCustomChildWhenDragging: true, //here
childWhenDragging: _childWhenDragging, //here
);
}
Widget _childWhenDragging(int index){
return Container(
width: _cardSize,
height: _cardSize,
color: Colors.transparent,
);
}
This time I made it the same color with the background.
The result is as follows.
Recent Posts
See AllWhat want to do I want to create an input form using TextField. For example, if the input content is a monetary amount, I would like to...
What want to do There is a variable that remain unchanged once the initial value is determined. However, it cannot be determined yet when...
What want to do As the title suggests. Place two widgets in one line on the screen One in the center of the screen and the other on the...
Comments