Skip to content

Instantly share code, notes, and snippets.

@kangabru
Last active August 17, 2020 08:45
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kangabru/883835ff2c160ef84d71a7552904c08d to your computer and use it in GitHub Desktop.
Save kangabru/883835ff2c160ef84d71a7552904c08d to your computer and use it in GitHub Desktop.
Allows you to display a 'magnifying glass' widget which helps users see what's under their finger while they are drawing
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
/// The radius of the mag glass.
const double _RADIUS = 60.0;
/// The distance the mag glass will be offset from the touch point.
const double _OFFSET = _RADIUS * 1.5;
/// The furthest left and top position the mag glass can have.
const double _PADDING_MAG_LEFT = 50.0, _PADDING_MAG_TOP = -10.0;
mixin MagnifyingGlass<T extends StatefulWidget> on State<T> {
static GlobalKey _key = new GlobalKey();
Offset _pos;
ui.Image _image;
bool _visible = false, _processing = false;
/// Wrap this around widget you wish to magnify.
Widget magnifyThis(Widget child) => RepaintBoundary(key: _key, child: child);
/// Show or hide the magnifying glass widget
showMagGlass(bool visible) => setState(() => _visible = visible);
/// Update the center position of the magnifying glass
updateMagGlassPosition(Offset position) => _updateMagPixels(position);
/// Draw the magnifying glass widget
Widget magnifyingGlass([Color background]) {
return Visibility(
visible: _visible,
child: EasyPaint(_MagGlass(_image, _pos, background)),
);
}
/// Takes a screenshot of the wrapped widgets and updates the mag glass image
/// Note that this whole widget only processes one screenshot at a time to improve
/// performance. There may be a slight image delay on lower powered devices.
_updateMagPixels(Offset pos) async {
setState(() => _pos = pos);
if (_processing) return;
setState(() => _processing = true);
RenderRepaintBoundary boundary = _key.currentContext.findRenderObject();
ui.Image image = await boundary.toImage();
setState(() {
_processing = false;
_image = image;
});
}
}
/// Draws the actual magnifying glass with the given cropped image
class _MagGlass extends CustomPainter {
final Offset pos;
final ui.Image image;
final Color background;
_MagGlass(this.image, this.pos, this.background);
@override
void paint(Canvas canvas, Size size) {
if (image == null) return;
var src = Rect.fromCircle(center: pos, radius: _RADIUS);
// Calculate the mag glass position accounting for the left and top limits
var offsetX = max(_PADDING_MAG_LEFT, pos.dx - _OFFSET);
var offsetY = max(_PADDING_MAG_TOP, pos.dy - _OFFSET);
var offset = Offset(offsetX, offsetY);
var dst = Rect.fromCircle(center: offset, radius: _RADIUS);
Path path = Path();
path.addOval(dst);
// Draw the image in a circle at the mag glass position
canvas.save();
canvas.clipPath(path);
canvas.drawPaint(Paint()..color = background ?? Colors.transparent);
canvas.drawImageRect(image, src, dst, Paint());
canvas.restore();
// Draw a border around the mag glass
var paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
canvas.drawOval(dst, paint);
}
bool shouldRepaint(_MagGlass other) => other.image.hashCode != image.hashCode;
}
class EasyPaint extends CustomPaint {
EasyPaint(CustomPainter painter)
: super(painter: painter, size: Size.infinite);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment