Skip to content

Instantly share code, notes, and snippets.

@Urkman
Created May 6, 2021 08:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Urkman/e765f5270c357f8f9d1f703d439d88c1 to your computer and use it in GitHub Desktop.
Save Urkman/e765f5270c357f8f9d1f703d439d88c1 to your computer and use it in GitHub Desktop.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
// ignore: must_be_immutable
class HeroImageView extends StatelessWidget {
String urlString;
double height;
double width;
BoxFit fit;
Color dragBgColor;
String heroTag;
String test;
HeroImageView({this.urlString, this.width, this.height, this.fit, this.dragBgColor, this.heroTag});
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
PageRouteBuilder(
opaque: true,
pageBuilder: (BuildContext context, _, __) {
return HeroPhotoViewRouteWrapper(
tag: heroTag ?? urlString,
imageProvider: NetworkImage(
urlString,
),
);
},
transitionsBuilder: (___, Animation<double> animation, ____, Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
),
);
},
child: Container(
child: Hero(
tag: heroTag ?? urlString,
child: Image.network(
urlString,
height: height ?? MediaQuery.of(context).size.height,
width: width ?? MediaQuery.of(context).size.width,
fit: fit ?? BoxFit.cover,
// loadingBuilder: (_, child, chunk) => chunk != null ? Text("loading") : child,
),
),
),
),
);
}
}
class HeroPhotoViewRouteWrapper extends StatelessWidget {
HeroPhotoViewRouteWrapper({this.imageProvider, this.tag, this.backgroundDecoration, this.minScale, this.maxScale, this.dragBgColor});
final ImageProvider imageProvider;
final String tag;
final BoxDecoration backgroundDecoration;
final dynamic minScale;
final dynamic maxScale;
final Color dragBgColor;
@override
Widget build(BuildContext context) {
return Container(
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
child: PhotoViewSwipe(
heroAttributes: PhotoViewHeroAttributes(tag: tag),
imageProvider: imageProvider,
dragBgColor: dragBgColor,
),
);
}
}
class PhotoViewSwipe extends StatefulWidget {
PhotoViewSwipe({
Key key,
@required this.imageProvider,
this.dragBgColor, // default Colors.black.withOpacity(0.5)
this.dragDistance, // default 160
// Standard photo_view
this.loadingBuilder,
this.loadFailedChild,
this.backgroundDecoration,
this.gaplessPlayback = false,
this.heroAttributes,
this.scaleStateChangedCallback,
this.enableRotation = false,
this.controller,
this.maxScale,
this.minScale,
this.initialScale,
this.basePosition,
this.scaleStateCycle,
this.onTapUp,
this.onTapDown,
this.customSize,
this.gestureDetectorBehavior,
this.tightMode,
this.filterQuality,
}) : child = null,
childSize = null,
super(key: key);
final ImageProvider imageProvider;
final Color dragBgColor;
final double dragDistance;
// Standard photo_view
final LoadingBuilder loadingBuilder;
final Widget loadFailedChild;
final Decoration backgroundDecoration;
final bool gaplessPlayback;
final PhotoViewHeroAttributes heroAttributes;
final Size customSize;
final ValueChanged<PhotoViewScaleState> scaleStateChangedCallback;
final bool enableRotation;
final Widget child;
final Size childSize;
final dynamic maxScale;
final dynamic minScale;
final dynamic initialScale;
final PhotoViewControllerBase controller;
final Alignment basePosition;
final ScaleStateCycle scaleStateCycle;
final PhotoViewImageTapUpCallback onTapUp;
final PhotoViewImageTapDownCallback onTapDown;
final HitTestBehavior gestureDetectorBehavior;
final bool tightMode;
final FilterQuality filterQuality;
@override
_PhotoViewSwipeState createState() => _PhotoViewSwipeState();
}
class _PhotoViewSwipeState extends State<PhotoViewSwipe> {
Offset _position = Offset(0.0, 0.0);
bool _isZoomed = false;
PhotoViewScaleStateController scaleStateController;
@override
void initState() {
scaleStateController = PhotoViewScaleStateController();
scaleStateController.outputScaleStateStream.listen((event) {
setState(() {
_isZoomed = event != PhotoViewScaleState.zoomedOut && event != PhotoViewScaleState.initial;
});
});
super.initState();
}
@override
void dispose() {
scaleStateController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: widget.dragBgColor ?? Colors.black.withOpacity(0.5),
body: Stack(
children: [
Positioned(
left: _position.dx,
top: _position.dy,
child: GestureDetector(
onVerticalDragUpdate: !_isZoomed
? (details) {
setState(() => _position = Offset(0.0, _position.dy + details.delta.dy));
}
: null,
onVerticalDragEnd: !_isZoomed
? (details) {
double pixelsPerSecond = _position.dy.abs();
if (pixelsPerSecond > (widget.dragDistance ?? 160)) {
Navigator.pop(context);
} else {
setState(() => _position = Offset(0.0, 0.0));
}
}
: null,
child: Container(
decoration: _position.dy == 0.0 ? (widget.backgroundDecoration ?? BoxDecoration(color: Colors.black)) : null,
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: PhotoView(
imageProvider: widget.imageProvider,
backgroundDecoration: BoxDecoration(color: Colors.transparent),
scaleStateController: scaleStateController,
loadingBuilder: widget.loadingBuilder,
gaplessPlayback: widget.gaplessPlayback,
heroAttributes: widget.heroAttributes,
scaleStateChangedCallback: widget.scaleStateChangedCallback,
enableRotation: widget.enableRotation,
controller: widget.controller,
maxScale: widget.maxScale,
minScale: widget.minScale,
initialScale: widget.initialScale,
basePosition: widget.basePosition,
scaleStateCycle: widget.scaleStateCycle,
onTapUp: widget.onTapUp,
onTapDown: widget.onTapDown,
customSize: widget.customSize,
gestureDetectorBehavior: widget.gestureDetectorBehavior,
tightMode: widget.tightMode,
filterQuality: widget.filterQuality,
),
),
),
),
],
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment