overlay_geometry.dart 5.43 KB
Newer Older
1 2 3 4
// 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.

Ian Hickson's avatar
Ian Hickson committed
5
import 'package:flutter/gestures.dart';
6 7
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
8 9 10 11 12 13 14

class CardModel {
  CardModel(this.value, this.height, this.color);
  int value;
  double height;
  Color color;
  String get label => "Card $value";
Hixie's avatar
Hixie committed
15
  Key get key => new ObjectKey(this);
16
  GlobalKey get targetKey => new GlobalObjectKey(this);
17 18 19 20
}

enum MarkerType { topLeft, bottomRight, touch }

21 22 23 24 25
class _MarkerPainter extends CustomPainter {
  const _MarkerPainter({
    this.size,
    this.type
  });
26 27 28 29

  final double size;
  final MarkerType type;

Adam Barth's avatar
Adam Barth committed
30
  void paint(Canvas canvas, _) {
31 32 33 34
    Paint paint = new Paint()..color = const Color(0x8000FF00);
    double r = size / 2.0;
    canvas.drawCircle(new Point(r, r), r, paint);

35 36
    paint
      ..color = const Color(0xFFFFFFFF)
37
      ..style = PaintingStyle.stroke
38
      ..strokeWidth = 1.0;
39 40 41 42 43 44 45 46 47 48
    if (type == MarkerType.topLeft) {
      canvas.drawLine(new Point(r, r), new Point(r + r - 1.0, r), paint);
      canvas.drawLine(new Point(r, r), new Point(r, r + r - 1.0), paint);
    }
    if (type == MarkerType.bottomRight) {
      canvas.drawLine(new Point(r, r), new Point(1.0, r), paint);
      canvas.drawLine(new Point(r, r), new Point(r, 1.0), paint);
    }
  }

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
  bool shouldRepaint(_MarkerPainter oldPainter) {
    return oldPainter.size != size
        || oldPainter.type != type;
  }
}

class Marker extends StatelessComponent {
  Marker({
    this.type: MarkerType.touch,
    this.position,
    this.size: 40.0,
    Key key
  }) : super(key: key);

  final Point position;
  final double size;
  final MarkerType type;

67
  Widget build(BuildContext context) {
68 69 70
    return new Positioned(
      left: position.x - size / 2.0,
      top: position.y - size / 2.0,
71 72
      width: size,
      height: size,
73
      child: new IgnorePointer(
74 75 76 77 78 79
        child: new CustomPaint(
          painter: new _MarkerPainter(
            size: size,
            type: type
          )
        )
80 81 82 83 84
      )
    );
  }
}

85 86 87 88 89
class OverlayGeometryApp extends StatefulComponent {
  OverlayGeometryAppState createState() => new OverlayGeometryAppState();
}

class OverlayGeometryAppState extends State<OverlayGeometryApp> {
90 91

  static const TextStyle cardLabelStyle =
92
    const TextStyle(color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.bold);
93 94 95 96 97 98 99

  List<CardModel> cardModels;
  Map<MarkerType, Point> markers = new Map<MarkerType, Point>();
  double markersScrollOffset;
  ScrollListener scrollListener;

  void initState() {
100
    super.initState();
101 102 103 104 105
    List<double> cardHeights = <double>[
      48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
      48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
      48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0
    ];
Hixie's avatar
Hixie committed
106
    cardModels = new List<CardModel>.generate(cardHeights.length, (int i) {
107
      Color color = Color.lerp(Colors.red[300], Colors.blue[900], i / cardHeights.length);
108 109 110 111
      return new CardModel(i, cardHeights[i], color);
    });
  }

112
  void handleScroll(double offset) {
113
    setState(() {
114 115
      double dy = markersScrollOffset - offset;
      markersScrollOffset = offset;
116 117 118 119 120 121 122
      for (MarkerType type in markers.keys) {
        Point oldPosition = markers[type];
        markers[type] = new Point(oldPosition.x, oldPosition.y + dy);
      }
    });
  }

Ian Hickson's avatar
Ian Hickson committed
123
  void handlePointerDown(GlobalKey target, PointerDownEvent event) {
124
    setState(() {
Ian Hickson's avatar
Ian Hickson committed
125
      markers[MarkerType.touch] = event.position;
126 127 128 129
      final RenderBox box = target.currentContext.findRenderObject();
      markers[MarkerType.topLeft] = box.localToGlobal(new Point(0.0, 0.0));
      final Size size = box.size;
      markers[MarkerType.bottomRight] = box.localToGlobal(new Point(size.width, size.height));
130
      final ScrollableState scrollable = Scrollable.of(target.currentContext);
131 132 133 134
      markersScrollOffset = scrollable.scrollOffset;
    });
  }

135
  Widget builder(BuildContext context, int index) {
136 137 138 139 140
    if (index >= cardModels.length)
      return null;
    CardModel cardModel = cardModels[index];
    return new Listener(
      key: cardModel.key,
Ian Hickson's avatar
Ian Hickson committed
141
      onPointerDown: (PointerDownEvent event) { return handlePointerDown(cardModel.targetKey, event); },
142 143 144 145 146 147 148 149 150
      child: new Card(
        key: cardModel.targetKey,
        color: cardModel.color,
        child: new Container(
          height: cardModel.height,
          padding: const EdgeDims.all(8.0),
          child: new Center(child: new Text(cardModel.label, style: cardLabelStyle))
        )
      )
151 152 153
    );
  }

154
  Widget build(BuildContext context) {
155 156
    List<Widget> layers = <Widget>[
      new Scaffold(
Adam Barth's avatar
Adam Barth committed
157
        toolBar: new ToolBar(center: new Text('Tap a Card')),
158 159 160 161 162 163 164 165
        body: new Container(
          padding: const EdgeDims.symmetric(vertical: 12.0, horizontal: 8.0),
          child: new ScrollableMixedWidgetList(
            builder: builder,
            token: cardModels.length,
            onScroll: handleScroll
          )
        )
166 167 168 169
      )
    ];
    for (MarkerType type in markers.keys)
      layers.add(new Marker(type: type, position: markers[type]));
170
    return new Stack(children: layers);
171 172 173 174
  }
}

void main() {
Adam Barth's avatar
Adam Barth committed
175
  runApp(new MaterialApp(
176 177 178 179 180 181
    theme: new ThemeData(
      brightness: ThemeBrightness.light,
      primarySwatch: Colors.blue,
      accentColor: Colors.redAccent[200]
    ),
    title: 'Cards',
Hixie's avatar
Hixie committed
182
    routes: <String, RouteBuilder>{
183
      '/': (RouteArguments args) => new OverlayGeometryApp()
184 185
    }
  ));
186
}