Hi all, I have had good success with a Custom Action using the pdf and printing pubspec packages following Dimitar Klaturov excellent tutorials. However, I am having no luck modifying it to also include images stored in Firebase.
Difficulty adding Images to PDF Export
Custom Code
My original code (working).
// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/actions/index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:flutter/services.dart' show rootBundle;
import '../../auth/firebase_auth/auth_util.dart';
import '../../backend/firebase_storage/storage.dart';
Future pdfBookDownload(
BuildContext context,
String? bookTitle,
List<ChaptersRecord>? chapters,
List<SectionsRecord>? sections,
) async {
// Null safety checks
bookTitle = bookTitle ?? '';
chapters = chapters ?? [];
sections = sections ?? [];
// Initialize the PDF document
final pdf = pw.Document();
// Add title page for the book
pdf.addPage(
pw.Page(
build: (pw.Context context) {
return pw.Center(
child: pw.Text(
bookTitle ?? '',
style: pw.TextStyle(fontSize: 36, fontWeight: pw.FontWeight.bold),
),
);
},
),
);
// Iterate through chapters
for (final chapter in chapters) {
final chapterTitle = chapter.chapterTitle ?? '';
final chapterSections = sections.where(
(section) => section.chapterId == chapter.reference,
);
// Add chapter page
pdf.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4,
build: (context) {
final List<pw.Widget> pageContents = [];
// Add chapter title
pageContents.add(
pw.Center(
child: pw.Text(
chapterTitle ?? '',
style: pw.TextStyle(fontSize: 24),
),
),
);
pageContents.add(pw.SizedBox(height: 20));
// Add section_title and section_body from chapter's sections
for (final section in chapterSections) {
final sectionTitle = section.sectionTitle ?? '';
final sectionBody = section.sectionBody ?? '';
if (sectionTitle.isNotEmpty) {
pageContents.add(pw.Header(
level: 1,
child: pw.Text(sectionTitle),
));
}
if (sectionBody.isNotEmpty) {
pageContents.add(pw.Paragraph(text: sectionBody));
}
}
return pageContents;
},
),
);
}
// Generate and download the PDF document
await Printing.layoutPdf(
onLayout: (format) async => pdf.save(),
);
// Show a snackbar to indicate that the download is complete
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('PDF Downloaded'),
),
);
}
I realize I need to asynchronously load all the images (which have a URL stored on the 'section' document) before I call the build function. But I cannot seem to use the networkImage function correctly.
// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/actions/index.dart'; // Imports other custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom action code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'dart:io';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
import 'package:flutter/services.dart' show rootBundle;
import '../../auth/firebase_auth/auth_util.dart';
import '../../backend/firebase_storage/storage.dart';
Future pdfBookDownloadImage4(
BuildContext context,
String? bookTitle,
List<ChaptersRecord>? chapters,
List<SectionsRecord>? sections,
) async {
// Null safety checks
bookTitle = bookTitle ?? '';
chapters = chapters ?? [];
sections = sections ?? [];
// Initialize the PDF document
final pdf = pw.Document();
// List to store ImageProvider objects
List<Image> images = [];
try {
// Fetch all image URLs
final List<String> imageUrls = sections
.where((section) => section.image != null && section.image!.isNotEmpty)
.map((section) => section.image!)
.toList();
// Load all images asynchronously
await Future.forEach(imageUrls, (imageUrl) async {
Image image = await networkImage(imageUrl);
images.add(image);
});
// Add title page for the book
pdf.addPage(
pw.Page(
build: (pw.Context context) {
return pw.Center(
child: pw.Text(
bookTitle ?? '',
style: pw.TextStyle(fontSize: 36, fontWeight: pw.FontWeight.bold),
),
);
},
),
);
// Iterate through chapters
for (final chapter in chapters) {
final chapterTitle = chapter.chapterTitle ?? '';
final chapterSections =
sections.where((section) => section.chapterId == chapter.reference);
// Add chapter page
pdf.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4,
build: (context) {
final List<pw.Widget> pageContents = [];
// Add chapter title
pageContents.add(
pw.Center(
child: pw.Text(
chapterTitle,
style: pw.TextStyle(fontSize: 24),
),
),
);
pageContents.add(pw.SizedBox(height: 20));
// Add section_title, section_body, and image with caption from chapter's sections
for (final section in chapterSections) {
final sectionTitle = section.sectionTitle ?? '';
final sectionBody = section.sectionBody ?? '';
final imageUrl = section.image ?? '';
final imageCaption = section.imageCaption ?? '';
if (sectionTitle.isNotEmpty) {
pageContents.add(pw.Header(
level: 1,
child: pw.Text(sectionTitle),
));
}
if (sectionBody.isNotEmpty) {
pageContents.add(pw.Paragraph(text: sectionBody));
}
// Add preloaded image with caption
if (imageUrl.isNotEmpty) {
final imageIndex = imageUrls.indexOf(imageUrl);
if (imageIndex != -1) {
pageContents.add(pw.Container(
alignment: pw.Alignment.center,
child: pw.Column(
children: [
pw.Image(images[imageIndex]),
pw.Text(
imageCaption,
style: pw.TextStyle(
fontStyle: pw.FontStyle.italic, fontSize: 12),
),
],
),
));
}
}
}
return pageContents;
},
),
);
}
// Generate and download the PDF document
await Printing.layoutPdf(
onLayout: (format) async => pdf.save(),
);
// Show a snackbar to indicate that the download is complete
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('PDF Downloaded'),
),
);
} catch (e) {
// Handle errors
print('Error: $e');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to download PDF'),
),
);
}
}
No
1 reply