gestures.dart 6.33 KB
Newer Older
1 2 3 4 5 6
// 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 'package:flutter/material.dart';

7 8 9 10 11 12 13 14 15
class _GesturePainter extends CustomPainter {
  const _GesturePainter({
    this.zoom,
    this.offset,
    this.swatch,
    this.forward,
    this.scaleEnabled,
    this.tapEnabled,
    this.doubleTapEnabled,
16
    this.longPressEnabled,
17 18
  });

19
  final double zoom;
20
  final Offset offset;
21
  final MaterialColor swatch;
22
  final bool forward;
23 24 25 26 27
  final bool scaleEnabled;
  final bool tapEnabled;
  final bool doubleTapEnabled;
  final bool longPressEnabled;

28
  @override
Adam Barth's avatar
Adam Barth committed
29
  void paint(Canvas canvas, Size size) {
30
    final Offset center = size.center(Offset.zero) * zoom + offset;
31
    final double radius = size.width / 2.0 * zoom;
32
    final Gradient gradient = RadialGradient(
33 34
      colors: forward ? <Color>[swatch.shade50, swatch.shade900]
                      : <Color>[swatch.shade900, swatch.shade50]
35
    );
36 37
    final Paint paint = Paint()
      ..shader = gradient.createShader(Rect.fromCircle(
38 39
        center: center,
        radius: radius,
40
      ));
41
    canvas.drawCircle(center, radius, paint);
42
  }
43

44
  @override
45 46 47 48 49 50 51 52 53
  bool shouldRepaint(_GesturePainter oldPainter) {
    return oldPainter.zoom != zoom
        || oldPainter.offset != offset
        || oldPainter.swatch != swatch
        || oldPainter.forward != forward
        || oldPainter.scaleEnabled != scaleEnabled
        || oldPainter.tapEnabled != tapEnabled
        || oldPainter.doubleTapEnabled != doubleTapEnabled
        || oldPainter.longPressEnabled != longPressEnabled;
54 55 56
  }
}

57
class GestureDemo extends StatefulWidget {
58
  @override
59
  GestureDemoState createState() => GestureDemoState();
60 61
}

62
class GestureDemoState extends State<GestureDemo> {
63

64
  Offset _startingFocalPoint;
65 66 67 68 69 70 71

  Offset _previousOffset;
  Offset _offset = Offset.zero;

  double _previousZoom;
  double _zoom = 1.0;

72
  static const List<MaterialColor> kSwatches = <MaterialColor>[
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    Colors.red,
    Colors.pink,
    Colors.purple,
    Colors.deepPurple,
    Colors.indigo,
    Colors.blue,
    Colors.lightBlue,
    Colors.cyan,
    Colors.green,
    Colors.lightGreen,
    Colors.lime,
    Colors.yellow,
    Colors.amber,
    Colors.orange,
    Colors.deepOrange,
    Colors.brown,
    Colors.grey,
    Colors.blueGrey,
  ];
  int _swatchIndex = 0;
  MaterialColor _swatch = kSwatches.first;
  MaterialColor get swatch => _swatch;
95 96 97 98 99 100 101

  bool _forward = true;
  bool _scaleEnabled = true;
  bool _tapEnabled = true;
  bool _doubleTapEnabled = true;
  bool _longPressEnabled = true;

102
  void _handleScaleStart(ScaleStartDetails details) {
103
    setState(() {
104
      _startingFocalPoint = details.focalPoint;
105 106 107 108 109
      _previousOffset = _offset;
      _previousZoom = _zoom;
    });
  }

110
  void _handleScaleUpdate(ScaleUpdateDetails details) {
111
    setState(() {
112
      _zoom = _previousZoom * details.scale;
113 114

      // Ensure that item under the focal point stays in the same place despite zooming
115 116
      final Offset normalizedOffset = (_startingFocalPoint - _previousOffset) / _previousZoom;
      _offset = details.focalPoint - normalizedOffset * _zoom;
117 118 119 120 121 122 123 124 125 126 127 128
    });
  }

  void _handleScaleReset() {
    setState(() {
      _zoom = 1.0;
      _offset = Offset.zero;
    });
  }

  void _handleColorChange() {
    setState(() {
129 130 131 132
      _swatchIndex += 1;
      if (_swatchIndex == kSwatches.length)
        _swatchIndex = 0;
      _swatch = kSwatches[_swatchIndex];
133 134 135 136 137 138 139 140 141
    });
  }

  void _handleDirectionChange() {
    setState(() {
      _forward = !_forward;
    });
  }

142
  @override
143
  Widget build(BuildContext context) {
144
    return Stack(
145
      fit: StackFit.expand,
146
      children: <Widget>[
147
        GestureDetector(
148 149 150 151 152
          onScaleStart: _scaleEnabled ? _handleScaleStart : null,
          onScaleUpdate: _scaleEnabled ? _handleScaleUpdate : null,
          onTap: _tapEnabled ? _handleColorChange : null,
          onDoubleTap: _doubleTapEnabled ? _handleScaleReset : null,
          onLongPress: _longPressEnabled ? _handleDirectionChange : null,
153 154
          child: CustomPaint(
            painter: _GesturePainter(
155 156
              zoom: _zoom,
              offset: _offset,
157
              swatch: swatch,
158 159 160 161
              forward: _forward,
              scaleEnabled: _scaleEnabled,
              tapEnabled: _tapEnabled,
              doubleTapEnabled: _doubleTapEnabled,
162 163 164
              longPressEnabled: _longPressEnabled,
            ),
          ),
165
        ),
166
        Positioned(
167 168
          bottom: 0.0,
          left: 0.0,
169 170
          child: Card(
            child: Container(
171
              padding: const EdgeInsets.all(4.0),
172
              child: Column(
173
                children: <Widget>[
174
                  Row(
175
                    children: <Widget>[
176
                      Checkbox(
177
                        value: _scaleEnabled,
178
                        onChanged: (bool value) { setState(() { _scaleEnabled = value; }); },
179
                      ),
180
                      const Text('Scale'),
181
                    ],
182
                  ),
183
                  Row(
184
                    children: <Widget>[
185
                      Checkbox(
186
                        value: _tapEnabled,
187
                        onChanged: (bool value) { setState(() { _tapEnabled = value; }); },
188
                      ),
189
                      const Text('Tap'),
190
                    ],
191
                  ),
192
                  Row(
193
                    children: <Widget>[
194
                      Checkbox(
195
                        value: _doubleTapEnabled,
196
                        onChanged: (bool value) { setState(() { _doubleTapEnabled = value; }); },
197
                      ),
198
                      const Text('Double Tap'),
199
                    ],
200
                  ),
201
                  Row(
202
                    children: <Widget>[
203
                      Checkbox(
204
                        value: _longPressEnabled,
205
                        onChanged: (bool value) { setState(() { _longPressEnabled = value; }); },
206
                      ),
207
                      const Text('Long Press'),
208
                    ],
209 210
                  ),
                ],
211 212 213 214
                crossAxisAlignment: CrossAxisAlignment.start,
              ),
            ),
          ),
215
        ),
216
      ],
217 218 219 220
    );
  }
}

221
void main() {
222 223 224 225
  runApp(MaterialApp(
    theme: ThemeData.dark(),
    home: Scaffold(
      appBar: AppBar(title: const Text('Gestures Demo')),
226 227
      body: GestureDemo(),
    ),
228 229
  ));
}