Files
tasq/lib/widgets/pdf_preview_dialog.dart
T
redz1029 167e208ffa feat(report): accomplishment report PDF + shared PDF helpers
- lib/utils/pdf/pdf_letterhead.dart: PdfBrand with load(), letterhead(),
  theme(), and pageFooter() shared across CRMC PDF documents
- lib/widgets/pdf_preview_dialog.dart: showPdfPreviewDialog() with
  pdfrx PdfViewer.data preview, Print (Printing.layoutPdf) and
  Download (Printing.sharePdf) buttons
- lib/screens/programmer_tasks/report/accomplishment_report_pdf.dart:
  buildAccomplishmentReportPdf() — A4 MultiPage with letterhead, info
  box, accomplishment table, footnote, summary, signatures, page footer
- test/accomplishment_report_pdf_test.dart: 2 passing tests

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-09-28 18:55:45 +08:00

125 lines
3.7 KiB
Dart

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:pdfrx/pdfrx.dart';
import 'package:printing/printing.dart';
/// Show a PDF preview dialog with Print and Download buttons.
///
/// [buildBytes] is called once and cached; the result is shared between the
/// preview viewer and the action buttons.
Future<void> showPdfPreviewDialog(
BuildContext context, {
required String title,
required String filename,
required Future<Uint8List> Function() buildBytes,
}) {
return showDialog<void>(
context: context,
builder: (_) => _PdfPreviewDialog(
title: title,
filename: filename,
buildBytes: buildBytes,
),
);
}
class _PdfPreviewDialog extends StatefulWidget {
const _PdfPreviewDialog({
required this.title,
required this.filename,
required this.buildBytes,
});
final String title;
final String filename;
final Future<Uint8List> Function() buildBytes;
@override
State<_PdfPreviewDialog> createState() => _PdfPreviewDialogState();
}
class _PdfPreviewDialogState extends State<_PdfPreviewDialog> {
late final Future<Uint8List> _future;
@override
void initState() {
super.initState();
_future = widget.buildBytes();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: SizedBox(
width: 800,
height: 900,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
Expanded(
child: Text(
widget.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
IconButton(
tooltip: 'Print',
icon: const Icon(Icons.print),
onPressed: () async {
final bytes = await _future;
await Printing.layoutPdf(
onLayout: (_) async => bytes,
name: widget.filename,
);
},
),
IconButton(
tooltip: 'Download',
icon: const Icon(Icons.download),
onPressed: () async {
final bytes = await _future;
await Printing.sharePdf(
bytes: bytes,
filename: widget.filename,
);
},
),
IconButton(
tooltip: 'Close',
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
),
],
),
),
const Divider(height: 1),
Expanded(
child: FutureBuilder<Uint8List>(
future: _future,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text(snapshot.error.toString()));
}
final data = snapshot.data;
if (data == null) return const SizedBox.shrink();
return PdfViewer.data(data, sourceName: widget.filename);
},
),
),
],
),
),
);
}
}