How to upload pic from assets? - flutter

I am using below code to upload the pic from gallery in flutter, if in case the pic is not picked up from the gallery I want the pic from the assets to be uploaded to the firebase storage, for that avatarImageFile should be equivalent to the image file from the assets.
How Can I achieve that?
Future getImage() async {
print("get image");
PickedFile image = await _picker.getImage(source: ImageSource.gallery);
if (image != null) {
setState(() {
final File file = File(image.path);
avatarImageFile = file;
isLoading = true;
});
}
else{
//if image is null then the image from the assets should be made picked into `avatarImageFile `
}
}

In Flutter you can load your assets in two ways:
Using rootBundle.loadString("assets/my_file.json") to load text files
Using rootBundle.load("assets/something.png") to load any kind of file (images, pdf or any other kind of binary).
Note that load also works with .json files but in general loadString is a better choice when it comes to retrieving text. For more info, read the doc.
avatarImageFile = await rootBundle.load("assets/a/b/c.png");
Do not use rootBundle when you're inside widgets: instead, prefer using DefaultAssetBundle:
class MyWidget extends StatelessWidget {
const MyWidget();
Future<String> loadConfig(BuildContext context) async =>
await DefaultAssetBundle
.of(context)
.loadString("myassets/some_cfg.json");
#override
Widget build(BuildContext context) {...}
}
Again, do the above when you're inside widgets. In any other case, go for rootBundle.

Related

Flutter widget unmounting without dispose

So a piece of code I wrote needs to run on both web and mobile. And since some libraries like dart: io on the web and can't use dart:html on mobile. So in order to pick images on the web, I tried to use ImagePicker which returns an X-File, turns the X-File to bytes, and uses Image. memory().
This is the piece of code that I've been trying:
class _CanvasImageItemState extends State<CanvasImageItem> {
Uint8List? imageBytes;
XFile? file;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
// widgets
}
Widget buildPicker(BuildContext context) {
if (kDebugMode) {
print("picker loaded");
}
return InkWell(
onTap: () async {
FocusedItemModel.of(context).focus(widget.item as LocalImageItem);
if (!kIsWeb) {
// Some piece of code for mobile
} else {
await _pickImage();
}
},
child: ..some other widgets
);
}
}
_pickImage() async {
final ImagePicker _picker = ImagePicker();
file = await _picker.pickImage(source: ImageSource.gallery);
Uint8List? bytes = await file!.readAsBytes();
if (mounted) {
setState(() {});
imageBytes = bytes;
} else {
imageBytes = bytes;
}
if (kDebugMode) {
print("Checking File: ${file!.path}");
print("Checking imageBytes: ${imageBytes!.length}");
}
}
The problem is although I did not dispose() the widget or used dispose() anywhere in the project itself but I am getting
setState() called after dispose() error. So I tried checking if(mounted) before trying to update the value of imageBytes but in that case, it's taking two tries to pick the image.
As you can see I even tried to print the path of xFile and imageBytes which is getting printed properly. But I cannot set the state.
N.B: The CanvasImageItem is child of another widget called CanvasMovableItem. Any suggestion where the problem is occurring from? I've checked all possible codes that might unmount or dispose of the widget.
Am I missing something very simple?

Cannot find cached network image in flutter test

I have a widget AvatarImage which renders a CircleAvatar if an image is provided or a default image DefaultAvatar otherwise.
Widget implementation looks like:
class AvatarImage extends StatelessWidget {
final ImageProvider? image;
const AvatarImage({this.image}): super();
Widget build(BuildContext context) {#
if (image == null) return DefaultAvatar();
return CicrcleAvatar(
image: image,
)
}
}
Now I wanted to test that image is actually rendered using the following test case:
testWidgets("Renders image avatar when image is provided", (WidgetTester tester) async {
mockNetworkImagesFor(() async {
final testNetworkImage = CachedNetworkImageProvider("https://placebear.com/100/100");
await tester.pumpWidget(TestApp(
child: AvatarImage(image: testNetworkImage)
));
await tester.pumpAndSettle();
final image = find.image(testNetworkImage);
debugDumpApp();
expect(image, findsOneWidget);
expect((image as Image).image, equals(testNetworkImage));
});
});
But it is unable to find the image. I have tried using find.byType(CachedNetworkImage) but this also has no effect the test is always failing because the image cannot be found.
I have verified that the widget is present in tree using debugDumpApp.
Why doesnt find find this image?
How can I find it without using keys?

Flutter: Can we save a Canvas/CustomPainter to a gif or continuous pictures or event a video?

The bounty expires in 4 days. Answers to this question are eligible for a +50 reputation bounty.
Danny is looking for a canonical answer:
Is it possible to save a flutter canvas to a gif or a video? Is it possible to directly convert the canvas to a video with ffmpeg?
Thanks
Can we export custom painter used animation controller to a gif image or continuous images or event a video such as mp4 file?
Yes I did it one time (2 years ago) and I converted a Flutter Animation to a mp4 file. unfortunately I couldn't find the code. please follow the steps to make what you want.
capture your widget with RenderRepaintBoundary
https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImage.html
class PngHome extends StatefulWidget {
const PngHome({super.key});
#override
State<PngHome> createState() => _PngHomeState();
}
class _PngHomeState extends State<PngHome> {
GlobalKey globalKey = GlobalKey();
Future<void> _capturePng() async {
final RenderRepaintBoundary boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
final ui.Image image = await boundary.toImage();
final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
final Uint8List pngBytes = byteData!.buffer.asUint8List();
print(pngBytes);
}
#override
Widget build(BuildContext context) {
return RepaintBoundary(
key: globalKey,
child: Center(
child: TextButton(
onPressed: _capturePng,
child: const Text('Hello World', textDirection: TextDirection.ltr),
),
),
);
}
}
you need to capture each frame of your Animation and save it to a directory. with special naming for example (1.png,2.png .... 1000.png)
import 'package:path_provider/path_provider.dart';
import 'dart:io';
Uint8List imageInUnit8List = // store unit8List image here ;
final tempDir = await getTemporaryDirectory();
File file = await File('${tempDir.path}/image.png').create();
file.writeAsBytesSync(imageInUnit8List);
install ffmpeg https://pub.dev/packages/ffmpeg_kit_flutter and use it to execute FFMPEG command
import 'package:ffmpeg_kit_flutter/ffmpeg_kit.dart';
FFmpegKit.execute('your command').then((session) async {
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
// SUCCESS
} else if (ReturnCode.isCancel(returnCode)) {
// CANCEL
} else {
// ERROR
}
});
search for a command to convert your images with ffmpeg to Gif Or Mp4 (some thing like these Example1 or Example2)
you can use screenshot library. by wrapping the parent container with Screenshot library you can convert widget to multiple images and those images can be converted to a gif but I think it is tricky, not efficient, and difficult to implement. you can give it a try.

How to display a Future<File> image?

My app load image from samba (instead of url) and then save thumbnail in local, so I can not use Image.network();, previouly I loaded image Uint8List from samba and then use Image.memory(bytes); to display image, it work, but I also have to combine it with Local Cache, so I save image file with flutter_cache_manager package DefaultCacheManager()(using (await DefaultCacheManager().getFileFromCache()).file), but the problem is, the DefaultCacheManager() only can return Future<File> cache instead of File, so I cannot use Image.file(cacheFile) to display image.
I have to try the FutureBuilder to solve the problem, but it cause flash in the image displaying.
I have considered to use FadeInImage and make my own ImageProvider to feed my need, but it is difficult for me to write ImageProvider.
In conclusion, I want to make something like:
Image.futureFile(Future<File>)
to use the Future cache return by DefaultCacheManager() to display a local image.If it cannot be solve(for example, a local cache file api should not return Future), I will conside to use another cache library.
Finally I end up with a custom ImageProvider, it refers to FileImage, I don't master at this so it may have problem (for example I don't test it with gif). After all, now image flashing is better than the FutureBuilder solution.
class CacheImageProvider extends ImageProvider<CacheImageProvider> {
final String fileId;//the cache id use to get cache
CacheImageProvider(this.fileId);
#override
ImageStreamCompleter load(CacheImageProvider key, DecoderCallback decode) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(decode),
scale: 1.0,
debugLabel: fileId,
informationCollector: () sync* {
yield ErrorDescription('Path: $fileId');
},
);
}
Future<Codec> _loadAsync(DecoderCallback decode) async {
// the DefaultCacheManager() encapsulation, it get cache from local storage.
final Uint8List bytes = await (await CacheThumbnail.getThumbnail(fileId)).readAsBytes();
if (bytes.lengthInBytes == 0) {
// The file may become available later.
PaintingBinding.instance?.imageCache?.evict(this);
throw StateError('$fileId is empty and cannot be loaded as an image.');
}
return await decode(bytes);
}
#override
Future<CacheImageProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<CacheImageProvider>(this);
}
//the custom == and hashCode is need, because the ImageProvider use to get the memory cache.
#override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
bool res = other is CacheImageProvider && other.fileId == fileId;
return res;
}
#override
int get hashCode => fileId.hashCode;
#override
String toString() => '${objectRuntimeType(this, 'CacheImageProvider')}("$fileId")';
}
and use it:
Image(image: CacheImageProvider(_data[index].fileId))
// This should be in a callback or outside the build() function:
File _file = await DefaultCacheManager().getSingleFile(file);
// In the widget tree:
Image.file(_file);

ImagePicker, how to set image again?

Minimal Code:
File _file;
Future<void> _pickImage() async {
final image = await ImagePicker.pickImage(source: ImageSource.camera);
if (image != null) {
final file = File("${(await getApplicationDocumentsDirectory()).path}/image.png");
await file.writeAsBytes(await image.readAsBytes());
setState(() => _file = file); // `_file = image` works though
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(child: Icon(Icons.camera_alt), onPressed: _pickImage),
body: _file == null ? Container() : Image.file(_file),
);
}
Watch video
As you can see, once I pick the image, it works, but on picking it second time, it doesn't work and I also don't run into any error. Can anyone please help?
you need 3 things:
first you have to use ImageProvider and its evict() method:
var image = FileImage(File('someImage.jpg'));
then you need Image widget that uses above ImageProvider and also assigns a unique key in order to be "different" each time build() method is called:
child: Image(
image: image,
key: UniqueKey(),
),
and finally after you overwrite someImage.jpg you have to call evict() method:
// part of your _pickImage() method
// here someImage.jpg contains updated content
image.evict();
setState(() {});
UPDATE: actually you dont need var image = FileImage(File('someImage.jpg')); - you can use it directly inside Image widget as image: FileImage(File('someImage.jpg')) and call FileImage(File('someImage.jpg')).evict() after your image is ovewritten