import 'dart:typed_data'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; import 'package:sport/application.dart'; import 'package:sport/bean/image.dart' as photo; import 'package:sport/utils/toast.dart'; import 'package:sport/widgets/dialog/modal_bottom_action.dart'; import 'package:sport/widgets/dialog/request_dialog.dart'; import 'package:sport/widgets/loading.dart'; void open(BuildContext context, final int index, List images) { Navigator.push( context, FadeRoute( page: GalleryPhotoViewWrapper( galleryItems: images, backgroundDecoration: const BoxDecoration( color: Colors.black, ), initialIndex: index, scrollDirection: Axis.horizontal, loadingBuilder: (_, __) => RequestLoadingWidget(), ), ), ); } 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, ), ); } 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: Stack( children: [ Container( decoration: widget.backgroundDecoration, constraints: BoxConstraints.expand( height: MediaQuery.of(context).size.height, ), child: GestureDetector( onLongPress: () async { await showActionDialog(context, { "保存至本地": () async { await request(context, () async { var file = await save(widget.galleryItems[currentIndex].src); if (file != null) { ToastUtil.show("已保存图片至:$file"); } }); } }); }, child: 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( child: SafeArea( child: IconButton( icon: Image.asset("lib/assets/img/topbar_return_white.png"), onPressed: () { Navigator.maybePop(context); }, ), )) ], ), ); } Future save(String url) async { var permission = await Application.requestPermission(Permission.storage); if (!permission) { ToastUtil.show("没有相应权限!"); return null; } var response = await Dio().get(url, options: Options(responseType: ResponseType.bytes)); final result = await ImageGallerySaver.saveImage(Uint8List.fromList(response.data), quality: 100, name: "shoes_${DateTime.now().millisecondsSinceEpoch}"); print(result); return result; } PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) { final photo.Image item = widget.galleryItems[index]; return PhotoViewGalleryPageOptions( imageProvider: CachedNetworkImageProvider(item.src), initialScale: PhotoViewComputedScale.contained, minScale: PhotoViewComputedScale.contained * (0.5 + index / 10), heroAttributes: PhotoViewHeroAttributes(tag: item.id ?? item.src), onTapUp: ( _, __, ___, ) { Navigator.of(context).pop(); }); } }