In my Flutter pr project, I am using Image Picker plugin to select images from the android mobile gallery or capture images with the camera and show them one after another with a delete icon below each image. On tapping the RaisedButton for selecting images from the gallery, the method imageSelectorGallery() is called. There inside the setState() method , I add a SizedBox and a delete icon to the List namely images_captured. I expect the images_captured to be rendered inside the Column in SingleChildScrollView.
But after selecting an image from the gallery, nothing happens. I also want to tap on the delete icon and remove the image above it. But flutter has no data binding mechanism as I know to correlate the image with the delete button.
Code follows:
class PrescriptionScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new UserOptionsState();
}
}
class UserOptionsState extends State<PrescriptionScreen> {
//save the result of gallery fileUserOptions
File galleryFile;
//save the result of camera file
File cameraFile;
#override
Widget build(BuildContext context) {
var images_captured=List<Widget>();
//display image selected from gallery
imageSelectorGallery() async {
galleryFile = await ImagePicker.pickImage(
source: ImageSource.gallery,
// maxHeight: 50.0,
// maxWidth: 50.0,
);
print("You selected gallery image : " + galleryFile.path);
setState(() {
var sized_box_indiv= new SizedBox(
height: 200.0,
width: 300.0,
//child: new Card(child: new Text(''+galleryFile.toString())),
//child: new Image.file(galleryFile),
child: galleryFile == null
? new Text('Sorry nothing selected from gallery!!')
: new Image.file(galleryFile),
);
images_captured.add(sized_box_indiv);
var delete_button = IconButton(icon: Icon(Icons.delete), onPressed: () {});
images_captured.add(delete_button);
});
}
//display image selected from camera
imageSelectorCamera() async {
cameraFile = await ImagePicker.pickImage(
source: ImageSource.camera,
//maxHeight: 50.0,
//maxWidth: 50.0,
);
print("You selected camera image : " + cameraFile.path);
setState(() {});
}
return new SingleChildScrollView(
child:Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new RaisedButton(
child: new Text('Select Image from Gallery'),
onPressed: imageSelectorGallery,
),
new RaisedButton(
child: new Text('Select Image from Camera'),
onPressed: imageSelectorCamera,
),
Column(
children: images_captured
),
],
),
);
/* },
),
);*/
}
}
How to show the images selected from gallery one after another with a delete icon button below each of them?
How to remove the corresponding image on tapping the delete icon button?
I think if I can accomplish it for gallery, I can do it for camera capturing as well.
I used the answer by jJuice and the images after being selected showed overflow error. The screenshot is given below:
My code is:
class UserOptionsState extends State<PrescriptionScreen> {
//save the result of gallery fileUserOptions
File galleryFile;
//save the result of camera file
File cameraFile;
var images_captured=List<Widget>();
List<File> images = List<File>();
#override
Widget build(BuildContext context) {
//display image selected from gallery
imageSelectorGallery() async {
galleryFile = await ImagePicker.pickImage(
source: ImageSource.gallery,
// maxHeight: 50.0,
// maxWidth: 50.0,
);
images.add(galleryFile);
print("You selected gallery image : " + galleryFile.path);
setState(() {
});
}
//display image selected from camera
imageSelectorCamera() async {
cameraFile = await ImagePicker.pickImage(
source: ImageSource.camera,
//maxHeight: 50.0,
//maxWidth: 50.0,
);
print("You selected camera image : " + cameraFile.path);
setState(() {});
}
return new SingleChildScrollView(
child:Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new RaisedButton(
child: new Text('Select Image from Gallery'),
onPressed: imageSelectorGallery,
),
new RaisedButton(
child: new Text('Select Image from Camera'),
onPressed: imageSelectorCamera,
),
new Container(
// new Column(
// children: <Widget>[
height: 1200,
child:GridView.count(
crossAxisSpacing: 6,
mainAxisSpacing: 6,
crossAxisCount: 3,
children: List.generate(images.length, (index) {
return Column(
children: <Widget>[
Container(
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: ClipRRect(
child: Image.file(images[index], fit: BoxFit.cover),
borderRadius: BorderRadius.circular(10),
)
),
GestureDetector(
onTap: () {
setState(() {
images.removeAt(index);
});
},
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Align(
alignment: Alignment.bottomCenter,
child: Icon(Icons.clear, color: Colors.black, size: 20),
),
),
),
]
);
}
),
),
// ]
)
/*displaySelectedFile(galleryFile),
displaySelectedFile(cameraFile)*/
],
),
);
}
Widget displaySelectedFile(File file) {
return new SizedBox(
height: 200.0,
width: 300.0,
//child: new Card(child: new Text(''+galleryFile.toString())),
//child: new Image.file(galleryFile),
child: file == null
? new Text('Sorry nothing selected!!')
: new Image.file(file),
);
}
}
Question 1: You first need to store the images picked by using ImagePicker (or MultiImagePicker plugin) in a collection. Here's an example on how to do that:
List<File> images = List<File>();
images.add(await ImagePicker.pickImage(source: ImageSource.gallery, imageQuality: 20););
When you want to show those images on the screen, you can use several different widgets, like ListView, GridView, Row, Column.
Here's an example where I use a GridView:
child: Container(
height: 1200,
child: GridView.count(
crossAxisSpacing: 6,
mainAxisSpacing: 6,
crossAxisCount: 3,
children: List.generate(images.length, (index) {
return Column(
children: <Widget>[
Container(
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: ClipRRect(
child: Image.file(images[index], fit: BoxFit.cover),
borderRadius: BorderRadius.circular(10),
)
),
GestureDetector(
onTap: () {
setState(() {
images.removeAt(index);
});
},
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Align(
alignment: Alignment.bottomCenter,
child: Icon(Icons.clear, color: Colors.white, size: 20),
),
),
),
]
),
}
),
),
I think using a Stack widget works best in this case. A Stack can be used to show Widgets, layered on top of each other. So in this case, a widget showing the image, with a Icon widget on top of it that is the button for your delete action.
Question 2:
You could delete the image by calling the removeAt method available on collections like a List. See the code inside the onTap method of the GestureDetector. By calling setState the page gets rebuild once the image has been deleted.
EDIT
Sorry, I misread your question and see that you want to show a button below the image, instead of on top of it. A Column widget could be used for this purpose. I edited the code accordingly.
Related
I'm new to flutter and trying to build an editing app using ColorFiltered Class in flutter the changes are reflected while editing the image but not reflected when I try to save the image to the gallery (Only the unedited image is getting saved).
Below is the code which helps to display the image while editing.
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.width,
width: MediaQuery.of(context).size.width,
child: AspectRatio(
aspectRatio: 1,
child: buildImage(),
),
),
],
)
),
Below is the code inside the buildImage() function which is called being called.
Widget buildImage() {
return ColorFiltered(
colorFilter: ColorFilter.matrix(calculateContrastMatrix(con)),
child: ColorFiltered(
colorFilter: ColorFilter.matrix(calculateSaturationMatrix(sat)),
child: Image.file(
_imageFile,
height: 400,
width: 250,),
),
);
}
And below is the code where I'm trying to savw the image.
TextButton(
child: const Text('Save Image'),
onPressed: () async {
final imageBytes = await _imageFile.readAsBytes();
final result = await ImageGallerySaver.saveImage(imageBytes);
},
Note I'm trying to update the _imageFile using setState.
void initState() {
super.initState();
_imageFile = File(widget.image.path);
}
Thank you.
I want the changes made through ColorFiltered class to be reflected on the saved image also.
I need a button to delete the currently viewed image. The image paths are stored in a List<String>.
The images appear via CarouselSlider from ImagePicker.
I've tried to see if I can delete the image via a hardcoded index with .removeAt() and that works but again, can't figure how to get current image
What is right way to grab the current index of the on screen image? I've tried with single images and it works find but getting the current index of the currently viewed photo is where I'm running into trouble.
This is how I am getting my Images:
List<String> pickedImageList = [];
final imagePicker = ImagePicker();
Future pickImage() async {
final userPickedImages = await imagePicker.pickMultiImage();
for (var image in userPickedImages!) {
pickedImageList.add(image.path);
}
setState(() {});
}
This is how I am showing the images. Button to delete currently selected photo is at the bottom with generic function:
Column(
children: [
ElevatedButton(
onPressed: pickImage,
child: const Text('Add another image')),
CarouselSlider(
options: CarouselOptions(
// height: 200,
viewportFraction: .9,
enlargeCenterPage: true,
),
items: pickedImageList
.map(
(item) => SizedBox(
width: 200,
height: 200,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
item,
height: 200,
width: 200,
)),
),
// color: Colors.green,
)
.toList(),
),
ElevatedButton(
onPressed: () {
setState(() {
removeImage();
});
},
child: const Text('Remove'))
],
),
All of the delete buttons that people have solved on SO all involve single images which is not the problem.
I thought maybe a GestureDetector counting up and down with left and right swipes, resetting the counter when it reaches the length of the list. Negatives would be multiplied by it self to get current index usable.
The images are too large to be a lock screen wallpaper, so I want the user to select the desired part by swiping left and right.
I am using InteractiveViewer to scroll left and right.
How can I make the scrolled image as wallpaper when the user presses the set as wallpaper button?
----------->
(wallpaper when I press the button) ----------- (wallpaper which I want to set)
Here is my widget tree:
Stack(
children: <Widget>[
Hero(
tag: widget.heroId,
child: Container(
alignment: Alignment.center,
child: InteractiveViewer(
maxScale: 5.0,
constrained: false,
child: FadeInImage(
height: MediaQuery.of(context).size.height,
placeholder: AssetImage('assets/images/loading-5.gif'),
image: NetworkImage(widget.imageUrl),
fit: BoxFit.cover,
),
),
),
),
Positioned(
right: 16.0,
top: 0.0,
child: ElevatedButton(
onPressed: () {
_setwallpaper(WallpaperManagerFlutter.LOCK_SCREEN);
},
child: Text("Set as Wallpaper")),
),
],
)
Here is set as wallpaper function:
Future<void> _setwallpaper(location) async {
var file = await DefaultCacheManager().getSingleFile(widget.imageUrl);
try {
WallpaperManagerFlutter().setwallpaperfromFile(file, location);
GlobalSnackBar.show(context, 'Wallpaper updated');
} catch (e) {
GlobalSnackBar.show(context, 'Error Setting Wallpaper');
print(e);
}
}
I have a container in my flutter project that loads the image selected by the flutter image_picker package. If the value is null, it load a placeholder image and text that says to upload an image.
GestureDetector(
onTap: () async {
setState(() {
_pickImage();
});
},
child: photo != null
? Image.file(
File(
photo!.path.toString(),
),
)
: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: const Color(0xff303434),
),
height: 200,
width: double.infinity,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/camera.png'),
Text(
'Upload Image',
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 15,
),
),
],
),
),
),
),
The function to pick the image is:
void _pickImage() async {
setState(() async {
final ImagePicker _picker = ImagePicker();
photo = await _picker.pickImage(
source: ImageSource.camera,
imageQuality: 90,
);
if (photo == null) return;
});
}
The issue is that when I select an image, it will only display in the container when I hot reload my project, and if I choose another image after, I need to hot restart to update the image in the container. How do I fix this issue and make the image in the container upload automatically when I select and image, rather than having to hot restart?
move everything out the setState and call set state after it, with nothing in it, not with async :D
You need to move all the code out of setState, and remove async.
I have a list of image paths and I am using the List.generate method to display images and a cross icon to remove image from list. Upload method is called on each image and when I remove the image from the list the method still keeps running until the image is uploaded. The behavior I am expecting is when I remove the image from the list the method should also stop running. I am using a future builder to display the circular progress bar and error icons while uploading an image.
What should I be doing to make sure the future method associated to the current widget also stops when I remove the widget from the list?
This is the code where I am creating a list
List.generate(
files.length,
(index) {
var image = files[index];
return Container(
height: itemSize,
width: itemSize,
child: Stack(
children: <Widget>[
Container(
getImagePreview(image, itemSize)
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
uploadHandler(image, field),
InkWell(
onTap: () => removeFileAtIndex(index, field),
child: Container(
margin: EdgeInsets.all(3),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(.7),
shape: BoxShape.circle,
),
alignment: Alignment.center,
height: 22,
width: 22,
child: Icon(Icons.close, size: 18, color: Colors.white),
),
),
],
),
],
),
);
},
)
This is Upload Handler method.
Widget uploadHandler(file, field) {
return FutureBuilder(
future: upload(file),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data.statusCode == 201) {
return doneUpload();
} else {
logger.d(snapshot.error);
return error();
}
} else {
return uploading();
}
},
);
}
The lifecycle of the widget isn't attached to the async functions invoked by the widget.
You can check the mounted variable to check if the widget still mounted from your async function.