flutter downloader and path provider on ios - flutter

I have managed to make flutter downloader and the path provider work on android but it won't work on ios saying that this is a android only operation , what's wrong ?
here is the code :
secondaryActions: <Widget>[
IconSlideAction(
caption: 'Download',
color: Colors.green,
icon: Icons.arrow_circle_down,
onTap: () async {
final status = await Permission.storage.request();
if (status.isGranted) {
final externalDir = await getExternalStorageDirectory();
final id = await FlutterDownloader.enqueue(
url: f.url,
savedDir: externalDir.path,
showNotification: true,
openFileFromNotification: true);
} else {
toast('Permission Denied');
}
},
),

For iOS, after having permission, you should use final externalDir = await getApplicationDocumentsDirectory() since getExternalStorageDirectory() is not supported for iOS.
You can do an OS check:
var externalDir;
if (Platform.isIOS) { // Platform is imported from 'dart:io' package
externalDir = await getApplicationDocumentsDirectory();
} else if (Platform.isAndroid) {
externalDir = await getExternalStorageDirectory();
}

Related

Image cropper not working in GetxController

The following is the code i was trying to implement in. The future method pickImage i guess it is the one having the problem. I am extending the class with GetxController. The method is expected to pick and crop the selected image using the image cropper, and then set the cropped image to the imageFile variable if the cropping was successful.
import 'dart:io';
import 'package:pamoja/app/data/const.dart';
import 'package:pamoja/app/data/firebase/firebase_functions.dart';
import 'package:pamoja/app/data/global_widgets/indicator.dart';
import 'package:pamoja/app/models/advert_model.dart';
import 'package:pamoja/app/modules/my_adverts/controllers/my_adverts_controller.dart';
import 'package:pamoja/app/routes/app_pages.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image_cropper/image_cropper.dart';
class UploadBlogController extends GetxController {
TextEditingController title = TextEditingController();
TextEditingController description = TextEditingController();
TextEditingController location = TextEditingController();
TextEditingController category = TextEditingController();
TextEditingController price = TextEditingController();
TextEditingController phone = TextEditingController();
final FirebaseFunctions _functions = FirebaseFunctions();
File? imageFile;
Future<void> pickImage() async {
try {
ImagePicker _picker = ImagePicker();
await _picker.pickImage(source: ImageSource.gallery).then((value) async {
if (value != null) {
File? croppedFile = await ImageCropper().cropImage(
sourcePath: value.path,
aspectRatio: CropAspectRatio(ratioX: 1, ratioY: 1),
compressQuality: 100,
maxWidth: 700,
maxHeight: 700,
// saveCircleCroppedImage: true,
);
if (croppedFile != null) {
imageFile = croppedFile;
update();
}
}
});
} catch (e) {
showAlert("$e");
}
}
void createBlog() async {
if (title.text.isNotEmpty && description.text.isNotEmpty) {
if (imageFile != null && imageFile != "") {
Indicator.showLoading();
await _functions
.uploadBlog(
title.text,
description.text,
imageFile!,
price.text,
category.text,
location.text,
phone.text,
)
.then((value) {
Indicator.closeLoading();
showAlert("Your advert created sucessfully");
// Get.back();
Get.toNamed(Routes.HOME);
});
} else {
showAlert("Image is required");
}
} else {
showAlert("All fields are required");
}
}
void editBlog(BlogsModel model) async {
Indicator.showLoading();
if (title.text.isNotEmpty && description.text.isNotEmpty) {
if (imageFile == null) {
Map<String, dynamic> map = {
'title': title.text,
'description': description.text,
};
await _functions.editBlog(model.id, map).then((value) {
Get.toNamed(Routes.HOME);
showAlert("Your ads Updated Sucessfully");
});
} else {
String imageUrl = await _functions.uploadImage(imageFile!);
Map<String, dynamic> map = {
'title': title.text,
'description': description.text,
'img': imageUrl,
};
await _functions.editBlog(model.id, map).then((value) {
Get.toNamed(Routes.HOME);
showAlert("Your Advert Updated Sucessfully");
});
}
} else {
showAlert("All fields are required");
}
Indicator.closeLoading();
updateData();
}
void updateData() {
Get.back();
Get.toNamed(Routes.HOME);
if (Get.isRegistered<MyBlogsController>()) {
final controller = Get.find<MyBlogsController>();
controller.myBlogs = [];
Indicator.showLoading();
controller.getMyBlogData();
}
}
}
this i created reusable single-tone code for my entire app to pick media image or video. you can try or modify according your requirement.
// Dart imports:
import 'dart:io';
// Package imports:
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
// Project imports:
import 'package:app/utils/export_utils.dart';
class MediaPicker {
MediaPicker._();
static const supportedImageFormates = [".jpeg", ".png"];
static const supportedVedioFormates = [".mp4"];
static final picker = ImagePicker();
// Single image / video selection
static Future<File?> pickMedia({
required ImageSource source,
bool isVideo = false,
bool isEditing = false,
}) async {
try {
final pickMedia = !isVideo
? await picker.pickImage(source: source)
: await picker.pickVideo(source: source);
if (pickMedia != null) {
return isEditing
? await editImage(file: File(pickMedia.path))
: File(pickMedia.path);
} else {
return null;
}
} catch (ex) {
"Pick Media error: $ex".printLog();
return null;
}
}
static Future<File> editImage({required File file}) async {
final CroppedFile? croppedFile = await ImageCropper().cropImage(
sourcePath: file.path,
uiSettings: [
AndroidUiSettings(
toolbarTitle: 'Cropper',
toolbarColor: ColorConst.themePrimaryColor,
toolbarWidgetColor: ColorConst.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
IOSUiSettings(
title: 'Cropper',
),
],
);
if (croppedFile != null) {
return File(croppedFile.path);
} else {
return file;
}
}
}
Usage add call method on button tap with Future & async method
Future<void> btnPickImageTap() async {
final File? selectImageFile = await MediaPicker.pickMedia(
source: ImageSource.gallery,
isEditing: true, // Default false
);
}
source: ImageSource.camera - to pick image from camera.
isEditing: false - avoid image cropping functionality.
isVideo: to pick video from gallery / camera.
Make sure following permission added for both platforms
Andriod- AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA"/> //Camera
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> // Read Storage
iOS Runner/info.plist
<key>NSCameraUsageDescription</key>
<string>App require camera permission to update profile pic</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>App require gallery permission to update profile pic</string>
As package mentioned in image_cropper add bellow code in AndroidManifest.xml under <applcaiton> TAG
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="#style/Theme.AppCompat.Light.NoActionBar"/>
I modified it following your instruction it worked. The following is a sample modification
Future<void> pickImage() async {
try {
ImagePicker _picker = ImagePicker();
await _picker.pickImage(source: ImageSource.gallery).then((value) async {
if (value != null) {
imageFile = File(value.path);
showAlert(imageFile.toString());
editImage(imageFile: imageFile);
// cropImage(imageFile);
} else {
showAlert("No image selected");
}
});
} catch (e) {
showAlert("$e");
}}
Future<void> editImage({required File? imageFile}) async {
final File? croppedFile = await ImageCropper().cropImage(
sourcePath: imageFile!.path,
androidUiSettings: AndroidUiSettings(
toolbarTitle: 'Advert Image',
toolbarColor: Colors.green.shade400,
toolbarWidgetColor: Colors.white,
initAspectRatio: CropAspectRatioPreset.original,
lockAspectRatio: false,
),
iosUiSettings: IOSUiSettings(
title: 'Cropper',
),
);
if (croppedFile != null) {
imageFile = File(croppedFile.path);
update();
} else {
// update();
}}

Flutter download a file from url automatically to downloads directory

Is there a way or a package in Flutter that downloads a file directly to downloads folder for Android and iOS from a direct URL for example: https://******/image.jpg, without any user overhead just a click and download..
Yes use following packages to completely achieve it :
dio: ^4.0.0
path_provider: ^2.0.2
permission_handler: ^8.0.0+2
then use following code :
define variables:
late String _localPath;
late bool _permissionReady;
late TargetPlatform? platform;
get device platform in initState()
#override
void initState() {
super.initState();
if (Platform.isAndroid) {
platform = TargetPlatform.android;
} else {
platform = TargetPlatform.iOS;
}
}
for check and requesting device's permissions :
Future<bool> _checkPermission() async {
if (platform == TargetPlatform.android) {
final status = await Permission.storage.status;
if (status != PermissionStatus.granted) {
final result = await Permission.storage.request();
if (result == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
prepare for finding localpath :
Future<void> _prepareSaveDir() async {
_localPath = (await _findLocalPath())!;
print(_localPath);
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
Future<String?> _findLocalPath() async {
if (platform == TargetPlatform.android) {
return "/sdcard/download/";
} else {
var directory = await getApplicationDocumentsDirectory();
return directory.path + Platform.pathSeparator + 'Download';
}
}
and at last for downloading file:
InkWell(
onTap: () async {
_permissionReady = await _checkPermission();
if (_permissionReady) {
await _prepareSaveDir();
print("Downloading");
try {
await Dio().download("https://******/image.jpg",
_localPath + "/" + "filename.jpg");
print("Download Completed.");
} catch (e) {
print("Download Failed.\n\n" + e.toString());
}
}
},
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.grey.withOpacity(0.5)),
padding: EdgeInsets.all(8),
child: Icon(Icons.download, color: Colors.black),
));
Make sure you have added required permissions in the AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
EDIT :
if your app directory is not shown in iOS files then add the below line in Info.plist
<key>UISupportsDocumentBrowser</key>
<true/>
You can use Dio package to download any file.
A powerful Http client for Dart, which supports Interceptors, Global
configuration, FormData, Request Cancellation, File downloading,
Timeout etc.
Example
import 'package:dio/dio.dart';
var dio = Dio();
response = await dio.download('https://******/image.jpg');

method not available: ext.flutter.inspector.setPubRootDirectories

I am getting the error below when I try to download a file using flutter_downloader. My Google searches seem to indicate that this is a problem with Firebase. I have firebase_messaging installed but the issue only comes up when I try to downlaod a file using flutter_downloader.
Error handling 'serviceExtension' custom request: method not available: ext.flutter.inspector.setPubRootDirectories
Please assist.
Herewith the Flutter code snippet for the downloader;
IconButton(
icon: Icon(Icons.download),
onPressed: () async {
final status = await Permission.storage.request();
if (status.isGranted) {
final externalDir = await getExternalStorageDirectory();
final id = await FlutterDownloader.enqueue(
url: files[i].file,
fileName: 'filename',
savedDir: externalDir.path,
showNotification: true,
openFileFromNotification: true,
);
} else {
print('Permission denied');
}
},
),

Share url+image+text with Twitter/Facebook/Instagram

Is there any lib/package that will help in both platform iOS and Android to share image+text+url on FB, Twitter And Insta? I have been searing for 4 hours but didn't find any result. https://pub.dev/packages/flutter_share_me
this package is only for android so. If you have any solution for it please guide me.
~ PS : I don't want to use Share package, I have buttons of FB, Insta and Twitter. When user will click on any one of the button then data should be share on any of the social media and I need call back of failure or success.
Edit
package social_share_plugin
https://github.com/romatroskin/social_share_plugin
provide share function and have call back
description page forget to mention twitter already include
full social_share_plugin example code
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:social_share_plugin/social_share_plugin.dart';
import 'package:image_picker/image_picker.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await SocialSharePlugin.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(
child: Text('Running on: $_platformVersion\n'),
),
RaisedButton(
child: Text('Share to Instagram'),
onPressed: () async {
File file = await ImagePicker.pickImage(source: ImageSource.gallery);
await SocialSharePlugin.shareToFeedInstagram("image/*", file.path);
},
),
RaisedButton(
child: Text('Share to Facebook'),
onPressed: () async {
File file = await ImagePicker.pickImage(source: ImageSource.gallery);
await SocialSharePlugin.shareToFeedFacebook('test', file.path);
},
),
RaisedButton(
child: Text('Share to Facebook Link'),
onPressed: () async {
String url = 'https://flutter.dev/';
final quote =
'Flutter is Google’s portable UI toolkit for building beautiful, natively-compiled applications for mobile, web, and desktop from a single codebase.';
final result = await SocialSharePlugin.shareToFeedFacebookLink(
quote: quote,
url: url,
onSuccess: (postId) {
print('FACEBOOK SUCCESS $postId');
return;
},
onCancel: () {
print('FACEBOOK CANCELLED');
return;
},
onError: (error) {
print('FACEBOOK ERROR $error');
return;
},
);
print(result);
},
),
RaisedButton(
child: Text('Share to Twitter'),
onPressed: () async {
String url = 'https://flutter.dev/';
final text =
'Flutter is Google’s portable UI toolkit for building beautiful, natively-compiled applications for mobile, web, and desktop from a single codebase.';
final result = await SocialSharePlugin.shareToTwitter(
text: text,
url: url,
onSuccess: (_) {
print('TWITTER SUCCESS');
return;
},
onCancel: () {
print('TWITTER CANCELLED');
return;
});
print(result);
},
),
],
),
),
);
}
}
You can use package https://pub.dev/packages/esys_flutter_share
github https://github.com/esysberlin/esys-flutter-share
iOS need special action, you can see IMPORTANT Note for iOS
Share text:
Share.text('my text title', 'This is my text to share with other applications.', 'text/plain');
Share file:
final ByteData bytes = await rootBundle.load('assets/image1.png');
await Share.file('esys image', 'esys.png', bytes.buffer.asUint8List(), 'image/png', text: 'My optional text.');
Share files:
final ByteData bytes1 = await rootBundle.load('assets/image1.png');
final ByteData bytes2 = await rootBundle.load('assets/image2.png');
final ByteData bytes3 = await rootBundle.load('assets/addresses.csv');
await Share.files(
'esys images',
{
'esys.png': bytes1.buffer.asUint8List(),
'bluedan.png': bytes2.buffer.asUint8List(),
'addresses.csv': bytes3.buffer.asUint8List(),
},
'*/*',
text: 'My optional text.');
Share file from url:
var request = await HttpClient().getUrl(Uri.parse('https://shop.esys.eu/media/image/6f/8f/af/amlog_transport-berwachung.jpg'));
var response = await request.close();
Uint8List bytes = await consolidateHttpClientResponseBytes(response);
await Share.file('ESYS AMLOG', 'amlog.jpg', bytes, 'image/jpg');
official demo
Allowing users to choose their social platform within your apps UI is not really viable anymore. Better to allow the user to enable their platforms in the OS and then let the Share API or Activity view in iOS do the rest. Just provide the correctly structured URLs with tags etc. This is a great resource: https://github.com/bradvin/social-share-urls

Images doesn't show in gallery

In my application i need to download image from URL and after that show the image in gallery.I'm using flutter_downloader plugin ,
and this my functions to download
Future<String> _findLocalPath() async {
final directory = widget.platform == TargetPlatform.android
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
return directory.path;
}
Future _downloadImg(String url) async {
_localPath = (await _findLocalPath()) + '/Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
FlutterDownloader.enqueue(
url: url,
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true,
fileName: '${DateTime.now().millisecondsSinceEpoch}.jpg');
}
the image downloaded but doesn't show in gallery , so how can i make it visible in gallery?.
Edit You can use pacakage image_downloader to download image with process and save image with image_gallery_saver
package https://pub.dev/packages/image_downloader have progress callback
ImageDownloader.callback(onProgressUpdate: (String imageId, int progress) {
setState(() {
_progress = progress;
});
});
You can use package https://github.com/hui-z/image_gallery_saver to save image or video file in temp directory to gallery.
code snippet for save File
_saveVideo() async {
var appDocDir = await getTemporaryDirectory();
String savePath = appDocDir.path + "/temp.mp4";
await Dio().download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
savePath);
final result = await ImageGallerySaver.saveFile(savePath);
print(result);
}
You can also save image to gallery with this plugin' saveImage function
_save() async {
var response = await Dio().get("https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a62e824376d98d1069d40a31113eb807/838ba61ea8d3fd1fc9c7b6853a4e251f94ca5f46.jpg", options: Options(responseType: ResponseType.bytes));
final result = await ImageGallerySaver.saveImage(Uint8List.fromList(response.data));
print(result);
}