flutter/dev/manual_tests/drag_and_drop.dart
Hixie 9fc29dbbb8 Support hairline borders
Previously, border with '0' was ambiguous. Sometimes we treated it as
hairline borders, sometimes as "don't show the border", though even in
the latter case we did some graphics work sometimes. Now we have an
explicit BorderStyle.none flag to not draw the border efficiently.
2016-03-24 14:05:38 -07:00

306 lines
8.1 KiB
Dart

// 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.
import 'dart:math' as math;
import 'package:flutter/material.dart';
class ExampleDragTarget extends StatefulWidget {
@override
ExampleDragTargetState createState() => new ExampleDragTargetState();
}
class ExampleDragTargetState extends State<ExampleDragTarget> {
Color _color = Colors.grey[500];
void _handleAccept(Color data) {
setState(() {
_color = data;
});
}
@override
Widget build(BuildContext context) {
return new DragTarget<Color>(
onAccept: _handleAccept,
builder: (BuildContext context, List<Color> data, List<Color> rejectedData) {
return new Container(
height: 100.0,
margin: new EdgeInsets.all(10.0),
decoration: new BoxDecoration(
border: new Border.all(
width: 3.0,
color: data.isEmpty ? Colors.white : Colors.blue[500]
),
backgroundColor: data.isEmpty ? _color : Colors.grey[200]
)
);
}
);
}
}
class Dot extends StatefulWidget {
Dot({ Key key, this.color, this.size, this.child, this.tappable: false }) : super(key: key);
final Color color;
final double size;
final Widget child;
final bool tappable;
@override
DotState createState() => new DotState();
}
class DotState extends State<Dot> {
int taps = 0;
@override
Widget build(BuildContext context) {
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,
border: new Border.all(width: taps.toDouble()),
shape: BoxShape.circle
),
child: config.child
)
);
}
}
class ExampleDragSource extends StatelessWidget {
ExampleDragSource({
Key key,
this.color,
this.heavy: false,
this.under: true,
this.child
}) : super(key: key);
final Color color;
final bool heavy;
final bool under;
final Widget child;
static const double kDotSize = 50.0;
static const double kHeavyMultiplier = 1.5;
static const double kFingerSize = 50.0;
@override
Widget build(BuildContext context) {
double size = kDotSize;
if (heavy)
size *= kHeavyMultiplier;
Widget contents = new DefaultTextStyle(
style: Theme.of(context).textTheme.body1.copyWith(textAlign: TextAlign.center),
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;
}
if (heavy) {
return new LongPressDraggable<Color>(
data: color,
child: contents,
feedback: feedback,
feedbackOffset: feedbackOffset,
dragAnchor: anchor
);
} else {
return new Draggable<Color>(
data: color,
child: contents,
feedback: feedback,
feedbackOffset: feedbackOffset,
dragAnchor: anchor
);
}
}
}
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
@override
void paint(Canvas canvas, Size size) {
final double radius = size.shortestSide / 2.0;
final Paint paint = new Paint()
..color = const Color(0xFF000000)
..style = PaintingStyle.stroke
..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);
}
@override
bool shouldRepaint(DashOutlineCirclePainter oldPainter) => false;
}
class MovableBall extends StatelessWidget {
MovableBall(this.position, this.ballPosition, this.callback);
final int position;
final int ballPosition;
final ValueChanged<int> callback;
static final GlobalKey kBallKey = new GlobalKey();
static const double kBallSize = 50.0;
@override
Widget build(BuildContext context) {
Widget ball = new DefaultTextStyle(
style: Theme.of(context).textTheme.body1.copyWith(
textAlign: TextAlign.center,
color: Colors.white
),
child: new Dot(
key: kBallKey,
color: Colors.blue[700],
size: kBallSize,
tappable: true,
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); },
builder: (BuildContext context, List<bool> accepted, List<bool> rejected) {
return dashedBall;
}
);
}
}
}
class DragAndDropApp extends StatefulWidget {
@override
DragAndDropAppState createState() => new DragAndDropAppState();
}
class DragAndDropAppState extends State<DragAndDropApp> {
int position = 1;
void moveBall(int newPosition) {
setState(() { position = newPosition; });
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Drag and Drop Flutter Demo')
),
body: new Column(
children: <Widget>[
new Flexible(
child: new Row(
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')
),
],
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround
)
),
new Flexible(
child: new Row(
children: <Widget>[
new Flexible(child: new ExampleDragTarget()),
new Flexible(child: new ExampleDragTarget()),
new Flexible(child: new ExampleDragTarget()),
new Flexible(child: new ExampleDragTarget()),
]
)
),
new Flexible(
child: new Row(
children: <Widget>[
new MovableBall(1, position, moveBall),
new MovableBall(2, position, moveBall),
new MovableBall(3, position, moveBall),
],
mainAxisAlignment: MainAxisAlignment.spaceAround
)
),
]
)
);
}
}
void main() {
runApp(new MaterialApp(
title: 'Drag and Drop Flutter Demo',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => new DragAndDropApp()
}
));
}