sh_pdf 1.0.0
sh_pdf: ^1.0.0 copied to clipboard
A high-performance, optimized custom PDF viewer for Flutter with support for URLs, local files, zoom, and pagination.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
import 'package:sh_pdf/sh_pdf.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Optimized PDF Viewer',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const MyHomePage(title: 'Custom PDF Viewer'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _urlController = TextEditingController(
text:
'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
);
@override
void dispose() {
_urlController.dispose();
super.dispose();
}
Future<void> _pickFile() async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['pdf'],
);
if (result != null && result.files.single.path != null) {
if (mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomPdfViewer.file(
result.files.single.path!,
),
),
);
}
}
}
void _loadFromUrl() {
final url = _urlController.text.trim();
if (url.isNotEmpty) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CustomPdfViewer.url(
url,
),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please enter a valid URL')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(Icons.picture_as_pdf, size: 80, color: Colors.blue),
const SizedBox(height: 32),
TextField(
controller: _urlController,
decoration: InputDecoration(
labelText: 'PDF URL',
hintText: 'https://example.com/sample.pdf',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () => _urlController.clear(),
),
),
keyboardType: TextInputType.url,
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _loadFromUrl,
icon: const Icon(Icons.link),
label: const Text('Load PDF from URL'),
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 56),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
const SizedBox(height: 16),
const Row(
children: [
Expanded(child: Divider()),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text('OR'),
),
Expanded(child: Divider()),
],
),
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: _pickFile,
icon: const Icon(Icons.storage),
label: const Text('Pick PDF from Storage'),
style: OutlinedButton.styleFrom(
minimumSize: const Size(double.infinity, 56),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
],
),
),
);
}
}