Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 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 | |
Ian Hickson | f162555 | 2015-12-03 17:39:47 -0800 | [diff] [blame] | 5 | import 'package:flutter/gestures.dart'; |
Adam Barth | 65eba90 | 2015-10-09 20:44:52 -0700 | [diff] [blame] | 6 | import 'package:flutter/material.dart'; |
| 7 | import 'package:flutter/rendering.dart'; |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 8 | |
| 9 | class CardModel { |
| 10 | CardModel(this.value, this.height, this.color); |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 11 | |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 12 | int value; |
| 13 | double height; |
| 14 | Color color; |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 15 | |
| 16 | String get label => 'Card $value'; |
Hixie | 9047830 | 2015-08-28 10:52:23 -0700 | [diff] [blame] | 17 | Key get key => new ObjectKey(this); |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 18 | GlobalKey get targetKey => new GlobalObjectKey(this); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 19 | } |
| 20 | |
| 21 | enum MarkerType { topLeft, bottomRight, touch } |
| 22 | |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 23 | class _MarkerPainter extends CustomPainter { |
| 24 | const _MarkerPainter({ |
| 25 | this.size, |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 26 | this.type, |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 27 | }); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 28 | |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 29 | final double size; |
| 30 | final MarkerType type; |
| 31 | |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 32 | @override |
Adam Barth | abf0359 | 2015-12-04 20:26:08 -0800 | [diff] [blame] | 33 | void paint(Canvas canvas, _) { |
Chris Bracken | 6c97dd2 | 2017-03-03 18:06:08 -0800 | [diff] [blame] | 34 | final Paint paint = new Paint()..color = const Color(0x8000FF00); |
| 35 | final double r = size / 2.0; |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 36 | canvas.drawCircle(new Offset(r, r), r, paint); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 37 | |
Adam Barth | 2aa79d5 | 2015-10-19 12:21:34 -0700 | [diff] [blame] | 38 | paint |
| 39 | ..color = const Color(0xFFFFFFFF) |
Ian Hickson | a94999b | 2016-02-10 18:11:03 -0800 | [diff] [blame] | 40 | ..style = PaintingStyle.stroke |
Adam Barth | 2aa79d5 | 2015-10-19 12:21:34 -0700 | [diff] [blame] | 41 | ..strokeWidth = 1.0; |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 42 | if (type == MarkerType.topLeft) { |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 43 | 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 Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 45 | } |
| 46 | if (type == MarkerType.bottomRight) { |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 47 | 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 Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 49 | } |
| 50 | } |
| 51 | |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 52 | @override |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 53 | bool shouldRepaint(_MarkerPainter oldPainter) { |
| 54 | return oldPainter.size != size |
| 55 | || oldPainter.type != type; |
| 56 | } |
| 57 | } |
| 58 | |
Adam Barth | 95fc5ae | 2016-03-12 12:13:16 -0800 | [diff] [blame] | 59 | class Marker extends StatelessWidget { |
Alexandre Ardhuin | 9541848 | 2017-04-21 23:09:42 +0200 | [diff] [blame] | 60 | const Marker({ |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 61 | Key key, |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 62 | this.type: MarkerType.touch, |
| 63 | this.position, |
| 64 | this.size: 40.0, |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 65 | }) : super(key: key); |
| 66 | |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 67 | final Offset position; |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 68 | final double size; |
| 69 | final MarkerType type; |
| 70 | |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 71 | @override |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 72 | Widget build(BuildContext context) { |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 73 | return new Positioned( |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 74 | left: position.dx - size / 2.0, |
| 75 | top: position.dy - size / 2.0, |
Adam Barth | c769d12 | 2015-11-16 23:23:54 -0800 | [diff] [blame] | 76 | width: size, |
| 77 | height: size, |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 78 | child: new IgnorePointer( |
Adam Barth | 72329cf | 2015-11-20 00:22:49 -0800 | [diff] [blame] | 79 | child: new CustomPaint( |
| 80 | painter: new _MarkerPainter( |
| 81 | size: size, |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 82 | type: type, |
| 83 | ), |
| 84 | ), |
| 85 | ), |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 86 | ); |
| 87 | } |
| 88 | } |
| 89 | |
Adam Barth | 95fc5ae | 2016-03-12 12:13:16 -0800 | [diff] [blame] | 90 | class OverlayGeometryApp extends StatefulWidget { |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 91 | @override |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 92 | OverlayGeometryAppState createState() => new OverlayGeometryAppState(); |
| 93 | } |
| 94 | |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 95 | typedef void CardTapCallback(GlobalKey targetKey, Offset globalPosition); |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 96 | |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 97 | class CardBuilder extends SliverChildDelegate { |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 98 | CardBuilder({ this.cardModels, this.onTapUp }); |
| 99 | |
| 100 | final List<CardModel> cardModels; |
| 101 | final CardTapCallback onTapUp; |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 102 | |
| 103 | static const TextStyle cardLabelStyle = |
Adam Barth | ab89d2e | 2015-12-09 14:02:58 -0800 | [diff] [blame] | 104 | const TextStyle(color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.bold); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 105 | |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 106 | @override |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 107 | Widget build(BuildContext context, int index) { |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 108 | if (index >= cardModels.length) |
| 109 | return null; |
Chris Bracken | 6c97dd2 | 2017-03-03 18:06:08 -0800 | [diff] [blame] | 110 | final CardModel cardModel = cardModels[index]; |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 111 | return new GestureDetector( |
| 112 | key: cardModel.key, |
Adam Barth | 7d9f8d9 | 2016-06-07 16:16:53 -0700 | [diff] [blame] | 113 | onTapUp: (TapUpDetails details) { onTapUp(cardModel.targetKey, details.globalPosition); }, |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 114 | 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 Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 120 | child: new Center(child: new Text(cardModel.label, style: cardLabelStyle)), |
| 121 | ), |
| 122 | ), |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 123 | ); |
| 124 | } |
| 125 | |
| 126 | @override |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 127 | int get estimatedChildCount => cardModels.length; |
Ian Hickson | 51f8fb9 | 2016-07-29 15:44:12 -0700 | [diff] [blame] | 128 | |
| 129 | @override |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 130 | bool shouldRebuild(CardBuilder oldDelegate) { |
| 131 | return oldDelegate.cardModels != cardModels; |
Ian Hickson | 51f8fb9 | 2016-07-29 15:44:12 -0700 | [diff] [blame] | 132 | } |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 133 | } |
| 134 | |
| 135 | class OverlayGeometryAppState extends State<OverlayGeometryApp> { |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 136 | List<CardModel> cardModels; |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 137 | Map<MarkerType, Offset> markers = <MarkerType, Offset>{}; |
Adam Barth | 6fd6859 | 2016-04-06 12:36:54 -0700 | [diff] [blame] | 138 | double markersScrollOffset = 0.0; |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 139 | |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 140 | @override |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 141 | void initState() { |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 142 | super.initState(); |
Chris Bracken | 6c97dd2 | 2017-03-03 18:06:08 -0800 | [diff] [blame] | 143 | final List<double> cardHeights = <double>[ |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 144 | 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 Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 146 | 48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0, |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 147 | ]; |
Hixie | a6c473e | 2015-10-22 13:29:12 -0700 | [diff] [blame] | 148 | cardModels = new List<CardModel>.generate(cardHeights.length, (int i) { |
Alexandre Ardhuin | 578ca0a | 2017-03-21 23:14:55 +0100 | [diff] [blame] | 149 | final Color color = Color.lerp(Colors.red.shade300, Colors.blue.shade900, i / cardHeights.length); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 150 | return new CardModel(i, cardHeights[i], color); |
| 151 | }); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 152 | } |
| 153 | |
Adam Barth | e0b12ca | 2017-02-17 14:06:15 -0800 | [diff] [blame] | 154 | bool handleScrollNotification(ScrollNotification notification) { |
Ian Hickson | da5be60 | 2017-12-04 13:53:23 -0800 | [diff] [blame] | 155 | if (notification is ScrollUpdateNotification && notification.depth == 0) { |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 156 | setState(() { |
Chris Bracken | 6c97dd2 | 2017-03-03 18:06:08 -0800 | [diff] [blame] | 157 | final double dy = markersScrollOffset - notification.metrics.extentBefore; |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 158 | markersScrollOffset = notification.metrics.extentBefore; |
| 159 | for (MarkerType type in markers.keys) { |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 160 | final Offset oldPosition = markers[type]; |
| 161 | markers[type] = oldPosition.translate(0.0, dy); |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 162 | } |
| 163 | }); |
| 164 | } |
| 165 | return false; |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 166 | } |
| 167 | |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 168 | void handleTapUp(GlobalKey target, Offset globalPosition) { |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 169 | setState(() { |
Adam Barth | 40899eb | 2016-04-05 20:18:35 -0700 | [diff] [blame] | 170 | markers[MarkerType.touch] = globalPosition; |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 171 | final RenderBox box = target.currentContext.findRenderObject(); |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 172 | markers[MarkerType.topLeft] = box.localToGlobal(const Offset(0.0, 0.0)); |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 173 | final Size size = box.size; |
Ian Hickson | bf017b7 | 2017-04-12 15:06:12 -0700 | [diff] [blame] | 174 | markers[MarkerType.bottomRight] = box.localToGlobal(new Offset(size.width, size.height)); |
Adam Barth | e0b12ca | 2017-02-17 14:06:15 -0800 | [diff] [blame] | 175 | final ScrollableState scrollable = Scrollable.of(target.currentContext); |
Adam Barth | 464a8e7 | 2017-02-14 21:03:55 -0800 | [diff] [blame] | 176 | markersScrollOffset = scrollable.position.pixels; |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 177 | }); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 178 | } |
| 179 | |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 180 | @override |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 181 | Widget build(BuildContext context) { |
Chris Bracken | 6c97dd2 | 2017-03-03 18:06:08 -0800 | [diff] [blame] | 182 | final List<Widget> layers = <Widget>[ |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 183 | new Scaffold( |
Ian Hickson | 3eb8783 | 2017-04-07 12:24:32 -0700 | [diff] [blame] | 184 | appBar: new AppBar(title: const Text('Tap a Card')), |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 185 | body: new Container( |
Adam Barth | e71bd77 | 2016-03-12 11:43:18 -0800 | [diff] [blame] | 186 | padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0), |
Adam Barth | e0b12ca | 2017-02-17 14:06:15 -0800 | [diff] [blame] | 187 | child: new NotificationListener<ScrollNotification>( |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 188 | onNotification: handleScrollNotification, |
| 189 | child: new ListView.custom( |
| 190 | childrenDelegate: new CardBuilder( |
| 191 | cardModels: cardModels, |
| 192 | onTapUp: handleTapUp, |
| 193 | ), |
| 194 | ), |
| 195 | ), |
| 196 | ), |
| 197 | ), |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 198 | ]; |
| 199 | for (MarkerType type in markers.keys) |
| 200 | layers.add(new Marker(type: type, position: markers[type])); |
Adam Barth | 0567664 | 2016-01-11 13:08:47 -0800 | [diff] [blame] | 201 | return new Stack(children: layers); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 202 | } |
| 203 | } |
| 204 | |
| 205 | void main() { |
Adam Barth | db3b9e8 | 2015-10-09 10:19:35 -0700 | [diff] [blame] | 206 | runApp(new MaterialApp( |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 207 | theme: new ThemeData( |
Todd Volkert | 7ac0ce7 | 2016-06-07 14:39:15 -0700 | [diff] [blame] | 208 | brightness: Brightness.light, |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 209 | primarySwatch: Colors.blue, |
Alexandre Ardhuin | 578ca0a | 2017-03-21 23:14:55 +0100 | [diff] [blame] | 210 | accentColor: Colors.redAccent, |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 211 | ), |
| 212 | title: 'Cards', |
Adam Barth | fe0b909 | 2017-02-10 16:41:37 -0800 | [diff] [blame] | 213 | home: new OverlayGeometryApp(), |
Hixie | 4d186e3 | 2015-10-01 12:20:08 -0700 | [diff] [blame] | 214 | )); |
Hans Muller | d2d9ae1 | 2015-08-10 15:58:29 -0700 | [diff] [blame] | 215 | } |