anchor.diff:  
   class _GridPhotoViewerState extends State<GridPhotoViewer> with SingleTickerProviderStateMixin {
     AnimationController _controller;
  -  double _lastScale = 1.0;
  +  Animation<Offset> _flingAnimation;
  +  Offset _offset = Offset.zero;
     double _scale = 1.0;
  -  Point _lastFocalPoint = Point.origin;
  -  Point _focalPoint = Point.origin;
  -  Animation<Point> _flingAnimation;
  +  Offset _normalizedOffset;
  +  double _previousScale;
   
     @override
     void initState() {
  
positive.code: 
    void _handleOnScaleUpdate(ScaleUpdateDetails details) {
      setState(() {
        _scale = (_scale + (details.scale - _lastScale)).clamp(1.0, 3.0);
        _lastScale = details.scale;
        _focalPoint = _clampFocalPoint(_focalPoint + (_lastFocalPoint - details.focalPoint));
        _lastFocalPoint = details.focalPoint;
      });
    }
  
  
negative.code:     super.dispose();
    }
  
    // The maximum offset value is 0,0. If the size of this renderer's box is w,h
    // then the minimum offset value is w - _scale * w, h - _scale * h.
    Offset _clampOffset(Offset offset) {
      final Size size = context.size;
      final Offset minOffset = new Offset(size.width, size.height) * (1.0 - _scale);
      return new Offset(offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
    }
  
    void _handleFlingAnimation() {
      setState(() {
        _offset = _flingAnimation.value;
      });
    }
  
    void _handleOnScaleStart(ScaleStartDetails details) {
      setState(() {
        _previousScale = _scale;
        _normalizedOffset = (details.focalPoint.toOffset() - _offset) / _scale;
        // The fling animation stops if an input gesture starts.
        _controller.stop();
      });
  