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 5import 'package:flutter/material.dart'; 6 7class _GesturePainter extends CustomPainter { 8 const _GesturePainter({ 9 this.zoom, 10 this.offset, 11 this.swatch, 12 this.forward, 13 this.scaleEnabled, 14 this.tapEnabled, 15 this.doubleTapEnabled, 16 this.longPressEnabled, 17 }); 18 19 final double zoom; 20 final Offset offset; 21 final MaterialColor swatch; 22 final bool forward; 23 final bool scaleEnabled; 24 final bool tapEnabled; 25 final bool doubleTapEnabled; 26 final bool longPressEnabled; 27 28 @override 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 colors: forward ? <Color>[swatch.shade50, swatch.shade900] 34 : <Color>[swatch.shade900, swatch.shade50] 35 ); 36 final Paint paint = Paint() 37 ..shader = gradient.createShader(Rect.fromCircle( 38 center: center, 39 radius: radius, 40 )); 41 canvas.drawCircle(center, radius, paint); 42 } 43 44 @override 45 bool shouldRepaint(_GesturePainter oldPainter) { 46 return oldPainter.zoom != zoom 47 || oldPainter.offset != offset 48 || oldPainter.swatch != swatch 49 || oldPainter.forward != forward 50 || oldPainter.scaleEnabled != scaleEnabled 51 || oldPainter.tapEnabled != tapEnabled 52 || oldPainter.doubleTapEnabled != doubleTapEnabled 53 || oldPainter.longPressEnabled != longPressEnabled; 54 } 55} 56 57class GestureDemo extends StatefulWidget { 58 @override 59 GestureDemoState createState() => GestureDemoState(); 60} 61 62class GestureDemoState extends State<GestureDemo> { 63 64 Offset _startingFocalPoint; 65 66 Offset _previousOffset; 67 Offset _offset = Offset.zero; 68 69 double _previousZoom; 70 double _zoom = 1.0; 71 72 static const List<MaterialColor> kSwatches = <MaterialColor>[ 73 Colors.red, 74 Colors.pink, 75 Colors.purple, 76 Colors.deepPurple, 77 Colors.indigo, 78 Colors.blue, 79 Colors.lightBlue, 80 Colors.cyan, 81 Colors.green, 82 Colors.lightGreen, 83 Colors.lime, 84 Colors.yellow, 85 Colors.amber, 86 Colors.orange, 87 Colors.deepOrange, 88 Colors.brown, 89 Colors.grey, 90 Colors.blueGrey, 91 ]; 92 int _swatchIndex = 0; 93 MaterialColor _swatch = kSwatches.first; 94 MaterialColor get swatch => _swatch; 95 96 bool _forward = true; 97 bool _scaleEnabled = true; 98 bool _tapEnabled = true; 99 bool _doubleTapEnabled = true; 100 bool _longPressEnabled = true; 101 102 void _handleScaleStart(ScaleStartDetails details) { 103 setState(() { 104 _startingFocalPoint = details.focalPoint; 105 _previousOffset = _offset; 106 _previousZoom = _zoom; 107 }); 108 } 109 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 final Offset normalizedOffset = (_startingFocalPoint - _previousOffset) / _previousZoom; 116 _offset = details.focalPoint - normalizedOffset * _zoom; 117 }); 118 } 119 120 void _handleScaleReset() { 121 setState(() { 122 _zoom = 1.0; 123 _offset = Offset.zero; 124 }); 125 } 126 127 void _handleColorChange() { 128 setState(() { 129 _swatchIndex += 1; 130 if (_swatchIndex == kSwatches.length) 131 _swatchIndex = 0; 132 _swatch = kSwatches[_swatchIndex]; 133 }); 134 } 135 136 void _handleDirectionChange() { 137 setState(() { 138 _forward = !_forward; 139 }); 140 } 141 142 @override 143 Widget build(BuildContext context) { 144 return Stack( 145 fit: StackFit.expand, 146 children: <Widget>[ 147 GestureDetector( 148 onScaleStart: _scaleEnabled ? _handleScaleStart : null, 149 onScaleUpdate: _scaleEnabled ? _handleScaleUpdate : null, 150 onTap: _tapEnabled ? _handleColorChange : null, 151 onDoubleTap: _doubleTapEnabled ? _handleScaleReset : null, 152 onLongPress: _longPressEnabled ? _handleDirectionChange : null, 153 child: CustomPaint( 154 painter: _GesturePainter( 155 zoom: _zoom, 156 offset: _offset, 157 swatch: swatch, 158 forward: _forward, 159 scaleEnabled: _scaleEnabled, 160 tapEnabled: _tapEnabled, 161 doubleTapEnabled: _doubleTapEnabled, 162 longPressEnabled: _longPressEnabled, 163 ), 164 ), 165 ), 166 Positioned( 167 bottom: 0.0, 168 left: 0.0, 169 child: Card( 170 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 crossAxisAlignment: CrossAxisAlignment.start, 212 ), 213 ), 214 ), 215 ), 216 ], 217 ); 218 } 219} 220 221void main() { 222 runApp(MaterialApp( 223 theme: ThemeData.dark(), 224 home: Scaffold( 225 appBar: AppBar(title: const Text('Gestures Demo')), 226 body: GestureDemo(), 227 ), 228 )); 229} 230