Skip to content

Commit

Permalink
Merge pull request #8 from gonzalogauto/master
Browse files Browse the repository at this point in the history
feat: add modalBarrier and new color argument
  • Loading branch information
Mayb3Nots committed Oct 8, 2022
2 parents 0fb1ad2 + f3e99db commit 492bca9
Showing 1 changed file with 84 additions and 55 deletions.
139 changes: 84 additions & 55 deletions lib/zoom_pinch_overlay.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import 'package:vector_math/vector_math_64.dart';
// Transform widget enables the overlay to be updated dynamically
//
class _TransformWidget extends StatefulWidget {
const _TransformWidget({
Key? key,
required this.child,
required this.matrix,
}) : super(key: key);

final Widget child;
final Matrix4 matrix;

const _TransformWidget({Key? key, required this.child, required this.matrix})
: super(key: key);

@override
_TransformWidgetState createState() => _TransformWidgetState();
}
Expand All @@ -22,7 +25,7 @@ class _TransformWidgetState extends State<_TransformWidget> {
@override
Widget build(BuildContext context) {
return Transform(
transform: (widget.matrix * _matrix),
transform: widget.matrix * _matrix,
child: widget.child,
);
}
Expand All @@ -39,6 +42,17 @@ class _TransformWidgetState extends State<_TransformWidget> {
/// by inserting a [OverlayEntry].
///
class ZoomOverlay extends StatefulWidget {
const ZoomOverlay({
Key? key,
this.twoTouchOnly = false,
required this.child,
this.minScale,
this.maxScale,
this.animationDuration = const Duration(milliseconds: 100),
this.animationCurve = Curves.fastOutSlowIn,
this.modalBarrierColor,
}) : super(key: key);

/// A widget to make zoomable.
final Widget child;

Expand All @@ -48,24 +62,20 @@ class ZoomOverlay extends StatefulWidget {
/// Specifies the maximum multiplier the user can zoom inwards.
final double? maxScale;

/// Specifies wither the zoom is enabled only with two fingers on the screen. Defaults to false.
/// Specifies wither the zoom is enabled only with two fingers on the screen.
/// Defaults to false.
final bool twoTouchOnly;

/// Specifies the animation duartion when the widget zoom has ended and is animating back to the original place.
/// Specifies the animation duartion when the widget zoom has ended and is
/// animating back to the original place.
final Duration animationDuration;

/// Specifies the animation curve when the widget zoom has ended and is animating back to the original place.
/// Specifies the animation curve when the widget zoom has ended and is
/// animating back to the original place.
final Curve animationCurve;

const ZoomOverlay(
{Key? key,
this.twoTouchOnly = false,
required this.child,
this.minScale,
this.maxScale,
this.animationDuration = const Duration(milliseconds: 100),
this.animationCurve = Curves.fastOutSlowIn})
: super(key: key);
/// Specifies the color of the modal barrier that shows in the background.
final Color? modalBarrierColor;

@override
_ZoomOverlayState createState() => _ZoomOverlayState();
Expand All @@ -88,16 +98,18 @@ class _ZoomOverlayState extends State<ZoomOverlay>
void initState() {
super.initState();

_controllerReset =
AnimationController(vsync: this, duration: widget.animationDuration);

_controllerReset.addListener(() {
_transformWidget.currentState!.setMatrix(_animationReset.value);
});
_controllerReset = AnimationController(
vsync: this,
duration: widget.animationDuration,
);

_controllerReset.addStatusListener((status) {
if (status == AnimationStatus.completed) hide();
});
_controllerReset
..addListener(() {
_transformWidget.currentState!.setMatrix(_animationReset.value);
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) hide();
});
}

@override
Expand All @@ -113,10 +125,11 @@ class _ZoomOverlayState extends State<ZoomOverlay>
onPointerUp: _incrementExit,
onPointerCancel: _incrementExit,
child: GestureDetector(
onScaleStart: onScaleStart,
onScaleUpdate: onScaleUpdate,
onScaleEnd: onScaleEnd,
child: Opacity(opacity: _isZooming ? 0 : 1, child: widget.child)),
onScaleStart: onScaleStart,
onScaleUpdate: onScaleUpdate,
onScaleEnd: onScaleEnd,
child: Opacity(opacity: _isZooming ? 0 : 1, child: widget.child),
),
);
}

Expand All @@ -129,11 +142,12 @@ class _ZoomOverlayState extends State<ZoomOverlay>
_matrix = Matrix4.identity();

// create an matrix of where the image is on the screen for the overlay
RenderBox renderBox = context.findRenderObject() as RenderBox;
Offset position = renderBox.localToGlobal(Offset.zero);
final renderBox = context.findRenderObject() as RenderBox;
final position = renderBox.localToGlobal(Offset.zero);

_transformMatrix =
Matrix4.translation(Vector3(position.dx, position.dy, 0));
_transformMatrix = Matrix4.translation(
Vector3(position.dx, position.dy, 0),
);

show();

Expand All @@ -145,47 +159,62 @@ class _ZoomOverlayState extends State<ZoomOverlay>
void onScaleUpdate(ScaleUpdateDetails details) {
if (!_isZooming || _controllerReset.isAnimating) return;

Offset translationDelta = details.focalPoint - _startFocalPoint;
final translationDelta = details.focalPoint - _startFocalPoint;

Matrix4 translate = Matrix4.translation(
Vector3(translationDelta.dx, translationDelta.dy, 0));
final translate = Matrix4.translation(
Vector3(translationDelta.dx, translationDelta.dy, 0),
);

RenderBox renderBox = context.findRenderObject() as RenderBox;
Offset focalPoint =
renderBox.globalToLocal(details.focalPoint - translationDelta);
final renderBox = context.findRenderObject() as RenderBox;
final focalPoint = renderBox.globalToLocal(
details.focalPoint - translationDelta,
);

double scaleby = details.scale;
if (widget.minScale != null && scaleby < widget.minScale!)
scaleby = this.widget.minScale ?? 0;
var scaleby = details.scale;
if (widget.minScale != null && scaleby < widget.minScale!) {
scaleby = widget.minScale ?? 0;
}

if (widget.maxScale != null && scaleby > widget.maxScale!)
scaleby = this.widget.maxScale ?? 0;
if (widget.maxScale != null && scaleby > widget.maxScale!) {
scaleby = widget.maxScale ?? 0;
}

var dx = (1 - scaleby) * focalPoint.dx;
var dy = (1 - scaleby) * focalPoint.dy;
final dx = (1 - scaleby) * focalPoint.dx;
final dy = (1 - scaleby) * focalPoint.dy;

Matrix4 scale =
final scale =
Matrix4(scaleby, 0, 0, 0, 0, scaleby, 0, 0, 0, 0, 1, 0, dx, dy, 0, 1);

_matrix = translate * scale;

if (_transformWidget.currentState != null)
if (_transformWidget.currentState != null) {
_transformWidget.currentState!.setMatrix(_matrix);
}
}

void onScaleEnd(ScaleEndDetails details) {
if (!_isZooming || _controllerReset.isAnimating) return;
_animationReset = Matrix4Tween(begin: _matrix, end: Matrix4.identity())
.animate(CurvedAnimation(
parent: _controllerReset, curve: widget.animationCurve));
_controllerReset.reset();
_controllerReset.forward();
_animationReset = Matrix4Tween(
begin: _matrix,
end: Matrix4.identity(),
).animate(
CurvedAnimation(
parent: _controllerReset,
curve: widget.animationCurve,
),
);
_controllerReset
..reset()
..forward();
}

Widget _build(BuildContext context) {
return IgnorePointer(
child: Stack(
children: [
ModalBarrier(
color: widget.modalBarrierColor,
),
_TransformWidget(
key: _transformWidget,
matrix: _transformMatrix,
Expand All @@ -196,15 +225,15 @@ class _ZoomOverlayState extends State<ZoomOverlay>
);
}

void show() async {
Future<void> show() async {
if (!_isZooming) {
final overlayState = Overlay.of(context);
_overlayEntry = OverlayEntry(builder: _build);
overlayState?.insert(_overlayEntry!);
}
}

void hide() async {
Future<void> hide() async {
setState(() {
_isZooming = false;
});
Expand Down

0 comments on commit 492bca9

Please sign in to comment.