Lib und Services für PDF Viewer hinzugefügt

This commit is contained in:
Sven committed 2026-01-09 15:46:03 +01:00
1 parent 92362a732d
commit 7a4af32758
7 files changed
+542 -243

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
{
"permissions": {
"allow": [
"Bash(flutter pub get:*)",
"Bash(/Users/svencarstensen/development/flutter/bin/flutter pub:*)",
"Bash(source:*)",
"Bash(source ~/.bashrc)",
"Bash(source ~/.bash_profile)",
"Bash(/Users/svencarstensen/Documents/Entwicklung/flutter/bin/flutter pub:*)",
"Bash(/Users/svencarstensen/Documents/Entwicklung/flutter/bin/flutter analyze:*)",
"Bash(/Users/svencarstensen/Documents/Entwicklung/flutter/bin/flutter:*)"
]
}
}
+130
View File
@@ -0,0 +1,130 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:stadtbote/content_views/utils/content_view_widget.dart';
import 'package:stadtbote/tools/pdf_service.dart';
import 'package:stadtbote/tools/util.dart';
class PdfViewerPage extends ContentViewWidget {
final String pdfUrl;
final String title;
const PdfViewerPage({
super.key,
required this.pdfUrl,
this.title = 'PDF',
});
@override
State<PdfViewerPage> createState() => _PdfViewerPageState();
@override
Future<Widget> getHeaderWidget(BuildContext context) async {
await Future.delayed(const Duration(microseconds: 0));
return Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
);
}
}
class _PdfViewerPageState extends State<PdfViewerPage> {
final PdfService _pdfService = PdfService();
File? _pdfFile;
bool _isLoading = true;
String? _errorMessage;
@override
void initState() {
super.initState();
_loadPdf();
}
Future<void> _loadPdf() async {
if (!_pdfService.isValidPdfUrl(widget.pdfUrl)) {
setState(() {
_isLoading = false;
_errorMessage = 'Ungültige PDF-URL';
});
return;
}
try {
final file = await _pdfService.getPdf(widget.pdfUrl);
setState(() {
_pdfFile = file;
_isLoading = false;
});
} catch (e) {
Util.logException('Error loading PDF: $e');
setState(() {
_isLoading = false;
_errorMessage = 'Fehler beim Laden der PDF: $e';
});
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('PDF wird geladen...'),
],
),
);
}
if (_errorMessage != null) {
return Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
Icons.error_outline,
size: 64,
color: Colors.red,
),
const SizedBox(height: 16),
Text(
_errorMessage!,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.red),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
setState(() {
_isLoading = true;
_errorMessage = null;
});
_loadPdf();
},
child: const Text('Erneut versuchen'),
),
],
),
),
);
}
if (_pdfFile == null) {
return const Center(
child: Text('Keine PDF-Datei verfügbar'),
);
}
return PdfViewer.file(
_pdfFile!.path,
);
}
}
+128
View File
@@ -0,0 +1,128 @@
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:stadtbote/tools/util.dart';
class PdfService {
static final PdfService _instance = PdfService._internal();
factory PdfService() => _instance;
PdfService._internal();
static const String _cacheSubDir = 'pdf_cache';
/// Generates a unique filename from URL using hash
String _getFilenameFromUrl(String url) {
final hash = Util.fastHash(url);
return '${hash.toRadixString(16)}.pdf';
}
/// Gets the cache directory for PDFs
Future<Directory> _getCacheDirectory() async {
final appDir = await getApplicationDocumentsDirectory();
final cacheDir = Directory('${appDir.path}/$_cacheSubDir');
if (!await cacheDir.exists()) {
await cacheDir.create(recursive: true);
}
return cacheDir;
}
/// Checks if PDF is already cached and returns the file if it exists
Future<File?> getCachedPdf(String url) async {
try {
final cacheDir = await _getCacheDirectory();
final filename = _getFilenameFromUrl(url);
final file = File('${cacheDir.path}/$filename');
if (await file.exists()) {
Util.logDebug('PDF found in cache: $filename');
return file;
}
return null;
} catch (e) {
Util.logException('Error checking PDF cache: $e');
return null;
}
}
/// Downloads PDF from URL and saves it to cache
Future<File> downloadAndCachePdf(String url) async {
Util.logInfo('Downloading PDF from: $url');
final response = await http.get(Uri.parse(url));
if (response.statusCode != 200) {
throw Exception('Failed to download PDF: HTTP ${response.statusCode}');
}
final cacheDir = await _getCacheDirectory();
final filename = _getFilenameFromUrl(url);
final file = File('${cacheDir.path}/$filename');
await file.writeAsBytes(response.bodyBytes);
Util.logInfo('PDF cached successfully: $filename');
return file;
}
/// Gets PDF from cache or downloads it if not cached
Future<File> getPdf(String url) async {
final cachedFile = await getCachedPdf(url);
if (cachedFile != null) {
return cachedFile;
}
return await downloadAndCachePdf(url);
}
/// Clears all cached PDFs
Future<void> clearCache() async {
try {
final cacheDir = await _getCacheDirectory();
if (await cacheDir.exists()) {
await cacheDir.delete(recursive: true);
Util.logInfo('PDF cache cleared');
}
} catch (e) {
Util.logException('Error clearing PDF cache: $e');
}
}
/// Gets the total size of cached PDFs in bytes
Future<int> getCacheSize() async {
try {
final cacheDir = await _getCacheDirectory();
if (!await cacheDir.exists()) {
return 0;
}
int totalSize = 0;
await for (final entity in cacheDir.list()) {
if (entity is File) {
totalSize += await entity.length();
}
}
return totalSize;
} catch (e) {
Util.logException('Error calculating cache size: $e');
return 0;
}
}
/// Validates if URL is a valid PDF URL
bool isValidPdfUrl(String url) {
try {
final uri = Uri.parse(url);
return uri.scheme == 'https' || uri.scheme == 'http';
} catch (e) {
return false;
}
}
}
+1
View File
@@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
pdfium_flutter
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)
+266 -242
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 347.0.0+347 version: 347.0.0+347
environment: environment:
sdk: '>=3.5.0 <4.0.0' sdk: '>=3.9.0 <4.0.0'
@@ -69,6 +69,7 @@ dependencies:
dart_mq: ^1.1.0 dart_mq: ^1.1.0
intl: ^0.20.2 intl: ^0.20.2
synchronized: ^3.1.0 synchronized: ^3.1.0
pdfrx: ^2.2.20
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
+1
View File
@@ -12,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST
pdfium_flutter
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)