I have searched for similar questions and answers to this question but haven't found any specific answer till now.
I am trying to save downloaded files to my internal phone storage. Preferably the download folder... Am using d i o and path provider.
Have tried using "get External Storage Directory". But even after the download I can't locate the file anywhere in my device.
Please how do I specify the download path to something like /storage/emulated/0/Download
You can copy paste run full code below
This example code download a pdf file with Dio and save to Downloads directory
Step 1: downloads_path_provider has archived by the owner, you can use package https://pub.dev/packages/ext_storage
code snippet
String path = await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
print(path);
Step 2: Add permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Step 3: pubspec.yaml , notice permission_handler is 4.4.0
dependencies:
flutter:
sdk: flutter
dio: any
permission_handler: 4.4.0
ext_storage: any
Step 4: Dio for download file
Future download2(Dio dio, String url, String savePath) async {
try {
Response response = await dio.get(
url,
onReceiveProgress: showDownloadProgress,
//Received data with List<int>
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
validateStatus: (status) {
return status < 500;
}),
);
print(response.headers);
File file = File(savePath);
var raf = file.openSync(mode: FileMode.write);
// response.data is List<int> type
raf.writeFromSync(response.data);
await raf.close();
} catch (e) {
print(e);
}
}
output
I/flutter (13605): full path /storage/emulated/0/Download/test.pdf
I/flutter (13605): 62%
I/flutter (13605): 100%
full code
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:ext_storage/ext_storage.dart';
import 'dart:io';
import 'package:permission_handler/permission_handler.dart';
final imgUrl =
"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
var dio = Dio();
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void getPermission() async {
print("getPermission");
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler().requestPermissions([PermissionGroup.storage]);
}
#override
void initState() {
getPermission();
super.initState();
}
Future download2(Dio dio, String url, String savePath) async {
try {
Response response = await dio.get(
url,
onReceiveProgress: showDownloadProgress,
//Received data with List<int>
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
validateStatus: (status) {
return status < 500;
}),
);
print(response.headers);
File file = File(savePath);
var raf = file.openSync(mode: FileMode.write);
// response.data is List<int> type
raf.writeFromSync(response.data);
await raf.close();
} catch (e) {
print(e);
}
}
void showDownloadProgress(received, total) {
if (total != -1) {
print((received / total * 100).toStringAsFixed(0) + "%");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton.icon(
onPressed: () async {
String path =
await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS);
//String fullPath = tempDir.path + "/boo2.pdf'";
String fullPath = "$path/test.pdf";
print('full path ${fullPath}');
download2(dio, imgUrl, fullPath);
},
icon: Icon(
Icons.file_download,
color: Colors.white,
),
color: Colors.green,
textColor: Colors.white,
label: Text('Dowload')),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I haven't tested it, but if you want to save your downloaded files to the download directory, there is a package for that : downloads_path_provider
If you encounter issues, you can use the path_provider package instead.
It gives you access to several directories of devices.
You can use "flutter_file_dialog" to handle the issue. After succesfully saving file into app you can provide the path of file to this package and save it in another folder of phone. You can use the function below; ** fromDest ** is the path of the saved file.
_saveFileToDest(String fromDest) async {
final params =
SaveFileDialogParams(sourceFilePath: fromDest);
final filePath = await FlutterFileDialog.saveFile(params: params);
if (kDebugMode) {
print(filePath);
}
}
Related
I have developed an image uploading API and called it an async function I need a spinner to make load until it fetches the result and after fetching the result it has to show the response received in an alert box
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
My whole flutter code is here
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool _isInAsyncCall = false;
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
inAsyncCall: _isInAsyncCall,
// demo of some additional parameters
opacity: 0.5,
progressIndicator: CircularProgressIndicator(),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
modal_progress_hud library helped lot on making this task
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_progress_hud/flutter_progress_hud.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool showSpinner = false;
uploadImage() async {
setState(() {
showSpinner = true;
});
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send();
http.Response res = await http.Response.fromStream(response);
print("reached");
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
showSpinner = false;
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
);
}
}
I am working on an app that displays pdf from URL(cloud firestore) that is passed from the previous screen to the pdf screen. I want every time the user loads the URL, the pdf should be cached so that no loading is required next time. I tried using cached_pdf_view it works but it is missing features and it is giving me problems when scrolling the pdf. I want to use advance_pdf_viewer or syncfusion pdf package to display the pdf and cache it. I got some help from https://www.syncfusion.com/forums/165812/cache-pdf-from-network on the package and replaced my code but am getting this new error FileSystemException: Cannot open file, path . This is my pdf screen.
class PdfScreen extends StatefulWidget {
final String url;
final String id;
const PdfScreen({super.key, required this.url, required this.id});
#override
_HomePage createState() => _HomePage();
}
class _HomePage extends State<PdfScreen> {
final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey();
File? _tempFile;
#override
void initState() {
initializeFile();
super.initState();
}
//ignore: avoid_void_async
void initializeFile() async {
final Directory tempPath = await getApplicationDocumentsDirectory();
final File tempFile = File('${tempPath.path}/${widget.url}');
final bool checkFileExist = await tempFile.exists();
if (checkFileExist) {
_tempFile = tempFile;
}
}
#override
Widget build(BuildContext context) {
Widget child;
if (_tempFile == null) {
print('no temp fille');
child = SfPdfViewer.network(widget.url, key: _pdfViewerKey,
onDocumentLoaded: (PdfDocumentLoadedDetails details) async {
final Directory tempPath = await getApplicationDocumentsDirectory();
_tempFile = await File('${tempPath.path}/${widget.url}')
.writeAsBytes(List.from(await details.document.save()));
});
} else {
child = SfPdfViewer.file(
_tempFile!,
key: _pdfViewerKey,
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Syncfusion Flutter PDF Viewer'),
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.bookmark,
color: Colors.white,
),
onPressed: () {
_pdfViewerKey.currentState?.openBookmarkView();
},
),
],
),
body: child,
);
`` }
` `}
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);
}
}
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
I want to share an image that I took from the CameraController.
I location of the file is as example /data/user/0/com.user.test/cache/2019-09-10 16:32:52.281842.png
How it is possible to share this local image?
I added these two line for read/write to local storage:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
I use the share component from https://pub.dev/packages/esys_flutter_share which works great.
void _sharePicture() async {
print('Share picture');
print(this.imagePath);
final ByteData bytes = await rootBundle.load(this.imagePath);
await Share.file('esys image', 'esys.png', bytes.buffer.asUint8List(), 'image/png', text: 'My optional text.');
}
this.imagePath is the local location of the file: :/data/user/0/com.user.test/cache/2019-09-10 16:32:52.281842.png
Do you first have to save the image? And the use it for sharing? How is it possible to share this local image?
The idea is share Uint8List
This demo use camera_camera package's example. https://github.com/gabuldev/camera_camera/tree/master/example
camera_camera package https://pub.dev/packages/camera_camera is an greate package have well made features and use camera plugin inside
code snippet
after click take picture, the system return a file (val in this example), read bytes and transfer to Uint8List
print("path ${val}");
List<int> bytes = await val.readAsBytes();
Uint8List ubytes = Uint8List.fromList(bytes);
await Share.file('ESYS AMLOG', 'amlog.jpg', ubytes, 'image/jpg');
full code
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:camera_camera/camera_camera.dart';
import 'dart:typed_data';
import 'package:esys_flutter_share/esys_flutter_share.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
File val;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Rully")),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.camera_alt),
onPressed: () async {
val = await showDialog(
context: context,
builder: (context) => Camera(
mode: CameraMode.fullscreen,
orientationEnablePhoto: CameraOrientation.landscape,
/*
imageMask: CameraFocus.square(
color: Colors.black.withOpacity(0.5),
),
*/
));
print("path ${val}");
List<int> bytes = await val.readAsBytes();
Uint8List ubytes = Uint8List.fromList(bytes);
await Share.file('ESYS AMLOG', 'amlog.jpg', ubytes, 'image/jpg');
setState(() {});
}),
body: Center(
child: Container(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width * 0.8,
child: val != null
? Image.file(
val,
fit: BoxFit.contain,
)
: Text("Tire a foto"))));
}
}
demo screen
In camera_camera example take picture button will show in landscape mdoe
file path display in bottom
For camera plugin official example, I only change the following
code snippet
void onTakePictureButtonPressed() {
takePicture().then((String filePath) async{
if (mounted) {
setState(() {
imagePath = filePath;
videoController?.dispose();
videoController = null;
});
if (filePath != null) {
showInSnackBar('Picture saved to $filePath');
File val = File(filePath);
List<int> bytes = await val.readAsBytes();
Uint8List ubytes = Uint8List.fromList(bytes);
await Share.file('ESYS AMLOG', 'amlog.jpg', ubytes, 'image/jpg');
}
}
});
}