Расшифруйте изображение и добавьте в список виджетов [флаттер] - PullRequest
0 голосов
/ 12 января 2020

Я хочу загрузить и отобразить несколько изображений из папки в Windows, группируя их по размеру (например, большие, средние, значки и т. Д.).

В Чтобы узнать их размер перед их отображением, я зацикливаю список с помощью функции decodeImage из 'package:image/image.dart' для каждого изображения.

Чего я хочу добиться, так это чтобы изображения появлялись на экране после каждой итерации, без блокировки пользовательского интерфейса, пока весь процесс загрузки не будет завершен.

У меня есть список моделей изображений, и я звоню setState, как только я добавляю каждое изображение в список, чтобы обновить представление, но все изображения отображаются в конце l oop.

Как это исправить?

Это вывод консоли отладки (для всех изображений требуется около 10 секунд чтобы появиться):

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 (!)

И это код (здесь я использовал одно изображение JPG 1920x1080):

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',
    );
  }
}

1 Ответ

1 голос
/ 17 января 2020

Это происходит потому, что вы звоните dart_image.decodeImage(bytes) 20 раз за один главный изолятор, в результате чего ядро ​​ЦП, назначенное основному изоляту, будет на 100% заниматься декодированием изображений, пока все они не будут завершены. Из-за этого, несмотря на правильный вызов setState, ЦП слишком занят, чтобы иметь возможность вычислить макет перед передачей его в графический процессор для его рисования, поэтому ваше приложение полностью заморожено.

Чтобы предотвратить это , вы захотите делегировать тяжелую работу другим изолятам. Flutter предоставляет функцию compute, которая будет порождать новый изолятор, запускать в нем предоставленную функцию, используя предоставленные параметры, и возвращать Future с результатом. Для вашего случая использования это будет выглядеть так:

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

Вот статья с очень похожим примером: https://www.gladimdim.org/flutter-unblocking-ui-thread-with-isolates-compute-function-ck03qrwnj000peks1zvhvko1x

Если ваше приложение будет делать это во многих случаях вам может потребоваться создать пул постоянных изолятов при запуске приложения - в идеале, с таким количеством изолятов, как у вас логических ядер ЦП (-1 для учета основного изолята) - и затем использовать их изолирует для декодирования изображений вместо функции compute. Это заметно повысит производительность вашего приложения, поскольку при создании нового изолята возникают издержки (в диапазоне сотен миллисекунд в зависимости от производительности устройства), тогда как накладные расходы на передачу данных между изолятами составляют всего десятки миллисекунд (опять же, в зависимости от производительности устройства и объема передаваемых данных).

Обратите внимание, что некоторые платформы могут работать неправильно, если вы используете compute чрезмерно. Flutter, возможно, улучшил это со времени моей последней попытки, но приложения iOS часто получали результаты от compute с задержкой в ​​несколько секунд или вовсе не при одновременном выполнении нескольких функций compute. Эта проблема полностью исчезла, когда я заменил compute постоянными изолятами.

...