flutter 图片查看浏览,保存到本地

这里介绍的是图片查看库photo_view的使用以及达到我们的效果我们使用了一下插件,效果还是很棒的
图片浏览的插件是不支持保存到本地的,所以我们这边结合image_gallery_saver保存到相册。写入相册是需要申请权限的所以用到 permission_handler。


Untitled.gif
  • 图片浏览查看
    photo_view 官方地址 https://pub.dev/flutter/packages?q=photo_view
  • 保存图片到相册
    image_gallery_saver 官方地址 https://pub.dev/packages/image_gallery_saver#-installing-tab-
  • 权限申请
    permission_handler 官方地址 https://pub.dev/packages/permission_handler
  • 弹框提醒
    fluttertoast https://pub.dev/packages/fluttertoast
  • 网络请求,保存图片的是需要使用
    dio https://pub.dev/packages/dio

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

你可能感兴趣的:(flutter 图片查看浏览,保存到本地)