pdf查看
This commit is contained in:
@@ -76,5 +76,11 @@
|
|||||||
<string>en</string>
|
<string>en</string>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
|
<key>UIFileSharingEnabled</key>
|
||||||
|
<true/>
|
||||||
|
<!-- 允许在“文件”App中直接打开文档 -->
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_pdfview/flutter_pdfview.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart' as dio;
|
import 'package:getx_scaffold/getx_scaffold.dart' as dio;
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:ln_jq_app/common/model/base_model.dart';
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/common/styles/theme.dart';
|
import 'package:ln_jq_app/common/styles/theme.dart';
|
||||||
import 'package:ln_jq_app/storage_service.dart';
|
import 'package:ln_jq_app/storage_service.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
import 'package:photo_view/photo_view_gallery.dart';
|
import 'package:photo_view/photo_view_gallery.dart';
|
||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
@@ -794,54 +796,83 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 保存图片到相册
|
/// 保存图片到相册
|
||||||
Future<void> saveImageToLocal(String url) async {
|
Future<void> saveFileToLocal(String url) async {
|
||||||
// 1. 权限请求
|
try {
|
||||||
if (Platform.isAndroid) {
|
// 权限请求
|
||||||
dio.PermissionStatus status;
|
if (Platform.isAndroid) {
|
||||||
|
dio.PermissionStatus status;
|
||||||
|
|
||||||
final deviceInfo = await DeviceInfoPlugin().androidInfo;
|
final deviceInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
final sdkInt = deviceInfo.version.sdkInt;
|
final sdkInt = deviceInfo.version.sdkInt;
|
||||||
|
|
||||||
if (sdkInt <= 32) {
|
if (sdkInt <= 32) {
|
||||||
status = await Permission.storage.request();
|
status = await Permission.storage.request();
|
||||||
|
} else {
|
||||||
|
status = await Permission.photos.request();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status.isGranted) {
|
||||||
|
showErrorToast("请在系统设置中开启存储权限");
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
status = await Permission.photos.request();
|
var status = await Permission.photos.request();
|
||||||
|
if (!status.isGranted) {
|
||||||
|
showErrorToast("请在系统设置中开启相册权限");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!status.isGranted) {
|
showLoading("正在保存...");
|
||||||
showErrorToast("请在系统设置中开启存储权限");
|
|
||||||
return;
|
// 下载文件
|
||||||
|
var response = await Dio().get(
|
||||||
|
url,
|
||||||
|
options: Options(responseType: ResponseType.bytes),
|
||||||
|
);
|
||||||
|
|
||||||
|
final Uint8List bytes = Uint8List.fromList(response.data);
|
||||||
|
|
||||||
|
if (url.toLowerCase().endsWith('.pdf')) {
|
||||||
|
String? savePath;
|
||||||
|
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
final directory = Directory('/storage/emulated/0/Download');
|
||||||
|
if (!await directory.exists()) {
|
||||||
|
await directory.create(recursive: true);
|
||||||
|
}
|
||||||
|
final String fileName = "certificate_${DateTime.now().millisecondsSinceEpoch}.pdf";
|
||||||
|
savePath = "${directory.path}/$fileName";
|
||||||
|
} else {
|
||||||
|
// iOS: 保存到文档目录
|
||||||
|
final directory = await getApplicationDocumentsDirectory();
|
||||||
|
final String fileName = "certificate_${DateTime.now().millisecondsSinceEpoch}.pdf";
|
||||||
|
savePath = "${directory.path}/$fileName";
|
||||||
|
}
|
||||||
|
|
||||||
|
final File file = File(savePath);
|
||||||
|
await file.writeAsBytes(bytes);
|
||||||
|
|
||||||
|
dismissLoading();
|
||||||
|
showSuccessToast(Platform.isAndroid ? "PDF已保存至系统下载目录" : "PDF已保存,请在'文件'App中查看");
|
||||||
|
} else {
|
||||||
|
// 保存图片到相册
|
||||||
|
final result = await SaverGallery.saveImage(
|
||||||
|
bytes,
|
||||||
|
quality: 100,
|
||||||
|
fileName: "certificate_${DateTime.now().millisecondsSinceEpoch}",
|
||||||
|
skipIfExists: false,
|
||||||
|
);
|
||||||
|
dismissLoading();
|
||||||
|
if (result.isSuccess) {
|
||||||
|
showSuccessToast("图片已保存至相册");
|
||||||
|
} else {
|
||||||
|
showErrorToast("保存失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} catch (e) {
|
||||||
var status = await Permission.photos.request();
|
dismissLoading();
|
||||||
if (!status.isGranted) {
|
showErrorToast("保存异常");
|
||||||
showErrorToast("请在系统设置中开启相册权限");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showLoading("正在保存...");
|
|
||||||
|
|
||||||
// 2. 下载图片
|
|
||||||
var response = await Dio().get(
|
|
||||||
url,
|
|
||||||
options: Options(responseType: ResponseType.bytes),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. 保存到相册
|
|
||||||
final result = await SaverGallery.saveImage(
|
|
||||||
Uint8List.fromList(response.data),
|
|
||||||
quality: 100,
|
|
||||||
fileName: "certificate_${DateTime.now().millisecondsSinceEpoch}",
|
|
||||||
skipIfExists: false,
|
|
||||||
);
|
|
||||||
|
|
||||||
dismissLoading();
|
|
||||||
|
|
||||||
if (result.isSuccess) {
|
|
||||||
showSuccessToast("图片已保存至相册");
|
|
||||||
} else {
|
|
||||||
showErrorToast("保存失败");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -888,12 +919,30 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
child: PhotoViewGallery.builder(
|
child: PhotoViewGallery.builder(
|
||||||
scrollPhysics: const BouncingScrollPhysics(),
|
scrollPhysics: const BouncingScrollPhysics(),
|
||||||
builder: (BuildContext context, int index) {
|
builder: (BuildContext context, int index) {
|
||||||
|
final String url = images[index];
|
||||||
|
final bool isPdf = url.toLowerCase().endsWith('.pdf');
|
||||||
|
|
||||||
|
if (isPdf) {
|
||||||
|
return PhotoViewGalleryPageOptions.customChild(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: (){
|
||||||
|
_showSaveMenu(url);
|
||||||
|
},
|
||||||
|
child: _buildPdfPreview(url),),
|
||||||
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
|
heroAttributes: PhotoViewHeroAttributes(tag: url),
|
||||||
|
onTapDown: (context, details, controllerValue) {
|
||||||
|
_showSaveMenu(url);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return PhotoViewGalleryPageOptions(
|
return PhotoViewGalleryPageOptions(
|
||||||
imageProvider: NetworkImage(images[index]),
|
imageProvider: NetworkImage(url),
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
initialScale: PhotoViewComputedScale.contained,
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: images[index]),
|
heroAttributes: PhotoViewHeroAttributes(tag: url),
|
||||||
onTapDown: (context, details, controllerValue) {
|
onTapDown: (context, details, controllerValue) {
|
||||||
_showSaveImageMenu(images[index]);
|
_showSaveMenu(url);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -939,7 +988,38 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSaveImageMenu(String url) {
|
/// PDF 预览小部件
|
||||||
|
Widget _buildPdfPreview(String url) {
|
||||||
|
return FutureBuilder<String>(
|
||||||
|
future: _downloadPdf(url),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||||
|
}
|
||||||
|
if (snapshot.hasError || snapshot.data == null) {
|
||||||
|
return const Center(child: Text("PDF 加载失败", style: TextStyle(color: Colors.white)));
|
||||||
|
}
|
||||||
|
return PDFView(
|
||||||
|
filePath: snapshot.data!,
|
||||||
|
enableSwipe: false,
|
||||||
|
swipeHorizontal: false,
|
||||||
|
autoSpacing: false,
|
||||||
|
pageFling: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _downloadPdf(String url) async {
|
||||||
|
final file = File('${(await getTemporaryDirectory()).path}/${url.hashCode}.pdf');
|
||||||
|
if (await file.exists()) return file.path;
|
||||||
|
var response = await Dio().get(url, options: Options(responseType: ResponseType.bytes));
|
||||||
|
await file.writeAsBytes(response.data);
|
||||||
|
return file.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSaveMenu(String url) {
|
||||||
|
final bool isPdf = url.toLowerCase().endsWith('.pdf');
|
||||||
Get.bottomSheet(
|
Get.bottomSheet(
|
||||||
Container(
|
Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@@ -949,10 +1029,10 @@ class SiteController extends GetxController with BaseControllerMixin {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.download),
|
leading: const Icon(Icons.download),
|
||||||
title: const Text('保存图片到相册'),
|
title: Text(isPdf ? '保存 PDF 文件' : '保存图片到相册'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Get.back();
|
Get.back();
|
||||||
saveImageToLocal(url);
|
saveFileToLocal(url);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(height: 1),
|
const Divider(height: 1),
|
||||||
|
|||||||
@@ -798,7 +798,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ dependencies:
|
|||||||
pull_to_refresh: ^2.0.0
|
pull_to_refresh: ^2.0.0
|
||||||
flutter_app_update: ^3.2.2
|
flutter_app_update: ^3.2.2
|
||||||
saver_gallery: ^4.0.0
|
saver_gallery: ^4.0.0
|
||||||
|
path_provider: ^2.1.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user