How to download a .pdf file from an URL with POST Request having headers in Flutter?
i have a different pdf files for each user and i want the link to be different depend on the user.
You can use flutter_downloader plugin for downloading your files.
dependencies:
flutter_downloader: ^1.8.4
And also you can use this codebase for that:
import 'dart:io';
import 'dart:ui';
import 'package:fimber/fimber.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadingService {
static const downloadingPortName = 'downloading';
static Future<void> createDownloadTask(String url) async {
final _storagePermission = await _permissionGranted();
Fimber.d('Current storage permission: $_storagePermission');
if (!_storagePermission) {
final _status = await Permission.storage.request();
if (!_status.isGranted) {
Fimber.d('Permission wasnt granted. Cancelling downloading');
return;
}
}
final _path = await _getPath();
Fimber.d('Downloading path $_path');
if (_path == null) {
Fimber.d('Got empty path. Cannot start downloading');
return;
}
final taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: _path,
showNotification: true,
// show download progress in status bar (for Android)
openFileFromNotification: true,
// click on notification to open downloaded file (for Android)
saveInPublicStorage: true);
await Future.delayed(const Duration(seconds: 1));
if (taskId != null) {
await FlutterDownloader.open(taskId: taskId);
}
}
static Future<bool> _permissionGranted() async {
return await Permission.storage.isGranted;
}
static Future<String?> _getPath() async {
if (Platform.isAndroid) {
final _externalDir = await getExternalStorageDirectory();
return _externalDir?.path;
}
return (await getApplicationDocumentsDirectory()).absolute.path;
}
static downloadingCallBack(id, status, progress) {
final _sendPort = IsolateNameServer.lookupPortByName(downloadingPortName);
if (_sendPort != null) {
_sendPort.send([id, status, progress]);
} else {
Fimber.e('SendPort is null. Cannot find isolate $downloadingPortName');
}
}
}
And in another page you can use this class to download your file:
final _receivePort = ReceivePort();
#override
void initState() {
super.initState();
IsolateNameServer.registerPortWithName(
_receivePort.sendPort, DownloadingService.downloadingPortName);
FlutterDownloader.registerCallback(DownloadingService.downloadingCallBack);
_receivePort.listen((message) {
Fimber.d('Got message from port: $message');
});
}
#override
void dispose() {
_receivePort.close();
super.dispose();
}
void _downloadFile() async {
try {
await DownloadingService.createDownloadTask(url.toString());
} catch (e) {
print("error")
}
}
Related
I'm trying to implement a "silent" login for my application, currently, after the application restarts, the user is signed out, and needs to signin again, but I do store the token.
How can I sign in the user automatically using the token (or am I doing something wrong?) when the application starts?
current code:
import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:syncornot/controllers/cache_manager.dart';
class GoogleAuthController extends GetxController with CacheManager {
var _googleSignIn = GoogleSignIn(scopes: ['email']);
var googleAcc = Rx<GoogleSignInAccount?>(null);
var isSignedIn = false.obs;
var googleToken;
#override
void onInit() {
checkLoginStatus();
super.onInit();
}
void signInWithGoogle() async {
try {
googleAcc.value = await _googleSignIn.signIn();
isSignedIn.value = true;
googleToken = await googleAcc.value?.authentication;
update(); // <-- without this the isSignedin value is not updated.
saveToken(googleToken.accessToken);
} catch (e) {
Get.snackbar(
'Error occured!',
e.toString(),
snackPosition: SnackPosition.BOTTOM,
);
}
}
void login() async {
isSignedIn.value = true;
await saveToken(googleToken);
}
void silentLogin() async {
googleAcc.value = await _googleSignIn.signIn();
isSignedIn.value = true;
await saveToken(googleToken);
}
void logOut() {
isSignedIn.value = false;
saveToken(googleToken);
}
void checkLoginStatus() {
final token = getToken();
if (token != null) {
silentLogin();
isSignedIn.value = true;
}
}
}
and the CacheManager:
import 'package:get_storage/get_storage.dart';
mixin CacheManager {
Future<bool> saveToken(String? token) async {
final box = GetStorage();
await box.write(CacheManagerKey.TOKEN.toString(), token);
return true;
}
String? getToken() {
final box = GetStorage();
return box.read(CacheManagerKey.TOKEN.toString());
}
Future<void> removeToken() async {
final box = GetStorage();
await box.remove(CacheManagerKey.TOKEN.toString());
}
}
enum CacheManagerKey { TOKEN }
I want some silent signing by using the credentials or something..
Thanks
I am using the flutter_sound package to record and play audio. On Flutter Web, on stopping the recording the recorder returns a path/URL of this type: blob:http://localhost:63986/b60f31ce-b94d-48c8-8a4a-2d939effe6d8
I want to upload the audio recording to Firebase Storage but dart.io can't be used for flutter-web so can't use the File method. Even after searching, I didn't find a way to achieve it. I don't know how to proceed. How can I write the audio to file and upload it to firebase?
My Code:
import 'dart:html' as html;
import 'dart:io' as io;
final recorder = FlutterSoundRecorder();
final player = FlutterSoundPlayer();
String fileName;
#override
void initState() {
super.initState();
initRecorder();
}
#override
void dispose() {
recorder.closeRecorder();
player.closePlayer();
super.dispose();
}
Future<void> initRecorder() async {
if (!kIsWeb) {
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Grant Permission form mic first!')),
);
}
}
await recorder.openRecorder();
await player.openPlayer();
recorder.setSubscriptionDuration(Duration(milliseconds: 500));
}
Future<void> record() async {
fileName = DateTime.now().toString();
await recorder.startRecorder(toFile: fileName);
}
Future<void> stop() async {
path = await recorder.stopRecorder();
if (kIsWeb) {
if (path == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Grant Permission for mic first!')),
);
} else {
// Get File from path and upload it to Firebase
print(path);
// not working for Web
// final audioFile = io.File(path);
// html.File() doesn't take path/Url as parameter but
// File(List<Object> fileBits, String fileName,[Map? options])
/*
await FirebaseStorage.instance
.ref()
.child('users/uploads/$fileName.mp3')
.putData(file!.bytes!);*/
}
} else if (!kIsWeb) {
if (path == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Grant Permission for mic first!')),
);
} else {
//final audioFile = io.File(path);
// await FirebaseStorage.instance
// .ref()
// .child('users/uploads/$fileName.mp3')
// .putFile(audioFile);
}
}
}
I've been working on this for days, and I finally figured it out!
When you call startRecorder(toFile: audioLocation), audioLocation will be the location of the file after you call stopRecorder(), stored in html.window.sessionStorage (html as in import 'dart:html' as html;).
So you need to add import 'package:http/http.dart' as http;, create a fileName (whatever you want the file to be called in Firebase Storage), and then insert the following piece of code.
var ref = await storage.ref().child('path/to/file/$fileName');
Uri blobUri = Uri.parse(html.window.sessionStorage[audioFile]!);
http.Response response = await http.get(blobUri);
await ref.putData(response.bodyBytes, SettableMetadata(contentType: 'video/mp4'));
This might not be the best way, but it is the way I finally got it to work! Good luck!!
I, for the life of me, can't figure this out. All I am trying to do is record an audio (as in a sound/voice recorder) and later be able to play it.
Recorder class:
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
//String _pathToAudio = '/sdcard/myAudio.aac';
String _fileName = 'myAudio.aac';
String _path = "/storage/emulated/0";
class Recorder {
FlutterSoundRecorder? _recorder;
bool _isRecorderInitialized = false;
bool get isRecording => _recorder!.isRecording;
Future init() async {
_recorder = FlutterSoundRecorder();
//final directory = "/sdcard/downloads/";
//Directory? extStorageDir = await getExternalStorageDirectory();
//String _path = directory.path;
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw RecordingPermissionException('Recording permission required.');
}
await _recorder!.openAudioSession();
_isRecorderInitialized = true;
}
void _writeFileToStorage() async {
File audiofile = File('$_path/$_fileName');
Uint8List bytes = await audiofile.readAsBytes();
audiofile.writeAsBytes(bytes);
}
void dispose() {
_recorder!.closeAudioSession();
_recorder = null;
_isRecorderInitialized = false;
}
Future record() async {
if (!_isRecorderInitialized) {
return;
}
print('recording....');
await _recorder!.startRecorder(
toFile: '$_fileName',
//codec: Codec.aacMP4,
);
}
Future stop() async {
if (!_isRecorderInitialized) {
return;
}
await _recorder!.stopRecorder();
_writeFileToStorage();
print('stopped....');
}
Future toggleRecording() async {
if (_recorder!.isStopped) {
await record();
} else {
await stop();
}
}
}
Currently the error I am getting is "Cannot open file, path = '/storage/emulated/0/myAudio.aac' (OS Error: No such file or directory, errno = 2)".
I am using flutter_sound
Try initializing your file path by using path_provider.
Add these 2 lines to the beginning of your init function.
final directory = await getApplicationDocumentsDirectory();
_path = directory.path; // instead of "/storage/emulated/0"
Not sure how you're trying to access and play that file but on my end it at least cleared the error.
String _fileName = 'Recording_';
String _fileExtension = '.aac';
String _directoryPath = '/storage/emulated/0/SoundRecorder';
This is what I have currently and it's working.
void _createFile() async {
var _completeFileName = await generateFileName();
File(_directoryPath + '/' + _completeFileName)
.create(recursive: true)
.then((File file) async {
//write to file
Uint8List bytes = await file.readAsBytes();
file.writeAsBytes(bytes);
print(file.path);
});
}
void _createDirectory() async {
bool isDirectoryCreated = await Directory(_directoryPath).exists();
if (!isDirectoryCreated) {
Directory(_directoryPath).create()
// The created directory is returned as a Future.
.then((Directory directory) {
print(directory.path);
});
}
}
void _writeFileToStorage() async {
_createDirectory();
_createFile();
}
So I've created an app that creates a static file server using the httpserver API and I used VirtualDirectory to generate a list of items in a specified directory on Android. The app is working but whenever there is a large file it crashes and from what I understand it is because it loads way too much data into the memory.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'dart:io';
import 'package:http_server/http_server.dart';
import 'package:path_provider/path_provider.dart';
void main(){
runApp(MaterialApp(
home:HomePage(),
));
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static Future<String> get getFilePath async{
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
static Future<File> getFile(String fileName) async{
final path = await getFilePath;
return File('$path/$fileName');
}
static Future<File> saveToFile(var data, String filePath, String fileName) async{
print("filePath+fileName: " + filePath+fileName);
final file = await getFile(filePath+fileName);
return file.writeAsString(data);
}
static Future<void> createDir(String filePath) async{
final path = await getFilePath;
Directory('$path/$filePath').create(recursive: true);
}
static Future<String> readFromFile(String filePath, String fileName) async{
try{
final file = await getFile('$filePath$fileName');
String fileContents = await file.readAsString();
return fileContents;
}catch(e){
final assetFile = await rootBundle.loadString('assets/$filePath$fileName');
if(filePath == ''){
await saveToFile(assetFile, '$filePath', '$fileName');
}else{
await createDir(filePath);
await saveToFile(assetFile, '$filePath', '$fileName');
}
print('copying the file from assets');
return '';
}
}
String data = '';
String rootDirPathStr;
assetFolder() async{
final v = await getFilePath;
Directory('$v').create(recursive: true);
await createDir('dist/css');
await createDir('dist/js');
await readFromFile('','index.html');
await readFromFile('dist/css','/style.min.css');
await readFromFile('dist/js','/serverCom.js');
await readFromFile('dist/js','/main.js');
await readFromFile('dist/js','/files.js');
await readFromFile('dist/js','/index.json');
}
serverInit() async{
// setState(() {
// data = "Server running on IP : "+server.address.toString()+" On Port : "+server.port.toString();
// });
//getting the dir
final rootDir = await getApplicationDocumentsDirectory();
rootDirPathStr = rootDir.path;
print("rootDirPathStr: " + rootDirPathStr);
//getting the dir
HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 8080);
print("Server running on IP : "+InternetAddress.anyIPv4.toString()+" On Port : "+server.port.toString());
VirtualDirectory rootVirDir = VirtualDirectory(rootDirPathStr)
..allowDirectoryListing = true;
VirtualDirectory userFilesVirDir = VirtualDirectory('/storage/emulated/0/IDM/')
..allowDirectoryListing = true;
// await userFilesVirDir.serve(server);
await for (HttpRequest request in server) {
String requestUriPath = request.uri.path;
String requestUriQuery = request.uri.query;
print('requestUriPath: $requestUriPath and requestUriQuery: $requestUriQuery');
if(requestUriPath == '/' && requestUriQuery == '') {
final path = await getFilePath;
await rootVirDir.serveFile(File('$path/index.html'), request);
}else if(requestUriQuery == 'file'){
print('file requested');
try{
await userFilesVirDir.serveRequest(request);
}catch(e){
print("error On file requested: $e");
}
}
else{
await rootVirDir.serveRequest(request);
}
}
}
#override
void initState() {
assetFolder();
serverInit();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child:Text('data'),
),
);
}
}
https://pub.dev/packages/shelf can return a ByteStream as the response... allowing virtually unlimited results. As reported in https://pub.dev/documentation/shelf/latest/shelf/Response/Response.ok.html:
body is the response body. It may be either a String, a List<int>, a Stream<List<int>>, or null to indicate no body.
So you can just open a stream on a large file, and hand it (perhaps through a StreamTransformer) directly in the response. I don't think http_server can do that.
I'm trying to use flutter_downloader package to download some files (images/pdf). There is a listView with ListTiles each containing a button to start downloading when clicked but this error occurs when scrolling the list view.
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:flutter_downloader/src/downloader.dart': Failed assertion: line 30 pos 12: '!_initialized': FlutterDownloader.initialize() must be called only once!
//my code is like this:
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadFile extends StatefulWidget {
DownloadFile({this.downloadUrl});
final String downloadUrl;
#override
_DownloadFileState createState() => _DownloadFileState();
}
class _DownloadFileState extends State<DownloadFile> {
String downloadId;
String _localPath;
ReceivePort _port = ReceivePort();
#override
void initState(){
super.initState();
_init();
}
Future<void> _init() async {
await FlutterDownloader.initialize();
IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
_port.listen((dynamic data) {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
print("status: $status");
print("progress: $progress");
print("id == downloadId: ${id == downloadId}");
});
FlutterDownloader.registerCallback(downloadCallback);
_localPath = (await _findLocalPath()) + '/Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
Future<String> _findLocalPath() async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
Future<bool> _checkPermission() async {
if (Theme.of(context).platform == TargetPlatform.android) {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.storage);
if (permission != PermissionStatus.granted) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.storage]);
if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
//----------------------------------------------------------------
#override
void dispose() {
super.dispose();
}
//---------------------------------------------------------------
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () async {
if (await _checkPermission()) {
final taskId = await FlutterDownloader.enqueue(
url: widget.downloadUrl,
savedDir: _localPath,
showNotification:
true, // show download progress in status bar (for Android)
openFileFromNotification:
true, // click on notification to open downloaded file (for Android)
);
downloadId = taskId;
}
},
child: Text('Downloa File',style: TextStyle(color: Colors.teal),)
);
}
}
According to the Usage section in the flutter_downloader package and the error you are getting, you must call the FlutterDownloader.initialize not more than once.
You can do that in the main method of your application, just like so:
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize();