gestures.dart 6.45 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// 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
class _GesturePainter extends CustomPainter {
  const _GesturePainter({
9 10 11 12 13 14 15 16
    required this.zoom,
    required this.offset,
    required this.swatch,
    required this.forward,
    required this.scaleEnabled,
    required this.tapEnabled,
    required this.doubleTapEnabled,
    required 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 35
      colors: forward
        ? <Color>[swatch.shade50, swatch.shade900]
        : <Color>[swatch.shade900, swatch.shade50],
36
    );
37 38
    final Paint paint = Paint()
      ..shader = gradient.createShader(Rect.fromCircle(
39 40
        center: center,
        radius: radius,
41
      ));
42
    canvas.drawCircle(center, radius, paint);
43
  }
44

45
  @override
46 47 48 49 50 51 52 53 54
  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;
55 56 57
  }
}

58
class GestureDemo extends StatefulWidget {
59
  const GestureDemo({super.key});
60

61
  @override
62
  GestureDemoState createState() => GestureDemoState();
63 64
}

65
class GestureDemoState extends State<GestureDemo> {
66

67
  late Offset _startingFocalPoint;
68

69
  late Offset _previousOffset;
70 71
  Offset _offset = Offset.zero;

72
  late double _previousZoom;
73 74
  double _zoom = 1.0;

75
  static const List<MaterialColor> kSwatches = <MaterialColor>[
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    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;
98 99 100 101 102 103 104

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

105
  void _handleScaleStart(ScaleStartDetails details) {
106
    setState(() {
107
      _startingFocalPoint = details.focalPoint;
108 109 110 111 112
      _previousOffset = _offset;
      _previousZoom = _zoom;
    });
  }

113
  void _handleScaleUpdate(ScaleUpdateDetails details) {
114
    setState(() {
115
      _zoom = _previousZoom * details.scale;
116 117

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

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

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

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

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

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