Update all the attached widgets with the dragged widget of a stack

243
  1. You should pass the callbacks (onDragStart, onDragEnd) to your TransformedWidget to get notified about the drag events.
  2. Based on callback you should rebuild your parent widget that in your case is WidgetStacks.

Following is the working code for your reference:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<BlockWidget> blockWidgets;
  final List<Color> widgetColors = [
    Colors.red,
    Colors.brown,
    Colors.black,
    Colors.pink,
    Colors.grey
  ];

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

    blockWidgets = new List();
    for (int i = 0; i < widgetColors.length; i++) {
      blockWidgets.add(
          BlockWidget(widgetId: i, widgetColor: widgetColors.elementAt(i)));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text('AppBar'),
      ),
      body: WidgetStacks(
        key:ValueKey('WidgetStacks_${blockWidgets.length}'),
        blocks: blockWidgets,
      ),
    );
  }
}

const blockHeight = 100.0;
const blockWidth = 100.0;

class BlockWidget {
  int widgetId;
  Color widgetColor;

  BlockWidget({
    @required this.widgetId,
    @required this.widgetColor,
  });
}

class TransformedWidget extends StatefulWidget {
  final BlockWidget block;
  final int stackIndex;
  final List<BlockWidget> attachedBlocks;
  final Function() onDragCanceled;
  final Function() onDragStart;

  TransformedWidget({
    Key key,
    @required this.block,
    @required this.stackIndex,
    @required this.attachedBlocks, this.onDragCanceled, this.onDragStart,
  }):super(key: key);

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

class _TransformedWidgetState extends State<TransformedWidget> {
  @override
  Widget build(BuildContext context) {
    return Transform(
      transform: Matrix4.identity()
        ..translate(
          widget.stackIndex * (blockHeight / 2),
          widget.stackIndex * (blockWidth / 2),
          0.0,
        ),
      child: Draggable<Map>(
        key: ValueKey(widget.stackIndex),
        onDragStarted: ()=>widget.onDragStart(),
        onDraggableCanceled: (_,__)=>widget.onDragCanceled(),
        child: _buildBlock(),
        feedback: WidgetStacks(
          key: ValueKey('WidgetStacks_${widget.attachedBlocks.length}'),
          blocks: widget.attachedBlocks,
        ),
        childWhenDragging: Container(),
      ),
    );
  }

  Widget _buildBlock() => Material(
          child: Container(
        height: blockHeight,
        width: blockWidth,
        color: widget.block.widgetColor,
        alignment: Alignment.centerLeft,
        child: Text(
          widget.block.widgetId.toString(),
          style: TextStyle(
            fontSize: 30.0,
            color: Colors.white,
          ),
        ),
      ),
    );
}

class WidgetStacks extends StatefulWidget {
  final List<BlockWidget> blocks;

  WidgetStacks({@required this.blocks, Key key}):super(key:key);

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

class _WidgetStacksState extends State<WidgetStacks> {
  ValueNotifier<List<BlockWidget>> blocksToBeShownNotifier;

  @override
  void initState() {
    blocksToBeShownNotifier = ValueNotifier<List<BlockWidget>>(widget.blocks);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 5 * blockHeight,
      width: 5 * blockWidth,
      margin: EdgeInsets.all(2.0),
      child: ValueListenableBuilder<List<BlockWidget>>(
        valueListenable: blocksToBeShownNotifier,
        builder: (BuildContext context,List<BlockWidget> value, Widget child) {
        return Stack(
        children: value.map((block) {
          int index = value.indexOf(block);
          return TransformedWidget(
            key: ValueKey(block.widgetId),
            block: block,
            stackIndex: index,
            onDragStart:(){
              blocksToBeShownNotifier.value = widget.blocks.sublist(0, index);
            },
            onDragCanceled:(){
              blocksToBeShownNotifier.value = widget.blocks;
            },
            attachedBlocks: widget.blocks.sublist(index),
          );
        }).toList(),
      );
      },),
    );
  }
}

I hope it helps, in case of doubts please comment.

Share:
243
Alex Roy
Author by

Alex Roy

Hi there! I'm a Computer Engineer who loves to code A huge movie geek

Updated on December 16, 2022

Comments

  • Alex Roy
    Alex Roy over 1 year

    Consider a stack of widgets as shown in the image. We can see that index 0 widget is attached with index 1, 2, 3, 4 widgets; ...; index 3 widget is attached with index 4 widget and index 4 widget is not attached with anyone.

    stack of widgets
    Now when I drag a widget, all the attached widget should also be dragged with it
    i.e. if I try to drag the index 2 widget, the dragged widgets should be the stack of index 2, 3, 4 widgets.
    if I try to drag the index 4 widget, the dragged widget should be only the index 4 widget.

    Now I know that I can handle the update of dragged widget with feedback & childWhenDragging parameters of Draggable class, but I don't know how to update all the other attached widgets with it.

    Here is my code:

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MyHomePage(),
        );
      }
    }
    
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      List<BlockWidget> blockWidgets;
      final List<Color> widgetColors = [Colors.red, Colors.brown, Colors.black, Colors.pink, Colors.grey];
    
      @override
      void initState() {
        super.initState();
    
        blockWidgets = new List();
        for(int i=0; i < widgetColors.length; i++) {
          blockWidgets.add(BlockWidget(widgetId: i, widgetColor: widgetColors.elementAt(i)));
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.white,
          appBar: AppBar(
            title: Text('AppBar'),
          ),
          body: WidgetStacks(
            blocks: blockWidgets,
          ),
        );
      }
    }
    
    
    const blockHeight = 100.0;
    const blockWidth = 100.0;
    
    class BlockWidget {
      int widgetId;
      Color widgetColor;
    
      BlockWidget({
        @required this.widgetId,
        @required this.widgetColor,
      });
    }
    
    
    class TransformedWidget extends StatefulWidget {
      final BlockWidget block;
      final int stackIndex;
      final List<BlockWidget> attachedBlocks;
    
      TransformedWidget(
          {@required this.block,
            @required this.stackIndex,
            @ required this.attachedBlocks,
          });
    
      @override
      _TransformedWidgetState createState() => _TransformedWidgetState();
    }
    
    class _TransformedWidgetState extends State<TransformedWidget> {
      @override
      Widget build(BuildContext context) {
        return Transform(
          transform: Matrix4.identity()
            ..translate(
              widget.stackIndex * (blockHeight / 2),
              widget.stackIndex * (blockWidth / 2),
              0.0,
            ),
          child: Draggable<Map>(
            child: _buildBlock(),
            feedback: WidgetStacks(
              blocks: widget.attachedBlocks,
            ),
            childWhenDragging: Container(),
          ),
        );
      }
    
      Widget _buildBlock() {
        return Container(
          height: blockHeight,
          width: blockWidth,
          color: widget.block.widgetColor,
          alignment: Alignment.centerLeft,
          child: Text(
            widget.block.widgetId.toString(),
            style: TextStyle(
              fontSize: 30.0,
              color: Colors.white,
            ),
          ),
        );
      }
    }
    
    
    class WidgetStacks extends StatefulWidget {
      final List<BlockWidget> blocks;
    
      WidgetStacks({@required this.blocks});
    
      @override
      _WidgetStacksState createState() => _WidgetStacksState();
    }
    
    class _WidgetStacksState extends State<WidgetStacks> {
      @override
      Widget build(BuildContext context) {
        return Container(
          height: 5 * blockHeight,
          width: 5 * blockWidth,
          margin: EdgeInsets.all(2.0),
          child: Stack(
            children: widget.blocks.map((block) {
              int index = widget.blocks.indexOf(block);
              return TransformedWidget(
                block: block,
                stackIndex: index,
                attachedBlocks: widget.blocks.sublist(index),
              );
            }).toList(),
          ),
        );
      }
    }