1. 插件介绍

flutter_pdfview_ohos 是一个基于 flutter_pdfview@1.3.3 开发的 HarmonyOS 适配版本,用于在 Flutter 应用中展示 PDF 文件。该插件提供了丰富的 PDF 浏览功能,包括页面导航、缩放、链接处理等,帮助开发者轻松实现 PDF 文档的查看功能。

在这里插入图片描述

核心功能

  • 支持本地和远程 PDF 文件加载
  • 提供页面导航控制
  • 支持缩放和页面适应策略
  • 链接处理和自定义链接导航
  • 页面渲染和错误处理回调

2. 安装与配置

2.1 添加依赖

由于这是一个自定义修改的 HarmonyOS 适配版本,需要通过 Git 形式引入。在项目的 pubspec.yaml 文件中添加以下依赖:

dependencies:
  flutter_pdfview_ohos:
    git:
      url: "https://atomgit.com/openharmony-sig/fluttertpc_flutter_pdfview.git"
      path: "ohos"
      ref: master
  path_provider:
    git:
      url: "https://atomgit.com/openharmony-sig/fluttertpc_path_provider.git"
      path: "path_provider"

然后执行以下命令获取依赖:

flutter pub get

3. API 使用

3.1 核心组件

PDFView 组件

PDFView 是主要的 PDF 浏览组件,提供了丰富的属性配置:

属性 类型 描述 HarmonyOS 支持
filePath String? PDF 文件路径 yes
pdfData Uint8List? PDF 文件二进制数据 no
enableSwipe bool 是否允许滑动翻页 yes
swipeHorizontal bool 是否允许水平滑动翻页 no
defaultPage int 默认显示页码 yes
fitPolicy FitPolicy 页面适应策略 yes
onViewCreated PDFViewCreatedCallback? 视图创建回调 yes
onRender RenderCallback? 渲染完成回调 yes
onPageChanged PageChangedCallback? 页面变化回调 yes
onError ErrorCallback? 错误回调 yes
PDFViewController 控制器

通过 onViewCreated 回调获取控制器,用于操作 PDF 视图:

方法 返回类型 描述
getPageCount() Future<int?> 获取总页数
getCurrentPage() Future<int?> 获取当前页码
setPage(int page) Future<bool?> 设置当前页码

3.2 基本使用

加载本地 PDF 文件
import 'package:flutter_pdfview_ohos/flutter_pdfview_ohos.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/services.dart';
import 'dart:io';

// 从 assets 复制文件到本地
Future<File> fromAsset(String asset, String filename) async {
  var dir = await getApplicationDocumentsDirectory();
  File file = File("${dir.path}/$filename");
  var data = await rootBundle.load(asset);
  var bytes = data.buffer.asUint8List();
  await file.writeAsBytes(bytes, flush: true);
  return file;
}

// 使用 PDFView 组件
class PDFScreen extends StatefulWidget {
  final String? path;
  PDFScreen({Key? key, this.path}) : super(key: key);
  _PDFScreenState createState() => _PDFScreenState();
}

class _PDFScreenState extends State<PDFScreen> {
  final Completer<PDFViewController> _controller = Completer<PDFViewController>();
  int? pages = 0;
  int? currentPage = 0;
  bool isReady = false;
  String errorMessage = '';

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("PDF Viewer")),
      body: Stack(
        children: <Widget>[
          PDFView(
            filePath: widget.path,
            enableSwipe: true,
            fitPolicy: FitPolicy.BOTH,
            defaultPage: currentPage!,
            onRender: (_pages) {
              setState(() {
                pages = _pages;
                isReady = true;
              });
            },
            onError: (error) {
              setState(() {
                errorMessage = error.toString();
              });
            },
            onPageChanged: (int? page, int? total) {
              print('page change: $page/$total');
              setState(() {
                currentPage = page;
              });
            },
            onViewCreated: (PDFViewController pdfViewController) {
              _controller.complete(pdfViewController);
            },
          ),
          errorMessage.isEmpty
              ? !isReady
                  ? Center(child: CircularProgressIndicator())
                  : Container()
              : Center(child: Text(errorMessage))
        ],
      ),
      floatingActionButton: FutureBuilder<PDFViewController>(
        future: _controller.future,
        builder: (context, AsyncSnapshot<PDFViewController> snapshot) {
          if (snapshot.hasData) {
            return FloatingActionButton.extended(
              label: Text("Go to Last Page"),
              onPressed: () async {
                await snapshot.data!.setPage(pages! - 1);
              },
            );
          }
          return Container();
        },
      ),
    );
  }
}
加载远程 PDF 文件
Future<File> createFileOfPdfUrl() async {
  final url = "http://www.pdf995.com/samples/pdf.pdf";
  final filename = url.substring(url.lastIndexOf("/") + 1);
  var request = await HttpClient().getUrl(Uri.parse(url));
  var response = await request.close();
  var bytes = await consolidateHttpClientResponseBytes(response);
  var dir = await getApplicationDocumentsDirectory();
  File file = File("${dir.path}/$filename");
  await file.writeAsBytes(bytes, flush: true);
  return file;
}

// 使用示例
createFileOfPdfUrl().then((file) {
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => PDFScreen(path: file.path),
    ),
  );
});

4. 约束与限制

在 HarmonyOS 平台上使用此插件时,需要注意以下限制:

  1. PDF 二进制数据加载pdfData 属性在 HarmonyOS 上无效,只能通过 filePath 加载文件
  2. 水平滑动翻页swipeHorizontal 设置无效,仅支持垂直滑动
  3. 夜间模式nightMode 设置无效
  4. 自动间距autoSpacing 设置无效
  5. 翻页效果pageFlingpageSnap 设置无效

5. 完整示例

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_pdfview_ohos/flutter_pdfview_ohos.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String pathPDF = "";
  String remotePDFpath = "";

  
  void initState() {
    super.initState();
    fromAsset('assets/demo.pdf', 'demo.pdf').then((f) {
      setState(() {
        pathPDF = f.path;
      });
    });

    createFileOfPdfUrl().then((f) {
      setState(() {
        remotePDFpath = f.path;
      });
    });
  }

  Future<File> createFileOfPdfUrl() async {
    final url = "http://www.pdf995.com/samples/pdf.pdf";
    final filename = url.substring(url.lastIndexOf("/") + 1);
    var request = await HttpClient().getUrl(Uri.parse(url));
    var response = await request.close();
    var bytes = await consolidateHttpClientResponseBytes(response);
    var dir = await getApplicationDocumentsDirectory();
    File file = File("${dir.path}/$filename");
    await file.writeAsBytes(bytes, flush: true);
    return file;
  }

  Future<File> fromAsset(String asset, String filename) async {
    var dir = await getApplicationDocumentsDirectory();
    File file = File("${dir.path}/$filename");
    var data = await rootBundle.load(asset);
    var bytes = data.buffer.asUint8List();
    await file.writeAsBytes(bytes, flush: true);
    return file;
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter PDF View Example',
      home: Scaffold(
        appBar: AppBar(title: Text('PDF View Example')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextButton(
                child: Text("打开本地 PDF"),
                onPressed: () {
                  if (pathPDF.isNotEmpty) {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => PDFScreen(path: pathPDF),
                      ),
                    );
                  }
                },
              ),
              TextButton(
                child: Text("打开远程 PDF"),
                onPressed: () {
                  if (remotePDFpath.isNotEmpty) {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => PDFScreen(path: remotePDFpath),
                      ),
                    );
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class PDFScreen extends StatefulWidget {
  final String? path;
  PDFScreen({Key? key, this.path}) : super(key: key);
  _PDFScreenState createState() => _PDFScreenState();
}

class _PDFScreenState extends State<PDFScreen> {
  final Completer<PDFViewController> _controller = Completer<PDFViewController>();
  int? pages = 0;
  int? currentPage = 0;
  bool isReady = false;
  String errorMessage = '';

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("PDF Document"),
      ),
      body: Stack(
        children: <Widget>[
          PDFView(
            filePath: widget.path,
            enableSwipe: true,
            autoSpacing: false,
            pageFling: true,
            defaultPage: currentPage!,
            fitPolicy: FitPolicy.BOTH,
            preventLinkNavigation: false,
            onRender: (_pages) {
              setState(() {
                pages = _pages;
                isReady = true;
              });
            },
            onError: (error) {
              setState(() {
                errorMessage = error.toString();
              });
            },
            onPageError: (page, error) {
              setState(() {
                errorMessage = '$page: ${error.toString()}';
              });
            },
            onViewCreated: (PDFViewController pdfViewController) {
              _controller.complete(pdfViewController);
            },
            onPageChanged: (int? page, int? total) {
              setState(() {
                currentPage = page;
              });
            },
          ),
          errorMessage.isEmpty
              ? !isReady
                  ? Center(child: CircularProgressIndicator())
                  : Container()
              : Center(child: Text(errorMessage))
        ],
      ),
      bottomNavigationBar: BottomAppBar(
        shape: const CircularNotchedRectangle(),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            IconButton(
              icon: Icon(Icons.chevron_left),
              onPressed: () {
                _controller.future.then((controller) {
                  controller.setPage(currentPage! - 1);
                });
              },
            ),
            Text("${currentPage! + 1}/${pages!}"),
            IconButton(
              icon: Icon(Icons.chevron_right),
              onPressed: () {
                _controller.future.then((controller) {
                  controller.setPage(currentPage! + 1);
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

6. 总结

flutter_pdfview_ohos 是一个功能强大的 PDF 查看插件,为 HarmonyOS 上的 Flutter 应用提供了便捷的 PDF 浏览功能。通过本文的介绍,你可以了解到如何在 HarmonyOS 项目中配置和使用这个插件,实现本地和远程 PDF 文件的加载和查看。

虽然在 HarmonyOS 平台上存在一些功能限制,但核心的 PDF 查看功能都得到了很好的支持。开发者可以根据实际需求,灵活配置和使用这个插件,为用户提供良好的 PDF 阅读体验。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐