Open LiWenHui96 opened 6 months ago
Although he seems to have no influence on the display effect, it is a problem after all!
@LiWenHui96 can you share your pdf_view.dart
code ?
@UnluckyY1 这是一套我自己的方案,仅供参考
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:pdfrx/pdfrx.dart';
export 'package:pdfrx/pdfrx.dart';
class PDFView extends StatefulWidget {
/// Create [PdfViewer] from an asset.
PDFView.asset(
String assetName, {
PdfPasswordProvider? passwordProvider,
bool firstAttemptByEmptyPassword = true,
super.key,
this.controller,
this.initialPageNumber = 1,
this.isSafeArea = false,
this.enableTextSelection = false,
this.onViewerReady,
}) : documentRef = CustomPdfDocumentRefAsset(
assetName,
passwordProvider: passwordProvider,
firstAttemptByEmptyPassword: firstAttemptByEmptyPassword,
hashCode: key != null ? key.hashCode : assetName.hashCode,
),
sourceType = ResourcesSourceType.asset;
/// Create [PdfViewer] from a file.
PDFView.file(
String filePath, {
PdfPasswordProvider? passwordProvider,
bool firstAttemptByEmptyPassword = true,
super.key,
this.controller,
this.initialPageNumber = 1,
this.isSafeArea = false,
this.enableTextSelection = false,
this.onViewerReady,
}) : documentRef = CustomPdfDocumentRefFile(
filePath,
passwordProvider: passwordProvider,
firstAttemptByEmptyPassword: firstAttemptByEmptyPassword,
hashCode: key != null ? key.hashCode : filePath.hashCode,
),
sourceType = ResourcesSourceType.file;
/// Create [PdfViewer] from a URI.
PDFView.uri(
Uri uri, {
PdfPasswordProvider? passwordProvider,
bool firstAttemptByEmptyPassword = true,
Map<String, String>? headers,
super.key,
this.controller,
this.initialPageNumber = 1,
this.isSafeArea = false,
this.enableTextSelection = false,
this.onViewerReady,
}) : documentRef = CustomPdfDocumentRefUri(
uri,
passwordProvider: passwordProvider,
firstAttemptByEmptyPassword: firstAttemptByEmptyPassword,
preferRangeAccess: true,
headers: headers,
hashCode: key != null ? key.hashCode : uri.hashCode,
),
sourceType = ResourcesSourceType.network;
/// [PdfDocumentRef] that represents the PDF document.
late final PdfDocumentRef documentRef;
/// 资源来源方式
final ResourcesSourceType sourceType;
/// Controller to control the viewer.
final PdfViewerController? controller;
/// Page number to show initially.
final int initialPageNumber;
/// 是否采用安全区域
final bool isSafeArea;
/// Experimental: Enable text selection on pages.
final bool enableTextSelection;
/// 是否可以查看
final ValueChanged<bool>? onViewerReady;
@override
State<PDFView> createState() => _PDFViewState();
}
class _PDFViewState extends State<PDFView> {
PdfDocumentRef? _documentRef;
/// 重试次数
int _retryCount = 3;
/// 加载时间过长的提示
String? moreTimeTip;
/// 加载时间过长的时间指示器
Timer? _timer;
@override
void initState() {
_getData();
super.initState();
}
@override
void dispose() {
_cancelTime();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_documentRef == null) return _buildLoading;
return _buildBody(_documentRef!);
}
/// 主体
Widget _buildBody(PdfDocumentRef documentRef) {
return PdfViewer(
documentRef,
controller: widget.controller,
params: PdfViewerParams(
margin: 12,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
layoutPages: (List<PdfPage> pages, PdfViewerParams params) {
final double width =
pages.fold<double>(0, (_, __) => max(_, __.width)) +
params.margin * 2;
final List<Rect> pageLayouts = <Rect>[];
double y = params.margin;
for (final PdfPage page in pages) {
pageLayouts.add(
Rect.fromLTWH(params.margin, y, page.width, page.height),
);
y += page.height + params.margin;
}
/// 用于添加安全区域
if (widget.isSafeArea) y += MediaQuery.paddingOf(context).bottom;
return PdfPageLayout(
pageLayouts: pageLayouts,
documentSize: Size(width, y),
);
},
enableTextSelection: widget.enableTextSelection,
pageDropShadow: const BoxShadow(
color: Colors.black26,
blurRadius: 4,
spreadRadius: 2,
offset: Offset(2, 2),
),
onViewerReady: (_, __) {
widget.onViewerReady?.call(true);
},
loadingBannerBuilder: (_, int count, int? total) => _buildLoading,
errorBannerBuilder: (_, Object error, StackTrace? stackTrace, __) {
/// 异常重试机制
if (_retryCount > 0) {
_retryCount--;
_getData();
} else {
/// 异常上报
}
widget.onViewerReady?.call(false);
return _buildError;
},
),
initialPageNumber: widget.initialPageNumber,
);
}
/// 加载布局
Widget get _buildLoading {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const CircularProgressIndicator(),
if (moreTimeTip != null)
Container(
margin: const EdgeInsets.only(top: 6),
child: Text(moreTimeTip ?? ''),
),
],
),
);
}
/// 异常布局
Widget get _buildError {
return const Center(child: Text('文档加载异常'));
}
/// 对于 [ResourcesSourceType.asset] 和 [ResourcesSourceType.file]
/// 直接返回 widget.documentRef
///
/// 对于 [ResourcesSourceType.network]
/// 进行网络请求后,下载至本地,改用 [CustomPdfDocumentRefFile] 展示
Future<void> _getData() async {
_openTime();
_documentRef = await _getPdfDocumentRef();
if (mounted) setState(() {});
}
Future<PdfDocumentRef> _getPdfDocumentRef() async {
final PdfDocumentRef documentRef = widget.documentRef;
/// 当资源为网络资源时,且重试次数小于3次,则强制加载本地数据
if (widget.sourceType == ResourcesSourceType.network &&
documentRef is PdfDocumentRefUri &&
_retryCount < 3) {
/// 文件存储地址
final Uri uri = documentRef.uri;
final PdfFileCacheNative cache = await PdfFileCacheNative.fromUri(uri);
if (cache.isInitialized) {
return CustomPdfDocumentRefFile(
cache.filePath,
passwordProvider: documentRef.passwordProvider,
firstAttemptByEmptyPassword: documentRef.firstAttemptByEmptyPassword,
autoDispose: documentRef.autoDispose,
hashCode: hashCode,
);
}
}
return documentRef;
}
/// 开启计时
void _openTime() {
if (_timer != null) _cancelTime();
_timer = Timer.periodic(const Duration(seconds: 3), (Timer timer) {
if (timer.isActive) {
setState(() => moreTimeTip = '努力加载中,请稍候...');
_cancelTime();
}
});
}
/// 取消计时
void _cancelTime() {
_timer?.cancel();
_timer = null;
}
}
@immutable
class CustomPdfDocumentRefAsset extends PdfDocumentRefAsset {
const CustomPdfDocumentRefAsset(
super.name, {
super.passwordProvider,
super.firstAttemptByEmptyPassword = true,
super.autoDispose = true,
required int hashCode,
}) : _hashCode = hashCode;
final int _hashCode;
@override
bool operator ==(Object other) =>
other is CustomPdfDocumentRefAsset && name == other.name;
@override
int get hashCode => _hashCode;
}
@immutable
class CustomPdfDocumentRefUri extends PdfDocumentRefUri {
const CustomPdfDocumentRefUri(
super.uri, {
super.passwordProvider,
super.firstAttemptByEmptyPassword = true,
super.autoDispose = true,
super.preferRangeAccess,
super.headers,
required int hashCode,
}) : _hashCode = hashCode;
final int _hashCode;
@override
bool operator ==(Object other) =>
other is CustomPdfDocumentRefUri && uri == other.uri;
@override
int get hashCode => _hashCode;
}
@immutable
class CustomPdfDocumentRefFile extends PdfDocumentRefFile {
const CustomPdfDocumentRefFile(
super.file, {
super.passwordProvider,
super.firstAttemptByEmptyPassword = true,
super.autoDispose = true,
required int hashCode,
}) : _hashCode = hashCode;
final int _hashCode;
@override
bool operator ==(Object other) =>
other is CustomPdfDocumentRefFile && file == other.file;
@override
int get hashCode => _hashCode;
}
/// 资源的来源类型
enum ResourcesSourceType {
/// 包含在应用程序的资产文件中
asset,
/// 从互联网上下载的
network,
/// 本地文件系统加载
file;
}
This happens to me as well, but only when I push to a separate Widget containing my PDF View FROM a Bottom Sheet View. And as he said:
Although he seems to have no influence on the display effect, it is a problem after all!
It's only showing the error in the console, and the App does not become red in debug or gray in production.
Scaffold (Home Screen) > Bottom Sheet (Some User Input Process) > Scaffold with PdfViewer.file() Hope that helps.
Emre Y.
PdfViewer
will provide me with the error message.Your support is needed, thank you very much!😄
Flutter doctor