FlutterFire PDFs
Working with PDFs in Flutter and Firebase.
Firebase x Flutter
Setup Firebse for your project as you normally would. Steps here.
Before any of the Firebase services can be used, FlutterFire needs to be initialized. We do this in main.dart
import 'package:flutter/material.dart';
import 'screens/homepage.dart';
import 'package:firebase_core/firebase_core.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatelessWidget {
// Create the initialization Future outside of `build`:
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
@override
Widget build(BuildContext context) {
return FutureBuilder(
// Initialize FlutterFire:
future: _initialization,
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return Text('Error in Firebase Initilisation');
}
// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
return MaterialApp(
title: 'College App',
home: HomePage(),
);
}
// Otherwise, show something whilst waiting for initialization to complete
return CircularProgressIndicator();
},
);
}
}
Working with PDFs
Dependencies used:
firebase_core: ^0.7.0
firebase_storage: ^7.0.0
flutter_document_picker: ^4.0.0
flutter_plugin_pdf_viewer: ^1.0.7
Note: I have used flutter_document_picker: ^4.0.0
instead of file_picker: ^2.1.5+1
because the latter was showing up an error related AndroidX incompatibilites of plugin which I'll dicuss in a YouTube video soon.
Import the required classes to homepage.dart
file:
import 'dart:io';
import 'dart:math';
import 'package:flutter_document_picker/flutter_document_picker.dart';
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
import 'package:flutter/material.dart';
Create a Stateful Widget for HomePage
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Container();
}
}
We'll work in this widget to:
- Upload PDF from device storage to Firebase Storage
- Download and View PDF in App
Upload PDF from device storage to Firebase Storage
We'll first create a Scaffold with an App Bar and a Floating Action Button to open File Storage and select a PDF file. Therefore, inside the HomePageState make the required changes.
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text("FlutterFire PDF"),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.purple,
child: Icon(
Icons.add,
color: Colors.white,
),
onPressed: () async {
//Functionality for Button to pick pdf will go here.
},
),
);
}
}
Once done, we need to now add some functionality to the onPressed of the FloatingActionButton to pick a PDF from storage. We'll use theflutter_document_picker: ^4.0.0
and make it async.
onPressed: () async {
final path = await FlutterDocumentPicker.openDocument();
print(path);
File file = File(path);
//firebase_storage.UploadTask task = await uploadFile(file);
},
Now that we have the file, we need to upload it. Uncomment the uploadFile function in the onPressed so it'll look like this:
onPressed: () async {
final path = await FlutterDocumentPicker.openDocument();
print(path);
File file = File(path);
firebase_storage.UploadTask task = await uploadFile(file);
},
and, let us define this uploadFile function.
Future<firebase_storage.UploadTask> uploadFile(File file) async {
if (file == null) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text("Unable to Upload")));
return null;
}
firebase_storage.UploadTask uploadTask;
// Create a Reference to the file
firebase_storage.Reference ref = firebase_storage.FirebaseStorage.instance
.ref()
.child('playground')
.child('/some-file.pdf');
final metadata = firebase_storage.SettableMetadata(
contentType: 'file/pdf',
customMetadata: {'picked-file-path': file.path});
print("Uploading..!");
uploadTask = ref.putData(await file.readAsBytes(), metadata);
print("done..!");
return Future.value(uploadTask);
}
The file would be saved inside playground as some-file.pdf on your Cloud Storage.
View PDF from Firebase Storage
To work with PDF Viewer we'll use two classes: a loader.dart
and a viewer.dart
. The homepage widget is where we get URL from Firebase Storage and then display PDF using URL in viewer widget.
Imports used:
import 'package:college_app/screens/notes.dart';
import 'package:flutter/material.dart';
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
import 'package:flutter_plugin_pdf_viewer/flutter_plugin_pdf_viewer.dart';
Initialise a LoadURL Stateful Widget:
class LoadURL extends StatefulWidget {
@override
_LoadURLState createState() => _LoadURLState();
}
class _LoadURLState extends State<LoadURL> {
@override
Widget build(BuildContext context) {
return Container();
}
}
Initialise an instance of Firebase Storage:
firebase_storage.FirebaseStorage storage = firebase_storage.FirebaseStorage.instance;
Define a function to list all the directories on Storage:
Future<void> listExample() async {
firebase_storage.ListResult result =
await firebase_storage.FirebaseStorage.instance.ref().child('notes').listAll();
result.items.forEach((firebase_storage.Reference ref) {
print('Found file: $ref');
});
result.prefixes.forEach((firebase_storage.Reference ref) {
print('Found directory: $ref');
});
}
Define a function to download URL:
Future<void> downloadURLExample() async {
String downloadURL = await firebase_storage.FirebaseStorage.instance
.ref('notes/name.pdf')
.getDownloadURL();
print(downloadURL);
PDFDocument doc = await PDFDocument.fromURL(downloadURL);
Navigator.push(context, MaterialPageRoute(builder: (context)=>ViewPDF(doc))); //Notice the Push Route once this is done.
}
Note: You can use the directories found from listExample function and save them to a variable and use string interpolation to define path in downloadURLExample function.
Inside the initState
we call the two functions:
@override
void initState() {
// TODO: implement initState
super.initState();
listExample();
downloadURLExample();
print("All done!");
}
While the initState
works in the background to fetch files, the builder can return a CircularProgressIndicator()
:
@override
Widget build(BuildContext context) {
return Center(
child: CircularProgressIndicator(
),
);
}
When the initState
is done, a new widget will be pushed in from viewer.dart
.
Initialise a Stateful Widget:
class ViewPDF extends StatefulWidget {
@override
_ViewPDFState createState() => _ViewPDFState();
}
class _ViewPDFState extends State<ViewPDF> {
@override
Widget build(BuildContext context) {
return Container();
}
}
Create a constructor that'll take in a PDFDocument document
as parameter and display the pdf in builder.
class ViewPDF extends StatefulWidget {
PDFDocument document;
ViewPDF(this.document);
@override
_ViewPDFState createState() => _ViewPDFState();
}
class _ViewPDFState extends State<ViewPDF> {
@override
Widget build(BuildContext context) {
return Center(
child: PDFViewer(document: widget.document));
}
}
and, this should load up the PDF when run :)