Dart 如何使用颤振在按钮网格中滑动/拖动2个或多个按钮
我已经用flatter创建了一个按钮网格,但是现在我想在一次拖动中滑动2个或更多按钮,这样我拖动的所有按钮都会被选中 我已经检查了同一个问题,我被重定向到使用手势检测器,但这还不够。我需要某些属性或更好的示例代码,以便我能够处理它Dart 如何使用颤振在按钮网格中滑动/拖动2个或多个按钮,dart,flutter,flutter-animation,Dart,Flutter,Flutter Animation,我已经用flatter创建了一个按钮网格,但是现在我想在一次拖动中滑动2个或更多按钮,这样我拖动的所有按钮都会被选中 我已经检查了同一个问题,我被重定向到使用手势检测器,但这还不够。我需要某些属性或更好的示例代码,以便我能够处理它 dragable应用程序的一个例子是我一点也不喜欢这段代码,但它似乎在工作 import 'package:flutter/material.dart'; class TestScaffold extends StatefulWidget { @override
dragable应用程序的一个例子是我一点也不喜欢这段代码,但它似乎在工作
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,
),);
}
}
导入“包装:颤振/材料.省道”;
类TestScaffold扩展了StatefulWidget{
@凌驾
State createState()=>_TestScaffoldState();
}
列表_selectedList=[];
类_TestScaffoldState扩展状态{
列表按钮=[
_SquareButton('1'),
_SquareButton('2'),
_SquareButton('3'),
_SquareButton('4'),
_方形按钮('5'),
_方形按钮('6'),
_SquareButton('7'),
_SquareButton('8'),
_SquareButton('9'),
_方形按钮('10'),
_SquareButton('11'),
_方形按钮('12'),
_SquareButton('13'),
_方形按钮('14'),
_SquareButton('15'),
_SquareButton('16'),
];
地图位置={};
@凌驾
小部件构建(构建上下文){
返回脚手架(
appBar:appBar(标题:文本('Test'),),
正文:手势检测器(
onPanDown:(详细信息){
勾选手势(细节。全局位置);
},
onPanUpdate:(详细信息){
勾选手势(细节。全局位置);
},
子项:GridView.count(crossAxisCount:4,
物理学:NeverscrollableScroll物理学(),
子项:按钮,),)
);
}
initPositions(){
如果(positions.isNotEmpty)返回;
按钮。forEach((btn){
RenderBox=btn.bKey.currentContext.FinderObject();
Offset start=box.localToGlobal(Offset.zero);
Rect Rect=Rect.fromLTWH(start.dx,start.dy,box.size.width,box.size.height);
positions.addAll({rect:btn});
});
}
检查手势(偏移位置){
initPositions();
位置。forEach((矩形,btn){
if(矩形包含(位置)){
如果(!\u selectedList.contains(btn)){
_选择列表。添加(btn);
btn.state.setState((){});
}
}
});
}
}
类SquareButton扩展StatefulWidget{
_SquareButton(此标题);
最后的字符串标题;
最终GlobalKey bKey=GlobalKey();
国家;
@凌驾
状态createState(){
状态=_SquareButtonState();
返回状态;
}
}
类_SquareButtonState扩展了状态{
@凌驾
小部件构建(构建上下文){
返回填充(键:widget.bKey,填充:EdgeInsets.all(4.0),子项:Container(
颜色:_selectedList.contains(小部件)?Colors.tealacent:Colors.teal,
子:文本(widget.title),
对齐:对齐.center,
),);
}
}
还有一刻。
如果启用滚动-GestureDetector不总是在垂直移动上工作,则可以手动点击test RenderBox并提取您选择的特定RenderObject 例如,我们可以在按钮上方添加以下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;
}
然后使用侦听器提取指针下找到的所有\u Foo
下面是使用此原则的完整应用程序:
import'package:flatter/signatures.dart';
进口“包装:颤振/材料.省道”;
导入“package:flatter/rendering.dart”;
void main(){
runApp(MyApp());
}
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
标题:“颤振演示”,
主题:主题数据(原始样本:颜色。蓝色),
主页:Grid(),
);
}
}
类网格扩展了StatefulWidget{
@凌驾
GridState createState(){
返回新的GridState();
}
}
类GridState扩展了状态{
最终集合SelectedIndex=Set();
final key=GlobalKey();
最终设置_trackTaped=Set();
_DetecttAppItem(PointerEvent事件){
final RenderBox=key.currentContext.finderObject();
最终结果=BoxHitTestResult();
偏移本地=框全局全局(事件位置);
if(方框hitTest(结果、位置:本地)){
for(result.path中的最终命中){
///临时变量,[is]允许访问[index]
最终目标=命中目标;
if(目标是_Foo&&!_trackTaped.contains(目标)){
_trackTaped.add(目标);
_选择索引(target.index);
}
}
}
}
_选择索引(整数索引){
设置状态(){
选择索引。添加(索引);
});
}
@凌驾
小部件构建(构建上下文){
返回侦听器(
onPointerDown:\u detectTapedItem,
onPointerMove:\u detectTapedItem,
onPointerUp:,
子项:GridView.builder(
钥匙:钥匙,
物品计数:6,
物理学:NeverscrollableScroll物理学(),
gridDelegate:SliverGridDelegateWithFixedCrossAxisCount(
交叉轴计数:3,
childAspectRatio:1.0,
交叉轴间距:5.0,
主轴间距:5.0,
),
itemBuilder:(上下文,索引){
返回Foo(
索引:索引,,
子:容器(
颜色:已选择索引。包含(索引)?颜色。红色:颜色。蓝色,
),
);
},
),
);
}
无效(PointerUpEvent事件){
_trackTaped.clear();
设置状态(){
selectedIndex.clear();
});
}
}
类Foo扩展了SingleChildRenderObjectWidget{
最终整数指数;
Foo({Widget child,this.index,Key}):super(child:child,Key:Key);
@凌驾
_Foo createRenderObject(构建上下文){
返回_Foo()…index=index;
}
@凌驾
void updatenderObject(BuildContext上下文,_foorenderObject){
renderObject..index=索引;
}
}
类_Foo扩展了RenderProxyBox{
整数指数;
}
Listener是更好的解决方案。作为手势检测器blo
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;
}