Iridium
Iridium 是一个功能强大的 Flutter 电子书阅读器组件集合,旨在提供高质量的电子书阅读体验。
📚 功能特点
- 支持 EPUB 格式电子书的显示和阅读
- 提供完整的阅读器 UI 组件
- 可自定义的主题设置
- 字体大小、行距等排版设置
- 页面导航和进度显示
- 可嵌入任何 Flutter 应用中
📦 包结构
该项目包含多个核心组件:
- reader_widget: 即插即用的阅读器 Widget 组件
- components/commons: 通用工具和辅助类
- components/navigator: 导航和页面控制
- components/opds: OPDS 目录支持
- components/server: 服务器相关功能
- components/shared: 共享组件
- components/streamer: 流媒体处理
- components/webview: Web 视图组件
🚀 快速开始
- 添加依赖到
pubspec.yaml
:
yaml
dependencies:
iridium_reader_widget:
path: path/to/iridium/reader_widget
1 加载EPUB

1. 从文件路径加载
EpubScreen.fromPath( filePath: "path/to/book.epub", location: "书签位置", settings: "100", // 字体大小设置 theme: "主题设置JSON" );
2. 从 File 对象加载
EpubScreen.fromFile( file: File("path/to/book.epub") );
3. 从 URI 加载
EpubScreen.fromUri( rootHref: "example.com/book.epub" );
加载完成后是BookScreen
- 阅读进度保存
- 自动记录上次阅读位置
- 支持书签功能
- 可在不同设备间同步进度(使用 Firestore 存储时)
- 阅读设置
- 字体大小调节
- 主题切换
- 自定义样式
- 交互功能
- 文本选择
- 页面导航
- 进度跳转
2 读取EPUB

状态管理
- 支持实时更新和状态同步
- 使用 BLoC 模式管理主题和阅读器设置
- 支持实时更新和状态同步
less
Widget buildWidgetWrapper(BuildContext context, Widget child,
List<Link> spineItems, ServerStarted state) =>
Stack(
children: <Widget>[
buildBackground(),
child,
Align(
alignment: Alignment.bottomCenter,
child: MultiBlocProvider(
providers: [
BlocProvider<ReaderThemeBloc>(
create: (_) => BlocProvider.of<ReaderThemeBloc>(context),
),
BlocProvider<ViewerSettingsBloc>(
create: (_) => BlocProvider.of<ViewerSettingsBloc>(context),
),
],
child: ReaderToolbar(
readerContext: readerContext,
onSkipLeft: publicationController.onSkipLeft,
onSkipRight: publicationController.onSkipRight,
),
),
),
// SafeArea(
// top: false,
// child: Align(
// alignment: Alignment.topCenter,
// child: ReaderAppBar(
// readerContext: readerContext,
// publicationController: publicationController,
// ),
// ),
// ),
],
);

1. 组件概述
这是一个用于 EPUB 电子书阅读的导航组件,文件名为 epub_navigator.dart 。它包含两个主要类:
- EpubNavigator :继承自 PublicationNavigator 的 Widget 类
- EpubNavigatorState :对应的 State 类,处理具体的导航逻辑
less
@override
Widget buildReaderView(List<Link> spine, ServerStarted serverState) =>
PreloadPageView.builder(
controller: epubController.pageController,
scrollDirection: Axis.horizontal,
// TODO Currently, with Hybrid Composition activated, preloadPagesCount > 1 provides erratic behavior.
// To investigate!
// preloadPagesCount: min(spine.length, 2),
preloadPagesCount: 1,
onPageChanged: epubController.onPageChanged,
physics: const AlwaysScrollableScrollPhysics(),
reverse: readerContext?.readingProgression?.isReverseOrder() ?? false,
itemCount: spine.length,
itemBuilder: (context, position) =>
(Platform.isAndroid || Platform.isIOS)
? WebViewScreen(
address: serverState.address,
link: spine[position],
position: position,
readerContext: readerContext!,
publicationController: epubController,
)
: const SizedBox.shrink(),
);
}
主要特性:
- 使用 PreloadPageView.builder 实现页面预加载
- 水平滚动方向
- 当前仅预加载 1 页(由于混合渲染模式下多页预加载存在问题)
- 支持根据阅读方向反转页面顺序
- 使用WebView来加载EPUB
3 功能解析
1. 整体架构
- 采用了 Flutter 框架实现跨平台电子书阅读器
- 主要支持 EPUB 和 CBZ 两种电子书格式
- 使用 WebView 来渲染 EPUB 内容
- 采用 BLoC 模式进行状态管理
2. 核心功能模块
阅读器导航
-
EpubNavigator 和 CbzNavigator 分别处理不同格式电子书的导航
-
支持页面跳转、目录导航和书签功能
-
通过 ReaderContext 管理阅读状态和上下文 EPUB 渲染
-
使用 WebViewScreen 渲染 EPUB 内容
-
支持自定义主题和排版设置
-
实现了手势控制和页面切换 注释和高亮
-
支持文本选择和高亮标注
-
提供书签和注释功能
-
可以保存和管理用户的标注 主题和设置
-
ReaderThemeBloc 管理阅读主题
-
ViewerSettingsBloc 处理阅读器设置
-
支持自定义字体、颜色和排版
3. 关键类说明
EpubController
-
控制 EPUB 阅读器的核心类
-
管理页面控制器和阅读状态
-
处理用户交互和导航 WebViewScreenState
-
负责 EPUB 内容的渲染和显示
-
处理 WebView 相关的事件和回调
-
管理注释和高亮功能 ReaderContext
-
维护阅读器的全局状态
-
管理书签和注释
-
处理阅读进度和位置信息
4. 特色功能
- 支持多种电子书格式 (EPUB, CBZ)
- 提供完整的注释和标注系统
- 支持主题自定义和排版设置
- 实现了跨平台兼容性
- 提供流畅的阅读体验和手势控制
5. 数据持久化
- 支持多种注释存储方式:
- 内存存储 (InMemoryReaderAnnotationRepository)
- SQLite 存储 (SqliteReaderAnnotationRepository)
- Firestore 存储 (FirestoreReaderAnnotationRepository)
###页面跳转解析书签功能
webview_screen_state.dart
使用js的方式进行跳转 _jsApi?.openPage
scss
void _onPageFinished(InAppWebViewController controller, Uri? url) async {
try {
if (_jsApi == null || !mounted) {
return;
}
// // 获取页面请求数据
OpenPageRequest? openPageRequestData =
_getOpenPageRequestFromCommand(readerContext.readerCommand);
List<String> elementIds =
readerContext.getElementIdsFromSpineItem(position);
_jsApi?.setElementIds(elementIds);
if (openPageRequestData != null) {
_jsApi?.openPage(openPageRequestData);
}
// 如果存在跳转请求,执行页面跳转
try {
_jsApi?.setStyles(_readerThemeBloc.state.readerTheme,
_viewerSettingsBloc.viewerSettings);
} catch (e) {
Fimber.d("Error setting styles", ex: e);
}
_updateSpineItemPosition(_currentSpineItemBloc.state);
await _loadDecorations();
await _loadBookmarks();
} catch (e, stacktrace) {
Fimber.d("Error in _onPageFinished", ex: e, stacktrace: stacktrace);
}
}
void _onPaginationInfo(PaginationInfo? paginationInfo) {
if (currentSelectedSpineItem && paginationInfo != null) {
// 更新书签
_updateBookmarks(paginationInfo);
// 通知当前位置变化
readerContext.notifyCurrentLocation(paginationInfo, spineItem);
}
}
- 直接跳转
- 通过目录跳转到指定章节
- 通过书签跳转到保存的位置
- 通过进度条跳转到指定位置
文本选择和高亮标注
1 文本选择监听器在 simple_selection_listener.dart 中实现了基本的选择监听功能:
scss
@override
void showHighlightPopup(Selection selection, HighlightStyle style, Color tint,
{String? highlightId}) {
_hideSelectionPopup();
_highlightPopup = HighlightPopup(this);
_highlightPopup!
.showHighlightPopup(context, selection, style, tint, highlightId);
}
Selection是选种的文本 HighlightStyle选择的样式 Color文本选种的颜色
- 文本选择:
- 用户在WebView中选择文本时触发选择事件
- SelectionListener监听选择事件并显示操作弹窗
- 用户可以选择进行高亮标注或其他操作
- 高亮标注:
- 用户选择高亮操作后,创建ReaderAnnotation对象保存标注信息
- 使用annotationMarkTemplate生成高亮样式
- 将高亮效果应用到WebView中的选中文本
- 标注管理:
- 标注数据通过ReaderAnnotationRepository进行持久化存储
- 支持查看、编辑和删除已有标注
- 可以通过ReaderNavigationScreen查看所有标注列表
书签和注释功能实现 webview_screen_state.dart
加载书签功能使用**_jsApi?.initPagination()**
scss
// 加载书签
Future<void> _loadBookmarks() async {
_spineItemContext.bookmarks.addAll(
await readerContext.readerAnnotationRepository.allWhere(
predicate: AnnotationTypeAndDocumentPredicate(
spineItem.href, AnnotationType.bookmark)));
_jsApi?.initPagination();
}
注释功能在simple_selection_listener.dart
scss
@override
void showAnnotationPopup(Selection selection,
{HighlightStyle? style, Color? tint, String? highlightId}) async {
// 隐藏其他弹窗
hidePopup();
// 清除文本选择
jsApi?.clearSelection();
// 设置默认样式和颜色
style ??= HighlightStyle.highlight;
tint ??= HighlightPopup.highlightTints[0];
// 获取已有注释(如果是编辑模式)
ReaderAnnotation? highlight;
if (highlightId != null) {
highlight = await readerAnnotationRepository.get(highlightId);
style = highlight?.style ?? style;
tint = highlight?.tint?.let((it) => Color(it)) ?? tint;
}
// 显示注释弹窗
if (state.mounted) {
AnnotationPopup.showAnnotationPopup(context, this, selection, style, tint,
highlight?.annotation, highlightId);
}
}
}