• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:math' as math;
6
7import 'package:flutter/material.dart';
8
9class ExampleDragTarget extends StatefulWidget {
10  @override
11  ExampleDragTargetState createState() => ExampleDragTargetState();
12}
13
14class ExampleDragTargetState extends State<ExampleDragTarget> {
15  Color _color = Colors.grey;
16
17  void _handleAccept(Color data) {
18    setState(() {
19      _color = data;
20    });
21  }
22
23  @override
24  Widget build(BuildContext context) {
25    return DragTarget<Color>(
26      onAccept: _handleAccept,
27      builder: (BuildContext context, List<Color> data, List<dynamic> rejectedData) {
28        return Container(
29          height: 100.0,
30          margin: const EdgeInsets.all(10.0),
31          decoration: BoxDecoration(
32            color: data.isEmpty ? _color : Colors.grey.shade200,
33            border: Border.all(
34              width: 3.0,
35              color: data.isEmpty ? Colors.white : Colors.blue,
36            ),
37          ),
38        );
39      },
40    );
41  }
42}
43
44class Dot extends StatefulWidget {
45  const Dot({ Key key, this.color, this.size, this.child, this.tappable = false }) : super(key: key);
46
47  final Color color;
48  final double size;
49  final Widget child;
50  final bool tappable;
51
52  @override
53  DotState createState() => DotState();
54}
55class DotState extends State<Dot> {
56  int taps = 0;
57
58  @override
59  Widget build(BuildContext context) {
60    return GestureDetector(
61      onTap: widget.tappable ? () { setState(() { taps += 1; }); } : null,
62      child: Container(
63        width: widget.size,
64        height: widget.size,
65        decoration: BoxDecoration(
66          color: widget.color,
67          border: Border.all(width: taps.toDouble()),
68          shape: BoxShape.circle,
69        ),
70        child: widget.child,
71      ),
72    );
73  }
74}
75
76class ExampleDragSource extends StatelessWidget {
77  const ExampleDragSource({
78    Key key,
79    this.color,
80    this.heavy = false,
81    this.under = true,
82    this.child,
83  }) : super(key: key);
84
85  final Color color;
86  final bool heavy;
87  final bool under;
88  final Widget child;
89
90  static const double kDotSize = 50.0;
91  static const double kHeavyMultiplier = 1.5;
92  static const double kFingerSize = 50.0;
93
94  @override
95  Widget build(BuildContext context) {
96    double size = kDotSize;
97    if (heavy)
98      size *= kHeavyMultiplier;
99
100    final Widget contents = DefaultTextStyle(
101      style: Theme.of(context).textTheme.body1,
102      textAlign: TextAlign.center,
103      child: Dot(
104        color: color,
105        size: size,
106        child: Center(child: child),
107      ),
108    );
109
110    Widget feedback = Opacity(
111      opacity: 0.75,
112      child: contents,
113    );
114
115    Offset feedbackOffset;
116    DragAnchor anchor;
117    if (!under) {
118      feedback = Transform(
119        transform: Matrix4.identity()
120                     ..translate(-size / 2.0, -(size / 2.0 + kFingerSize)),
121        child: feedback,
122      );
123      feedbackOffset = const Offset(0.0, -kFingerSize);
124      anchor = DragAnchor.pointer;
125    } else {
126      feedbackOffset = Offset.zero;
127      anchor = DragAnchor.child;
128    }
129
130    if (heavy) {
131      return LongPressDraggable<Color>(
132        data: color,
133        child: contents,
134        feedback: feedback,
135        feedbackOffset: feedbackOffset,
136        dragAnchor: anchor,
137      );
138    } else {
139      return Draggable<Color>(
140        data: color,
141        child: contents,
142        feedback: feedback,
143        feedbackOffset: feedbackOffset,
144        dragAnchor: anchor,
145      );
146    }
147  }
148}
149
150class DashOutlineCirclePainter extends CustomPainter {
151  const DashOutlineCirclePainter();
152
153  static const int segments = 17;
154  static const double deltaTheta = math.pi * 2 / segments; // radians
155  static const double segmentArc = deltaTheta / 2.0; // radians
156  static const double startOffset = 1.0; // radians
157
158  @override
159  void paint(Canvas canvas, Size size) {
160    final double radius = size.shortestSide / 2.0;
161    final Paint paint = Paint()
162      ..color = const Color(0xFF000000)
163      ..style = PaintingStyle.stroke
164      ..strokeWidth = radius / 10.0;
165    final Path path = Path();
166    final Rect box = Offset.zero & size;
167    for (double theta = 0.0; theta < math.pi * 2.0; theta += deltaTheta)
168      path.addArc(box, theta + startOffset, segmentArc);
169    canvas.drawPath(path, paint);
170  }
171
172  @override
173  bool shouldRepaint(DashOutlineCirclePainter oldDelegate) => false;
174}
175
176class MovableBall extends StatelessWidget {
177  const MovableBall(this.position, this.ballPosition, this.callback);
178
179  final int position;
180  final int ballPosition;
181  final ValueChanged<int> callback;
182
183  static final GlobalKey kBallKey = GlobalKey();
184  static const double kBallSize = 50.0;
185
186  @override
187  Widget build(BuildContext context) {
188    final Widget ball = DefaultTextStyle(
189      style: Theme.of(context).primaryTextTheme.body1,
190      textAlign: TextAlign.center,
191      child: Dot(
192        key: kBallKey,
193        color: Colors.blue.shade700,
194        size: kBallSize,
195        tappable: true,
196        child: const Center(child: Text('BALL')),
197      ),
198    );
199    final Widget dashedBall = Container(
200      width: kBallSize,
201      height: kBallSize,
202      child: const CustomPaint(
203        painter: DashOutlineCirclePainter()
204      ),
205    );
206    if (position == ballPosition) {
207      return Draggable<bool>(
208        data: true,
209        child: ball,
210        childWhenDragging: dashedBall,
211        feedback: ball,
212        maxSimultaneousDrags: 1,
213      );
214    } else {
215      return DragTarget<bool>(
216        onAccept: (bool data) { callback(position); },
217        builder: (BuildContext context, List<bool> accepted, List<dynamic> rejected) {
218          return dashedBall;
219        },
220      );
221    }
222  }
223}
224
225class DragAndDropApp extends StatefulWidget {
226  @override
227  DragAndDropAppState createState() => DragAndDropAppState();
228}
229
230class DragAndDropAppState extends State<DragAndDropApp> {
231  int position = 1;
232
233  void moveBall(int newPosition) {
234    setState(() { position = newPosition; });
235  }
236
237  @override
238  Widget build(BuildContext context) {
239    return Scaffold(
240      appBar: AppBar(
241        title: const Text('Drag and Drop Flutter Demo'),
242      ),
243      body: Column(
244        children: <Widget>[
245          Expanded(
246            child: Row(
247              crossAxisAlignment: CrossAxisAlignment.center,
248              mainAxisAlignment: MainAxisAlignment.spaceAround,
249              children: <Widget>[
250                ExampleDragSource(
251                  color: Colors.yellow.shade300,
252                  under: true,
253                  heavy: false,
254                  child: const Text('under'),
255                ),
256                ExampleDragSource(
257                  color: Colors.green.shade300,
258                  under: false,
259                  heavy: true,
260                  child: const Text('long-press above'),
261                ),
262                ExampleDragSource(
263                  color: Colors.indigo.shade300,
264                  under: false,
265                  heavy: false,
266                  child: const Text('above'),
267                ),
268              ],
269            ),
270          ),
271          Expanded(
272            child: Row(
273              children: <Widget>[
274                Expanded(child: ExampleDragTarget()),
275                Expanded(child: ExampleDragTarget()),
276                Expanded(child: ExampleDragTarget()),
277                Expanded(child: ExampleDragTarget()),
278              ],
279            ),
280          ),
281          Expanded(
282            child: Row(
283              mainAxisAlignment: MainAxisAlignment.spaceAround,
284              children: <Widget>[
285                MovableBall(1, position, moveBall),
286                MovableBall(2, position, moveBall),
287                MovableBall(3, position, moveBall),
288              ],
289            ),
290          ),
291        ],
292      ),
293    );
294  }
295}
296
297void main() {
298  runApp(MaterialApp(
299    title: 'Drag and Drop Flutter Demo',
300    home: DragAndDropApp(),
301  ));
302}
303