这里介绍的是图片查看库photo_view的使用以及达到我们的效果我们使用了一下插件,效果还是很棒的

图片浏览的插件是不支持保存到本地的,所以我们这边结合image_gallery_saver保存到相册。写入相册是需要申请权限的所以用到 permission_handler。

Untitled.gif

1。配置pubspec.yaml

#图片浏览查看

photo_view: ^0.9.2

#图片保存到本地相册

image_gallery_saver: ^1.2.2

#权限申请

permission_handler: ^5.0.0+hotfix.3

#弹框提醒

fluttertoast: ^4.0.1

#dio 网络请求

dio: 3.0.9

这里是路由跳转的动画可以试下在一个页面打开没有push效果

//路由跳转

class FadeRoute extends PageRouteBuilder {

final Widget page;

FadeRoute({this.page}): super(

pageBuilder: (

BuildContext context,

Animation animation,

Animation secondaryAnimation,

) =>page,transitionsBuilder: (

BuildContext context,

Animation animation,

Animation secondaryAnimation,

Widget child,

) =>FadeTransition(

opacity: animation,

child: child,

),

);

}

//图片浏览以及保存图片跟权限申请

import 'package:photo_view/photo_view.dart';

import 'package:photo_view/photo_view_gallery.dart';

import 'package:image_gallery_saver/image_gallery_saver.dart';

import 'package:permission_handler/permission_handler.dart';

import 'package:dio/dio.dart';

import 'dart:typed_data';

import 'package:fluttertoast/fluttertoast.dart';

import 'dart:io';

//自己写的弹框

import 'package:flutter_app_demo/app/CoustomWidget/alert_widget.dart';

class GalleryPhotoViewWrapper extends StatefulWidget {

GalleryPhotoViewWrapper({

this.loadingBuilder,

this.backgroundDecoration,

this.minScale,

this.maxScale,

this.initialIndex,

@required this.galleryItems,

this.scrollDirection = Axis.horizontal,

}) : pageController = PageController(initialPage: initialIndex);

final LoadingBuilder loadingBuilder;

final Decoration backgroundDecoration;

final dynamic minScale;

final dynamic maxScale;

final int initialIndex;

final PageController pageController;

final List galleryItems;

final Axis scrollDirection;

@override

State createState() {

return _GalleryPhotoViewWrapperState();

}

}

class _GalleryPhotoViewWrapperState extends State {

int currentIndex;

@override

void initState() {

currentIndex = widget.initialIndex;

super.initState();

}

void onPageChanged(int index) {

setState(() {

currentIndex = index;

});

}

@override

Widget build(BuildContext context) {

return Scaffold(

body: Container(

decoration: widget.backgroundDecoration,

constraints: BoxConstraints.expand(

height: MediaQuery.of(context).size.height,

),

child: Stack(

alignment: Alignment.bottomRight,

children: [

PhotoViewGallery.builder(

scrollPhysics: const BouncingScrollPhysics(),

builder: _buildItem,

itemCount: widget.galleryItems.length,

loadingBuilder: widget.loadingBuilder,

backgroundDecoration: widget.backgroundDecoration,

pageController: widget.pageController,

onPageChanged: onPageChanged,

scrollDirection: widget.scrollDirection,

),

Positioned(//图片index显示

top: MediaQuery.of(context).padding.top+15,

width: MediaQuery.of(context).size.width,

child: Center(

child: Text("${currentIndex+1}/${widget.galleryItems.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();

},

),

),

Positioned(

bottom: MediaQuery.of(context).padding.bottom +16,

child: FlatButton(

child: Text('保存图片',style: TextStyle(color: Colors.white,fontSize: 16),),

onPressed: (){

if (Platform.isIOS){

return _savenNetworkImage();

}

requestPermission().then((bool){

if (bool){

_savenNetworkImage();

}

});

},

),

)

],

),

),

);

}

//动态申请权限,ios 要在info.plist 上面添加

Future requestPermission() async {

var status = await Permission.photos.status;

if (status.isUndetermined) {

Map statuses = await [

Permission.photos,

].request();

}

return status.isGranted;

}

//保存网络图片到本地

_savenNetworkImage() async {

var status = await Permission.photos.status;

if (status.isDenied) {

// We didn't ask for permission yet.

print('暂无相册权限');

showDialog(context: context,builder: (context){

return AlertWidget(title: '微信提示',message: '您当前没有开启相册权限',confirm: '去开启',

confirmCallback: (){

//打开ios的设置

openAppSettings();

},);

});

return;

}

var response = await Dio().get(widget.galleryItems[currentIndex].resource, options: Options(responseType: ResponseType.bytes));

final result = await ImageGallerySaver.saveImage(Uint8List.fromList(response.data));

print(result);

if (Platform.isIOS){

if(result){

Fluttertoast.showToast(msg: '保存成功',gravity:ToastGravity.CENTER);

}else{

Fluttertoast.showToast(msg: '保存失败',gravity:ToastGravity.CENTER);

}

}else{

if(result !=null){

Fluttertoast.showToast(msg: '保存成功',gravity:ToastGravity.CENTER);

}else{

Fluttertoast.showToast(msg: '保存失败',gravity:ToastGravity.CENTER);

}

}

}

PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {

final GalleryExampleItem item = widget.galleryItems[index];

return PhotoViewGalleryPageOptions(

onTapUp: (BuildContext context, TapUpDetails details, PhotoViewControllerValue controllerValue){

Navigator.of(context).pop();

},

imageProvider:NetworkImage(item.resource),

// initialScale: PhotoViewComputedScale.contained,

// minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),

// maxScale: PhotoViewComputedScale.covered * 1.1,

heroAttributes: PhotoViewHeroAttributes(tag: item.id),

);

}

}

//Hero 动画组件

class GalleryExampleItemThumbnail extends StatelessWidget {

const GalleryExampleItemThumbnail(

{Key key, this.galleryExampleItem, this.onTap})

: super(key: key);

final GalleryExampleItem galleryExampleItem;

final GestureTapCallback onTap;

@override

Widget build(BuildContext context) {

return Container(

decoration: BoxDecoration(

borderRadius: BorderRadius.circular(4),

),

child: GestureDetector(

onTap: onTap,

child: Hero(

tag: galleryExampleItem.id,

child:Image.network(galleryExampleItem.resource,fit: BoxFit.cover,),

),

),

);

}

}

//model

class GalleryExampleItem {

GalleryExampleItem({this.id, this.resource, this.isSvg = false});

//hero的id 不能重复

String id;

String resource;

bool isSvg;

}

使用

model.imageUrlArray 路面存放的是 服务器上拉去的 多个图片的地址 ,我们需要把url转换成 GalleryExampleItem model,里面,有个字段 id就是Hero的id 要唯一 不能重复

GridView.builder(

shrinkWrap: true,

physics: NeverScrollableScrollPhysics(),

itemCount: model.imageUrlArray ==null?0 :model.imageUrlArray.length,

itemBuilder: (context,index){

return AspectRatio(

aspectRatio: 1,

child: GalleryExampleItemThumbnail(

galleryExampleItem: imageModel()[index],

onTap: (){

Navigator.of(context).push(

new FadeRoute(page: GalleryPhotoViewWrapper(

galleryItems: imageModel(),

backgroundDecoration: const BoxDecoration(

color: Colors.black,

),

initialIndex: index,

)));

},

),

);

},

gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(

crossAxisCount: model.imageUrlArray.length ==1?2: 3,

mainAxisSpacing: 8,

crossAxisSpacing: 8,

),

)

我们这里使用到的是自定义的一个 id,通过所以跟服务器获取列表每一行的id来拼接

List imageModel(){

List imgList = List();

for (int x = 0;x

{

GalleryExampleItem item = GalleryExampleItem();

item.id = 'tag${x}+${model.id}';

item.resource =model.imageUrlArray[x];

imgList.add(item);

}

print(imgList.first.id);

return imgList;

}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐