gallery_photo_view.dart 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import 'dart:typed_data';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:dio/dio.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:image_gallery_saver/image_gallery_saver.dart';
  6. import 'package:permission_handler/permission_handler.dart';
  7. import 'package:photo_view/photo_view.dart';
  8. import 'package:photo_view/photo_view_gallery.dart';
  9. import 'package:sport/application.dart';
  10. import 'package:sport/bean/image.dart' as photo;
  11. import 'package:sport/utils/toast.dart';
  12. import 'package:sport/widgets/dialog/modal_bottom_action.dart';
  13. import 'package:sport/widgets/dialog/request_dialog.dart';
  14. import 'package:sport/widgets/loading.dart';
  15. void open(BuildContext context, final int index, List<photo.Image> images) {
  16. Navigator.push(
  17. context,
  18. FadeRoute(
  19. page: GalleryPhotoViewWrapper(
  20. galleryItems: images,
  21. backgroundDecoration: const BoxDecoration(
  22. color: Colors.black,
  23. ),
  24. initialIndex: index,
  25. scrollDirection: Axis.horizontal,
  26. loadingBuilder: (_, __) => RequestLoadingWidget(),
  27. ),
  28. ),
  29. );
  30. }
  31. class FadeRoute extends PageRouteBuilder {
  32. final Widget page;
  33. FadeRoute({required this.page})
  34. : super(
  35. pageBuilder: (
  36. BuildContext context,
  37. Animation<double> animation,
  38. Animation<double> secondaryAnimation,
  39. ) =>
  40. page,
  41. transitionsBuilder: (
  42. BuildContext context,
  43. Animation<double> animation,
  44. Animation<double> secondaryAnimation,
  45. Widget child,
  46. ) =>
  47. FadeTransition(
  48. opacity: animation,
  49. child: child,
  50. ),
  51. );
  52. }
  53. class GalleryPhotoViewWrapper extends StatefulWidget {
  54. GalleryPhotoViewWrapper({
  55. this.loadingBuilder,
  56. this.backgroundDecoration,
  57. this.minScale,
  58. this.maxScale,
  59. this.initialIndex = 0,
  60. required this.galleryItems,
  61. this.scrollDirection = Axis.horizontal,
  62. }) : pageController = PageController(initialPage: initialIndex);
  63. final LoadingBuilder? loadingBuilder;
  64. final BoxDecoration? backgroundDecoration;
  65. final dynamic minScale;
  66. final dynamic maxScale;
  67. final int initialIndex;
  68. final PageController pageController;
  69. final List<photo.Image> galleryItems;
  70. final Axis scrollDirection;
  71. @override
  72. State<StatefulWidget> createState() {
  73. return _GalleryPhotoViewWrapperState();
  74. }
  75. }
  76. class _GalleryPhotoViewWrapperState extends State<GalleryPhotoViewWrapper> {
  77. late int currentIndex;
  78. @override
  79. void initState() {
  80. currentIndex = widget.initialIndex;
  81. super.initState();
  82. }
  83. void onPageChanged(int index) {
  84. setState(() {
  85. currentIndex = index;
  86. });
  87. }
  88. @override
  89. Widget build(BuildContext context) {
  90. return Scaffold(
  91. body: Stack(
  92. children: <Widget>[
  93. Container(
  94. decoration: widget.backgroundDecoration,
  95. constraints: BoxConstraints.expand(
  96. height: MediaQuery.of(context).size.height,
  97. ),
  98. child: GestureDetector(
  99. onLongPress: () async {
  100. await showActionDialog(context, {
  101. "保存至本地": () async {
  102. await request(context, () async {
  103. var file =
  104. await save(widget.galleryItems[currentIndex].src??"");
  105. print("11111111111111 ${widget.galleryItems[currentIndex].src} $file");
  106. if (file != null) {
  107. ToastUtil.show("已保存图片至:$file");
  108. }
  109. });
  110. }
  111. });
  112. },
  113. child: PhotoViewGallery.builder(
  114. scrollPhysics: const BouncingScrollPhysics(),
  115. builder: _buildItem,
  116. itemCount: widget.galleryItems.length,
  117. loadingBuilder: widget.loadingBuilder,
  118. backgroundDecoration: widget.backgroundDecoration,
  119. pageController: widget.pageController,
  120. onPageChanged: onPageChanged,
  121. scrollDirection: widget.scrollDirection,
  122. ),
  123. ),
  124. ),
  125. Positioned(
  126. child: SafeArea(
  127. child: IconButton(
  128. icon: Image.asset("lib/assets/img/topbar_return_white.png"),
  129. onPressed: () {
  130. Navigator.maybePop(context);
  131. },
  132. ),
  133. ))
  134. ],
  135. ),
  136. );
  137. }
  138. Future<String?> save(String url) async {
  139. var permission = await Application.requestPermission(Permission.storage);
  140. if (!permission) {
  141. ToastUtil.show("没有相应权限!");
  142. return null;
  143. }
  144. var response = await Dio()
  145. .get(url, options: Options(responseType: ResponseType.bytes));
  146. final result = await ImageGallerySaver.saveImage(
  147. Uint8List.fromList(response.data),
  148. quality: 100,
  149. name: "shoes_${DateTime.now().millisecondsSinceEpoch}");
  150. if(result is Map){
  151. // print("1111111111111111111111111111 Map $result");
  152. if(result["isSuccess"] == true){
  153. return result["filePath"];
  154. }
  155. }
  156. return null;
  157. }
  158. PhotoViewGalleryPageOptions _buildItem(BuildContext context, int index) {
  159. final photo.Image item = widget.galleryItems[index];
  160. return PhotoViewGalleryPageOptions(
  161. imageProvider: CachedNetworkImageProvider(item.src??""),
  162. initialScale: PhotoViewComputedScale.contained,
  163. minScale: PhotoViewComputedScale.contained * (0.5 + index / 10),
  164. heroAttributes: PhotoViewHeroAttributes(tag: (item.id ?? item.src).toString()),
  165. onTapUp: (
  166. _,
  167. __,
  168. ___,
  169. ) {
  170. Navigator.of(context).maybePop();
  171. });
  172. }
  173. }