Флаттер: перетаскивание с помощью сетки - PullRequest
0 голосов
/ 09 мая 2018

Я хочу создать виджет, в который вы можете добавить несколько виджетов разных размеров и изменить их положение, используя технику перетаскивания. Что-то вроде вида сетки с перетаскиванием, где вы можете изменить положение как по горизонтали, так и по вертикали. При перетаскивании выбранного виджета другие виджеты будут перемещаться, чтобы освободить место для него.

У кого-нибудь есть предложения, с чего начать, или уже есть примеры, которые реализуют то, что я ищу?

Ответы [ 5 ]

0 голосов
/ 03 марта 2019

Я создал пакет под названием reorderables , который решил эту проблему. Вам просто нужно сообщить пакету, что ваша функция будет вызвана, когда перетаскивание выполнено onReorder(int oldIndex, int newIndex).

В этом примере 9 сетчатых виджетов - Снимок экрана: ReorderableWrap

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

  @override
  void initState() {
    super.initState();
    _tiles = <Widget>[
      Icon(Icons.filter_1, key: ValueKey(1), size: _iconSize),
      Icon(Icons.filter_2, key: ValueKey(2), size: _iconSize),
      Icon(Icons.filter_3, key: ValueKey(3), size: _iconSize),
      Icon(Icons.filter_4, key: ValueKey(4), size: _iconSize),
      Icon(Icons.filter_5, key: ValueKey(5), size: _iconSize),
      Icon(Icons.filter_6, key: ValueKey(6), size: _iconSize),
      Icon(Icons.filter_7, key: ValueKey(7), size: _iconSize),
      Icon(Icons.filter_8, key: ValueKey(8), size: _iconSize),
      Icon(Icons.filter_9, key: ValueKey(9), size: _iconSize),
    ];
  }

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

    return ReorderableWrap(
      spacing: 8.0,
      runSpacing: 4.0,
      padding: const EdgeInsets.all(8),
      children: _tiles,
      onReorder: _onReorder
    );
  }
}

Если вы хотите ограничить количество столбцов, вы можете использовать необязательный параметр с именем maxMainAxisCount

0 голосов
/ 10 января 2019

Вы также можете попробовать это проще (не включает обратную связь)

enter image description here

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: Scaffold(body: HomePage()));
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  Offset offset = Offset.zero;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned(
          left: offset.dx,
          top: offset.dy,
          child: GestureDetector(
            onPanUpdate: (details) {
              setState(() {
                offset = Offset(offset.dx + details.delta.dx, offset.dy + details.delta.dy);
              });
            },
            child: Container(width: 100, height: 100, color: Colors.blue),
          ),
        ),
      ],
    );
  }
}
0 голосов
/ 07 октября 2018

Хотя это может не ответить на ваш вопрос, но люди, которые ищут простой виджет перетаскивания, вот пример.

Смотрите мой второй ответ для более простого способа

enter image description here

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Drag app"),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  double width = 100.0, height = 100.0;
  Offset position ;

  @override
  void initState() {
    super.initState();
    position = Offset(0.0, height - 20);
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned(
          left: position.dx,
          top: position.dy - height + 20,
          child: Draggable(
            child: Container(
              width: width,
              height: height,
              color: Colors.blue,
              child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
            ),
            feedback: Container(
              child: Center(
                child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
              color: Colors.blue[300],
              width: width,
              height: height,
            ),
            onDraggableCanceled: (Velocity velocity, Offset offset){
              setState(() => position = offset);
            },
          ),
        ),
      ],
    );
  }
}
0 голосов
/ 30 ноября 2018

Я не могу писать комментарии из-за своей репутации, но я хотел ответить на этот вопрос из комментариев ответа CopsOnRoad:

Я не хочу показывать отзыв вместо того, что я хочу перетащить оригинальный вид. Возможно ли это?

Если кто-то тоже ищет это, вы можете использовать: childWhenDragging: Container (). Вы по-прежнему перетаскиваете отзыв, но исходный ребенок будет скрыт.

        ...
        child: Draggable(
                child: Container(
                  width: width,
                  height: height,
                  color: Colors.blue,
                  child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
                ),
                feedback: Container(
                  child: Center(
                    child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
                  color: Colors.blue[300],
                  width: width,
                  height: height,
                ),
                childWhenDragging: Container(), // <-- so it looks like the original view is beeing dragged
                onDraggableCanceled: (Velocity velocity, Offset offset){
                  setState(() => position = offset);
                },
              ),
       ...
0 голосов
/ 18 мая 2018

Вот пример перетаскиваемого текста

class DraggableText extends StatefulWidget {
  final Offset initialOffset;
  final String text;

  DraggableText(this.text, this.initialOffset);

  @override
  _DraggableTextState createState() => new _DraggableTextState();
}

class _DraggableTextState extends State<DraggableText> {
  Offset position = new Offset(0.0, 0.0);

  @override
  void initState() {
    super.initState();
    position = widget.initialOffset;
  }

  @override
  Widget build(BuildContext context) {
    final item = new LabelBox(size: new Size.square(100.0), label: widget.text);
    final avatar = new LabelBox(
      size: new Size.square(150.0), label: widget.text, opacity: 0.4);
    final draggable = new Draggable(
      data: widget.text,
      feedback: avatar,
      child: item,
      childWhenDragging: new Opacity(opacity: 0.0, child: item),
      onDraggableCanceled: (velocity, offset) {
        print('_DragBoxState.build -> offset ${offset}');
        setState(() => position = offset);
      });
    return new Positioned(
      left: position.dx, top: position.dy, child: draggable);
  }
}

Полный пример и более продвинутый вы можете посмотреть здесь https://github.com/rxlabz/flutter_dropcity

...