前言
Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。
IT界著名的尼古拉斯·高尔包曾说:轮子是IT进步的阶梯!热门的框架千篇一律,好用轮子万里挑一!Flutter作为这两年开始崛起的跨平台开发框架,其第三方生态相比其他成熟框架还略有不足,但轮子的数量也已经很多了。本系列文章挑选日常app开发常用的轮子分享出来,给大家提高搬砖效率,同时也希望flutter的生态越来越完善,轮子越来越多。
本系列文章准备了超过50个轮子推荐,工作原因,尽量每1-2天出一篇文章。
tip:本系列文章合适已有部分flutter基础的开发者,入门请戳:flutter官网
正文
轮子
- 轮子名称:photo_view
- 轮子概述:可定制的图片预览查看器:photo_view.
- 轮子作者:caraujo.me
- 推荐指数:★★★★★
- 常用指数:★★★★★
-
效果预览:
安装
dependencies:
photo_view: ^0.7.0
import 'package:photo_view/photo_view.dart';
使用
默认最简单的使用方式:
@override
Widget build(BuildContext context) {
return Container(
child: PhotoView(
imageProvider: AssetImage("assets/large-image.jpg"),
)
);
}
初步的效果是这样的:
可以放大查看,但这是一个已经打开预览界面的样子,日常使用我们需要从缩略图点击打开预览页面,就像上面效果图那样,所以我们需要自己写一个单独的预览界面,然后从缩略图点击打开。
单图片预览
单独写一个页面,作为图片预览的界面:
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
class PhotoViewSimpleScreen extends StateleeWidget{
const PhotoViewSimpleScreen({
this.imageProvider,//图片
this.loadingChild,//加载时的widget
this.backgroundDecoration,//背景修饰
this.minScale,//最大缩放倍数
this.maxScale,//最小缩放倍数
this.heroTag,//hero动画tagid
});
final ImageProvider imageProvider;
final Widget loadingChild;
final Decoration backgroundDecoration;
final dynamic minScale;
final dynamic maxScale;
final String heroTag;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
child: Stack(
children: <Widget>[
Positioned(
top: 0,
left: 0,
bottom: 0,
right: 0,
child: PhotoView(
imageProvider: imageProvider,
loadingChild: loadingChild,
backgroundDecoration: backgroundDecoration,
minScale: minScale,
maxScale: maxScale,
heroAttributes: PhotoViewHeroAttributes(tag: heroTag),
enableRotation: true,
),
),
Positioned(//右上角关闭按钮
right: 10,
top: MediaQuery.of(context).padding.top,
child: IconButton(
icon: Icon(Icons.close,size: 30,color: Colors.white,),
onPressed: (){
Navigator.of(context).pop();
},
),
)
],
),
),
);
}
}
给你展示缩图的地方加上点击事件,打开写好的预览界面:
onTap: (){
Navigator.of(context).push(new FadeRoute(page: PhotoViewSimpleScreen(
imageProvider:NetworkImage(img),
heroTag: 'simple',
)));
},
效果如上面gif的第一个效果。
多图片预览
再单独写一个页面,作为多图片预览的界面:
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
class PhotoViewGalleryScreen extends StatefulWidget {
List images=[];
int index=0;
String heroTag;
PageController controller;
PhotoViewGalleryScreen({Key key,@required this.images,this.index,this.controller,this.heroTag}) : super(key: key){
controller=PageController(initialPage: index);
}
@override
_PhotoViewGalleryScreenState createState() => _PhotoViewGalleryScreenState();
}
class _PhotoViewGalleryScreenState extends State<PhotoViewGalleryScreen> {
int currentIndex=0;
@override
void initState() {
// TODO: implement initState
super.initState();
currentIndex=widget.index;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Positioned(
top: 0,
left: 0,
bottom: 0,
right: 0,
child: Container(
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
builder: (BuildContext context, int index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(widget.images[index]),
heroAttributes: widget.heroTag.isNotEmpty?PhotoViewHeroAttributes(tag: widget.heroTag):null,
);
},
itemCount: widget.images.length,
loadingChild: Container(),
backgroundDecoration: null,
pageController: widget.controller,
enableRotation: true,
onPageChanged: (index){
setState(() {
currentIndex=index;
});
},
)
),
),
Positioned(//图片index显示
top: MediaQuery.of(context).padding.top+15,
width: MediaQuery.of(context).size.width,
child: Center(
child: Text("${currentIndex+1}/${widget.images.length}",style: TextStyle(color: Colors.white,fontSize: 16)),
),
),
Positioned(//右上角关闭按钮
right: 10,
top: MediaQuery.of(context).padding.top,
child: IconButton(
icon: Icon(Icons.close,size: 30,color: Colors.white,),
onPressed: (){
Navigator.of(context).pop();
},
),
),
],
),
);
}
}
给你展示缩图的地方加上点击事件,打开写好的预览界面:
onTap: (){
//FadeRoute是自定义的切换过度动画(渐隐渐现) 如果不需要 可以使用默认的MaterialPageRoute
Navigator.of(context).push(new FadeRoute(page: PhotoViewGalleryScreen(
images:imgs,//传入图片list
index: index,//传入当前点击的图片的index
heroTag: img,//传入当前点击的图片的hero tag (可选)
)));
},
FadeRoute的源码:
class FadeRoute extends PageRouteBuilder {
final Widget page;
FadeRoute({this.page}): super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>page,transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>FadeTransition(
opacity: animation,
child: child,
),
);
}
效果如上面gif的第二个效果。
从上面的代码可以看出,不管是单图还是多图预览,预览界面的布局都是完全自己定义的,虽然不是拿来即用,但是可定制度非常高,非常合适改造成自己的项目风格。
常用的参数
PhotoView(
imageProvider: imageProvider, //要显示的图片 AssetImage 或者 NetworkImage
loadingChild: loadingChild,//loading时显示的widget
backgroundDecoration: backgroundDecoration,//背景修饰
minScale: minScale,//最大缩放倍数
maxScale: maxScale,//最小缩放倍数
heroAttributes: PhotoViewHeroAttributes(tag: heroTag),//hero动画tag 不设置或null为不启用hero动画
enableRotation: true,//是否允许旋转
.....
)
查看所有的参数:https://pub.flutter-io.cn/documentation/photo_view/latest/photo_view/PhotoView-class.html