How to display future image with image provider in flutter - flutter

I'm building edit Screen where user can change Image. I would like user to display one of three images: - image from server - temporary image if previously not uploaded - new image to upload.
Problem appear when I'm going to display image from server. With use of future builder it's possible to display image but new image cannot be uploaded. If I'm not using future builder I get error
future is not subtype of type ImageProvider
My avatar for displaying images
CircleAvatar(
radius: MediaQuery.of(context).size.width / 6,
backgroundColor: Colors.grey,
backgroundImage: getImage(),
),
getImage()
getImage() {
if (_image != null) {
if (uploadCounter == 0) {
uploadImage();
AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))),
backgroundColor: Colors.lightBlue[900],
content: Container(
child: Text('Uploading...'),
),
);
}
return FileImage(_image);
} else {
return _emptyImage;
}
}
and my upload image
uploadImage() async {
if (_image == null) {
showAlertDialog(
context: context,
title: "Error Uploading!",
content: "No Image was selected.");
return;
} else {
setState(() {
uploadStatus = true;
});
final preffs = await SharedPreferences.getInstance();
final token = preffs.getString('token');
final id = preffs.getString('id');
var uri = Uri.parse('http://10.0.2.2:8000/profiles/update/');
var request = http.MultipartRequest('PUT', uri)
..fields['id'] = id
..headers.addAll({"Authorization": token})
..files.add(await http.MultipartFile.fromPath('image_1', _image.path));
var response = await request.send();
if (response.statusCode == 200) {
print('Uploaded!');
print(response);
}
setState(
() {
uploadStatus = false;
uploadCounter = 1;
},
);
}
}
I was trying to get image from server trough future builder or to save image before entering this screen then reload it from file, but saving files in flutter is not best solution I think

The only solution works at the moment looks like this.
FutureBuilder<Profile>(
future: profileService.getProfile(),
builder: (context, snapshot) {
if (snapshot.hasData) {
profile = snapshot.data;
return CircleAvatar(
radius: MediaQuery.of(context).size.width / 6,
backgroundColor: Colors.grey,
backgroundImage: Image.network(
"http://10.0.2.2:8000/" +
profile.image_1.toString())
.image,
);
} else {
return Container();
}
}),
CircleAvatar(
radius: 0.1,
backgroundColor: Colors.grey,
backgroundImage: getImage(),
),
If I put second CircleAvatar in return of if-else I get some problems Like no photo just gray Image etc, problem with upload photo.

Related

Preview Picked Image in Flutter

Hi I am using the image_picker package in flutter to pick images and store them to firestore.
I want to preview the image in a circle avatar before I actually upload it so how would I go about doing that?
This is the code I have to pick the image:
String imageUrl = '';
Future pickImage(ImageSource imageSource) async {
ImagePicker imagePicker = ImagePicker();
XFile? file = await imagePicker.pickImage(
source: imageSource);
if (file == null) return;
String uniqueFileName = DateTime
.now()
.millisecondsSinceEpoch
.toString();
//Getting a reference to storage root
Reference referenceRoot = FirebaseStorage.instance.ref();
Reference referenceDirImages = referenceRoot.child(
'images');
//Create a reference for the image to be stored
Reference referenceImageToUpload = referenceDirImages
.child(uniqueFileName);
try {
//Store the file
await referenceImageToUpload.putFile(File(file.path));
// Success: get download URL
imageUrl =
await referenceImageToUpload.getDownloadURL();
} catch (error) {
// Some error occurred
}
}
This is where I ask the user to pick the image either from the gallery or camera and then in the circle avatar I want them to preview the image before actually uploading it:
InkWell(
child: CircleAvatar(
backgroundColor: Colors.blue[900],
radius: 100,
),
onTap: () async {
return showDialog(context: context, builder: (context) {
return Center(
child: SizedBox(
height: 200,
child: AlertDialog(
backgroundColor: Colors.blue[900],
actionsPadding: const EdgeInsets.all(10.0),
content: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(onPressed: () {
pickImage(ImageSource.gallery);
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.white)
),
child: const Text("Gallery",
style: TextStyle(
color: Colors.black
),)),
const SizedBox(
height: 10,
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.white)
),
onPressed: () {
pickImage(ImageSource.camera);
},
child: const Text("Camera", style: TextStyle(
color: Colors.black
),),
)
],
),
),
),
),
);
}
);
}
),
Thanks!
Well you need to separate your functions into sub-functions, but first I will create this variable:
File? imageFile;
then it should be a function that picks that image from the device and update the state of the imageFile variable we made.
Future pickImage(ImageSource imageSource) async {
ImagePicker imagePicker = ImagePicker();
XFile? file = await imagePicker.pickImage(source: imageSource);
if (file == null) return;
setState(() {
imageFile = File(file.path);
});
}
and a method that returns the widget which will show the actual image we pick:
Image genratePreviowWidget(File file) {
return Image.file(file);
}
and in your UI, you should show it under an if condition like this:
Column(
children: [
if(imageFile != null) genratePreviowWidget(imageFile ),
],
),
now you have a method that pick the image and show it directly in your UI after it's done.
forwe still have that imageFile, so you can simply upload it ( confirm uploading it) from a separate method:
Future confirmUpload(File file) async {
String uniqueFileName = DateTime.now().millisecondsSinceEpoch.toString();
//Getting a reference to storage root
Reference referenceRoot = FirebaseStorage.instance.ref();
Reference referenceDirImages = referenceRoot.child('images');
//Create a reference for the image to be stored
Reference referenceImageToUpload = referenceDirImages.child(uniqueFileName);
try {
//Store the file
await referenceImageToUpload.putFile(file);
// Success: get download URL
imageUrl = await referenceImageToUpload.getDownloadURL();
} catch (error) {}
}
and you will need to call it when you want to upload it like this:
confirmUpload(imageFile);

I tried to upload multi images, but it wasn't display preview photos

I tried to upload multi images, but it wasn't display preview photos.
Original code is can display only photo, but I modified the code then tried to upload multi images. Still cannot show to me.
Original Code, working well, but just show one image
SizedBox(
height: 250,
child: AspectRatio(
aspectRatio: 487 / 451,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(_file!),
fit: BoxFit.fill,
alignment: FractionalOffset.topCenter,
),
),
),
),
),
Then I tried to modified to this one
Expanded(
child: GridView.builder(
itemCount: selectedFiles.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return Image.file(File(selectedFiles[index].path));
},
),
),
It wasn't show to me.
I can got the image list
Future<void> selectImage() async {
if (selectedFiles != null) {
selectedFiles.clear();
}
try {
final List<XFile>? imgs = await _picker.pickMultiImage();
if (imgs!.isNotEmpty) {
selectedFiles.addAll(imgs);
}
print("image list : " + imgs.length.toString());
} catch (e) {
print(e.toString());
}
setState(() {});
}
Or I need to modify this code??
SimpleDialogOption(
padding: const EdgeInsets.all(20),
child: const Text('Choose from gallery'),
onPressed: () async {
Navigator.of(context).pop();
Uint8List file = await pickImage(ImageSource.gallery);
// final List<XFile>? imgs = await _picker.pickMultiImage();
// if (imgs!.isNotEmpty) {
// selectedFiles.addAll(imgs);
// }
setState(() {
_file = file;
});
},
),
For the GridView to display images the Ui has to rebuild
So when you add images to your list
if (imgs!.isNotEmpty) {
selectedFiles.addAll(imgs);
}
you dont notify the UI to rebuild.
you can call an empty setstate below the selectedFiles to force UI to rebuild.
if (imgs!.isNotEmpty) {
selectedFiles.addAll(imgs);
setState((){
})
}
For example when picking a single file
File? myfile;
pickFile()async{
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
File file = File(result.files.single.path);
setState((){
myFile=file;
})
} else {
// User canceled the picker
}}

How to render image from Firebase without rebuilding?

I'm trying to implement profile image picking in my flutter app using Firebase Storage. I use image_picker to get the image and upload it to Firebase, get the download link and add the download link to the imgsrc field in the cloud firestore, from where I can render the NetworkImage.
Center(
child: Stack(
children: [
buildImage(),
Positioned(
bottom: 5,
right: 5,
child: GestureDetector(
onTap: showPhotoAlertDialog,
child: buildEditIcon(Color(0xff407bff))),
),
],
),
),
How can I get the default Icons.person kind image for when the user has no profile image, and get the image from the database otherwise?
The code I'm using right now is as follows:
Widget buildImage() {
return CircleAvatar(
backgroundImage: NetworkImage(loggedInUser.imgsrc ??
'https://th.bing.com/th/id/R.945f33b643f2ceffcdae90fb57c61854?rik=XcI0SYBgSefoCA&riu=http%3a%2f%2fgetdrawings.com%2ffree-icon-bw%2fanonymous-avatar-icon-19.png&ehk=5n%2buJG66CeLQZsmhaMt8gag5rXuM3TdebAL6W35K1E4%3d&risl=&pid=ImgRaw&r=0'),
backgroundColor: Colors.grey[350],
radius: 100,
);
}
I created an Alert Dialog widget to choose whether to choose the image from camera or from the gallery.
showPhotoAlertDialog() {
AlertDialog alert = AlertDialog(
title: Text("Upload from"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
imageFromCamera()
.then((value) => uploadFile())
.whenComplete(() => postSource());
setState(() {}); ----->
},
child: Text("Upload from camera"),
),
TextButton(
onPressed: () {
imageFromGallery().then((value) => uploadFile());
postSource();
setState(() {});
},
child: Text("Upload from gallery"),
)
],
),
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
To upload the image to storage and post the source to cloud firestore, I use the following methods:
Future uploadFile() async {
if (file == null) return;
final fileName = path.basename(file!.path);
final destination = 'files/$fileName';
task = FirebaseApi.uploadFile(destination, file!);
setState(() {});
if (task == null) return;
final snapshot = await task!.whenComplete(() {});
urlDownload = await snapshot.ref.getDownloadURL();
print('Download-Link: $urlDownload');
}
postSource() async {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
await firebaseFirestore
.collection("users")
.doc(user?.uid)
.update({'imgsrc': urlDownload});
}
The link gets uploaded properly and I'm able to get the link in my NetworkImage, but it doesn't get rendered immediately. I have to close the parent drawer and open it again to get it. I call setState(){} as well after posting the source, but it doesn't make any difference. How can I get the image without having to close and open the drawer?
Any help would be appreciated!
Thanks
You also have to update image in model class or in this imgsrc also just add this line above setState in onPressed of TextButton.
loggedInUser.imgsrc = urlDownload;

flutter, trying to update a picture from Storage

I'm trying to upload, and update a picture from firebase storage. But for some reason this is not working properly.
This would be the scenario; A user take a picture from his camera, so this picture is uploaded to my storage, so after that I get the url picture, so I can use this like NetworkImage(url) to update the picture.
This is what's happening currently:
Let's say I don't have any picture.
I update my profile with a new picture. (This works perfectly).
Now let's say I want to update my profile picture, so I take another picture, let's call it A so I upload this one.
Nothing happens, picture doesn't change.
But If I try again with a B picture, for some reason the picture is updated with the A picture.
This would be my code and how I'm facing this feature.
I have the next method which is invoked when a user click over his picture. I wait for the result (value) so when I got the result I upload the picture. Then, when the picture is uploaded, I just call to setState and save the url into the _imageUrl variable.
After that, I change the "profilePic" attribute from my data base to true.
String _imageUrl = 'assets/profileDefault.jpg';
bool profilePicture = false;
io.File profilePic;
Future getFromCamara() async {
await ImagePicker().getImage(source: ImageSource.camera).then((value) {
profilePic = io.File(value.path);
FirebaseStorage.instance.ref().child('picture').putFile(profilePic);
}).then((result) {
var ref = FirebaseStorage.instance.ref().child('picture');
ref.getDownloadURL().then((loc) => setState(() => _imageUrl = loc));
});
try {
FirebaseFirestore.instance
.collection('Usuarios')
.doc(uid)
.update({'profilePic': true});
} catch (e) {
print(e.toString());
}
So now, using a StreamBuilder, I get the result of profilePic from my DataBase, if is True, I download the URL, and if don't, I just use the Asset default's pic.
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('Usuarios').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot1) {
if (!snapshot1.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
List<DocumentSnapshot> users = snapshot1.data.docs;
for (DocumentSnapshot user in users) {
if (user.id == userID) {
profilePicture = user['profilePic'];
}
}
if (profilePicture) {
FirebaseStorage.instance
.ref()
.child('picture')
.getDownloadURL()
.then((loc) => _imageUrl = loc);
} else {
_imageUrl = 'assets/profileDefault.jpg';
}
return Stack(
alignment: Alignment.center,
children: <Widget>[
Positioned(
top: 0,
child: Container(
color: Color.fromRGBO(255, 0, 0, 70),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * .55,
),
),
Positioned(
top: MediaQuery.of(context).size.height * .015,
left: 15,
right: 15,
child: Container(
width: MediaQuery.of(context).size.height * .90,
height: 300,
padding: EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () => _setPic(context),
child: CircleAvatar(
radius: 79,
backgroundColor: Color(0xFFFFFFFFF),
child: CircleAvatar(
radius: 75,
backgroundImage: profilePicture
? NetworkImage(_imageUrl)
: AssetImage(_imageUrl),
),
),
),
// More code...
What I'm doing wrong? Why my picture isn't getting updated?
Updated:
I tried this, so I can save in pictureTemp the picture's bytes, calling to setState to rebuild the widget and put the picture with Image.memory(pictureTemp). But it doesn't seem to work.
Uint8List pictureTemp;
io.File profilePic;
Future getFromCamara() async {
var pickedFile = await ImagePicker().getImage(source: ImageSource.camera);
var image = await pickedFile.readAsBytes();
FirebaseStorage.instance
.ref()
.child('picture')
.putFile(io.File(pickedFile.path));
setState(() {
pictureTemp = image;
});
child: CircleAvatar(
radius: 75,
backgroundImage: profilePicture
? pictureTemp == null
? AssetImage(_imageUrl)
: Image.memory(pictureTemp)
: AssetImage(_imageUrl),
),
Here when the image is picked you should directly set it or you can set it after the firebase upload is finished successfully.
But rather than loading an image from Firebase URL after the upload, you can directly load from the picked file as follows;
image = Image.memory(await pickedFile.readAsBytes())
This will instantly set the image and will save you a read call to Firebase. You should always try to minimize the Firebase Reads whenever possible.

Get The Image variable from the File Path and upload to backend in FileFormat in Flutter

I'm new to flutter and in an app I'm building I use ImagePicker plugin.
with that the file path image is viewed in my app after I get it from camera/gallery.
the image.path is like this
/storage/emulated/0/Android/data/.../files/Pictures/234d9437-8652-48de-a2b6-711b5f8b702d3492716976084944343.jpg
I need to get the image "234d9437-8652-48de-a2b6-711b5f8b702d3492716976084944343.jpg" part from that and send it to a backend db. I need to convert this before sending. Backend only accept Images in FileFormat.
How can get the image from the file path. in this case it's the variable _imageURI. Then from that retrieve the image and convert it into a FileFormat. After that I need to pass it to a Backend using a json POST request.
In my json request I have a field for 'image': that I need to set the value got from image selected in file format and set to there. how can do this?
can someone explain me with a code ?
much appreciated.
My Code
File _imageURI;
Future _getImageFromCamera() async {
var petImage = await ImagePicker.pickImage(source: ImageSource.camera); //or gallery
setState(() {
_imageURI = petImage;
print(_imageURI.path);
}
}
Image Viewed as
Container(
width: 120.0,
height: 120.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.cover,
image: new FileImage(
_imageURI,
scale: 1.0,
),
),
),
),
Json Request
dogData = {
{
"user_email": "email#m.com,
"user_token": "thisistoken",
"pet": {
"age": "integer",
"birth_date": "%d %b %Y (01 JAN 1996)",
"image": "!Swagger doesn't allow to put file upload. Use formdata instead of base64 in frontend module.",
"name": "string",
"sex": "string",
"user_id": "id"
}
}
My API call
final pet = await CallApi().createThePet(dogData, 'pets/create');
////
Future<dynamic> createThePet(data, apiUrl) async{
var fullUrl = _baseUrl + apiUrl; // + await _getToken();
final response = await http.post(fullUrl, body: jsonEncode(data), headers: _setHeaders());
....
The term you are looking for is called Multi-Part.
There isn't any definite piece of code for that in flutter so i am posting an example snippet.
Future<void> _uploadImage(File image) async {
String tempURL = https://YOUR_WEBSITE/upload";
var request = new http.MultipartRequest("POST", Uri.parse(tempURL));
request.headers["authorization"] = YOUR_TOKEN_HERE;
// prepare file to send.
var profilePhoto = http.MultipartFile(
'upload', image.readAsBytes().asStream(), image.lengthSync(),
filename: image.path);
request.files.add(profilePhoto);
try {
// send files to server.
var response = await request.send();
if (response.statusCode == 200) {
// now we will update the data with the
response.stream.bytesToString().asStream().listen((data) {
_updatePhoto(data);
});
} else {
print("res status code in upload func : ${response.statusCode}");
}
} catch (error) {
print("Error : $error");
}
}
Hope this helps,it is working fine with my project
Install imagepicker,mime,http and import it
import 'package:mime/mime.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
Initialize variable
File _image;
final Imagepicker = ImagePicker();
View for showing image after selecting
Container(
width: 350,
height: 250,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
border: Border.all(
color: Colors.deepOrangeAccent[400], width: 1.0)),
child: GestureDetector(
child: ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: Container(
width: 350,
height: 250,
child: _image == null
? Text('No image selected.')
: Image.file(
_image,
fit: BoxFit.cover,
),
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.white,
),
),
),
onTap: () {
getImage();
},
),
),
ImagePicker from camera
Future getImage() async {
PickedFile image =
await Imagepicker.getImage(source: ImageSource.camera, maxHeight: 1000);
setState(() {
_image = File(image.path);
});
}
Submit Function
void submit(File image, String descrption) async {
try {
///Spilits the path and returns only the filename and type
final mimeTypeData =lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
///Url of your api
final request =new http.MultipartRequest("POST", Uri.parse(Urls.ImageInsert));
///replace AreaImage with your database value
final file = await http.MultipartFile.fromPath('AreaImage',
image.path,contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
///In case there is text fields or other details use like this
request.fields['user_email'] = "Email";
request.fields['user_token'] = "thisistoken";
request.files.add(file);
StreamedResponse response = await request.send();
//waiting for response
response.stream.transform(utf8.decoder).listen((value) {
//Response can be pass with map object to alertbox
Map<String, dynamic> map = jsonDecode(value);
try {
// hide progrssbar
pr.hide();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Alert"),
//here we show response message from api/or you can show your own message
here
content: Text(map['message']),
actions: [
FlatButton(
child: Text("Close"),
onPressed: () {
//Do Something here
},
)
],
);
},
);
} catch (e) {
e.toString();
}
});
}
}
}
You can checkout my github for image upload with either gallery or cameragithub