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

自古美人都是妖i 提交于 2021-02-10 18:29:27

问题


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.


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(),
      ),
    );
  }
}

回答1:


  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.



来源:https://stackoverflow.com/questions/59487079/update-all-the-attached-widgets-with-the-dragged-widget-of-a-stack

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!