如何使用颤动在按钮网格中滑动/拖动 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 个或更多按钮的主要内容,如果未能解决你的问题,请参考以下文章