添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
微醺的伤痕  ·  MySQL :: MySQL 8.4 ...·  12 月前    · 
俊秀的小刀  ·  jquery ...·  1 年前    · 
好帅的签字笔  ·  ios 通知权限判断-掘金·  2 年前    · 

比如,我想把最近使用的 红包 添加到 我的应用 当中,支付宝这里是用的 + 号。

那如果产品说我就想让它拖过去,这可咋整?

不慌,Flutter 也为我们提供了相关的 Widget。

Draggable

Flutter 如果要实现这种效果,那么非 Draggable 不可。

照例我们查看官方文档。

A widget that can be dragged from to a DragTarget.
可拖动到 DragTarget 的小部件。
复制代码

那也就是说,除了 Draggable ,还有一个 DragTarget。

DragTarget 是用来接收我们拖过去的 Widget 的,我们后面再说。

先说Draggable。

在官方文档找了一圈没发现Demo,那没办法了,直接开撸。

先看构造函数:

class Draggable<T> extends StatefulWidget {
  /// Creates a widget that can be dragged to a [DragTarget].
  /// The [child] and [feedback] arguments must not be null. If
  /// [maxSimultaneousDrags] is non-null, it must be non-negative.
  const Draggable({
    Key key,
    @required this.child,
    @required this.feedback,
    this.data,
    this.axis,
    this.childWhenDragging,
    this.feedbackOffset = Offset.zero,
    this.dragAnchor = DragAnchor.child,
    this.affinity,
    this.maxSimultaneousDrags,
    this.onDragStarted,
    this.onDraggableCanceled,
    this.onDragEnd,
    this.onDragCompleted,
    this.ignoringFeedbackSemantics = true,
  }) : assert(child != null),
       assert(feedback != null),
       assert(ignoringFeedbackSemantics != null),
       assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0),
       super(key: key);
复制代码

可以看到该类还支持泛型,这个泛型是来界定 data 的,后面再说。

先来看看必须的参数,child 和 feedback。

child 应该不比多说,说一下 feedback。

点击查看feedback 参数,上面的注释这样写着:

当拖动正在进行时在指针下显示的小部件。

既然搞懂了必传参数,那就开撸:

Widget _createGridView(List<String> _items) {
  return GridView.builder(
    itemCount: _items.length,
    shrinkWrap: true, // 相当于Android的 wrap_content ,也就是包裹住该 widget的高度
    physics: NeverScrollableScrollPhysics(), // 不允许滑动
    padding: EdgeInsets.all(10),
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 5,  // 设置 gridview 每一行的数量
      mainAxisSpacing: 10,
      crossAxisSpacing: 10,
    itemBuilder: (context, index) {
      return Draggable( // 返回一个Draggable
        // 必须要一个Material,不然拖动时Text会有双下划线
        feedback: Material(
          child: Container(
            height: 100,
            width: 100,
            color: Colors.blueAccent,
            alignment: Alignment.center,
            child: Text(
              _items[index],
              style: TextStyle(color: Colors.white),
        child: Container(
          color: Colors.blueAccent,
          alignment: Alignment.center,
          child: Text(
            _items[index],
            style: TextStyle(color: Colors.white),
复制代码

我们定义一个 GridView,里面每一个 item 都是一个 Draggable。

来运行一下看效果:

可以看到我们确实是可以拖动了,大功已经告成一半了。

那么我们下面开始定义接收的部件 DragTarget。

DragTarget

废话也不多说,直接看构造函数,看看什么是必填:

class DragTarget<T> extends StatefulWidget {
  /// Creates a widget that receives drags.
  /// The [builder] argument must not be null.
  const DragTarget({
    Key key,
    @required this.builder,
    this.onWillAccept,
    this.onAccept,
    this.onLeave,
  }) : super(key: key);
复制代码

可以看到必传的参数也就是一个builder,用来构建我们的页面使用。

其他参数看名字也都能明白:

  • onWillAccept 拖到该控件上时调用
  • onAccept 放到该控件时调用
  • onLeave 没有放到该控件时调用
  • 那我们这里只需要一个确认已经放到该控件时的回调,来接收我们传过来的值。

    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('DraggablePage'),
        body: Column(
          children: <Widget>[
            _createGridView(_items1),
            SizedBox(height: 100,),// 占位
            DragTarget<String>(	// 用来接收数据的 Widget
              builder: (
                BuildContext context,
                List<dynamic> accepted,
                List<dynamic> rejected,
                return _createGridView(_items2);
              // 用来接收数据
              onAccept: (String data) {
                setState(() {
                  _items2.add(data);
    复制代码

    这是完整的 build 代码,但是还需要改造一下我们的 Draggable 。

    再看一下Draggable的构造函数:

    class Draggable<T> extends StatefulWidget {
      /// Creates a widget that can be dragged to a [DragTarget].
      /// The [child] and [feedback] arguments must not be null. If
      /// [maxSimultaneousDrags] is non-null, it must be non-negative.
      const Draggable({
        Key key,
        @required this.child,
        @required this.feedback,
        this.data,
        this.axis,
        this.childWhenDragging,
        this.feedbackOffset = Offset.zero,
        this.dragAnchor = DragAnchor.child,
        this.affinity,
        this.maxSimultaneousDrags,
        this.onDragStarted,
        this.onDraggableCanceled,
        this.onDragEnd,
        this.onDragCompleted,
        this.ignoringFeedbackSemantics = true,
      }) : assert(child != null),
           assert(feedback != null),
           assert(ignoringFeedbackSemantics != null),
           assert(maxSimultaneousDrags == null || maxSimultaneousDrags >= 0),
           super(key: key);
    复制代码

    因为如果想要传入数据,那也就必须要有数据可以传,也就是我们构造函数里看到的 data 字段。

    还需要删除我们的源数据,那也就是要监听拖动结束的回调,这里就是 onDragCompleted

    我们来看改造后的Draggable:

    Widget _createGridView(List<String> _items) {
      return GridView.builder(
        itemCount: _items.length,
        shrinkWrap: true,
        physics: NeverScrollableScrollPhysics(),
        padding: EdgeInsets.all(10),
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 5,
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
        itemBuilder: (context, index) {
          return Draggable<String>(
            onDragCompleted: (){
              // 在拖动结束后删除数据
              setState(() {
                _items.removeAt(index);
            feedback: Material(
              child: Container(
                height: 100,
                width: 100,
                color: Colors.blueAccent,
                alignment: Alignment.center,
                child: Text(
                  _items[index],
                  style: TextStyle(color: Colors.white),
            // 当前组件的数据
            data: _items[index],
            child: Container(
              color: Colors.blueAccent,
              alignment: Alignment.center,
              child: Text(
                _items[index],
                style: TextStyle(color: Colors.white),
    复制代码

    运行看一下效果:

    可以看到这样就基本完成我们的需求了,但是有人说,能不能把我拖着的源控件加个特效?

    没问题,我们通过 childWhenDragging 参数来控制。

    如,加个蒙层:

    childWhenDragging: Container(
      color: Colors.blueAccent,
      alignment: Alignment.center,
      foregroundDecoration: BoxDecoration(color: Colors.white30),
      child: Text(
        _items[index],
        style: TextStyle(color: Colors.white),
    复制代码

    添加一个 foregroundDecoration 就ok了,效果如下:

    通过这个小例子我们可以实现特别多的效果。

    而且默认拖动的控件时可以多指触控的,也就是说我们可以同时拖动N个控件。

    可以通过 Draggable 的 maxSimultaneousDrags 来控制。

    构造函数里其他的参数大家可以自行了解一下。

    不得不说 Flutter 是真的好用。

    关注我,每天更新 Flutter & Dart 知识。

    完整代码已经传至GitHub: github.com/wanglu1209/…

    分类:
    前端
    标签: