drag_and_drop.dart 7.98 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:math' as math;

7
import 'package:flutter/material.dart';
8

9
class ExampleDragTarget extends StatefulWidget {
10
  @override
11
  ExampleDragTargetState createState() => new ExampleDragTargetState();
12
}
13

14
class ExampleDragTargetState extends State<ExampleDragTarget> {
Hixie's avatar
Hixie committed
15
  Color _color = Colors.grey[500];
16

Hixie's avatar
Hixie committed
17
  void _handleAccept(Color data) {
18
    setState(() {
Hixie's avatar
Hixie committed
19
      _color = data;
20 21 22
    });
  }

23
  @override
24
  Widget build(BuildContext context) {
Hixie's avatar
Hixie committed
25
    return new DragTarget<Color>(
26
      onAccept: _handleAccept,
27
      builder: (BuildContext context, List<Color> data, List<dynamic> rejectedData) {
28 29
        return new Container(
          height: 100.0,
30
          margin: new EdgeInsets.all(10.0),
31 32 33
          decoration: new BoxDecoration(
            border: new Border.all(
              width: 3.0,
34
              color: data.isEmpty ? Colors.white : Colors.blue[500]
35
            ),
Hixie's avatar
Hixie committed
36
            backgroundColor: data.isEmpty ? _color : Colors.grey[200]
37 38 39 40 41 42 43
          )
        );
      }
    );
  }
}

44
class Dot extends StatefulWidget {
45
  Dot({ Key key, this.color, this.size, this.child, this.tappable: false }) : super(key: key);
46

Hixie's avatar
Hixie committed
47
  final Color color;
48
  final double size;
Hixie's avatar
Hixie committed
49
  final Widget child;
50
  final bool tappable;
51 52

  @override
53 54 55 56
  DotState createState() => new DotState();
}
class DotState extends State<Dot> {
  int taps = 0;
57 58

  @override
59
  Widget build(BuildContext context) {
60 61 62 63 64 65 66
    return new GestureDetector(
      onTap: config.tappable ? () { setState(() { taps += 1; }); } : null,
      child: new Container(
        width: config.size,
        height: config.size,
        decoration: new BoxDecoration(
          backgroundColor: config.color,
Hixie's avatar
Hixie committed
67
          border: new Border.all(width: taps.toDouble()),
68 69 70 71
          shape: BoxShape.circle
        ),
        child: config.child
      )
72 73 74 75
    );
  }
}

76
class ExampleDragSource extends StatelessWidget {
Hixie's avatar
Hixie committed
77 78 79 80 81 82 83 84
  ExampleDragSource({
    Key key,
    this.color,
    this.heavy: false,
    this.under: true,
    this.child
  }) : super(key: key);

Hixie's avatar
Hixie committed
85
  final Color color;
Hixie's avatar
Hixie committed
86 87 88
  final bool heavy;
  final bool under;
  final Widget child;
89

Hixie's avatar
Hixie committed
90 91 92
  static const double kDotSize = 50.0;
  static const double kHeavyMultiplier = 1.5;
  static const double kFingerSize = 50.0;
93

94
  @override
Hixie's avatar
Hixie committed
95
  Widget build(BuildContext context) {
Hixie's avatar
Hixie committed
96
    double size = kDotSize;
97
    if (heavy)
Hixie's avatar
Hixie committed
98 99
      size *= kHeavyMultiplier;

100
    Widget contents = new DefaultTextStyle(
101 102
      style: Theme.of(context).textTheme.body1,
      textAlign: TextAlign.center,
Hixie's avatar
Hixie committed
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
      child: new Dot(
        color: color,
        size: size,
        child: new Center(child: child)
      )
    );

    Widget feedback = new Opacity(
      opacity: 0.75,
      child: contents
    );

    Offset feedbackOffset;
    DragAnchor anchor;
    if (!under) {
      feedback = new Transform(
        transform: new Matrix4.identity()
                     ..translate(-size / 2.0, -(size / 2.0 + kFingerSize)),
        child: feedback
      );
      feedbackOffset = const Offset(0.0, -kFingerSize);
      anchor = DragAnchor.pointer;
    } else {
      feedbackOffset = Offset.zero;
      anchor = DragAnchor.child;
    }

130
    if (heavy) {
Hixie's avatar
Hixie committed
131
      return new LongPressDraggable<Color>(
132 133 134 135 136 137 138
        data: color,
        child: contents,
        feedback: feedback,
        feedbackOffset: feedbackOffset,
        dragAnchor: anchor
      );
    } else {
Hixie's avatar
Hixie committed
139
      return new Draggable<Color>(
140 141 142 143 144 145 146
        data: color,
        child: contents,
        feedback: feedback,
        feedbackOffset: feedbackOffset,
        dragAnchor: anchor
      );
    }
Hixie's avatar
Hixie committed
147 148 149
  }
}

150 151 152 153 154 155 156 157
class DashOutlineCirclePainter extends CustomPainter {
  const DashOutlineCirclePainter();

  static const int segments = 17;
  static const double deltaTheta = math.PI * 2 / segments; // radians
  static const double segmentArc = deltaTheta / 2.0; // radians
  static const double startOffset = 1.0; // radians

158
  @override
159 160 161 162
  void paint(Canvas canvas, Size size) {
    final double radius = size.shortestSide / 2.0;
    final Paint paint = new Paint()
      ..color = const Color(0xFF000000)
163
      ..style = PaintingStyle.stroke
164 165 166 167 168 169 170 171
      ..strokeWidth = radius / 10.0;
    final Path path = new Path();
    final Rect box = Point.origin & size;
    for (double theta = 0.0; theta < math.PI * 2.0; theta += deltaTheta)
      path.addArc(box, theta + startOffset, segmentArc);
    canvas.drawPath(path, paint);
  }

172
  @override
173 174 175
  bool shouldRepaint(DashOutlineCirclePainter oldPainter) => false;
}

176
class MovableBall extends StatelessWidget {
177
  MovableBall(this.position, this.ballPosition, this.callback);
178

179 180 181
  final int position;
  final int ballPosition;
  final ValueChanged<int> callback;
182 183

  static final GlobalKey kBallKey = new GlobalKey();
184
  static const double kBallSize = 50.0;
185

186
  @override
187
  Widget build(BuildContext context) {
188
    Widget ball = new DefaultTextStyle(
189
      style: Theme.of(context).primaryTextTheme.body1,
190
      textAlign: TextAlign.center,
191
      child: new Dot(
192
        key: kBallKey,
193 194
        color: Colors.blue[700],
        size: kBallSize,
195
        tappable: true,
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
        child: new Center(child: new Text('BALL'))
      )
    );
    Widget dashedBall = new Container(
      width: kBallSize,
      height: kBallSize,
      child: new CustomPaint(
        painter: const DashOutlineCirclePainter()
      )
    );
    if (position == ballPosition) {
      return new Draggable<bool>(
        data: true,
        child: ball,
        childWhenDragging: dashedBall,
        feedback: ball,
        maxSimultaneousDrags: 1
      );
    } else {
      return new DragTarget<bool>(
        onAccept: (bool data) { callback(position); },
217
        builder: (BuildContext context, List<bool> accepted, List<dynamic> rejected) {
218 219 220 221 222 223 224
          return dashedBall;
        }
      );
    }
  }
}

225
class DragAndDropApp extends StatefulWidget {
226
  @override
227 228 229 230 231
  DragAndDropAppState createState() => new DragAndDropAppState();
}

class DragAndDropAppState extends State<DragAndDropApp> {
  int position = 1;
232

233 234 235
  void moveBall(int newPosition) {
    setState(() { position = newPosition; });
  }
236 237

  @override
238
  Widget build(BuildContext context) {
Hixie's avatar
Hixie committed
239
    return new Scaffold(
240 241
      appBar: new AppBar(
        title: new Text('Drag and Drop Flutter Demo')
242
      ),
243 244
      body: new Column(
        children: <Widget>[
245
          new Expanded(
246
            child: new Row(
247 248
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.spaceAround,
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
              children: <Widget>[
                new ExampleDragSource(
                  color: Colors.yellow[300],
                  under: true,
                  heavy: false,
                  child: new Text('under')
                ),
                new ExampleDragSource(
                  color: Colors.green[300],
                  under: false,
                  heavy: true,
                  child: new Text('long-press above')
                ),
                new ExampleDragSource(
                  color: Colors.indigo[300],
                  under: false,
                  heavy: false,
                  child: new Text('above')
                ),
              ],
            )
          ),
271
          new Expanded(
272 273
            child: new Row(
              children: <Widget>[
274 275 276 277
                new Expanded(child: new ExampleDragTarget()),
                new Expanded(child: new ExampleDragTarget()),
                new Expanded(child: new ExampleDragTarget()),
                new Expanded(child: new ExampleDragTarget()),
278 279 280
              ]
            )
          ),
281
          new Expanded(
282
            child: new Row(
283
              mainAxisAlignment: MainAxisAlignment.spaceAround,
284 285 286 287 288 289 290
              children: <Widget>[
                new MovableBall(1, position, moveBall),
                new MovableBall(2, position, moveBall),
                new MovableBall(3, position, moveBall),
              ],
            )
          ),
291 292
        ]
      )
293 294 295 296 297
    );
  }
}

void main() {
Adam Barth's avatar
Adam Barth committed
298
  runApp(new MaterialApp(
Hixie's avatar
Hixie committed
299
    title: 'Drag and Drop Flutter Demo',
300
    home: new DragAndDropApp()
Hixie's avatar
Hixie committed
301
  ));
302
}