【问题标题】:flutter for web: Get width/height form NetworkImage before displaying it网页颤动:在显示之前从 NetworkImage 获取宽度/高度
【发布时间】:2020-04-05 08:45:33
【问题描述】:

我想在显示图像之前从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 • 频道开发(由于 Flutter Web)
  • Chrome 版本 79.0.3945.79

【问题讨论】:

  • 使用您选择的 HTTP 包自行下载图像,提取尺寸信息,然后使用 Image.memory 显示它。
  • 感谢您的建议。我会这样调查。同时,我仍然想知道为什么上述方法不起作用。

标签: flutter dart flutter-web


【解决方案1】:

您的自我回答代码已经完成了一半。从那里,您可以使用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 对象,我认为没有对此我们可以做很多事情。

【讨论】:

    【解决方案2】:

    基于您提供的参考 post 的一些观察结果。

    1. 您将ui.ImageImage 小部件混淆了。
    2. 如果您将信息逻辑与小部件构建分开,那么您将无法访问图像小部件,这意味着您必须重新创建它。相反,您可以创建一个小部件并将其返回。
    3. 在基于 http 的答案中,response.body.length 可能不完全代表图像尺寸。您必须查看响应标头是否包含有关图像的任何信息。
    4. 另请注意FutureBuilder 的构建方法将多次调用不同的ConnectionState,具体取决于未来的状态,例如waitingdone。检查here

    选项 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: 只需使用原始 post 中的代码,将 Image 小部件创建和信息提取带入 build 方法。

    【讨论】:

      【解决方案3】:

      根据 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;
        }
      

      【讨论】:

      • response.bodyBytes 的长度只是响应正文中的字节数 - 它不会与图像的尺寸一致。一方面,像素将代表图像的每个通道,可能有 3/4 个通道,每个像素每个通道每个像素可能有多个字节,等等。然后图像可能会被压缩(作为 JPG 或 PNG ,例如),所以像素数据会因为随便阅读而出现乱码。还有要考虑的标头字节,其中包含有关图像的元数据。简而言之,bodyBytes 的长度远不及你所需要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-10
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      相关资源
      最近更新 更多