skip to Main Content

I am attempting to create a Drag and Drop with a GridView.custom. What I desire to have happen, is that when dragging an item, if it reaches the top or bottom of the screen, the gridview scrolls. Is there a build in way to do that, or is there a work around that has to be implemented. I am using the flutter_staggered_grid_view package in this case.

GridView.custom(
      shrinkWrap: true,
      primary: false,
      scrollDirection: Axis.vertical,
      gridDelegate: SliverQuiltedGridDelegate(
        crossAxisCount: 4,
        mainAxisSpacing: 10,
        crossAxisSpacing: 10,
        repeatPattern: QuiltedGridRepeatPattern.inverted,
        pattern: widget.pattern,
      ),
      childrenDelegate: SliverChildBuilderDelegate(
        childCount: widget.children.length,
        (context, index) {
          var selectedWidget = widget.children[index];
          LongPressDraggable<GlobalKey>(
                  data: selectedWidget.key,
                  onDragStarted: _onDragStarted,
                  onDragEnd: _onDragEnd,
                  feedback:
                      SizedBox(width: 100, height: 100, child: selectedWidget),
                  childWhenDragging: Container(),
                  child: DragTarget<GlobalKey>(
                    builder: (context, accepted, rejected) => selectedWidget,
                    onWillAccept: (GlobalKey? accept) {
                      return true;
                    },
                    onAccept: (GlobalKey item) {
                      int startIndex =
                          widget.children.indexWhere((x) => x.key == item);
                      int endIndex = widget.children
                          .indexWhere((x) => x.key == selectedWidget.key);
                      widget.onReorder(startIndex, endIndex);
                    },
                  ),
                )
              },
            ),
           );

2

Answers


  1. Chosen as BEST ANSWER

    After some research, I cobbled together a method that created the effect I desired.

    
    
    return Stack(
          children: [
            renderDraggableGrid(),
            _isDragStart
                ? Align(
                    alignment: Alignment.topCenter,
                    child: DragTarget(
                      builder: (context, accepted, rejected) => Container(
                        height: 40,
                        width: double.infinity,
                        color: Colors.transparent,
                      ),
                      onWillAccept: (GlobalKey? accept) {
                        _moveUp();
                        return false;
                      },
                    ),
                  )
                : Container(),
            _isDragStart
                ? Align(
                    alignment: Alignment.bottomCenter,
                    child: DragTarget(
                      builder: (context, accepted, rejected) => Container(
                        height: 40,
                        width: double.infinity,
                        color: Colors.transparent,
                      ),
                      onWillAccept: (GlobalKey? accept) {
                        _moveDown();
                        return false;
                      },
                    ),
                  )
                : Container()
          ],
        );
    
    _moveUp() {
        _scrollController!.animateTo(_scrollController!.offset - _gridViewHeight,
            curve: Curves.linear, duration: Duration(milliseconds: 500));
      }
    
      _moveDown() {
        _scrollController!.animateTo(_scrollController!.offset + _gridViewHeight,
            curve: Curves.linear, duration: Duration(milliseconds: 500));
      }
    

    I also added ClampingScrollPhysics() to avoid over scrolling. Credit to https://github.com/DevOrbiter/drag_and_drop_gridview/ for giving me the base code.


  2. compare Draggable position of the Y axis in GridView area.

    Here is the sample; Scroll up if Draggle positions on a top area where is height 20, scroll down if it positions on a bottom area where is height 20.

    import 'dart:async';
    import 'dart:math';
    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MaterialApp(home: MyApp()));
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
    
      final List<int> droppedIndexList = [];
      late final ScrollController scrollController;
    
      final GlobalKey gridViewKey = GlobalKey();
    
      void initState() {
        super.initState();
        scrollController = ScrollController();
        Timer.periodic(const Duration(milliseconds: 100), (_) {
          if(scrollDirection == 0) {
            return;
          } else if(scrollDirection == 1) { // scroll down
            scrollController.animateTo(
              min(scrollController.offset + 20, scrollController.position.maxScrollExtent),
              duration: const Duration(milliseconds: 100),
              curve: Curves.linear,
            );
          } else if(scrollDirection == -1) { // scroll up
            scrollController.animateTo(
              max(scrollController.offset - 20, scrollController.position.minScrollExtent),
              duration: const Duration(milliseconds: 100),
              curve: Curves.linear,
            );
          }
        });
      }
    
      int scrollDirection = 0;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Padding(
              padding: const EdgeInsets.symmetric(vertical: 100),
              child: SizedBox(
                width: 600,
                child: Column(
    
                  children: [
                    SizedBox(
                      height: 200,
                      child: Center(
                        child: Draggable(
                          onDragUpdate: (details) {
                            final s = gridViewKey.currentContext!.size!;
                            final p = (gridViewKey.currentContext!.findRenderObject() as RenderBox).globalToLocal(details.globalPosition);
    
                            print(p.dy);
    
                            if(0 <= p.dy && p.dy <= 20) {
                              scrollDirection = -1;
                            } else if(s.height - 20 <= p.dy && p.dy <= s.height) {
                              scrollDirection = 1;
                            } else {
                              scrollDirection = 0;
                            }
                          },
                          data: Colors.black,
                          feedback: Container(
                            width: 100,
                            height: 100,
                            color: Colors.black,
                          ),
                          child: Container(
                            width: 100,
                            height: 100,
                            color: Colors.black,
                          ),
                        ),
                      ),
                    ),
                    Expanded(
                      child: GridView.builder(
                        key: gridViewKey,
                        controller: scrollController,
                        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                          crossAxisCount: 3,
                        ),
                        itemCount: 300,
                        itemBuilder: (context, index) {
                          return DragTarget<Color>(
                            onAccept: (color) {
                              setState(() {
                                droppedIndexList.add(index);
                              });
                            },
                            builder: (BuildContext context, List<Color?> candidateData, List<dynamic> rejectedData) {
    
                              return Container(
                                width: 200,
                                height: 200,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    color: Colors.black,
                                    width: 1,
                                  ),
                                  color: droppedIndexList.contains(index) ? Colors.black : Colors.transparent,
                                ),
                                child: Center(
                                  child: Text(
                                      "$index"
                                  ),
                                ),
                              );
                            },
                          );
                        }
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      }
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search