我正在开发一个 flutter 应用程序,想在我的应用程序中显示 PDF。 pdf 是使用 URL 下载的,可通过 API 获取该 URL。文档 URL 示例为:
https://drive.google.com/uc?export=view&id=GHwcjxgvKNYU3HuNsdajkdn5
import 'dart:developer';
import 'dart:io';
import 'package:dio/dio.dart' as dio;
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import '../constants/appConstants/AppConstants.dart';
import '../constants/appConstants/Urls.dart';
import '../util/SecureStorage.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
class AlertController extends GetxController {
final SecureStorage _secureStorage = SecureStorage();
final _dio = dio.Dio();
RxList<Map<String, dynamic>> alerts = <Map<String, dynamic>>[].obs;
RxMap<String, dynamic> alertDetails = <String, dynamic>{}.obs;
RxBool isLoadingDetails = false.obs;
Rx<File?> document = Rx<File?>(null);
Future<int> loadAlertDetails({required String alertToken}) async {
String? token = await _secureStorage.readAccessToken();
isLoadingDetails.value = true;
Options options = Options(
validateStatus: (_) => true,
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
headers: {HttpHeaders.authorizationHeader: token!});
try {
dio.Response response = await _dio.get(alertDetailsUrl,
options: options, queryParameters: {"token": alertToken});
Map<String, dynamic> responseData = response.data;
if (responseData.isNotEmpty) {
if (responseData['code'] == 2000) {
alertDetails.value = responseData['data'];
File? docFile =
await _checkForDoc(); // Assign to nullable File variable
document = Rx<File?>(docFile);
; // Assign nullable File variable to nullable Rx variable
// log('response: ${responseData['data']} ${document!.value}');
}
isLoadingDetails.value = false;
return responseData['code'];
}
} catch (err) {
log(err.toString());
}
isLoadingDetails.value = false;
return -1;
}
Future<File?> _checkForDoc() async {
if (alertDetails['alertInputType'] == AlertInputType.document.value) {
if (alertDetails['alertDocuments'] != null &&
alertDetails['alertDocuments'].length > 0) {
final response = await http
.get(Uri.parse(alertDetails['alertDocuments'][0]['documentUrl']));
final bytes = response.bodyBytes;
File? file = await _storeFile(
alertDetails['alertDocuments'][0]['documentUrl'], bytes);
return file;
}
}
return null;
}
Future<File?> _storeFile(String url, List<int> bytes) async {
try {
final filename = alertDetails['alertDocuments'][0]['documentName'];
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/$filename');
await file.writeAsBytes(bytes, flush: true);
log(file.path);
return file;
} catch (error) {
log('Error storing file: $error');
return null; // Return null if file cannot be stored
}
}
}
我确信该文件已正确下载,并且可以获得该文件的路径(如下所示)。
File: '/data/user/0/com.example.cacmp_app/app_flutter/demo.pdf'
逻辑是这样的,当alertInputType==DOCUMENT时,我用PDF显示详细信息,否则我用图像显示详细信息:
import 'dart:developer';
import 'package:cacmp_app/constants/appConstants/AppConstants.dart';
import 'package:cacmp_app/constants/themes/ColorConstants.dart';
import 'package:cacmp_app/constants/widgets/AppSnackbar.dart';
import 'package:cacmp_app/constants/widgets/CustomLoadingIndicator2.dart';
import 'package:cacmp_app/pages/LoginPage.dart';
import 'package:cacmp_app/stateUtil/AlertController.dart';
import 'package:cacmp_app/util/DateFormatUtil.dart';
import 'package:cacmp_app/util/SecureStorage.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
import '../constants/widgets/DetailsImageContainer.dart';
import '../constants/widgets/DetailsItems.dart';
class AlertDetailsPage extends StatefulWidget {
final String alertToken;
const AlertDetailsPage({super.key, required this.alertToken});
@override
State<AlertDetailsPage> createState() => _AlertDetailsPageState();
}
class _AlertDetailsPageState extends State<AlertDetailsPage> {
final AlertController _alertController = Get.find();
final SecureStorage _secureStorage = SecureStorage();
PDFViewController? controller;
int pages = 0;
int indexPage = 0;
@override
void initState() {
super.initState();
_loadDetails();
}
void _loadDetails() async {
final int code =
await _alertController.loadAlertDetails(alertToken: widget.alertToken);
log('details response code: $code');
if (code == 2000) {
} else if (code == 2003) {
_secureStorage.deleteOnLogOut();
Get.offAll(() => const LoginPage());
AppSnackbar.showSnackbar(title: "Expired!", description: "Login again!");
} else {
AppSnackbar.showSnackbar(
title: "Failed!", description: "Cannot load data!");
}
}
final AppBar _appBar = AppBar(
title: const Text('Details'),
);
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: kPrimaryColor,
statusBarIconBrightness: Brightness.light,
systemStatusBarContrastEnforced: true,
systemNavigationBarColor: kPrimaryColor,
systemNavigationBarIconBrightness: Brightness.light,
systemNavigationBarDividerColor: Colors.white,
));
final height = MediaQuery.of(context).size.height -
_appBar.preferredSize.height -
MediaQuery.of(context).padding.top -
MediaQuery.of(context).padding.bottom;
final width = MediaQuery.of(context).size.width;
return Scaffold(
appBar: _appBar,
body: Obx(
() => SingleChildScrollView(
child: (_alertController.isLoadingDetails.value)
? CustomLoadingIndicator2(color: kPrimaryColor)
: Padding(
padding: EdgeInsets.symmetric(horizontal: width * 0.02),
child: (_alertController
.alertDetails.value['alertInputType'] ==
AlertInputType.text.value)
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(_alertController.alertDetails.value['alertImages']
.length >
0)
? Container(
height: height * 0.3,
width: width,
padding: EdgeInsets.symmetric(
horizontal: width * 0.05,
vertical: height * 0.01),
child: CarouselSlider(
items: _alertController
.alertDetails.value['alertImages']
.map<Widget>((url) {
return DetailsImageContainer(
height: height,
width: width,
imageUrl: url);
}).toList(),
options: CarouselOptions(
autoPlay: false,
aspectRatio: 16 / 9,
enlargeCenterPage: true,
pauseAutoPlayOnTouch: true,
autoPlayInterval:
const Duration(seconds: 3),
autoPlayAnimationDuration:
const Duration(milliseconds: 800),
viewportFraction: 0.8,
onPageChanged: (index, reason) {},
scrollPhysics:
const BouncingScrollPhysics()),
),
)
: Container(),
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Details',
style:
Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 10),
DetailsItem(
title: 'Subject',
value: _alertController
.alertDetails.value['subject'],
),
DetailsItem(
title: 'Description',
value: _alertController
.alertDetails.value['message'],
),
DetailsItem(
title: 'Published On',
value: formatDate(DateTime.parse(
_alertController.alertDetails
.value['publishedOn'])),
),
],
),
),
],
)
: (_alertController.document.value != null)
? Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: width,
height: height * 0.1,
alignment: Alignment.centerLeft,
child: Text(
'Details',
style:
Theme.of(context).textTheme.bodyLarge,
),
),
DetailsItem(
title: 'Subject',
value: _alertController
.alertDetails.value['subject'],
),
DetailsItem(
title: 'Published On',
value: formatDate(DateTime.parse(
_alertController
.alertDetails.value['publishedOn'])),
),
Container(
color: Colors.red,
height: height * 0.5,
child: PDFView(
filePath:
_alertController.document.value!.path,
autoSpacing: false,
swipeHorizontal: true,
pageSnap: false,
pageFling: false,
onRender: (pages) =>
setState(() => this.pages = pages!),
onViewCreated: (controller) => setState(
() => this.controller = controller),
onPageChanged: (indexPage, _) => setState(
() => this.indexPage = indexPage!),
),
)
],
)
: Container(),
),
),
),
);
}
}
当“alertInputType”为“DOCUMENT”时,将渲染 PDFView,显示 PDF。但是,只有 PDFView 框得到渲染,PDF 不可见。
我从下面的例子中采用了这个方法-
https://github.com/JohannesMilke/pdf_viewer_example/blob/master/lib/page/pdf_viewer_page.dart
https://www.youtube.com/watch?v=uizZbJWziEg&ab_channel=HeyFlutter%E2%80%A4com
这是我的页面截图-
我正在使用以下软件包(如上面的示例)-
flutter_pdfview: ^1.2.7
path: ^1.7.0
path_provider: ^1.6.27
请帮忙!
使用这个包--->syncfusion_flutter_pdfviewer:^25.1.38