【问题标题】:How to swipe/drag 2 or more buttons in a grid of buttons using flutter如何使用颤动在按钮网格中滑动/拖动 2 个或更多按钮
【发布时间】: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

【问题讨论】:

    标签: dart flutter flutter-animation


    【解决方案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 想要一些卷轴?
    猜你喜欢
    • 1970-01-01
    • 2019-02-01
    • 2022-11-01
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 2014-03-05
    相关资源
    最近更新 更多