Displaying a PDF in Flutter - flutter

I'm brand new to Flutter and am struggling to display a PDF from a Base64 string. I can get it to work, but only with a pointless UI sitting between the list and the PDF.
The app simply crashes with 'Lost connection to device' and no other information appears in the Android Studio debug console.
So, this works:
import 'dart:developer';
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
import 'package:path_provider/path_provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class PdfBase64Viewer extends StatefulWidget {
final DocumentSnapshot document;
const PdfBase64Viewer(this.document);
#override
_PdfBase64ViewerState createState() => new _PdfBase64ViewerState();
}
class _PdfBase64ViewerState extends State<PdfBase64Viewer> {
String pathPDF = "";
#override
void initState() {
super.initState();
createFile().then((f) {
setState(() {
pathPDF = f.path;
print("Local path: " + pathPDF);
});
});
}
Future<File> createFile() async {
final filename = widget.document.data()['name'];
var base64String = widget.document.data()['base64'];
var decodedBytes = base64Decode(base64String.replaceAll('\n', ''));
String dir = (await getApplicationDocumentsDirectory()).path;
File file = new File('$dir/$filename');
await file.writeAsBytes(decodedBytes.buffer.asUint8List());
return file;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('This UI is pointless')),
body: Center(
child: RaisedButton(
child: Text("Open PDF"),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => PDFScreen(pathPDF)),
),
),
),
);
}
}
class PDFScreen extends StatelessWidget {
String pathPDF = "";
PDFScreen(this.pathPDF);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Document"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
),
path: pathPDF);
}
}
But this doesn't:
import 'dart:developer';
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
import 'package:path_provider/path_provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class PdfBase64Viewer extends StatefulWidget {
final DocumentSnapshot document;
const PdfBase64Viewer(this.document);
#override
_PdfBase64ViewerState createState() => new _PdfBase64ViewerState();
}
class _PdfBase64ViewerState extends State<PdfBase64Viewer> {
String pathPDF = "";
#override
void initState() {
super.initState();
createFile().then((f) {
setState(() {
pathPDF = f.path;
print("Local path: " + pathPDF);
});
});
}
Future<File> createFile() async {
final filename = widget.document.data()['name'];
var base64String = widget.document.data()['base64'];
var decodedBytes = base64Decode(base64String.replaceAll('\n', ''));
String dir = (await getApplicationDocumentsDirectory()).path;
File file = new File('$dir/$filename');
await file.writeAsBytes(decodedBytes.buffer.asUint8List());
return file;
}
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Document"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
),
path: pathPDF);
}
}
Can anyone help me understand why?

You should pay more attention to what the output is (or isn't), it will make your debugging process a lot faster and without needing SO most of the time.
The app simply crashes with 'Lost connection to device' and no other
information appears in the Android Studio debug console.
So that means you aren't seeing print("Local path: " + pathPDF); Which means your app hasn't made it that far.
As to why it is crashing, welp without any testing I can see you aren't handling your asyncs and futures properly. You are just assuming they will be ok and you are assuming they will finish quickly (before the widget gets built). Your PDFViewerScaffold is most probably getting an empty string. A simple check can fix that:
#override
Widget build(BuildContext context) {
return (pathPDF == null || pathPDF.isEmpty) ? Center(child: CircularProgressIndicator())
: PDFViewerScaffold( //....
For a really nice and clean code you should move pdf generation stuff into a separate class (or at least file) and you should also take into consideration how much time is acceptable for such a task (timeout).
PS The reason why your code works with "pointless UI" is because your fingers are slower than file generation, by the time you've tapped on RaisedButton the file has been already created and luckily has a path. That first code also doesn't do any checks, so if file creation fails for whatever reason you'd most probably end up with a crash or a blank screen.
EDIT: Bellow is a full example of how I would go about doing this. It should make your app a lot more resilient to crashes.
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PdfBase64Viewer(),
);
}
}
Future<File> createFile(String base64String) async {
final fileName = "test";
final fileType = "pdf";
var decodedBytes = base64Decode(base64String);
String dir = (await getApplicationDocumentsDirectory()).path;
// uncomment the line bellow to trigger an Exception
// dir = null;
File file = new File('$dir/$fileName.$fileType');
// uncomment the line bellow to trigger an Error
// decodedBytes = null;
await file.writeAsBytes(decodedBytes.buffer.asUint8List());
// uncomment the line bellow to trigger the TimeoutException
// await Future.delayed(const Duration(seconds: 6),() async {});
return file;
}
class PdfBase64Viewer extends StatefulWidget {
const PdfBase64Viewer();
#override
_PdfBase64ViewerState createState() => new _PdfBase64ViewerState();
}
class _PdfBase64ViewerState extends State<PdfBase64Viewer> {
bool isFileGood = true;
String pathPDF = "";
/*
Bellow is a b64 pdf, the smallest one I could find that has some content and it fits within char limit of StackOverflow answer.
It might not be 100% valid, ergo some readers might not accept it, so I recommend swapping it out for a proper PDF.
Output of the PDF should be a large "Test" text aligned to the center left of the page.
source: https://stackoverflow.com/questions/17279712/what-is-the-smallest-possible-valid-pdf/17280876
*/
String b64String = """JVBERi0xLjIgCjkgMCBvYmoKPDwKPj4Kc3RyZWFtCkJULyA5IFRmKFRlc3QpJyBFVAplbmRzdHJlYW0KZW5kb2JqCjQgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCA1IDAgUgovQ29udGVudHMgOSAwIFIKPj4KZW5kb2JqCjUgMCBvYmoKPDwKL0tpZHMgWzQgMCBSIF0KL0NvdW50IDEKL1R5cGUgL1BhZ2VzCi9NZWRpYUJveCBbIDAgMCA5OSA5IF0KPj4KZW5kb2JqCjMgMCBvYmoKPDwKL1BhZ2VzIDUgMCBSCi9UeXBlIC9DYXRhbG9nCj4+CmVuZG9iagp0cmFpbGVyCjw8Ci9Sb290IDMgMCBSCj4+CiUlRU9G""";
#override
void initState() {
super.initState();
Future.delayed(Duration.zero,() async {
File tmpFile;
try {
tmpFile = await createFile(b64String).timeout(
const Duration(seconds: 5));
} on TimeoutException catch (ex) {
// executed when an error is a type of TimeoutException
print('PDF taking too long to generate! Exception: $ex');
isFileGood = false;
} on Exception catch (ex) {
// executed when an error is a type of Exception
print('Some other exception was caught! Exception: $ex');
isFileGood = false;
} catch (err) {
// executed for errors of all types other than Exception
print("An error was caught! Error: $err");
isFileGood = false;
}
setState(() {
if(tmpFile != null){
pathPDF = tmpFile.path;
}
});
});
}
#override
Widget build(BuildContext context) {
return (!isFileGood) ? Scaffold(body: Center(child: Text("Something went wrong, can't display PDF!")))
: (pathPDF == null || pathPDF.isEmpty) ? Scaffold(body: Center(child: CircularProgressIndicator()))
: PDFViewerScaffold(
appBar: AppBar(
title: Text("Document"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
),
path: pathPDF);
}
}

Related

StateProvider does not rebuild when state changed

I have a serious problem with my Riverpod. Specifically, I am using StateProvider in Riverpod package. But when I update state, the widget tree does not rebuild. I checked the new state whether is updated by printing out state to see, I see that they are actually updated.
I have some same situations but when I click hot restart/reload page/scroll up,down mouse to change size chrome window, the widget tree rebuild one time.
Please help me and explain everything the most detail and easy to understand. Thank you very much
new state print out but UI not update
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = new Data(data: rawString);
ref.watch(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider.notifier).state;
final dataText = data?.data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [
Text(dataText)
]
)
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
));
}
}
I don't want to use other pattern as Provider, Bloc, StateNotifierProvider, ChangeNotifierProvider... I only want to run StateProvider successfully. I have refered to many articles and stackoverflows answer but I did't found any useful helps to my case.
final data = ref.watch(helloWorldProvider.notifier).state;
is watching the notifier, which rarely changes. You want to watch the state change, as in:
final data = ref.watch(helloWorldProvider);
Fixed, Tested your code.
I recommend this article Flutter Riverpod 2.0: The Ultimate Guide to Advanced Riverpod 😀
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = Data(data: rawString);
ref.read(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(
List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider)?.data;
final dataText = data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [Text(dataText)]),
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
),
);
}
}

Isar does not persist data

Isar does not persist state, every time I close my mobile application and open it again, previous values are not there, when they should be there. I'm fetching those values from Isar.
Please show with a counter app example how this works.
I'm attaching files.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isarapp/counter_schema.dart';
import 'package:isar/isar.dart';
import 'package:isarapp/home_screen.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var directory = await getApplicationDocumentsDirectory();
var path = directory.path;
await Isar.open(
[CounterObjectSchema],
directory: path,
inspector: true,
name: "isardb",
);
runApp(const MyApp());
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context, WidgetRef ref) {
return ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
),
);
}
}
I have edited android manifest file to get store permission then using permission_handler for getting storage access.
home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:isarapp/counter_schema.dart';
import 'package:permission_handler/permission_handler.dart';
final counterProvider1 = StateProvider<int>((ref) {
return 0;
});
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
#override
ConsumerState<ConsumerStatefulWidget> createState() => _HomeScreenState();
}
getStoragePermission() async {
var status = await Permission.storage.status;
if (status.isDenied) {
Permission.storage.request();
}
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
Isar? isar = Isar.getInstance("isardb");
CounterObject counterObject = CounterObject(counter: 9);
initCounter() async {
var count = await isar?.collection<CounterObject>().get(counterObject.id);
//reset counter on launch
ref.read(counterProvider1.notifier).state = count?.counter ?? -1;
}
increment() async {
counterObject.counter = counterObject.counter + 1;
//update state
ref.read(counterProvider1.notifier).state = counterObject.counter;
//writing counterValue to isardb on every increment call
isar?.writeTxn(
() async => await isar?.collection<CounterObject>().put(counterObject),
);
}//increment
#override
void initState() {
getStoragePermission();
//putting counterObject in
isar?.writeTxn(
() async => await isar?.counterObjects.put(counterObject),
);
initCounter();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
Text(
ref.watch(counterProvider1).toString(),
style: const TextStyle(fontSize: 54),
),
GestureDetector(
onTap: () => increment(),
child: Container(
width: 200,
height: 40,
color: Colors.blue[300],
child: const Center(child: Text("Add")),
),
),
],
),
),
),
);
}
}
CounterObjectSchema
import 'package:isar/isar.dart';
part 'counter_schema.g.dart';
#collection
class CounterObject {
Id id = Isar.autoIncrement;
int counter;
CounterObject({required this.counter});
}

How to add widget to appear only when sharing to social

I tried share package and it works but I want to add a Widget that only appears on the shared image, not on the app. So please help me this is the code so far:
import 'dart:ui';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:share/share.dart';
void main() {
runApp(MaterialApp(home: HomePage()));
}
class HomePage extends StatelessWidget {
final _contentKey = GlobalKey();
final bool forSharing;
HomePage({
Key key,
this.forSharing = false,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: const Icon(Icons.share),
onPressed: () => onTakeScreenShotAndSharing(
'Image',
_contentKey.currentContext,
),
),
],
),
body: RepaintBoundary(
key: _contentKey,
child: Column(
children: [
if (forSharing) Text('Text appear only for Sharing'), // This is the widget that I want it appear only on the shared image
Text('Content'),
],
),
),
);
}
void onTakeScreenShotAndSharing(String fileName, BuildContext context) async {
try {
RenderRepaintBoundary boundary = context.findRenderObject();
final image = await boundary?.toImage();
final bytes = await image?.toByteData(format: ImageByteFormat.png);
if (bytes == null || bytes.buffer == null) return;
final dir = (await getApplicationDocumentsDirectory()).path;
final file = File('$dir/$fileName${DateTime.now().toString()}.png');
final newFile = await file.writeAsBytes(bytes.buffer.asUint8List());
await Share.shareFiles([newFile.path]);
} catch (error) {
print(error);
}
}
}

1 positional argument(s) expected, but 0 found. in the widget test.dart

i am writing an travel app that have several list of location detail and fact. and now I want to make a list view for the several list of location. but after writing the widget_test.dart tell me that I positional argument(s) expected, but 0 found. i don't understand what it means in the widget_test.dart. i have ask a lot people but nobody knows. can you help. this is my code. and the error part is here
await tester. pumpWidget(LocationDetail());
my whole code line
screen file
import 'package:flutter/material.dart';
import 'style.dart';
import 'main.dart';
import 'locationroutefile.dart';
const LocationsRoute='/';
const LocationDetailRoute='/location_detail';
class App extends StatelessWidget{
#override
Widget build (BuildContext context){
return MaterialApp(
onGenerateRoute: _routes(),
theme:_theme(),
);
}
RouteFactory _routes() {
return (settings) {
final Map<String, dynamic> arguments = settings.arguments;
Widget screen;
switch (settings.name) {
case LocationsRoute:
screen = LocationListView();
break;
case LocationDetailRoute:
screen = LocationDetail(arguments['id']);
break;
default:
return null;
}
return MaterialPageRoute(builder: (BuildContext context) => screen);
};
}
ThemeData _theme() {
return ThemeData(
appBarTheme: AppBarTheme(textTheme: TextTheme(headline6: AppBarTextStyle)),
textTheme: TextTheme(
headline6: TitleTextStyle,
bodyText2: Body1TextStyle,
));
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:second_tutorial/screen.dart';
import 'textSection.dart';
import 'image_banner.dart';
import 'location.dart';
import 'screen.dart';
void main() => runApp(App());
class LocationDetail extends StatelessWidget {
final int _locationID;
LocationDetail(this._locationID);
#override
Widget build(BuildContext context) {
final location = Location.fetchByID(_locationID);
return MaterialApp(
title:'second tutorial from youtube ',
home: Scaffold(
appBar: AppBar(
title: Text(location.name),
),
body:Column(
mainAxisAlignment: MainAxisAlignment. start,
children: [
ImageBanner(location.imagePath),
]..addAll(textSections(location))),
),
);
}
List<Widget> textSections (Location location){
return location .babi.map((babb) => TextSection (babb.title,babb.text)).toList();
}
}
widget test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:second_tutorial/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(LocationDetail());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
location file
import 'location_fact.dart';
class Location{
final int id;
final String name;
final String imagePath;
final List<LocationFact> babi;
Location (this.id,this.name,this.imagePath,this.babi);
static List<Location> fetchAll() {
return [
Location(1, 'Kiyomizu-dera', 'assets/images/kiyomizu-dera.jpg', [
LocationFact('summary', ';ldjf;ja;ldjf;lajdl;jf;adj;lakdjf;jad;fj'),
]),
Location(2, 'Mount Fuji', 'assets/images/fuji.jpg', [
LocationFact('summary', 'hflajd;lfj;ladjf;lj;dljf;aj'),
]),
Location(3, 'Arashiyama Bamboo Grove', 'assets/images/arashiyama.jpg',[
LocationFact('sumary ', 'aldjf;lajd;lfj;aldjf;ladjf;lkj')
]),
];
}
static Location fetchByID(int locationadfadfID){
List<Location> asdfgh = Location.fetchAll();
for(var i =0;i <asdfgh.length; i++){
if(asdfgh[i].id == locationadfadfID){
return asdfgh[i];
}
}
return null;
}
}
location list view.dart
import 'package:flutter/material.dart';
import 'package:second_tutorial/screen.dart';
import 'location.dart';
class LocationListView extends StatelessWidget{
#override
Widget build(BuildContext context){
final variables = Location.fetchAll();
return Scaffold(
appBar: AppBar(
title: Text('location route file'),
),
body: ListView(
children: variables
.map((testing) => GestureDetector(child:Text(testing.name), onTap:()=> onLocationTap(context, testing.id),)).toList()
),
);
}
onLocationTap(BuildContext context, int locationID) {
Navigator.pushNamed(context, LocationsRoute, arguments: {'id': locationID});
}
}
location fact
class LocationFact {
final String title;
final String text;
LocationFact(this.title,this.text);
}
hope you can help me
Ah, you seem to be using your LocationDetail incorrectly.
In widget test.dart, you have written await tester.pumpWidget(LocationDetail()); .
But, in your constructor, you have LocationDetail(this._locationID);. So you have pass this _locationID param whenever you are using your LocationDetail widget.
So use it something like this, await tester.pumpWidget(LocationDetail(1)); . But you can replace the 1 with anything you feel is appropriate.
More on parameters in Dart, follow this.

How can i show pdf in base64 format in flutter

I have a table in postgresql that has a field with a pdf file in base64 format. When I get it from flutter, how can I show it with pdf viewer ?, has anyone done it ??
I appreciate your help
In pubspec.yaml add the following dependency:
dependencies:
flutter_full_pdf_viewer: ^1.0.6 # for pdf
Then run flutter pub get in Android Studio terminal to update dependencies.
Create the file 'pdfBase64Viewer.dart:
import 'dart:async'; // asynchroneous function (await)
import 'dart:io'; // write the file on user's phone
import 'dart:convert'; // handle base64 decoding
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
import 'package:path_provider/path_provider.dart';
class PdfBase64Viewer extends StatefulWidget {
final String url;
const PdfBase64Viewer(String this.url);
#override
_PdfBase64ViewerState createState() => new _PdfBase64ViewerState();
}
class _PdfBase64ViewerState extends State<PdfBase64Viewer> {
String pathPDF = "";
#override
void initState() {
super.initState();
createFileOfPdfUrl(widget.url).then((f) {
setState(() {
pathPDF = f.path;
print(pathPDF);
});
});
}
Future<File> createFileOfPdfUrl(is_base_64) async {
final filename = widget.url.substring(widget.url.lastIndexOf("/") + 1);
var request = await HttpClient().getUrl(Uri.parse(widget.url));
var response = await request.close();
var bytes = await consolidateHttpClientResponseBytes(response);
var base64String = utf8.decode(bytes);
var decodedBytes = base64Decode(base64String.replaceAll('\n', ''));
String dir = (await getApplicationDocumentsDirectory()).path;
File file = new File('$dir/$filename');
await file.writeAsBytes(decodedBytes.buffer.asUint8List());
return file;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Plugin example app')),
body: Center(
child: RaisedButton(
child: Text("Open PDF"),
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => PDFScreen(pathPDF)),
),
),
),
);
}
}
class PDFScreen extends StatelessWidget {
String pathPDF = "";
PDFScreen(this.pathPDF);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Document"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.share),
onPressed: () {},
),
],
),
path: pathPDF);
}
}
You can adapt above class in order to assign to the variable base64String the String value that you get from your DB.
You can use it the following way:
import 'pdfBase64Viewer.dart';
(...)
child: PdfBase64Viewer("https://filebin.net/qmvmxbgn6pzgkbzb/sample.base64?t=7g5s9rwr") // url of your base64 file
(...)
You might want to check the following documentation:
Base64
Full Pdf Viewer