Flutter Image Widget won't update on change state - flutter

I am creating an Image Editor application using sliders, something like Instagram, and I am using library image/image.dart.
The problem is that once you move the slider it updates the image but just that time, if you move it again, it won't update.
I have set everything as expected, setState() functions as flutter asks, but I don't know why it won't update again.
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as br;
import 'package:path_provider/path_provider.dart';
import 'package:image_picker/image_picker.dart';
class ImageManager extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ImageManagerState();
}
}
class _ImageManagerState extends State<ImageManager> {
File imageFile;
br.Image image;
Image _imageWidget;
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
// get tmp file
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/tmp.jpg');
}
// pick image on button click
Future<void> _pickImage(ImageSource source) async{
File selectedFile = await ImagePicker.pickImage(source: source);
br.Image selectedImage;
if (selectedFile != null){
selectedImage = br.decodeImage(selectedFile.readAsBytesSync());
br.grayscale(selectedImage);
selectedFile.writeAsBytesSync(br.encodeJpg(selectedImage));
}
setState((){
image = selectedImage;
imageFile = selectedFile;
_imageWidget = Image.file(imageFile);
});
}
// MAIN PROBLEM, UPDATING THE CONTRAST WILL ONLY DO IT ONCE
Future<void> updateContrast(value) async{
File contrastFile = imageFile;
br.Image contrast = br.decodeImage(contrastFile.readAsBytesSync());
contrast = br.adjustColor(contrast, contrast: value);
contrastFile.writeAsBytesSync(br.encodeJpg(contrast));
// Save the thumbnail as a jpg.
File path = await _localFile;
path.writeAsBytesSync(br.encodeJpg(contrast));
setState(() {
image = contrast;
imageFile = contrastFile;
if(path != null){
_imageWidget = Image.file(path);
print(value);
}
});
}
//
Widget _buildImage(BuildContext context){
return Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width,
child: _imageWidget,
),
Column(
children: [
Container(
padding: const EdgeInsets.only(left: 8, right: 8),
child: Column(
children: <Widget>[
// contrast
Text("Contraste"),
Padding(
padding: const EdgeInsets.only(bottom: 4.0, top: 0.0),
child: Container(
child: Slider(
min: 0.0,
max: 1.0,
divisions: 100,
value: _contrast,
activeColor: Colors.blue[500],
inactiveColor: Colors.blue[50],
label: "${(_contrast *100).round()}",
onChanged: (value) async{
changeContrast(value);
},
onChangeEnd: (value) async{
updateContrast(value);
},
),
),
),
],
),
),
],
),
]
);
}
I expect the image to update every time the slider is changed.

imageCache.clear() will do the job.
I was also not able to reload the image to save locally on the screen. From debugging, I observed the old image is in fact deleted, and new image is copied there, but nothing was changing on the screen. Following is the code you need.
So, in the body of Scaffold, I have made a FutureBuilder that calls another function reload(), which then loads the file and return the image.
FutureBuilder(
future: reload(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.connectionState == ConnectionState.done){
return snapshot.data;
}
else{
return CircularProgressIndicator();
}
},
),
Here is the reload() function:
reload() async {
String path = "path of your image";
File profileImage = File("$path/name.jpg");
if(profileImage.existsSync() == false){
return Text("File Not Found");
}
else{
imageCache.clear();
return Image.file(profileImage);
}
}
I checked Flutter's github, and jamesncl has already suggested this.

I solved the issue, which is pretty weird to me, but if someone can explain it, I would appreciate it.
Future<void> adjustImage() async{
File toAdjustFile = imageFile;
br.Image toAdjust = br.decodeImage(toAdjustFile.readAsBytesSync());
toAdjust = br.adjustColor(toAdjust, contrast: _contrast, brightness: _brightness, exposure: _exposure);
setState(() {
_imageWidget = Image.memory(br.encodeJpg(toAdjust));
});
}
I refactored my function and set the widget to another constructor, Image.memory().

Use Image.memory instaead Image.file, A line from my code
Image.memory(
File(widget.imagePath).readAsBytesSync(),),

Related

image_picker flutter error, how can I save image in application permanently?

so i am having trouble saving the users pfp when they navigate to a new section on my application, when the user clicks of from the current tab and comes back the pfp disappears, it kills the path it seems. i want to be able to upload a image as the pfp, and when i navigate to a new section within the application for it to still be there.
code below:
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'save_pfp.dart';
File? image;
class BuildProfile extends StatefulWidget {
const BuildProfile({super.key});
#override
State<BuildProfile> createState() => _BuildProfileState();
}
class _BuildProfileState extends State<BuildProfile> {
Future getImage() async{
try{
image = (await ImagePicker().pickImage(source: ImageSource.gallery)) as File? ;
if (image == null) return;
final imagePermanent = await saveImagePermanently(image!.path);
setState(() => image = imagePermanent);
} on PlatformException catch (e) {
print('failed to pick image: $e');
}
}
Future<File> saveImagePermanently(String imagePath) async{
final directory = await getApplicationDocumentsDirectory();
final fileName = basename(imagePath);
image = File('${directory.path}/$fileName');
return File(imagePath).copy(image!.path);
}
#override
Widget build(BuildContext context) {
return CircleAvatar(
backgroundColor: Colors.grey.shade400,
backgroundImage: image == null ? null
: FileImage(image!),
radius: 56,
child: Align(
alignment:const Alignment(3.2, 0.73),
child: RawMaterialButton(
onPressed: () {
showModalBottomSheet(context: context,
backgroundColor: Colors.black38,
builder: (context) => Container(
height: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('camera',
style: TextStyle(color: Colors.white)),
IconButton(
onPressed: () {
// _getFromCamera;
getImage();
},
icon: const Icon(CupertinoIcons.camera,
size: 26,
color: Colors.white,
)),
IconButton(
// pick from gallery
onPressed:() {
getImage();
I have tried different solutions, but run into the same error.
In my case, I'm using Provider strategy to save the picked photos and consume them whenever I want in whatever code I am, this is how I proceeded:
First, get the photos from the image picker:
void _setImageFromPhone(ImageSource imageSource) async {
final pickedFile = await ImagePicker().pickImage(source: imageSource, imageQuality: 75);
if (pickedFile == null) {
return;
}
log(pickedFile.path);
widget.formModel.addNewPhotos(pickedFile.path);
}
The provider where I save the picked photos:
class FormModel extends ChangeNotifier {
final List<String> _photos = [];
void addNewPhotos(String photo) {
_photos.add(photo);
notifyListeners();
}
//getter for photos
UnmodifiableListView<String> get photos => UnmodifiableListView(_photos);
}
After that you can consume the photos you add wherever you want :
#override
Widget build(BuildContext context) {
return Consumer<FormModel>(
key: formModelConsumerKey,
builder: (
context,
formModel,
child,
) {
return
Row(
children:[
]
)
Container(
height: MediaQuery.of(context).size.width * 0.35,
width: MediaQuery.of(context).size.width * 0.35,
decoration: BoxDecoration(
color: LIGHT_GREEN_COLOR,
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
image: DecorationImage(
image: Image.file(File(formModel.photos.first!)).image,
fit: BoxFit.fill,
),
),
child: SizedBox();
);
}
);
}
Also do not forget to provide your Provider class in the top parent widgets, small example bellow :
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => FormModel()),
],
child: MaterialApp()
)

error when using File with ImagePicker Flutter

The error is with this line: File selectedImage
I only have dart.io imported, not even dart.html so I'm not sure why I'm getting this error.
here is the longer piece of code
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:random_string/random_string.dart';
import 'package:tennis_event_app/services/crud.dart';
class CreateBlog extends StatefulWidget {
#override
_CreateBlogState createState() => _CreateBlogState();
}
class _CreateBlogState extends State<CreateBlog> {
late String pass, authorName, title, desc;
File selectedImage;
final picker = ImagePicker();
bool _isLoading = false;
CrudMethods crudMethods = new CrudMethods();
Future getImage() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
selectedImage = File(pickedFile.path);
} else {
print('No image selected.');
}
});
}
(this is not the entire code but just a larger piece)
What exact error are you getting?
Suggestion
Please do well to put setState((){...}); in the if-statement like this:
if(pickedFile != null){
setState((){
selectedImage = File(pickedFile.path);
});
}else{
print('No image selected');
}
and Hot Reload.
SEE COMPLETE SOLUTION THAT WORKED FOR ME
class _ImageSelectorState extends State<ImageSelector> {
var imageFile;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: 200.0,
height: 200.0,
color: Colors.grey.shade300,
child: imageFile != null
? Image.file(imageFile)
: Icon(
Icons.image,
),
),
SizedBox(height: 20.0),
//Button
Center(
child: ElevatedButton(
onPressed: _pickImage,
child: Text('Upload Image'),
),
),
],
),
);
}
_pickImage() async {
try {
final picker = await ImagePicker().getImage(
source: ImageSource.gallery,
);
if (picker != null) {
setState(() {
imageFile = File(picker.path);
});
}
} catch (e) {
print(e);
}
}
}
Result
Solution I Used:
I ended up changing File selectedImage; to File ? selectedImage;

Is their any way to convert .jpeg images to .gif in flutter?

Like my title, is there anyway to convert images to a video in flutter? (It doesn't have to be .gif necessarily)
To be more specific, I am using Google Firebase as my storage cloud server to upload pictures taken by camera plugin of flutter.
I want to convert my images to a video so that it looks like a time-lapse video.
Any advice would be nice.
===============================
the pictures are taken by camera plugin which uses camera.dart, and it is stored it firebase storage like this:
onCapture(context) async {
try {
final p = await getTemporaryDirectory();
var now = DateTime.now();
var formattedDate = DateFormat('yy-MM-dd HH:mm:ss').format(now);
final path = '${p.path}/$now.png';
await cameraController.takePicture(path).then((value) {
print('here');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PreviewScreen(
imgPath: path,
fileName: '$formattedDate.png',
pickedTime: '$formattedDate',
)));
});
} catch (e) {
showCameraException(e);
}
}
========================================
edit :
Before constructing gif, i am having trouble downloading an image from firebase and putting this image on the canvas. I think I can use your code to make a gif once I do this.
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as IMG;
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
import 'package:firebase_core/firebase_core.dart' as firebase_core;
import 'package:provider/provider.dart';
import 'package:weighit/models/user_info.dart';
class ToGif extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Timelapse Video'),
),
body: Container(
child: Thumbnail(),
),
);
}
}
class Thumbnail extends StatefulWidget {
const Thumbnail({Key key}) : super(key: key);
#override
_ThumbnailState createState() => _ThumbnailState();
}
class _ThumbnailState extends State<Thumbnail> {
List<int> imgBytes;
Isolate isolate;
File image;
#override
void initState() {
_asyncInit();
super.initState();
}
static _isolateEntry(dynamic d) async {
final ReceivePort receivePort = ReceivePort();
d.send(receivePort.sendPort);
final config = await receivePort.first;
print(config);
final file = File(config['path']);
final bytes = await file.readAsBytes();
IMG.Image image = IMG.decodeImage(bytes);
IMG.Image thumbnail = IMG.copyResize(
image,
width: config['size'].width.toInt(),
);
d.send(IMG.encodeNamedImage(thumbnail, basename(config['path'])));
}
_asyncInit() async {
final receivePort = ReceivePort();
isolate = await Isolate.spawn(_isolateEntry, receivePort.sendPort);
receivePort.listen((dynamic data) {
if (data is SendPort) {
if (mounted) {
data.send({
'path': image.path,
'size': Size(500, 500),
});
}
} else {
if (mounted) {
setState(() {
imgBytes = data;
});
}
}
});
}
#override
void dispose() {
if (isolate != null) {
isolate.kill();
}
super.dispose();
}
// Download on DocumnetDirectory, not temporary directory https://flutter-ko.dev/docs/cookbook/persistence/reading-writing-files
Future<void> downloadFileExample() async {
final appDocDir = await getApplicationDocumentsDirectory();
image = File('${appDocDir.path}/download-logo.png');
try {
await firebase_storage.FirebaseStorage.instance
// can not find proper reference path...
.ref('gs://weighit-f506b.appspot.com/guny/21-04-26 10:56:21.png')
.writeToFile(image);
} on firebase_core.FirebaseException catch (e) {
print('couldnt find the reference');
}
}
#override
Widget build(BuildContext context) {
final _user = Provider.of<TheUser>(context);
return FutureBuilder(
future: downloadFileExample(),
builder: (context, snapshot) {
//해당 부분은 data를 아직 받아 오지 못했을때 실행되는 부분을 의미한다.
if (snapshot.hasData == false) {
return Center(child: CircularProgressIndicator());
}
//error가 발생하게 될 경우 반환하게 되는 부분
else if (snapshot.hasError) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Error: ${snapshot.error}',
style: TextStyle(fontSize: 15),
),
);
} else {
return SizedBox(
height: 500,
width: 500,
child: imgBytes != null
? Image.memory(
imgBytes,
fit: BoxFit.cover,
)
: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.grey[100], Colors.grey[300]],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
),
);
}
},
);
}
}
You can use the image package to created animated GIFs from multiple images.
First add it as a dependency:
dependencies:
image: ^3.0.2
Then to you can make a function to generate an animated GIF from multiple images:
List<int>? generateGIF(Iterable<Image> images) {
final Animation animation = Animation();
for(Image image in images) {
animation.addFrame(image);
}
return encodeGifAnimation(animation);
}
To use this function with multiple images that are in files from the camera package in the form of XFiles, you have to decode the images for each of those files and pass it to this function. The following code assumes you know that these are JPEG images:
List<XFile> imageFiles = ...;
final JpegDecoder decoder = JpegDecoder();
final List<Image> images = [];
for(var imgFile in imageFiles) {
Uint8List data = await imgFile.readAsBytes();
images.add(decoder.decodeImage(data));
}
List<int>? gifData = generateGIF(images);

Why flutter app doesn't load previously written data when relaunch of the app

I have wrote image file to flutter application document directory using image picker but it says path is null when relaunch(Go to home screen and relaunch the app again) the flutter app.I can't find the exact problem causing this.
Path is load when start up of the application.
#override
void initState() {
super.initState();
getApplicationDocumentsDirectory().then((directory) {
_localPath = directory.path;
});
}
Image selection.
_decideImageView() {
if(FileSystemEntity.typeSync('$_localPath/name.jpg') == FileSystemEntityType.notFound){
return AssetImage("assets/example.jpeg");
}else {
return FileImage(File('$_localPath/name.jpg'));
}
Image picking
_openGallery(BuildContext context) async{
var picture = await ImagePicker.pickImage(source: ImageSource.gallery);
this.setState((){
imageFile = picture;
});
Directory pathd = await getApplicationDocumentsDirectory();
String path =pathd.path;
final File newImage = await imageFile.copy('$_localpath/name.jpg');
Navigator.of(context).pop();
}
getApplicationDocumentsDirectory().then((directory) {
_localPath = directory.path;
});
Does not guarantee that the _localPath will load at the time _decideImageView() is called.
Add print to check this:
_decideImageView() {
print(_localPath);
if(FileSystemEntity.typeSync('$_localPath/name.jpg') == FileSystemEntityType.notFound){
return AssetImage("assets/example.jpeg");
}else {
return FileImage(File('$_localPath/name.jpg'));
}
This work for me after reload/restart app:
_decideImageView(BuildContext context) async {
Directory pathd = await getApplicationDocumentsDirectory();
return Navigator.of(context)
.push(MaterialPageRoute<void>(builder: (BuildContext context) {
return Scaffold(
body: Container(
color: Colors.transparent,
alignment: Alignment.center,
child: InkWell(
child: PhotoView(
imageProvider: FileImage(File('${pathd.path}/name.jpg')),
backgroundDecoration: BoxDecoration(color: Colors.transparent),
minScale: PhotoViewComputedScale.contained * 0.9,
maxScale: 4.0,
),
onTap: () => Navigator.of(context).pop(),
),
),
);
}));
}

Display Image from preferences in Flutter App

I am having a problem. I have a Drawer in Flutter App and I want to implement a feature where you can pick a photo from gallery. Thats the easy part. But i want to save this photo in the preferences and load it when the App starts again. The imageFromPreferences variable have to be an Future to use it in the preferenceImage() Future builder. I got not idea how to it after hours of research. Maybe its the total wrong approach and you got a different idea.
import 'dart:io';
import 'dart:ui';
import 'package:firstapp/database/database.dart';
import 'package:firstapp/views/note.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:image_picker/image_picker.dart';
import 'package:firstapp/Utility/Utility.dart';
class NoteList extends StatefulWidget {
#override
NoteListState createState() {
return new NoteListState();
}
}
class NoteListState extends State<NoteList> {
Future<File> imageFile;
Image imageFromPreferences;
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
loadImageFromPreferences() {
Utility.getImageFromPreferences().then((img) {
setState(() {
imageFromPreferences = Utility.imageFromBase64String(img);
});
});
}
Widget preferenceImage() {
return FutureBuilder<Image>(
future: loadImageFromPreferences(),
builder: (BuildContext context, AsyncSnapshot<Image> image) {
print(image);
if (image.connectionState == ConnectionState.done && image.hasData) {
return image.data;
} else {
return Text("error");
}
},
);
}
Widget imageFromGallery() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
Utility.saveImageToPreferences(
Utility.base64String(snapshot.data.readAsBytesSync()));
return Image.file(
snapshot.data,
);
}
return null;
},
);
}
finalPicker() {
if (imageFromGallery() == null) {
return preferenceImage();
} else if (imageFromGallery() != null) {
return imageFromGallery();
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Taking Notes')),
drawer: Drawer(
child: ListView(
children: <Widget>[
UserAccountsDrawerHeader(
accountEmail: Text('lala#web.de'),
accountName: Text('Max'),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
),
currentAccountPicture: GestureDetector(
onTap: () {
pickImageFromGallery(ImageSource.gallery);
},
child: Column(
children: <Widget>[finalPicker()],
),
),
),
Container(
padding: EdgeInsets.all(20.0),
child: Text("Locked files"),
color: Theme.of(context).primaryColor,
),
],
),
),
This is how I save the Image as a String in the preferences. Maybe I could instead save it in a file?
import 'dart:async';
import 'dart:convert';
class Utility{
static const String IMG_KEY = "IMAGE_KEY";
static Future<bool> saveImageToPreferences(String value) async {
final SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.setString(IMG_KEY, value);
}
static Future<String> getImageFromPreferences() async{
final SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getString(IMG_KEY);
}
static String base64String(Uint8List data) {
return base64Encode(data);
}
static Image imageFromBase64String(String base64String){
return Image.memory(base64Decode(base64String), fit: BoxFit.fill);
}
}
// using your method of getting an image
final File image = await ImagePicker.pickImage(source: imageSource);
// getting a directory path for saving
final String path = await getApplicationDocumentsDirectory().path;
// copy the file to a new path
final File newImage = await image.copy('$path/image1.png');
setState(() {
_image = newImage;
});
you can store this path in your shared preference.
here you make copy in root directory and then store path of that image.
also you can used this plugin
https://pub.dev/packages/image_picker_saver
[image_picker_saver][1]