如何使用颤动在按钮网格中滑动/拖动 2 个或更多按钮

Posted

技术标签:

【中文标题】如何使用颤动在按钮网格中滑动/拖动 2 个或更多按钮【英文标题】:How to swipe/drag 2 or more buttons in a grid of buttons using flutter 【发布时间】:2018-11-11 08:57:13 【问题描述】:

我已经使用颤振制作了一个按钮网格,但现在我想在一次拖动中滑动 2 个或更多按钮,以便选中我拖动的所有按钮。

我已经检查了一些相同的问题,我被重定向到使用手势检测器,但这还不够。我需要某些属性或更好的示例代码,以便我能够完成它。

可拖动应用的示例是http://a5.mzstatic.com/us/r30/Purple60/v4/6f/00/35/6f0035d3-1bab-fcbb-cb13-8ab46cf3c44d/screen696x696.jpeg

【问题讨论】:

【参考方案1】:

您可以手动点击测试 RenderBox 并提取您选择的特定 RenderObject。

例如,我们可以在按钮上方添加以下渲染对象:

class Foo extends SingleChildRenderObjectWidget 
  final int index;

  Foo(Widget child, this.index, Key key) : super(child: child, key: key);

  @override
  RenderObject createRenderObject(BuildContext context) 
    return _Foo()..index = index;
  

  @override
  void updateRenderObject(BuildContext context, _Foo renderObject) 
    renderObject..index = index;
  


class _Foo extends RenderProxyBox 
  int index;

然后使用Listener提取指针下找到的所有_Foo

这是一个使用此原理的完整应用程序:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: Grid(),
    );
  


class Grid extends StatefulWidget 
  @override
  GridState createState() 
    return new GridState();
  


class GridState extends State<Grid> 
  final Set<int> selectedIndexes = Set<int>();
  final key = GlobalKey();
  final Set<_Foo> _trackTaped = Set<_Foo>();

  _detectTapedItem(PointerEvent event) 
    final RenderBox box = key.currentContext.findRenderObject();
    final result = BoxHitTestResult();
    Offset local = box.globalToLocal(event.position);
    if (box.hitTest(result, position: local)) 
      for (final hit in result.path) 
        /// temporary variable so that the [is] allows access of [index]
        final target = hit.target;
        if (target is _Foo && !_trackTaped.contains(target)) 
          _trackTaped.add(target);
          _selectIndex(target.index);
        
      
    
  

  _selectIndex(int index) 
    setState(() 
      selectedIndexes.add(index);
    );
  

  @override
  Widget build(BuildContext context) 
    return Listener(
      onPointerDown: _detectTapedItem,
      onPointerMove: _detectTapedItem,
      onPointerUp: _clearSelection,
      child: GridView.builder(
        key: key,
        itemCount: 6,
        physics: NeverScrollableScrollPhysics(),
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          childAspectRatio: 1.0,
          crossAxisSpacing: 5.0,
          mainAxisSpacing: 5.0,
        ),
        itemBuilder: (context, index) 
          return Foo(
            index: index,
            child: Container(
              color: selectedIndexes.contains(index) ? Colors.red : Colors.blue,
            ),
          );
        ,
      ),
    );
  

  void _clearSelection(PointerUpEvent event) 
    _trackTaped.clear();
    setState(() 
      selectedIndexes.clear();
    );
  


class Foo extends SingleChildRenderObjectWidget 
  final int index;

  Foo(Widget child, this.index, Key key) : super(child: child, key: key);

  @override
  _Foo createRenderObject(BuildContext context) 
    return _Foo()..index = index;
  

  @override
  void updateRenderObject(BuildContext context, _Foo renderObject) 
    renderObject..index = index;
  


class _Foo extends RenderProxyBox 
  int index;

【讨论】:

很好的答案!我不确定您为什么需要 _trackTaped 集。您可以简单地检查 target.index 是否不在 selectedIndexes 集中? 嗯,是的。但我这样做是因为最终你不应该将 selectedIndexes 存储在这个类中。相反,你会有一个回调,但我这样做是为了示例【参考方案2】:

我根本不喜欢这段代码,但它似乎可以工作

import 'package:flutter/material.dart';

class TestScaffold extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => _TestScaffoldState();


List<_SquareButton> _selectedList = [];

class _TestScaffoldState extends State<TestScaffold> 

  List<_SquareButton> buttons = [
    _SquareButton('1'),
    _SquareButton('2'),
    _SquareButton('3'),
    _SquareButton('4'),
    _SquareButton('5'),
    _SquareButton('6'),
    _SquareButton('7'),
    _SquareButton('8'),
    _SquareButton('9'),
    _SquareButton('10'),
    _SquareButton('11'),
    _SquareButton('12'),
    _SquareButton('13'),
    _SquareButton('14'),
    _SquareButton('15'),
    _SquareButton('16'),
  ];
  Map<Rect, _SquareButton> positions = ;

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(title: Text('Test'),),
      body: GestureDetector(
        onPanDown: (details) 
          checkGesture(details.globalPosition);
        ,
        onPanUpdate: (details) 
          checkGesture(details.globalPosition);
        ,
        child: GridView.count(crossAxisCount: 4,
        physics: NeverScrollableScrollPhysics(),
        children: buttons,),)
    );
  

  initPositions() 
    if (positions.isNotEmpty) return;
    buttons.forEach((btn) 
      RenderBox box = btn.bKey.currentContext.findRenderObject();
      Offset start = box.localToGlobal(Offset.zero);
      Rect rect = Rect.fromLTWH(start.dx, start.dy, box.size.width, box.size.height);
      positions.addAll(rect: btn);
    );
  

  checkGesture(Offset position) 
    initPositions();
    positions.forEach((rect, btn) 
      if (rect.contains(position)) 
        if (!_selectedList.contains(btn)) 
            _selectedList.add(btn);
        btn.state.setState(());
        
      
    );
  


class _SquareButton extends StatefulWidget 
  _SquareButton(this.title);
  final String title;
  final GlobalKey bKey = GlobalKey();
  State state;
  @override
  State<StatefulWidget> createState() 
    state = _SquareButtonState();
    return state;
  


class _SquareButtonState extends State<_SquareButton> 
  @override
  Widget build(BuildContext context) 
    return Padding(key: widget.bKey, padding: EdgeInsets.all(4.0), child: Container(
        color: _selectedList.contains(widget) ? Colors.tealAccent : Colors.teal,
        child: Text(widget.title),
        alignment: Alignment.center,
      ),);
  

有一瞬间。 如果启用滚动 - GestureDetector 并不总是适用于垂直移动

【讨论】:

监听器会是更好的解决方案。由于 GestureDetector 在启动一个事件时会阻止其他事件(例如,这会阻止垂直+水平平移) 谁说 OP 想要一些卷轴?

以上是关于如何使用颤动在按钮网格中滑动/拖动 2 个或更多按钮的主要内容,如果未能解决你的问题,请参考以下文章

在按钮按下颤动时开始滑入动画

如何在手指从1按钮拖动到下一个按钮时触发UIButton事件

matlabgui滑动条看不见

如何限制可拖动滚动表根据其子高度在颤动中获取高度?

滑动列表项以获取更多选项(Flutter)

在颤动中打开对话框时检测返回按钮按下