Flutter for Web: 在显示 NetworkImage 前获取其宽度/高度

5

在显示图片之前,我想从NetworkImageImage.network中获取属性值,例如宽度和高度。

我找到了以下好的帖子,但它们不起作用。 它得到了大小值,但图像没有在FutureBuilder中加载。

我的代码如下:

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: FutureBuilder(
          future: _getImage(),
          builder: (BuildContext context, AsyncSnapshot<Image> snapshot) {
            print(snapshot.hasData);
            if (snapshot.hasData) {
              return snapshot.data;
            } else {
              return Text('Loading...');
            }
          },
        ),
      ),
    );
  }

  Future<Image> _getImage() async {
    final Completer completer = Completer();
    final String url = 'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
    final image = NetworkImage(url);

    image.resolve(ImageConfiguration())
          .addListener(ImageStreamListener((ImageInfo info, bool isSync) {
      print(info.image.width);
      completer.complete(info.image);
    }));

    return completer.future;
  }
}

结果是; - 屏幕只显示 "正在加载...",图片没有被加载。 - print 输出如下。这应该意味着,在加载图像之前 FutureBuilder 被调用了两次,我们可以得到宽度,但在那之后 FutureBuilder 没有被调用。
false
false
112

环境:

  • Flutter 1.13.0 • dev 频道 (因为需要支持 Flutter web)
  • Chrome 版本 79.0.3945.79

使用您选择的HTTP包自行下载图像,提取大小信息,然后使用Image.memory显示它。 - Abion47
谢谢您的建议。我会研究这种方法。同时,我仍然希望了解为什么上述方法不起作用的答案。 - Hirotaka Nishimiya
3个回答

1
根据您提供的参考post,有几点观察:
  1. 您混淆了ui.ImageImage小部件。
  2. 如果将信息逻辑与小部件构建分开,则无法访问Image小部件,这意味着您必须重新创建它。相反,您可以创建一个小部件并返回它。
  3. 在基于http的答案中,response.body.length可能不完全表示图像尺寸。您必须查看响应标头是否有关于图像的任何信息。
  4. 还要注意,FutureBuilder的构建方法将根据未来的状态(如等待或完成)多次调用,并且具有不同的ConnectionState。请在此处检查。

选项1: 如果您不关心Image小部件,那么您当前的代码可以稍作修改就可以工作。这与原始的post完全相同,但已经修改以匹配您在帖子中定义的方式。

import 'dart:async';

import 'package:flutter/material.dart';
import 'dart:ui' as ui;

class ImageSizeTestWidget extends StatefulWidget {
  ImageSizeTestWidget({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _ImageSizeTestWidgetState createState() => _ImageSizeTestWidgetState();
}

class _ImageSizeTestWidgetState extends State<ImageSizeTestWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: FutureBuilder<ui.Image>(
          future: _getImage(),
          builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
            print(snapshot.hasData);
            if (snapshot.hasData) {
              return Text('${snapshot.data.width} X ${snapshot.data.height}');
            } else {
              return Text('Loading...');
            }
          },
        ),
      ),
    );
  }

  Future<ui.Image> _getImage() async {
    final Completer<ui.Image> completer = Completer<ui.Image>();
    final String url =
        'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
    Image image = Image.network(url);

    image.image
        .resolve(ImageConfiguration())
        .addListener(ImageStreamListener((ImageInfo info, bool isSync) {
      print(info.image.width);
      completer.complete(info.image);
    }));

    return completer.future;
  }
}

选项2: 只需将原始文章中的代码按原样使用,将Image小部件创建和信息提取带入构建方法中。


0

你的自答代码已经完成了一半。从那里开始,你可以使用instantiateImageCodec将字节转换为ui.Image对象。

Future<Image> _getImage() async {
  Image _image;
  final String url = 'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
  var response = await http.get(url);
  print("Response status: ${response.statusCode}"); // 200, ideally

  final bytes = response.bodyBytes);
  final codec = await instantiateImageCodec(bytes);
  final frame = await codec.getNextFrame();
  final uiImage = frame.image; // a ui.Image object, not to be confused with the Image widget

  print(uiImage.width); // The width of the image in pixels
  print(uiImage.height); // The heightof the image in pixels

  _image = Image.memory(bytes);

  return _image;
}

有点糟糕的是,你必须以这种方式进行操作,因为图像将被解码两次,但除了创建自己的自定义Image小部件可以直接使用ui.Image对象之外,我认为没有太多可以做的。


0
基于 Abion47 的建议,我成功地使用 http 包获取了图像。但是,即使在获取到图像后,我仍然无法获得宽度和/或高度值。 作为替代方案,我使用response.body.length来检查已下载的图像是否有效。
  Future<Image> _getImage() async {
    Image _image;
    final String url = 'http://images-jp.amazon.com/images/P/4101098018.09.MZZZZZZZ';
    var response = await http.get(url);

    print("Response status: ${response.statusCode}"); // 200
    _image = Image.memory(response.bodyBytes);
    print(_image.width); // still null
    print(response.body.length); // this shows numbers. I'll use this.

    return _image;
  }

1
response.bodyBytes的长度仅仅代表响应正文中的字节数,它不会与你的图像尺寸相匹配。首先,像素将表示图像的每个通道,而且可能有3/4个通道,每个像素每个通道可能有多于一个字节,等等。接着,图像很可能会被压缩(例如JPG或PNG),因此像素数据对于仅仅浏览来说可能已经损坏了。还有头信息要考虑,其中包含关于图像的元数据。简而言之,bodyBytes的长度远非你需要的。 - Abion47

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接