mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

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.
306 lines
8.1 KiB
Dart
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()
|
|
}
|
|
));
|
|
}
|