【问题标题】:How can I select Widgets by dragging over them but also clicking them individually in flutter?如何通过拖动它们来选择小部件,但也可以在颤动中单独单击它们?
【发布时间】:2022-01-13 13:15:34
【问题描述】:

我想创建一个界面,您可以在其中将手指拖过多个区域。这会将区域的状态更改为选定状态(参见图片)。

解决这个问题的最佳方法是什么?

起始位置:

开始拖动:

选择第一个区域:

选择所有区域:

【问题讨论】:

  • 固定内部小部件的区域大小?圆形?

标签: flutter swipe drag hittest tap


【解决方案1】:

当前 Flutter/Dart 版本的代码需要一些更新,但 this 对我有用。

更新代码:


    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!.findAncestorRenderObjectOfType<RenderBox>()!;
    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({required Widget child, required this.index, Key? key}) : super(child: child, key: key);

  @override
  _Foo createRenderObject(BuildContext context) {
    return _Foo(index);
  }

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

class _Foo extends RenderProxyBox {
  int index;
  _Foo(this.index);
}

【讨论】:

  • 我正要回答。我的代码有点不同,所以我认为它可以工作。
  • 非常感谢您的回答。这正是我的想法!
【解决方案2】:

我使用 Rect 类。

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

class StackOverflow extends StatefulWidget {
  const StackOverflow({Key? key}) : super(key: key);

  @override
  _StackOverflowState createState() => _StackOverflowState();
}

class _StackOverflowState extends State<StackOverflow> {
  late List<bool> isSelected;
  late List<GlobalKey> myGlobalKey;
  late List<Offset> offsetWidgets;
  late List<Size> sizeWidgets;
  late List<Rect> listRect;

  @override
  void initState() {
    super.initState();
    isSelected = List.generate(3, (index) => false);
    myGlobalKey = List.generate(3, (index) => GlobalKey());
    offsetWidgets = <Offset>[];
    sizeWidgets = <Size>[];
    listRect = <Rect>[];
    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
      for (final key in myGlobalKey) {
        sizeWidgets
            .add((key.currentContext!.findRenderObject() as RenderBox).size);
        offsetWidgets.add((key.currentContext!.findRenderObject() as RenderBox)
            .localToGlobal(Offset.zero));
      }
      for (int i = 0; i < 3; i++) {
        final dx = offsetWidgets[i].dx + sizeWidgets[i].width;
        final dy = offsetWidgets[i].dy + sizeWidgets[i].height;
        listRect.add(Rect.fromPoints(offsetWidgets[i], Offset(dx, dy)));
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
      onPointerMove: (PointerMoveEvent pointerMoveEvent) {

        if (listRect[0].contains(pointerMoveEvent.position)) {
          if (!isSelected[0]) {
            setState(() {
              isSelected[0] = true;
            });
          }
        } else if (listRect[1].contains(pointerMoveEvent.position)) {
          if (!isSelected[1]) {
            setState(() {
              isSelected[1] = true;
            });
          }
        } else if (listRect[2].contains(pointerMoveEvent.position)) {
          if (!isSelected[2]) {
            setState(() {
              isSelected[2] = true;
            });
          }
        }
      },

      child: Container(
        color: Colors.amber,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            RawMaterialButton(
              key: myGlobalKey[0],
              fillColor: isSelected[0] ? Colors.blueGrey : Colors.transparent,
              shape:
                  const CircleBorder(side: BorderSide(color: Colors.blueGrey)),
              onPressed: () {
                setState(() {
                  isSelected[0] = false;
                });
              },
            ),
            RawMaterialButton(
              key: myGlobalKey[1],
              fillColor: isSelected[1] ? Colors.blueGrey : Colors.transparent,
              shape:
                  const CircleBorder(side: BorderSide(color: Colors.blueGrey)),
              onPressed: () {
                setState(() {
                  isSelected[1] = false;
                });
              },
            ),
            RawMaterialButton(
              key: myGlobalKey[2],
              fillColor: isSelected[2] ? Colors.blueGrey : Colors.transparent,
              shape:
                  const CircleBorder(side: BorderSide(color: Colors.blueGrey)),
              onPressed: () {
                setState(() {
                  isSelected[2] = false;
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    相关资源
    最近更新 更多