mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Many of the widgets that use CustomPaint were spamming repaints because CustomPaint repaints when the identity of the onPaint callback changes, which it does every build for StatelessComponents. This patch changes CustomPaint to use a CustomPainter, similar to the new custom layout widgets. The CustomPainter has a `shouldRepaint` function along with its `paint` function. This function gives clients explicit control over when the custom paint object repaints.
209 lines
6.7 KiB
Dart
209 lines
6.7 KiB
Dart
// 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';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
class ScaleApp extends StatefulComponent {
|
|
ScaleAppState createState() => new ScaleAppState();
|
|
}
|
|
|
|
class _GesturePainter extends CustomPainter {
|
|
const _GesturePainter({
|
|
this.zoom,
|
|
this.offset,
|
|
this.swatch,
|
|
this.forward,
|
|
this.scaleEnabled,
|
|
this.tapEnabled,
|
|
this.doubleTapEnabled,
|
|
this.longPressEnabled
|
|
});
|
|
|
|
final double zoom;
|
|
final Offset offset;
|
|
final Map<int, Color> swatch;
|
|
final bool forward;
|
|
final bool scaleEnabled;
|
|
final bool tapEnabled;
|
|
final bool doubleTapEnabled;
|
|
final bool longPressEnabled;
|
|
|
|
void paint(PaintingCanvas canvas, Size size) {
|
|
Point center = (size.center(Point.origin).toOffset() * zoom + offset).toPoint();
|
|
double radius = size.width / 2.0 * zoom;
|
|
Gradient gradient = new RadialGradient(
|
|
center: center, radius: radius,
|
|
colors: forward ? <Color>[swatch[50], swatch[900]]
|
|
: <Color>[swatch[900], swatch[50]]
|
|
);
|
|
Paint paint = new Paint()
|
|
..shader = gradient.createShader();
|
|
canvas.drawCircle(center, radius, paint);
|
|
}
|
|
|
|
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 ScaleAppState extends State<ScaleApp> {
|
|
|
|
Point _startingFocalPoint;
|
|
|
|
Offset _previousOffset;
|
|
Offset _offset = Offset.zero;
|
|
|
|
double _previousZoom;
|
|
double _zoom = 1.0;
|
|
|
|
Map<int, Color> _swatch = Colors.blue;
|
|
|
|
bool _forward = true;
|
|
bool _scaleEnabled = true;
|
|
bool _tapEnabled = true;
|
|
bool _doubleTapEnabled = true;
|
|
bool _longPressEnabled = true;
|
|
|
|
void _handleScaleStart(Point focalPoint) {
|
|
setState(() {
|
|
_startingFocalPoint = focalPoint;
|
|
_previousOffset = _offset;
|
|
_previousZoom = _zoom;
|
|
});
|
|
}
|
|
|
|
void _handleScaleUpdate(double scale, Point focalPoint) {
|
|
setState(() {
|
|
_zoom = (_previousZoom * scale);
|
|
|
|
// Ensure that item under the focal point stays in the same place despite zooming
|
|
Offset normalizedOffset = (_startingFocalPoint.toOffset() - _previousOffset) / _previousZoom;
|
|
_offset = focalPoint.toOffset() - normalizedOffset * _zoom;
|
|
});
|
|
}
|
|
|
|
void _handleScaleReset() {
|
|
setState(() {
|
|
_zoom = 1.0;
|
|
_offset = Offset.zero;
|
|
});
|
|
}
|
|
|
|
void _handleColorChange() {
|
|
setState(() {
|
|
switch (_swatch) {
|
|
case Colors.blueGrey: _swatch = Colors.red; break;
|
|
case Colors.red: _swatch = Colors.pink; break;
|
|
case Colors.pink: _swatch = Colors.purple; break;
|
|
case Colors.purple: _swatch = Colors.deepPurple; break;
|
|
case Colors.deepPurple: _swatch = Colors.indigo; break;
|
|
case Colors.indigo: _swatch = Colors.blue; break;
|
|
case Colors.blue: _swatch = Colors.lightBlue; break;
|
|
case Colors.lightBlue: _swatch = Colors.cyan; break;
|
|
case Colors.cyan: _swatch = Colors.teal; break;
|
|
case Colors.teal: _swatch = Colors.green; break;
|
|
case Colors.green: _swatch = Colors.lightGreen; break;
|
|
case Colors.lightGreen: _swatch = Colors.lime; break;
|
|
case Colors.lime: _swatch = Colors.yellow; break;
|
|
case Colors.yellow: _swatch = Colors.amber; break;
|
|
case Colors.amber: _swatch = Colors.orange; break;
|
|
case Colors.orange: _swatch = Colors.deepOrange; break;
|
|
case Colors.deepOrange: _swatch = Colors.brown; break;
|
|
case Colors.brown: _swatch = Colors.grey; break;
|
|
case Colors.grey: _swatch = Colors.blueGrey; break;
|
|
default: assert(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
void _handleDirectionChange() {
|
|
setState(() {
|
|
_forward = !_forward;
|
|
});
|
|
}
|
|
|
|
|
|
Widget build(BuildContext context) {
|
|
return new Theme(
|
|
data: new ThemeData.dark(),
|
|
child: new Scaffold(
|
|
toolBar: new ToolBar(
|
|
center: new Text('Gestures Demo')),
|
|
body: new Stack([
|
|
new GestureDetector(
|
|
onScaleStart: _scaleEnabled ? _handleScaleStart : null,
|
|
onScaleUpdate: _scaleEnabled ? _handleScaleUpdate : null,
|
|
onTap: _tapEnabled ? _handleColorChange : null,
|
|
onDoubleTap: _doubleTapEnabled ? _handleScaleReset : null,
|
|
onLongPress: _longPressEnabled ? _handleDirectionChange : null,
|
|
child: new CustomPaint(
|
|
painter: new _GesturePainter(
|
|
zoom: _zoom,
|
|
offset: _offset,
|
|
swatch: _swatch,
|
|
forward: _forward,
|
|
scaleEnabled: _scaleEnabled,
|
|
tapEnabled: _tapEnabled,
|
|
doubleTapEnabled: _doubleTapEnabled,
|
|
longPressEnabled: _longPressEnabled
|
|
)
|
|
)
|
|
),
|
|
new Positioned(
|
|
bottom: 0.0,
|
|
left: 0.0,
|
|
child: new Card(
|
|
child: new Container(
|
|
padding: new EdgeDims.all(4.0),
|
|
child: new Column([
|
|
new Row([
|
|
new Checkbox(
|
|
value: _scaleEnabled,
|
|
onChanged: (bool value) { setState(() { _scaleEnabled = value; }); }
|
|
),
|
|
new Text('Scale'),
|
|
]),
|
|
new Row([
|
|
new Checkbox(
|
|
value: _tapEnabled,
|
|
onChanged: (bool value) { setState(() { _tapEnabled = value; }); }
|
|
),
|
|
new Text('Tap'),
|
|
]),
|
|
new Row([
|
|
new Checkbox(
|
|
value: _doubleTapEnabled,
|
|
onChanged: (bool value) { setState(() { _doubleTapEnabled = value; }); }
|
|
),
|
|
new Text('Double Tap'),
|
|
]),
|
|
new Row([
|
|
new Checkbox(
|
|
value: _longPressEnabled,
|
|
onChanged: (bool value) { setState(() { _longPressEnabled = value; }); }
|
|
),
|
|
new Text('Long Press'),
|
|
]),
|
|
],
|
|
alignItems: FlexAlignItems.start
|
|
)
|
|
)
|
|
)
|
|
),
|
|
])
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
void main() => runApp(new ScaleApp());
|