pdf_bookshelf_viewer 0.1.5
pdf_bookshelf_viewer: ^0.1.5 copied to clipboard
A Flutter package for displaying PDFs in a beautiful bookshelf-style list with realistic page-turning animations.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:pdf_bookshelf_viewer/pdf_bookshelf_viewer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PDF Bookshelf Viewer Demo',
theme: ThemeData(primarySwatch: Colors.brown, useMaterial3: true),
home: const BookshelfScreen(),
);
}
}
class BookshelfScreen extends StatefulWidget {
const BookshelfScreen({super.key});
@override
State<BookshelfScreen> createState() => _BookshelfScreenState();
}
class _BookshelfScreenState extends State<BookshelfScreen> {
List<PdfBook> books = [];
bool isLoading = true;
@override
void initState() {
super.initState();
_loadSampleBooks();
}
Future<void> _loadSampleBooks() async {
// Create sample PDF books from different sources
final sampleBooks = [
// Example: PDF from network URL
PdfBook(
title: 'Flutter Basics',
source: PdfSource.network(
'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
),
author: 'John Doe',
description: 'Learn the fundamentals of Flutter development',
),
// Example: PDF from Firebase Storage (requires Firebase setup)
// Uncomment and configure Firebase to use
// PdfBook(
// title: 'Dart Programming',
// source: PdfSource.firebase('pdfs/dart_programming.pdf'),
// author: 'Jane Smith',
// description: 'Master Dart programming language',
// ),
// Example: PDF from assets (add PDF files to assets/pdfs/)
PdfBook(
title: 'Mobile Design',
source: PdfSource.asset('assets/pdfs/sample.pdf'),
author: 'Bob Johnson',
description: 'Best practices for mobile UI/UX design',
),
// Example: Another network PDF
PdfBook(
title: 'State Management',
source: PdfSource.network(
'https://www.africau.edu/images/default/sample.pdf',
),
author: 'Alice Williams',
description: 'Managing state in Flutter applications',
),
// Example: PDF with custom headers
PdfBook(
title: 'Advanced Flutter',
source: PdfSource.network(
'https://www.clickdimensions.com/links/TestPDFfile.pdf',
headers: {'Authorization': 'Bearer token'},
),
author: 'Charlie Brown',
description: 'Advanced Flutter techniques and patterns',
),
// Example: Local file (if you have a PDF file path)
// PdfBook(
// title: 'Testing Guide',
// source: PdfSource.file('/path/to/local/file.pdf'),
// author: 'David Lee',
// description: 'Comprehensive guide to testing Flutter apps',
// ),
];
setState(() {
books = sampleBooks;
isLoading = false;
});
}
void _openBook(PdfBook book) {
// Use cross-platform viewer (works on all platforms)
_openCrossPlatformViewer(book);
// TODO: Advanced and Basic viewers need refactoring for pdfx
// Uncomment when migration is complete
/*
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Choose Viewer'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.devices),
title: const Text('Cross-Platform Viewer'),
subtitle: const Text('Works on all platforms'),
onTap: () {
Navigator.pop(context);
_openCrossPlatformViewer(book);
},
),
ListTile(
leading: const Icon(Icons.auto_stories),
title: const Text('Advanced Viewer'),
subtitle: const Text('With curl animation (mobile only)'),
onTap: () {
Navigator.pop(context);
_openAdvancedViewer(book);
},
),
ListTile(
leading: const Icon(Icons.book),
title: const Text('Basic Viewer'),
subtitle: const Text('Simple page flip (mobile only)'),
onTap: () {
Navigator.pop(context);
_openBasicViewer(book);
},
),
],
),
),
);
*/
}
bool _useImprovedCurl = true;
void _openCrossPlatformViewer(PdfBook book) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CrossPlatformPdfViewer(
pdfSource: book.source!,
title: book.title,
enablePageFlip: _useImprovedCurl,
settings: const ReaderSettings(
enableTwoPageMode:
false, // Page flip usually works best with single page view
immersiveModeEnabled: true,
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My Bookshelf'),
centerTitle: true,
elevation: 0,
actions: [
Row(
children: [
const Text('Curl'),
Switch(
value: _useImprovedCurl,
onChanged: (value) {
setState(() {
_useImprovedCurl = value;
});
},
),
],
),
IconButton(
icon: const Icon(Icons.info_outline),
onPressed: () => _showInfoDialog(),
tooltip: 'About',
),
],
),
body: isLoading
? const Center(child: CircularProgressIndicator())
: books.isEmpty
? const Center(
child: Text(
'No books available',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16),
),
)
: BookshelfView(
books: books,
onBookTap: _openBook,
booksPerShelf: 3,
shelfHeight: 200,
),
floatingActionButton: FloatingActionButton(
onPressed: _showCacheInfo,
tooltip: 'Cache Info',
child: const Icon(Icons.storage),
),
);
}
void _showInfoDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('PDF Sources'),
content: const SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'This demo shows PDFs from different sources:',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 12),
Text('📡 Network URLs'),
Text('🔥 Firebase Storage'),
Text('📦 App Assets'),
Text('📁 Local Files'),
SizedBox(height: 12),
Text('Features:', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('✓ Automatic caching'),
Text('✓ Download progress'),
Text('✓ Offline support'),
Text('✓ Page flip animation'),
Text('✓ Error handling with retry'),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
],
),
);
}
Future<void> _showCacheInfo() async {
final loader = PdfLoaderService();
final cacheSize = await loader.getCacheSize();
final cacheSizeMB = (cacheSize / (1024 * 1024)).toStringAsFixed(2);
if (!mounted) return;
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Cache Information'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Cache Size: $cacheSizeMB MB'),
const SizedBox(height: 16),
const Text(
'Cached PDFs are stored locally for offline access and faster loading.',
),
],
),
actions: [
TextButton(
onPressed: () async {
await loader.clearCache();
if (!context.mounted) return;
Navigator.pop(context);
ScaffoldMessenger.of(
context,
).showSnackBar(const SnackBar(content: Text('Cache cleared')));
},
child: const Text('Clear Cache'),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
],
),
);
}
}