mosh.jinton
mosh.jinton

Reputation: 328

Flutter ReorderableListView -- resizing item on drag and adjusting layout of other items accordingly

I have a ReorderableListView, and I'm using proxyDecorator to detect when an item starts being dragged and then adjust its height, and the height of the underlying Widget which is actually present in ListView (as I assume the widget returned by proxyDecorator is just used to show what is being dragged.) The issue is that the 'space' in the list where the dragged item was, or is being moved to, remains its original size, rather than reflecting the new size of the widget. Once the dragged item is placed, if I disable the code to return it to it's original size, the rest of the listview bunches up to reflect the new size. But I want this to happen as soon as the drag begins. Is this possible? I have tried using a setState call in the proxyDecorator, but this causes an error with triggering a rebuild during a rebuild.

Upvotes: 3

Views: 2819

Answers (2)

okan
okan

Reputation: 934

You can achieve it smoothly with proxyDecorator member of ReorderableListView. Check the code below:

proxyDecorator: (child, index, animation) {
  return Material(
    color: Colors.transparent,
    child: ScaleTransition(
      scale: animation.drive(
        Tween<double>(begin: 1, end: 1.1).chain(
          CurveTween(curve: Curves.linear),
        ),
      ),
      child: child,
    ),
  );
},

If you want to scale down the widget instead of scaling up when dragging starts, you should change the end value to a smaller value, like 0.8.

Upvotes: 1

WSBT
WSBT

Reputation: 36323

I'm not sure what you meant by proxy decorator, but if you just want to resize an item when it's being dragged, it can be done.

demo gif

Obviously this is just a quick demo, lots of edge cases are not tested. But if you are interested, here's the full source code used in the above demo:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  final colors = [
    Colors.red,
    Colors.blue,
    Colors.green,
    Colors.orange,
    Colors.purple,
    Colors.pink,
    Colors.cyan
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ReorderableListView'),
      ),
      body: ReorderableListView(
        children: colors
            .map((color) => Box(
                  key: ValueKey(color),
                  color: color,
                ))
            .toList(),
        onReorder: (int oldIndex, int newIndex) {
          if (newIndex > oldIndex) newIndex--;
          setState(() {
            final color = colors.removeAt(oldIndex);
            colors.insert(newIndex, color);
          });
        },
      ),
    );
  }
}

class Box extends StatefulWidget {
  final Color color;

  const Box({Key? key, required this.color}) : super(key: key);

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

class _BoxState extends State<Box> {
  bool _big = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (_) => setState(() => _big = true),
      onTapCancel: () => setState(() => _big = false),
      child: AnimatedContainer(
        duration: Duration(milliseconds: 200),
        height: _big ? 100 : 50,
        margin: EdgeInsets.all(4.0),
        color: widget.color,
      ),
    );
  }
}

Upvotes: 3

Related Questions