Flutter app crash on release mode reading PDF - flutter

I have a pdf reader in my app, with this function I get de pdf from URL and save the file in local path
Future<File> getFileFromUrl(String url) async {
try {
var data = await http.get(url);
var bytes = data.bodyBytes;
var dir = await getApplicationSupportDirectory();
File file = File("${dir.path}/some.pdf");
File urlFile = await file.writeAsBytes(bytes);
return urlFile;
} catch (e) {
throw Exception("Error opening url file");
}
}
After this process, I call a class to show this PDF in a new route
import 'package:flutter/material.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
class CustomPdfView extends StatefulWidget {
final String title;
final String urlPdf;
CustomPdfView(
this.title,
this.urlPdf
);
#override
_CustomPdfViewState createState() => _CustomPdfViewState();
}
class _CustomPdfViewState extends State<CustomPdfView> {
//int _totalPages = 0;
//int _currentPage = 0;
bool pdfReady = false;
//PDFViewController _pdfViewController;
#override
Widget build(BuildContext context) {
print('Aqui entra antes: ${widget.urlPdf}');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Stack(
children: <Widget>[
PDFView(
filePath: widget.urlPdf,
autoSpacing: true,
enableSwipe: true,
pageSnap: true,
swipeHorizontal: true,
nightMode: false,
onError: (e) {
print("error $e");
},
onRender: (_pages) {
setState(() {
//_totalPages = _pages;
pdfReady = true;
});
},
onViewCreated: (PDFViewController vc) {
//_pdfViewController = vc;
},
onPageChanged: (int page, int total) {
setState(() {});
},
onPageError: (page, e) {},
),
!pdfReady
? Center(
child: CircularProgressIndicator(),
)
: Offstage()
],
),
);
}
}
All works fine in debug mode, but when I run my app on release, the app crashes in CustomPdfView.
I don't know what is the error, I already added, STORAGE permissions in my /app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
And I don't know how can I get error line in the console, because the app is running on release.

same issue.....
i fixed that one
clean your project after build your apk
if still problem persist
than try these commands
flutter build apk --no-shrink
flutter build apk --release --split-per-abi --no-shrink

your way of reading pdf is very nice, here you try to launch pdf in your app,
i almost did the same way.
where i was getting Uint8List from url of pdf, and reading it in my app using printing 5.9.3
but when size of pdf or number of pages more then my app keep getting crash, and also before launching pdf in my app i need to save it locally as you did.
so, to solve this i just need to use flutter_cached_pdfview: ^0.4.1
using this flutter_cached_pdfview i don't need to save pdf locally, just pass the url of pdf and you can launch it in your app without worrying about size of pdf or number of pages in pdf.
i just launch using a button and i pass url of pdf
// navigate in another screen
onPressed: () => nextScreen(
PDFViewerPage(
url: widget.magazineModel.fileUrl,
title: widget.magazineModel.name,
),
),
and MyPdfViewerPage is below
class PDFViewerPage extends StatelessWidget {
final String url;
final String title;
const PDFViewerPage({
Key? key,
required this.title,
required this.url,
}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint('url : $url');
return Scaffold(
appBar: AppBar(
leading: const LeadingIconComponent(), // for popScreen()
title: Text(title),
),
body: const PDF().cachedFromUrl(
Uri.parse(url).toString(),
placeholder: (progress) => const LoadingComponent(),
errorWidget: (error) {
debugPrint('errorInOprnPDF : $error');
return const ErrorPageComponent(
title: 'File Not Exist',
content: "This file is either removed or not valid",
image: "assets/images/empty_data_component/new data not found.svg",
);
},
),
);
}
}

Related

How can I integrate Google FIT in Flutter app?

I want steps count and calories burned data in my flutter app. I am using health: ^3.1.1+1 package but I'm getting "Authorization not granted" even after giving all permission. I even used permission handler for permission and I was successfully getting permission with permission handler, still I am not getting data from health package. Please help me with the process to authorize my app to fetch data from Google Fit API.
I have successfully generate my OAuth client id from google console and added the json file in my project. Please let me know if there is any other place where I need to add my client id.
I am using given below sample code provided in with the package.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:health/health.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
enum AppState {
DATA_NOT_FETCHED,
FETCHING_DATA,
DATA_READY,
NO_DATA,
AUTH_NOT_GRANTED
}
class _MyAppState extends State<MyApp> {
List<HealthDataPoint> _healthDataList = [];
AppState _state = AppState.DATA_NOT_FETCHED;
#override
void initState() {
super.initState();
}
/// Fetch data from the healt plugin and print it
Future fetchData() async {
// get everything from midnight until now
DateTime startDate = DateTime(2020, 11, 07, 0, 0, 0);
DateTime endDate = DateTime(2025, 11, 07, 23, 59, 59);
HealthFactory health = HealthFactory();
// define the types to get
List<HealthDataType> types = [
HealthDataType.STEPS,
HealthDataType.WEIGHT,
HealthDataType.HEIGHT,
HealthDataType.BLOOD_GLUCOSE,
HealthDataType.DISTANCE_WALKING_RUNNING,
];
setState(() => _state = AppState.FETCHING_DATA);
// you MUST request access to the data types before reading them
bool accessWasGranted = await health.requestAuthorization(types);
int steps = 0;
if (accessWasGranted) {
try {
// fetch new data
List<HealthDataPoint> healthData =
await health.getHealthDataFromTypes(startDate, endDate, types);
// save all the new data points
_healthDataList.addAll(healthData);
} catch (e) {
print("Caught exception in getHealthDataFromTypes: $e");
}
// filter out duplicates
_healthDataList = HealthFactory.removeDuplicates(_healthDataList);
// print the results
_healthDataList.forEach((x) {
print("Data point: $x");
steps += x.value.round();
});
print("Steps: $steps");
// update the UI to display the results
setState(() {
_state =
_healthDataList.isEmpty ? AppState.NO_DATA : AppState.DATA_READY;
});
} else {
print("Authorization not granted");
setState(() => _state = AppState.DATA_NOT_FETCHED);
}
}
Widget _contentFetchingData() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: CircularProgressIndicator(
strokeWidth: 10,
)),
Text('Fetching data...')
],
);
}
Widget _contentDataReady() {
return ListView.builder(
itemCount: _healthDataList.length,
itemBuilder: (_, index) {
HealthDataPoint p = _healthDataList[index];
return ListTile(
title: Text("${p.typeString}: ${p.value}"),
trailing: Text('${p.unitString}'),
subtitle: Text('${p.dateFrom} - ${p.dateTo}'),
);
});
}
Widget _contentNoData() {
return Text('No Data to show');
}
Widget _contentNotFetched() {
return Text('Press the download button to fetch data');
}
Widget _authorizationNotGranted() {
return Text('''Authorization not given.
For Android please check your OAUTH2 client ID is correct in Google Developer Console.
For iOS check your permissions in Apple Health.''');
}
Widget _content() {
if (_state == AppState.DATA_READY)
return _contentDataReady();
else if (_state == AppState.NO_DATA)
return _contentNoData();
else if (_state == AppState.FETCHING_DATA)
return _contentFetchingData();
else if (_state == AppState.AUTH_NOT_GRANTED)
return _authorizationNotGranted();
return _contentNotFetched();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.file_download),
onPressed: () {
fetchData();
},
)
],
),
body: Center(
child: _content(),
)),
);
}
}
Go to Google Cloud Platform > Apis & Services > OAuth consent screen and check if your app is in Testing mode and add gmails to allow user to use the Oauth
Remember to upload the android debug key sha1!
New version of the health package solved my problem.

How to write platform specific code for web in flutter?

In flutter, we use platform channels that allows us to call platform-specific APIs whether available in Kotlin or Java code on Android, or in Swift or Objective-C code on iOS.
How to achieve the same with web in flutter ? How can one use a npm package and write some javascript code and send the result to flutter? Is this even possible ? There is official docs for writing platform specific code for Android and iOS, but I couldn't find any docs for writing platform specific code for web.
Also, I tried using the js package. If this is the one that has to be used for this case, how to use it ?
This is what I do to display Hubspot chat on Flutter Web.
I have a folder for Hubspot with:
index.html
script.js
style.css
Then a Flutter Widget with webview_flutter_plus plugin:
class HubspotWebview extends StatefulWidget {
#override
_HubspotWebviewState createState() => _HubspotWebviewState();
}
class _HubspotWebviewState extends State<HubspotWebview> {
final _javascriptChannels = Set<JavascriptChannel>();
bool loading = true;
#override
void initState() {
super.initState();
_javascriptChannels.add(JavascriptChannel(
onMessageReceived: (JavascriptMessage message) {
debugPrint('message: ' + message.message);
_toggleLoading();
},
name: 'Loading'));
}
#override
Widget build(BuildContext context) {
final path = '${kIsWeb ? 'assets/' : ''}assets/hubspot_web_page/index.html';
final key = 'web_bot_key';
if (kIsWeb) {
ui.platformViewRegistry.registerViewFactory(
key,
(int viewId) => IFrameElement()
..width = '640'
..height = '360'
..src = path
..style.border = 'none'
..onLoadedData.listen((event) {
_toggleLoading();
}));
}
return Scaffold(
appBar: new AppBar(
backgroundColor: MyColors.blue_business,
title: MyText.subtitle(
getText('business_help_chat', backUpText: 'Help Chat'),
color: MyColors.white_rice,
)),
body: Stack(
children: [
kIsWeb
? HtmlElementView(viewType: key)
: WebViewPlus(
javascriptMode: JavascriptMode.unrestricted,
initialUrl: path,
javascriptChannels: _javascriptChannels,
),
if (loading)
Center(child: CircularProgressIndicator()),
],
),
);
}
void _toggleLoading() => setState(() => loading = !loading);
}
On Javascript file Loading.postMessage('') triggers toggleLoading() on Flutter:
function onConversationsAPIReady() {
window.hsConversationsSettings = {
inlineEmbedSelector: '#hubspot-conversations-inline-parent',
enableWidgetCookieBanner: true,
disableAttachment: true
};
window.history.pushState({}, 'bot', '?bot=true');
window.HubSpotConversations.widget.refresh({openToNewThread: true});
Loading.postMessage('');
}
if (window.HubSpotConversations) {
onConversationsAPIReady();
} else {
window.hsConversationsOnReady = [onConversationsAPIReady];
}

How to show picked image with file_picker in web?

how can I show image picked by file_picker in web while the file path is null in web platform ?
If the path was not null, showing the image is too easy with Image.file(File):
Image.file(context.select<BlogProvider, File>((BlogProvider p) => p.image))
but It can not create File for image in web because browsers don't give file path and It's null.
Future<void> pickImage() async {
/// If [withReadStream] is set, picked files will have its byte data available as a [Stream<List<int>>]
/// which can be useful for uploading and processing large files.
FilePickerResult result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'jpeg'],
withReadStream: true,
);
if (result != null) {
PlatformFile file = result.files.single; //single means I am Picking just One file not more
_blogImage = File(file.path);//Null in web ,but Ok in Android
notifyListeners();
} else {
// User canceled the picker
}
}
When withReadStream is set to true, selected image can be accessed as:
file.readStream.listen((event) {
_blogImage = Image.memory(event);
notifyListeners();
});
but when withReadStream is false:
_blogImage = Image.memory(file.bytes);
notifyListeners();
And although file.path is null in flutter for web, the file.name is set correctly and we can display it.
More info here
Another way (without file_picker package):
import 'dart:html' as html;
// ...
void pickFile() {
final input = html.FileUploadInputElement()..accept = 'image/*';
input.onChange.listen((event) {
if (input.files.isNotEmpty) {
fileName = input.files.first.name; // file name without path!
// synthetic file path can be used with Image.network()
url = html.Url.createObjectUrl(input.files.first);
});
}
});
input.click();
}
You can use Image.memory()
an exemple using the package universal_html
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: DemoApp0(),
),
),
);
}
class DemoApp0 extends StatefulWidget {
DemoApp0({
Key key,
}) : super(key: key);
#override
_DemoApp0State createState() => _DemoApp0State();
}
class _DemoApp0State extends State<DemoApp0> {
final Map<String, Uint8List> files = {};
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
TextButton(
onPressed: ()=>pickWebFile(),
child: Text("select file"),
),
Column(
children: files.entries
.map((e) => Column(
children: [
Text(e.key),
SizedBox(
width: 200,
height: 300,
child: Image.memory(e.value),
)
],
))
.toList(),
)
],
),
);
}
Future<void> pickWebFile() async {
List<html.File> webFiles = [];
html.InputElement uploadInput = html.FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen((e) {
webFiles = uploadInput.files;
for (html.File webFile in webFiles) {
var r = new html.FileReader();
Uint8List fileData;
r.readAsArrayBuffer(webFile);
r.onLoadEnd.listen((e) async {
fileData = r.result;
if (webFile.size < 4194304) {
setState(() {
files[webFile.name] = fileData;
});
}
});
}
});
}
}

PDF File generation with Flutter web

I'm in the process of Web'ifing a Flutter mobile application and attempting to get around the lack for support for Path Provider in Web.
I'm using the PDF (pdf: ^1.9.0) package to generate a PDF document and upload it to Google Drive and I'm trying to find out if I can generate and store the PDF In memory to make it web compatible.
Example current code using Path Provider.
createFile() async {
final downloads = await getApplicationSupportDirectory();
final file = File("${downloads.path}/$filename.pdf");
await file.writeAsBytes(pdf.save());
await GoogleDriveController.uploadFileToGoogleDrive(file.path);
}
Question: Is there a way to generate and store Fies in memory for web using Flutter web?
I managed to find a work around to generate the PDF and trigger a download via the browser instead and thought I should post incase anyone stumbles across this.
//Create PDF in Bytes
Uint8List pdfInBytes = pdf.save();
//Create blob and link from bytes
final blob = html.Blob([pdfInBytes], 'application/pdf');
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = 'pdf.pdf';
html.document.body.children.add(anchor);
//Trigger the download of this PDF in the browser.
RaisedButton(
child: Text('Press'),
onPressed: () {
anchor.click();
Navigator.pop(context);
},
)
My answer is a variant on Yonkee above specifically for flutter web. In this answer, I have added the imports required (dart:html and dart:typed_data) and added formatting of text as I needed that feature.
import 'package:flutter/material.dart';
import 'package:pdf/widgets.dart' as pw;
import 'dart:typed_data';
import 'dart:html' as html;
class PDFSave extends StatefulWidget {
#override
_PDFSaveState createState() => _PDFSaveState();
}
class _PDFSaveState extends State<PDFSave> {
final pdf = pw.Document();
var anchor;
savePDF() async {
Uint8List pdfInBytes = await pdf.save();
final blob = html.Blob([pdfInBytes], 'application/pdf');
final url = html.Url.createObjectUrlFromBlob(blob);
anchor = html.document.createElement('a') as html.AnchorElement
..href = url
..style.display = 'none'
..download = 'pdf.pdf';
html.document.body.children.add(anchor);
}
createPDF() async {
pdf.addPage(
pw.Page(
build: (pw.Context context) => pw.Column(
children: [
pw.Text('Hello World', style: pw.TextStyle(fontSize: 40)),
],
),
),
);
savePDF();
}
#override
void initState() {
super.initState();
createPDF();
}
#override
Widget build(BuildContext context) {
return Scaffold(backgroundColor: Colors.transparent,
appBar: AppBar(
title: Text('PDF Creator'),
),
body: Center(
child:RaisedButton(
child: Text('Press'),
onPressed: () {
anchor.click();
Navigator.pop(context);
},
)
));
}
}

flutter download an Image from url

I'm trying to load image from server using networkimage() and I want to download the same once it is loaded.. can anyone suggest me some ideas.
CircleAvatar(
backgroundImage: NetworkImage(url),
maxRadius: 15.0,
);
Here I'm loading image from my server. I want to save to the image to particular path after the image is loaded.
I recently battled this, and decided to solve it without plugins. I hope it helps someone.
The below program downloads a picture from the web, stores it in the device's local path, and then displays it when run. (note, it does not work for flutter web because you don't have access to the local file storage on that platform. Instead you would have to save the image to a local database using a plugin like sqflite, or hive from pub.dev.) Here's the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' show get;
import 'dart:io';
import 'package:path_provider/path_provider.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: 'Test Image',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Test Image'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
initState() {
_asyncMethod();
super.initState();
}
_asyncMethod() async {
//comment out the next two lines to prevent the device from getting
// the image from the web in order to prove that the picture is
// coming from the device instead of the web.
var url = "https://www.tottus.cl/static/img/productos/20104355_2.jpg"; // <-- 1
var response = await get(url); // <--2
var documentDirectory = await getApplicationDocumentsDirectory();
var firstPath = documentDirectory.path + "/images";
var filePathAndName = documentDirectory.path + '/images/pic.jpg';
//comment out the next three lines to prevent the image from being saved
//to the device to show that it's coming from the internet
await Directory(firstPath).create(recursive: true); // <-- 1
File file2 = new File(filePathAndName); // <-- 2
file2.writeAsBytesSync(response.bodyBytes); // <-- 3
setState(() {
imageData = filePathAndName;
dataLoaded = true;
});
}
String imageData;
bool dataLoaded = false;
#override
Widget build(BuildContext context) {
if (dataLoaded) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.file(File(imageData), width: 600.0, height: 290.0)
],
),
),
);
} else {
return CircularProgressIndicator(
backgroundColor: Colors.cyan,
strokeWidth: 5,
);
}
}
}
pubspec.yaml file:
http: ^0.12.1
path_provider: ^1.6.5
flutter version: 1.20.0-3.0.pre.112
dart version 2.9.0-19.0.dev
I recommend image_downloader.
For ios, image is saved in Photo Library.
For Android, image is saved in Environment.DIRECTORY_DOWNLOADS or specified location. By calling inExternalFilesDir(), specification of permission becomes unnecessary.
By callback(), you can get progress status.
The following is the simplest example. It will be saved.
await ImageDownloader.downloadImage(url);
I used image_downloader.
Use await ImageDownloader.downloadImage("url") of image_downloader package's method to download image using it's url.
Note : above method will return value as follows :-
imageId of the saved image if saving succeeded.
null if not been granted permission.
for this you have to ask for storage permission, just add following line into android manifest file :
uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
Otherwise it is a PlatformException.
I tried many solution, but this is simplest solution for my... Just try it
STEP - 1
Add this package in your pubspec.yaml file
dependencies:
image_downloader: ^0.20.1
STEP - 2
Add this in your dart file
import 'package:image_downloader/image_downloader.dart';
STEP - 3
Write this code on press download button
ColButton(
title: 'Download',
icon: Icons.file_download,
onTap: () async {
try {
showLoadingDialog(context);
// Saved with this method.
var imageId =
await ImageDownloader.downloadImage("https://raw.githubusercontent.com/wiki/ko2ic/image_downloader/images/bigsize.jpg");
if (imageId == null) {
return;
}
// Below is a method of obtaining saved image information.
var fileName = await ImageDownloader.findName(imageId);
var path = await ImageDownloader.findPath(imageId);
var size = await ImageDownloader.findByteSize(imageId);
var mimeType = await ImageDownloader.findMimeType(imageId);
Navigator.pop(context);
showToast('Image downloaded.');
} on PlatformException catch (error) {
print(error);
}
},
),
I use this plugin to save image in the phone using an URL
https://pub.dartlang.org/packages/image_picker_saver
For more advanced handling of Image/File downloads, you can consider the flutter_downloader package.
Some of the features that I like are :
Shows OS level download progress
can track all downloads
Has notification