简体   繁体   中英

Decode image and add to list of widgets [flutter]

I want to load and display multiple images from a folder in Windows, grouping them based on their sizes (for example, large, medium, icons, and so on ...).

In order to know their size before displaying them, I'm looping a list using decodeImage function from 'package:image/image.dart' for each image.

What I want to achieve is that images appear on the screen after each iteration, without blocking the UI until all the load process is done.

I have a list of image models and I'm calling setState once I add each image to the list, in order to update the view, but all the images appear at the end of the loop.

How can I fix that ?

This is the output of the debug console (it takes about 10 seconds for all the images to appear):

C:\flutter\flutter-desktop-embedding\example>flutter run
Launching lib\main.dart on Windows in debug mode...
Building Windows application...
flutter: 2020-01-12 16:00:12.604871
flutter: 2020-01-12 16:00:13.160340
flutter: 2020-01-12 16:00:13.656014
Syncing files to device Windows...
flutter: 2020-01-12 16:00:14.050997
flutter: 2020-01-12 16:00:14.561593
flutter: 2020-01-12 16:00:15.040311
flutter: 2020-01-12 16:00:15.502076
flutter: 2020-01-12 16:00:15.936912
flutter: 2020-01-12 16:00:16.404174
flutter: 2020-01-12 16:00:16.919285
flutter: 2020-01-12 16:00:17.481826
flutter: 2020-01-12 16:00:17.941551
flutter: 2020-01-12 16:00:18.400322
flutter: 2020-01-12 16:00:18.864382
flutter: 2020-01-12 16:00:19.297922
flutter: 2020-01-12 16:00:19.745731
flutter: 2020-01-12 16:00:20.218457
flutter: 2020-01-12 16:00:20.654293
flutter: 2020-01-12 16:00:21.120047
flutter: 2020-01-12 16:00:21.629715
Syncing files to device Windows...                              13.892ms (!)

And this is the code (here i used a single 1920x1080 jpg image):

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as dart_image;

void main() {
  // See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override
  debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  runApp(new MyApp());
}

class ImageModel {
  final File file;
  final double height;
  final double width;

  ImageModel(this.file, {this.width, this.height});
}

class MyApp extends StatefulWidget {
  MyApp();

  @override
  State<StatefulWidget> createState() {
    return MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  List<ImageModel> imageModelList = [];

  @override
  void initState() {
    super.initState();
    imageModelList = [];
    _loadImages();
  }

  void _loadImages() {
    final imagePaths = List.filled(20, 'C:/flutter/test/elephant.jpg');
    for (final imagePath in imagePaths) {
      final file = File(imagePath);
      file.readAsBytes().then((bytes) {
        final image = dart_image.decodeImage(bytes);
        // Here i should read the size to group the images based on their size.
        // For now i just add images with 1920x1080 size.
        if (image != null && image.width == 1920 && image.height == 1080) {
          final width = image.width.toDouble();
          final height = image.height.toDouble();
          final imageModel = ImageModel(file, width: width, height: height);
          print(DateTime.now());
          setState(() => imageModelList.add(imageModel));
        }
      }).catchError((err) {});
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: Text('title'),
        ),
        body: SingleChildScrollView(
          child: Wrap(
            children: imageModelList.map((imageModel) {
              return new Container(
                child: Image.file(
                  imageModel.file,
                  width: 160,
                  filterQuality: FilterQuality.medium,
                ),
              );
            }).toList(),
          ),
        ),
      ),
      title: 'title',
    );
  }
}

This happens because you are calling dart_image.decodeImage(bytes) 20 times at once on the main isolate, which will keep the CPU core assigned to the main isolate 100% busy with decoding the images until they are all completed. Because of this, despite correctly calling setState , the CPU is too busy to be able to compute the layout before passing it on to the GPU to draw it, so your app is completely frozen.

To prevent this, you will want to delegate the hard work to other isolates. Flutter provides the compute function which will spawn a new isolate, run the provided function within it using the provided parameters, and return a Future with the result. For your use case it would look like this:

dart_image.Image image = await compute<List<int>, dart_image.Image>(dart_image.decodeImage, byteArray);

Here's an article with a very similar example: https://www.gladimdim.org/flutter-unblocking-ui-thread-with-isolates-compute-function-ck03qrwnj000peks1zvhvko1x

If your app will be doing this a lot, you might want to create a pool of persistent isolates when the app starts - ideally with as many isolates as you have logical CPU cores (-1 to account for the main isolate) - and then use those isolates for decoding the images instead of the compute function. This will noticeably improve the performance of your app as there is an overhead when creating a new isolate (in the range of hundreds of milliseconds depending on device performance), while the overhead of passing data between isolates is of only tens of milliseconds (again, depending on device performance and amount of data being passed).

Note that certain platforms might even misbehave if you use compute excessively. Flutter may have improved this since the last time I tried it, but the iOS apps were often receiving the results from compute with several seconds delay or not at all when several compute functions were executed at once. This issue completely disappeared when I replaced compute with persistent isolates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM