epub_rtl 0.1.0
epub_rtl: ^0.1.0 copied to clipboard
Pure-Flutter EPUB 2/3 parser and reader with right-to-left support for Arabic books: page-progression-direction, dir, bidi, paginated RTL paging.
import 'package:epub_rtl/epub_rtl.dart';
import 'package:flutter/material.dart';
import 'sample_book.dart';
void main() => runApp(const ExampleApp());
/// Demo app for epub_rtl.
class ExampleApp extends StatelessWidget {
/// Creates the app.
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'epub_rtl example',
theme: ThemeData(colorSchemeSeed: Colors.brown, useMaterial3: true),
home: ReaderPage(book: EpubBook.fromBytes(buildSampleBook())),
);
}
/// A reader screen with a TOC drawer and settings in the app bar.
class ReaderPage extends StatefulWidget {
/// Creates the page.
const ReaderPage({super.key, required this.book});
/// The book to read.
final EpubBook book;
@override
State<ReaderPage> createState() => _ReaderPageState();
}
class _ReaderPageState extends State<ReaderPage> {
final _controller = EpubReaderController();
var _mode = EpubReaderMode.paginated;
var _settings = const EpubReaderSettings(justify: true);
String? _savedLocation;
static const _themes = [
EpubReaderTheme.light,
EpubReaderTheme.sepia,
EpubReaderTheme.dark
];
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _cycleTheme() {
final next =
_themes[(_themes.indexOf(_settings.theme) + 1) % _themes.length];
setState(() => _settings = _settings.copyWith(theme: next));
}
void _fontSize(double delta) => setState(() => _settings =
_settings.copyWith(fontSize: (_settings.fontSize + delta).clamp(12, 40)));
@override
Widget build(BuildContext context) {
final book = widget.book;
return Scaffold(
appBar: AppBar(
title: Text(book.metadata.title, overflow: TextOverflow.ellipsis),
actions: [
IconButton(
tooltip: 'Scroll / pages',
icon: Icon(_mode == EpubReaderMode.scroll
? Icons.auto_stories
: Icons.view_day),
onPressed: () => setState(() => _mode =
_mode == EpubReaderMode.scroll
? EpubReaderMode.paginated
: EpubReaderMode.scroll),
),
IconButton(
tooltip: 'Theme',
icon: const Icon(Icons.palette),
onPressed: _cycleTheme),
IconButton(
tooltip: 'Smaller text',
icon: const Icon(Icons.text_decrease),
onPressed: () => _fontSize(-2)),
IconButton(
tooltip: 'Larger text',
icon: const Icon(Icons.text_increase),
onPressed: () => _fontSize(2)),
],
),
drawer: EpubTocDrawer(book: book, controller: _controller),
body: EpubReaderView(
book: book,
controller: _controller,
mode: _mode,
settings: _settings,
// In a real app, persist location.encode() (for example with
// shared_preferences) and pass EpubLocation.decode(saved) here.
initialLocation: _savedLocation == null
? null
: EpubLocation.decode(_savedLocation!),
onLocationChanged: (location) => _savedLocation = location.encode(),
onExternalLink: (url) => ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text('External link: $url'))),
),
bottomNavigationBar: SafeArea(
child: ListenableBuilder(
listenable: _controller,
builder: (context, _) => Row(
children: [
IconButton(
tooltip: 'Previous chapter',
icon: const Icon(Icons.chevron_left),
onPressed: _controller.previousChapter,
),
Expanded(
child: Text(
'Chapter ${_controller.location.spineIndex + 1} / ${book.spine.items.length}',
textAlign: TextAlign.center,
),
),
IconButton(
tooltip: 'Next chapter',
icon: const Icon(Icons.chevron_right),
onPressed: _controller.nextChapter,
),
],
),
),
),
);
}
}