pdf查看

This commit is contained in:
2026-03-03 17:51:06 +08:00
parent ce6bd3edd2
commit 8df16f0787
4 changed files with 136 additions and 50 deletions

View File

@@ -76,5 +76,11 @@
<string>en</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<!-- 允许在“文件”App中直接打开文档 -->
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
</dict>
</plist>

View File

@@ -3,12 +3,14 @@ import 'dart:io';
import 'package:flutter/material.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';
import 'package:image_picker/image_picker.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/storage_service.dart';
import 'package:path_provider/path_provider.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.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 {
// 1. 权限请求
if (Platform.isAndroid) {
dio.PermissionStatus status;
Future<void> saveFileToLocal(String url) async {
try {
// 权限请求
if (Platform.isAndroid) {
dio.PermissionStatus status;
final deviceInfo = await DeviceInfoPlugin().androidInfo;
final sdkInt = deviceInfo.version.sdkInt;
final deviceInfo = await DeviceInfoPlugin().androidInfo;
final sdkInt = deviceInfo.version.sdkInt;
if (sdkInt <= 32) {
status = await Permission.storage.request();
if (sdkInt <= 32) {
status = await Permission.storage.request();
} else {
status = await Permission.photos.request();
}
if (!status.isGranted) {
showErrorToast("请在系统设置中开启存储权限");
return;
}
} else {
status = await Permission.photos.request();
var status = await Permission.photos.request();
if (!status.isGranted) {
showErrorToast("请在系统设置中开启相册权限");
return;
}
}
if (!status.isGranted) {
showErrorToast("请在系统设置中开启存储权限");
return;
showLoading("正在保存...");
// 下载文件
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 {
var status = await Permission.photos.request();
if (!status.isGranted) {
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("保存失败");
} catch (e) {
dismissLoading();
showErrorToast("保存异常");
}
}
@@ -888,12 +919,30 @@ class SiteController extends GetxController with BaseControllerMixin {
child: PhotoViewGallery.builder(
scrollPhysics: const BouncingScrollPhysics(),
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(
imageProvider: NetworkImage(images[index]),
imageProvider: NetworkImage(url),
initialScale: PhotoViewComputedScale.contained,
heroAttributes: PhotoViewHeroAttributes(tag: images[index]),
heroAttributes: PhotoViewHeroAttributes(tag: url),
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(
Container(
color: Colors.white,
@@ -949,10 +1029,10 @@ class SiteController extends GetxController with BaseControllerMixin {
children: [
ListTile(
leading: const Icon(Icons.download),
title: const Text('保存图片到相册'),
title: Text(isPdf ? '保存 PDF 文件' : '保存图片到相册'),
onTap: () {
Get.back();
saveImageToLocal(url);
saveFileToLocal(url);
},
),
const Divider(height: 1),

View File

@@ -798,7 +798,7 @@ packages:
source: hosted
version: "1.1.0"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"

View File

@@ -54,7 +54,7 @@ dependencies:
pull_to_refresh: ^2.0.0
flutter_app_update: ^3.2.2
saver_gallery: ^4.0.0
path_provider: ^2.1.5
dev_dependencies:
flutter_test: