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
ui.Image
loads the frames but it delays for about 10secs before showing all of them using a breakpoint to debug it.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