blob: b595c015444ff2f31a0afe9888e8c2b876c83a07 [file] [log] [blame]
Hans Mullerd2d9ae12015-08-10 15:58:29 -07001// 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
Ian Hicksonf1625552015-12-03 17:39:47 -08005import 'package:flutter/gestures.dart';
Adam Barth65eba902015-10-09 20:44:52 -07006import 'package:flutter/material.dart';
7import 'package:flutter/rendering.dart';
Hans Mullerd2d9ae12015-08-10 15:58:29 -07008
9class CardModel {
10 CardModel(this.value, this.height, this.color);
Adam Barthfe0b9092017-02-10 16:41:37 -080011
Hans Mullerd2d9ae12015-08-10 15:58:29 -070012 int value;
13 double height;
14 Color color;
Adam Barthfe0b9092017-02-10 16:41:37 -080015
16 String get label => 'Card $value';
Hixie90478302015-08-28 10:52:23 -070017 Key get key => new ObjectKey(this);
Hixie4d186e32015-10-01 12:20:08 -070018 GlobalKey get targetKey => new GlobalObjectKey(this);
Hans Mullerd2d9ae12015-08-10 15:58:29 -070019}
20
21enum MarkerType { topLeft, bottomRight, touch }
22
Adam Barth72329cf2015-11-20 00:22:49 -080023class _MarkerPainter extends CustomPainter {
24 const _MarkerPainter({
25 this.size,
Adam Barthfe0b9092017-02-10 16:41:37 -080026 this.type,
Adam Barth72329cf2015-11-20 00:22:49 -080027 });
Hans Mullerd2d9ae12015-08-10 15:58:29 -070028
Hans Mullerd2d9ae12015-08-10 15:58:29 -070029 final double size;
30 final MarkerType type;
31
Hixie797e27e2016-03-14 13:31:43 -070032 @override
Adam Barthabf03592015-12-04 20:26:08 -080033 void paint(Canvas canvas, _) {
Chris Bracken6c97dd22017-03-03 18:06:08 -080034 final Paint paint = new Paint()..color = const Color(0x8000FF00);
35 final double r = size / 2.0;
Ian Hicksonbf017b72017-04-12 15:06:12 -070036 canvas.drawCircle(new Offset(r, r), r, paint);
Hans Mullerd2d9ae12015-08-10 15:58:29 -070037
Adam Barth2aa79d52015-10-19 12:21:34 -070038 paint
39 ..color = const Color(0xFFFFFFFF)
Ian Hicksona94999b2016-02-10 18:11:03 -080040 ..style = PaintingStyle.stroke
Adam Barth2aa79d52015-10-19 12:21:34 -070041 ..strokeWidth = 1.0;
Hans Mullerd2d9ae12015-08-10 15:58:29 -070042 if (type == MarkerType.topLeft) {
Ian Hicksonbf017b72017-04-12 15:06:12 -070043 canvas.drawLine(new Offset(r, r), new Offset(r + r - 1.0, r), paint);
44 canvas.drawLine(new Offset(r, r), new Offset(r, r + r - 1.0), paint);
Hans Mullerd2d9ae12015-08-10 15:58:29 -070045 }
46 if (type == MarkerType.bottomRight) {
Ian Hicksonbf017b72017-04-12 15:06:12 -070047 canvas.drawLine(new Offset(r, r), new Offset(1.0, r), paint);
48 canvas.drawLine(new Offset(r, r), new Offset(r, 1.0), paint);
Hans Mullerd2d9ae12015-08-10 15:58:29 -070049 }
50 }
51
Hixie797e27e2016-03-14 13:31:43 -070052 @override
Adam Barth72329cf2015-11-20 00:22:49 -080053 bool shouldRepaint(_MarkerPainter oldPainter) {
54 return oldPainter.size != size
55 || oldPainter.type != type;
56 }
57}
58
Adam Barth95fc5ae2016-03-12 12:13:16 -080059class Marker extends StatelessWidget {
Alexandre Ardhuin95418482017-04-21 23:09:42 +020060 const Marker({
Adam Barthfe0b9092017-02-10 16:41:37 -080061 Key key,
Adam Barth72329cf2015-11-20 00:22:49 -080062 this.type: MarkerType.touch,
63 this.position,
64 this.size: 40.0,
Adam Barth72329cf2015-11-20 00:22:49 -080065 }) : super(key: key);
66
Ian Hicksonbf017b72017-04-12 15:06:12 -070067 final Offset position;
Adam Barth72329cf2015-11-20 00:22:49 -080068 final double size;
69 final MarkerType type;
70
Hixie797e27e2016-03-14 13:31:43 -070071 @override
Hixie4d186e32015-10-01 12:20:08 -070072 Widget build(BuildContext context) {
Hans Mullerd2d9ae12015-08-10 15:58:29 -070073 return new Positioned(
Ian Hicksonbf017b72017-04-12 15:06:12 -070074 left: position.dx - size / 2.0,
75 top: position.dy - size / 2.0,
Adam Barthc769d122015-11-16 23:23:54 -080076 width: size,
77 height: size,
Hans Mullerd2d9ae12015-08-10 15:58:29 -070078 child: new IgnorePointer(
Adam Barth72329cf2015-11-20 00:22:49 -080079 child: new CustomPaint(
80 painter: new _MarkerPainter(
81 size: size,
Adam Barthfe0b9092017-02-10 16:41:37 -080082 type: type,
83 ),
84 ),
85 ),
Hans Mullerd2d9ae12015-08-10 15:58:29 -070086 );
87 }
88}
89
Adam Barth95fc5ae2016-03-12 12:13:16 -080090class OverlayGeometryApp extends StatefulWidget {
Hixie797e27e2016-03-14 13:31:43 -070091 @override
Hixie4d186e32015-10-01 12:20:08 -070092 OverlayGeometryAppState createState() => new OverlayGeometryAppState();
93}
94
Ian Hicksonbf017b72017-04-12 15:06:12 -070095typedef void CardTapCallback(GlobalKey targetKey, Offset globalPosition);
Adam Barth40899eb2016-04-05 20:18:35 -070096
Adam Barthfe0b9092017-02-10 16:41:37 -080097class CardBuilder extends SliverChildDelegate {
Adam Barth40899eb2016-04-05 20:18:35 -070098 CardBuilder({ this.cardModels, this.onTapUp });
99
100 final List<CardModel> cardModels;
101 final CardTapCallback onTapUp;
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700102
103 static const TextStyle cardLabelStyle =
Adam Barthab89d2e2015-12-09 14:02:58 -0800104 const TextStyle(color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.bold);
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700105
Adam Barth40899eb2016-04-05 20:18:35 -0700106 @override
Adam Barthfe0b9092017-02-10 16:41:37 -0800107 Widget build(BuildContext context, int index) {
Adam Barth40899eb2016-04-05 20:18:35 -0700108 if (index >= cardModels.length)
109 return null;
Chris Bracken6c97dd22017-03-03 18:06:08 -0800110 final CardModel cardModel = cardModels[index];
Adam Barth40899eb2016-04-05 20:18:35 -0700111 return new GestureDetector(
112 key: cardModel.key,
Adam Barth7d9f8d92016-06-07 16:16:53 -0700113 onTapUp: (TapUpDetails details) { onTapUp(cardModel.targetKey, details.globalPosition); },
Adam Barth40899eb2016-04-05 20:18:35 -0700114 child: new Card(
115 key: cardModel.targetKey,
116 color: cardModel.color,
117 child: new Container(
118 height: cardModel.height,
119 padding: const EdgeInsets.all(8.0),
Adam Barthfe0b9092017-02-10 16:41:37 -0800120 child: new Center(child: new Text(cardModel.label, style: cardLabelStyle)),
121 ),
122 ),
Adam Barth40899eb2016-04-05 20:18:35 -0700123 );
124 }
125
126 @override
Adam Barthfe0b9092017-02-10 16:41:37 -0800127 int get estimatedChildCount => cardModels.length;
Ian Hickson51f8fb92016-07-29 15:44:12 -0700128
129 @override
Adam Barthfe0b9092017-02-10 16:41:37 -0800130 bool shouldRebuild(CardBuilder oldDelegate) {
131 return oldDelegate.cardModels != cardModels;
Ian Hickson51f8fb92016-07-29 15:44:12 -0700132 }
Adam Barth40899eb2016-04-05 20:18:35 -0700133}
134
135class OverlayGeometryAppState extends State<OverlayGeometryApp> {
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700136 List<CardModel> cardModels;
Ian Hicksonbf017b72017-04-12 15:06:12 -0700137 Map<MarkerType, Offset> markers = <MarkerType, Offset>{};
Adam Barth6fd68592016-04-06 12:36:54 -0700138 double markersScrollOffset = 0.0;
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700139
Hixie797e27e2016-03-14 13:31:43 -0700140 @override
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700141 void initState() {
Hixie4d186e32015-10-01 12:20:08 -0700142 super.initState();
Chris Bracken6c97dd22017-03-03 18:06:08 -0800143 final List<double> cardHeights = <double>[
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700144 48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
145 48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
Adam Barthfe0b9092017-02-10 16:41:37 -0800146 48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700147 ];
Hixiea6c473e2015-10-22 13:29:12 -0700148 cardModels = new List<CardModel>.generate(cardHeights.length, (int i) {
Alexandre Ardhuin578ca0a2017-03-21 23:14:55 +0100149 final Color color = Color.lerp(Colors.red.shade300, Colors.blue.shade900, i / cardHeights.length);
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700150 return new CardModel(i, cardHeights[i], color);
151 });
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700152 }
153
Adam Barthe0b12ca2017-02-17 14:06:15 -0800154 bool handleScrollNotification(ScrollNotification notification) {
Ian Hicksonda5be602017-12-04 13:53:23 -0800155 if (notification is ScrollUpdateNotification && notification.depth == 0) {
Adam Barthfe0b9092017-02-10 16:41:37 -0800156 setState(() {
Chris Bracken6c97dd22017-03-03 18:06:08 -0800157 final double dy = markersScrollOffset - notification.metrics.extentBefore;
Adam Barthfe0b9092017-02-10 16:41:37 -0800158 markersScrollOffset = notification.metrics.extentBefore;
159 for (MarkerType type in markers.keys) {
Ian Hicksonbf017b72017-04-12 15:06:12 -0700160 final Offset oldPosition = markers[type];
161 markers[type] = oldPosition.translate(0.0, dy);
Adam Barthfe0b9092017-02-10 16:41:37 -0800162 }
163 });
164 }
165 return false;
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700166 }
167
Ian Hicksonbf017b72017-04-12 15:06:12 -0700168 void handleTapUp(GlobalKey target, Offset globalPosition) {
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700169 setState(() {
Adam Barth40899eb2016-04-05 20:18:35 -0700170 markers[MarkerType.touch] = globalPosition;
Hixie4d186e32015-10-01 12:20:08 -0700171 final RenderBox box = target.currentContext.findRenderObject();
Ian Hicksonbf017b72017-04-12 15:06:12 -0700172 markers[MarkerType.topLeft] = box.localToGlobal(const Offset(0.0, 0.0));
Hixie4d186e32015-10-01 12:20:08 -0700173 final Size size = box.size;
Ian Hicksonbf017b72017-04-12 15:06:12 -0700174 markers[MarkerType.bottomRight] = box.localToGlobal(new Offset(size.width, size.height));
Adam Barthe0b12ca2017-02-17 14:06:15 -0800175 final ScrollableState scrollable = Scrollable.of(target.currentContext);
Adam Barth464a8e72017-02-14 21:03:55 -0800176 markersScrollOffset = scrollable.position.pixels;
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700177 });
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700178 }
179
Hixie797e27e2016-03-14 13:31:43 -0700180 @override
Hixie4d186e32015-10-01 12:20:08 -0700181 Widget build(BuildContext context) {
Chris Bracken6c97dd22017-03-03 18:06:08 -0800182 final List<Widget> layers = <Widget>[
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700183 new Scaffold(
Ian Hickson3eb87832017-04-07 12:24:32 -0700184 appBar: new AppBar(title: const Text('Tap a Card')),
Hixie4d186e32015-10-01 12:20:08 -0700185 body: new Container(
Adam Barthe71bd772016-03-12 11:43:18 -0800186 padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0),
Adam Barthe0b12ca2017-02-17 14:06:15 -0800187 child: new NotificationListener<ScrollNotification>(
Adam Barthfe0b9092017-02-10 16:41:37 -0800188 onNotification: handleScrollNotification,
189 child: new ListView.custom(
190 childrenDelegate: new CardBuilder(
191 cardModels: cardModels,
192 onTapUp: handleTapUp,
193 ),
194 ),
195 ),
196 ),
197 ),
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700198 ];
199 for (MarkerType type in markers.keys)
200 layers.add(new Marker(type: type, position: markers[type]));
Adam Barth05676642016-01-11 13:08:47 -0800201 return new Stack(children: layers);
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700202 }
203}
204
205void main() {
Adam Barthdb3b9e82015-10-09 10:19:35 -0700206 runApp(new MaterialApp(
Hixie4d186e32015-10-01 12:20:08 -0700207 theme: new ThemeData(
Todd Volkert7ac0ce72016-06-07 14:39:15 -0700208 brightness: Brightness.light,
Hixie4d186e32015-10-01 12:20:08 -0700209 primarySwatch: Colors.blue,
Alexandre Ardhuin578ca0a2017-03-21 23:14:55 +0100210 accentColor: Colors.redAccent,
Hixie4d186e32015-10-01 12:20:08 -0700211 ),
212 title: 'Cards',
Adam Barthfe0b9092017-02-10 16:41:37 -0800213 home: new OverlayGeometryApp(),
Hixie4d186e32015-10-01 12:20:08 -0700214 ));
Hans Mullerd2d9ae12015-08-10 15:58:29 -0700215}