In Flutter, how to extract frame by frame from an animated gif? - flutter

I'm trying to extract frames from an animated gif using Flutter. By researching the Flutter API, I thought the following code should work but it only gives me the first frame although it gives me the correct frames count.
static Future<List<ui.Image>> loadAnimatedGif(String assetPath) async {
List<ui.Image> gifImage = [];
ByteData data = await rootBundle.load(assetPath);
var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
int frmCnt = codec.frameCount;
print("frmcnt is $frmCnt"); //it gives correct frames count
for (int i = 0; i < frmCnt; i++) {
var frame = await codec.getNextFrame();
gifImage.add(frame.Image);
}
return gifImage;
}

I made a simple app out of this and it works using the same code but added a converter in it.
What I notice at first because nothing
Using the ui.Image loads the frames but it delays for about 10secs before showing all of them using a breakpoint to debug it.
Converting ui.Image to Uint8List takes time as much as the frames but the format shows all frames as soon the extraction is done.
Here is the dart pad to quickly run and test it.
Here is the code if you will like to scan through it, I am loading the gif from Network.
class GifFramesDisplay extends StatefulWidget {
const GifFramesDisplay({super.key});
#override
State<GifFramesDisplay> createState() => _GifFramesDisplayState();
}
class _GifFramesDisplayState extends State<GifFramesDisplay> {
List<Uint8List> _frames = [];
#override
void initState() {
super.initState();
loadGif();
}
Future<void> loadGif() async {
// Load the gif image from the network url and store the bytes in a ByteData object
final url = Uri.parse(
'https://raw.githubusercontent.com/Aman-Malhotra/animate_icons/main/demo/animate_icons.gif');
final ByteData data = await NetworkAssetBundle(url).load(url.path);
// Using the _extractGifFrames function to extract the frames
_extractGifFrames(data).then((List<Uint8List> frames) {
// Set the state to update the UI
setState(() {
_frames = frames;
});
});
}
// Function to extract gif image frames
Future<List<Uint8List>> _extractGifFrames(ByteData data) async {
// Create a list to store the frames
final List<Uint8List> frames = <Uint8List>[];
// Create a codec to decode the gif
final ui.Codec codec =
await ui.instantiateImageCodec(data.buffer.asUint8List());
// Count the number of frames in the gif
final int frameCount = codec.frameCount;
print('Total frameCount: $frameCount');
// Loop through the frames and add them to the list
for (int i = 0; i < frameCount; i++) {
// Get the next frame
final ui.FrameInfo fi = await codec.getNextFrame();
// Add the frame to the list
final frame = await loadImage(fi.image);
// Add the frame to the list if it is not null
if (frame != null) {
frames.add(frame);
}
}
return frames;
}
Future<Uint8List?> loadImage(ui.Image image) async {
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
return byteData?.buffer.asUint8List();
}
#override
Widget build(BuildContext context) {
return Center(
child: _frames.isEmpty
? const CircularProgressIndicator()
: ListView.builder(
itemCount: _frames.length,
itemBuilder: (BuildContext context, int index) {
return Image.memory(_frames[index]);
},
),
);
}
}
Click to watch a preview of the loaded gif frames of the code above

Related

how to handle the time lag after capturing the image ImagePicker Flutter

im learning flutter and now tried to capture the photo with ImagePicker package from the following method, but after I successfully capture the photo, there is always 1 sec until app get the data and jump to next page:
Future pickImage(ImageSource source) async {
try {
var image = await ImagePicker().pickImage(source: source);
if (image == null) return;
final imagePermanent = await saveImagePermanently(image.path);
selectedImage = File(imagePermanent.path);
isUploaded.value = !isUploaded.value;
update();
} on PlatformException catch (e) {
print('Failed to pick image: $e');
}
}
Future<File> saveImagePermanently(String imagePath) async {
final directory = await getApplicationDocumentsDirectory();
final name = basename(imagePath);
final image = File('${directory.path}/$name');
return File(imagePath).copy(image.path);
}
now my solution is adding a listener onInit when image is created with GetX:
#override
void onInit() {
super.onInit();
ever(isUploaded, (value) {
Get.to(
() => AddPersonProfileAddDetails(),
);
});
}
So is there a way to detect the status of capturing the image like finished/failed/progressing, thanks for any clue or let me know a better way to jump to next page after capturing the image, thanks a lot!

Image Picker does not work properly with google_mlkit_object_detector only on IOS Simlutor

Once i added the google_mlkit_object_detector then the image_picker stopped working. i can access the gallery but there's no image will return when added this package.
void _incrementCounter() async {
print('start');
image = await ImagePicker().pickImage(source: ImageSource.gallery); // IT WILL STOP HERE AND CAN"T PROCESS THE EXECUTION
print('succeed');
setState(() {});
}
void processing() async {
final inputImage = InputImage.fromFile(File(image!.path));
final objectDetector = ObjectDetector(
options: ObjectDetectorOptions(mode: DetectionMode.singleImage));
final List<DetectedObject> objects =
await objectDetector.processImage(inputImage);
for (DetectedObject detectedObject in objects) {
final rect = detectedObject.boundingBox;
final trackingId = detectedObject.trackingId;
for (Label label in detectedObject.labels) {
print('${label.text} ${label.confidence}');
}
}
}

Can't able to save and display camera images in gridView in flutter using image_picker

I am creating a widget in which I want to display a gridview of image clicked through mobile camera at runtime. I had used image_picker package from flutter. But it is only working for picking image from gallery and everytime I click image it crashes.
class _ImageInputState extends State<ImageInput>
with AutomaticKeepAliveClientMixin {
// list of images
final List<File> _imageFileList = [];
final ImagePicker _picker = ImagePicker();
Function for picking image from camera using image_picker
final imageFile = await _picker.pickImage(source: imageSource);
if (imageFile == null) return;
File tmpFile = File(imageFile.path);
final appDir = await getApplicationDocumentsDirectory();
final fileName = basename(imageFile.path);
final localFile = await tmpFile.copy('${appDir.path}/$fileName');
// setState(() {
// _imageFileList.add(localFile);
//
// });
_pickImageGall(ImageSource.gallery);
}
function for picking images from the gallery of phone
Future<void> _pickImageGall(ImageSource imageSource) async {
final _pickedImage = await _picker.pickImage(source: imageSource);
if (_pickedImage != null) {
setState(() {
_imageFileList.add(File(_pickedImage.path));
});
}
}
and in the ui there is a gridview to display the images in which the last index is a input widget where there is two IconButton one for camera input and other for gallery input. As I am making this thing for the first time I am confused how to procedure will be . I had try to implement every solution from the stackoverflow but none worked for me. Please give the solution.

how to set limit of selected image items in list used image picker package? (Flutter)

I want to set limit list for selected multiple images and keep listing that all have 4 images to used image picker in flutter
List<XFile> imageFileList = [];
void selectImage() async {
final List<XFile>? selectedImages = await _picker.pickMultiImage();
if (selectedImages!.isNotEmpty ) {
imageFileList.addAll(selectedImages);
}
setState(() {});
}
imagePicker does not allow selecting a specific count.you can do a heck like this
List<XFile> imageFileList = [];
void selectImage() async {
final List<XFile>? selectedImages = await _picker.pickMultiImage();
if(imageFileList.length>4){
showASnackBarOrDialogThatUserNotAlowwedMoreThan4();
return;
}
if (selectedImages!.isNotEmpty ) {
imageFileList.addAll(selectedImages);
}
setState(() {});
}

Extreme Lag After Loading Palette from Image

I am currently making a wallpaper app in Flutter. I am using the palette_generator package to extract colors from the image. The problem is while the app is extracting colors from the image. The UI freezes for a solid 3 seconds, after which the colors are shown on the screen. After the colors have been rendered, the whole screen suffers from jank and lag. All animations on that screen perform very badly. Popping off that screen removes the jank and lag. Here is the code I am using:
Future<PaletteGenerator?> updatePalette() async {
var cache = DefaultCacheManager();
File file = await cache.getSingleFile(widget.wall.url);
ui.Image image = await load(file);
palette = await PaletteGenerator.fromImage(
image,
maximumColorCount: 7,
);
return palette;
}
Future<ui.Image> load(File file) async {
var data = await file.readAsBytes();
ui.Codec codec = await ui.instantiateImageCodec(data);
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
FutureBuilder<PaletteGenerator?>(
future: updatePalette(),
builder: (context, snapshot) {
if (snapshot.hasData)
return MyBottomSheet(
wall: widget.wall, palette: snapshot.data);
return LinearProgressIndicator();
}),
Is there anything I can do to fix this issue?
I recommend using the flutter_isolate package to implement Isolates. The function you are using is a heavy function. Using an Isolated function avoids putting too much pressure on the main thread.
Example code:
void updatePalette() async {
var cache = DefaultCacheManager();
File file = await cache.getSingleFile(url);
var port = ReceivePort();
var isolate = await FlutterIsolate.spawn(
computePalette,
[file.readAsBytesSync(), port.sendPort],
);
port.listen((msg) {
List<int> data = msg;
data.forEach((d) => colorList.add(Color(d)));
isolate.kill();
port.close();
});
}
void computePalette(List<Object> args) async {
var image = args[0] as Uint8List;
var port = args[1] as SendPort;
var img = Image.memory(image);
var palette = await PaletteGenerator.fromImageProvider(
img.image,
maximumColorCount: 7,
);
List<int> colors = [];
palette.colors.forEach((element) => colors.add(element.value));
port.send(colors);
}