// Copyright 2014 The Flutter 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'; class _GesturePainter extends CustomPainter { const _GesturePainter({ required this.zoom, required this.offset, required this.swatch, required this.forward, required this.scaleEnabled, required this.tapEnabled, required this.doubleTapEnabled, required this.longPressEnabled, }); final double zoom; final Offset offset; final MaterialColor swatch; final bool forward; final bool scaleEnabled; final bool tapEnabled; final bool doubleTapEnabled; final bool longPressEnabled; @override void paint(Canvas canvas, Size size) { final Offset center = size.center(Offset.zero) * zoom + offset; final double radius = size.width / 2.0 * zoom; final Gradient gradient = RadialGradient( colors: forward ? [swatch.shade50, swatch.shade900] : [swatch.shade900, swatch.shade50], ); final Paint paint = Paint() ..shader = gradient.createShader(Rect.fromCircle( center: center, radius: radius, )); canvas.drawCircle(center, radius, paint); } @override 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; } } class GestureDemo extends StatefulWidget { const GestureDemo({Key? key}) : super(key: key); @override GestureDemoState createState() => GestureDemoState(); } class GestureDemoState extends State { late Offset _startingFocalPoint; late Offset _previousOffset; Offset _offset = Offset.zero; late double _previousZoom; double _zoom = 1.0; static const List kSwatches = [ 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; bool _forward = true; bool _scaleEnabled = true; bool _tapEnabled = true; bool _doubleTapEnabled = true; bool _longPressEnabled = true; void _handleScaleStart(ScaleStartDetails details) { setState(() { _startingFocalPoint = details.focalPoint; _previousOffset = _offset; _previousZoom = _zoom; }); } void _handleScaleUpdate(ScaleUpdateDetails details) { setState(() { _zoom = _previousZoom * details.scale; // Ensure that item under the focal point stays in the same place despite zooming final Offset normalizedOffset = (_startingFocalPoint - _previousOffset) / _previousZoom; _offset = details.focalPoint - normalizedOffset * _zoom; }); } void _handleScaleReset() { setState(() { _zoom = 1.0; _offset = Offset.zero; }); } void _handleColorChange() { setState(() { _swatchIndex += 1; if (_swatchIndex == kSwatches.length) _swatchIndex = 0; _swatch = kSwatches[_swatchIndex]; }); } void _handleDirectionChange() { setState(() { _forward = !_forward; }); } @override Widget build(BuildContext context) { return Stack( fit: StackFit.expand, children: [ GestureDetector( onScaleStart: _scaleEnabled ? _handleScaleStart : null, onScaleUpdate: _scaleEnabled ? _handleScaleUpdate : null, onTap: _tapEnabled ? _handleColorChange : null, onDoubleTap: _doubleTapEnabled ? _handleScaleReset : null, onLongPress: _longPressEnabled ? _handleDirectionChange : null, child: CustomPaint( painter: _GesturePainter( zoom: _zoom, offset: _offset, swatch: swatch, forward: _forward, scaleEnabled: _scaleEnabled, tapEnabled: _tapEnabled, doubleTapEnabled: _doubleTapEnabled, longPressEnabled: _longPressEnabled, ), ), ), Positioned( bottom: 0.0, left: 0.0, child: Card( child: Container( padding: const EdgeInsets.all(4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Checkbox( value: _scaleEnabled, onChanged: (bool? value) { setState(() { _scaleEnabled = value!; }); }, ), const Text('Scale'), ], ), Row( children: [ Checkbox( value: _tapEnabled, onChanged: (bool? value) { setState(() { _tapEnabled = value!; }); }, ), const Text('Tap'), ], ), Row( children: [ Checkbox( value: _doubleTapEnabled, onChanged: (bool? value) { setState(() { _doubleTapEnabled = value!; }); }, ), const Text('Double Tap'), ], ), Row( children: [ Checkbox( value: _longPressEnabled, onChanged: (bool? value) { setState(() { _longPressEnabled = value!; }); }, ), const Text('Long Press'), ], ), ], ), ), ), ), ], ); } } void main() { runApp(MaterialApp( theme: ThemeData.dark(), home: Scaffold( appBar: AppBar(title: const Text('Gestures Demo')), body: const GestureDemo(), ), )); }