使用堆栈的拖动小部件更新所有附加的小部件

Posted

技术标签:

【中文标题】使用堆栈的拖动小部件更新所有附加的小部件【英文标题】:Update all the attached widgets with the dragged widget of a stack 【发布时间】:2020-04-16 14:48:31 【问题描述】:

考虑如图所示的一堆小部件。我们可以看到索引 0 的小部件附加了索引 1、2、3、4 的小部件; ...; index 3 小部件附有 index 4 小部件,而 index 4 小部件未附有任何人。

现在,当我拖动一个小部件时,所有附加的小部件也应该一起拖动 即,如果我尝试拖动 index 2 小部件,则拖动的小部件应该是 index 2、3、4 小部件的堆栈。 如果我尝试拖动 index 4 小部件,则拖动的小部件应该只是 index 4 小部件。

现在我知道我可以使用 Draggable 类的 feedbackchildWhenDragging 参数来处理拖动小部件的更新,但我不知道如何用它更新所有其他附加的小部件。

这是我的代码:

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】:
    您应该将回调(onDragStart、onDragEnd)传递给您的 TransformedWidget 以获得有关拖动事件的通知。 根据回调,您应该重建您的父小部件,在您的情况下是 WidgetStacks。

以下是供您参考的工作代码:

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

希望对你有帮助,如有疑问请留言。

【讨论】:

以上是关于使用堆栈的拖动小部件更新所有附加的小部件的主要内容,如果未能解决你的问题,请参考以下文章

Gridstack 动态创建的小部件不拖动

可在 angular-gridster 中的小部件之间拖动

鼠标可调整大小,可拖动的小部件

用键替换堆栈中的小部件

Flutter:在堆栈小部件中动态添加拖放小部件

Flutter - 堆栈中定位的小部件导致异常