【问题标题】:Flutter: Image.asset actual size issueFlutter:Image.asset 实际大小问题
【发布时间】:2021-10-23 01:45:30
【问题描述】:

我正在尝试在加载图像并且小部件已呈现时获取图像的大小。

所以,如果在initState 中调用_getSizes() 函数,它会返回width: 0.0, height: 0.0

当我在frameBuilder 中调用_getSizes() 时,结果相同。

现在,如果我单击 floatingActionButton 或使用 Future.delayed 至少延迟 500 毫秒,我可以从 _getSizes() 函数获得所需的结果,然后它会打印实际容器大小:width: 375.0, height: 210.9375

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({ Key? key }) : super(key: key);

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

class _MyAppState extends State<MyApp> {
  final GlobalKey _imgKey = GlobalKey();

  _getSizes() {
    final RenderBox renderBox = _imgKey.currentContext!.findRenderObject() as RenderBox;
    final boxSize = renderBox.size;
    print("width: ${boxSize.width}, height: ${boxSize.height}");
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      print("WidgetsBinding");
      _getSizes();
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test Image Load',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Image Asset Load'),
        ),
        body: Center(
          child: Container(
            key: _imgKey,
            child: Image.asset(
              'assets/images/landscape_big.jpg',
              frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
                if (wasSynchronouslyLoaded) {
                  print('wasSynchronouslyLoaded');
                }
                if (frame != null) {
                  _getSizes();
                }
                return child;
              }
            ),
          )
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            _getSizes();
          },
          child: const Icon(Icons.update),
          backgroundColor: Colors.green,
        ),
      )
    );
  }
}

加载图片资源并渲染父容器时如何获取实际大小?是否有任何onLoad 方法或Image.assets 小部件的控制器?

【问题讨论】:

  • This 你在找什么。
  • 感谢您的链接。这种方法有助于部分解决这个问题。 Completer 给出原始图像大小,但不是布局中 Image 小部件的实际大小。同样在Completer 返回原始图像大小的那一刻,RenderBox 仍然返回零。这样即使原始图像尺寸已知,Flutter 也没有时间计算实际的小部件尺寸。我不知道为什么会这样,这不好。至少我可以借助来自Completer 的已知纵横比来计算高度,但如果您不知道任何布局尺寸,这将是不可能的。

标签: flutter flutter-layout


【解决方案1】:

经过一些测试并尝试了多种方法,我找到了可行的解决方案,但它看起来仍然像拐杖。

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({ Key? key }) : super(key: key);

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

class _MyAppState extends State<MyApp> {
  final GlobalKey _imgKey = GlobalKey();

  _getSizes() {
    final RenderBox renderBox = _imgKey.currentContext!.findRenderObject() as RenderBox;
    final boxSize = renderBox.size;
    print("width: ${boxSize.width}, height: ${boxSize.height}");
  }

  @override
  void initState() {
    super.initState();
    // https://medium.com/flutterworld/flutter-schedulerbinding-vs-widgetsbinding-149c71cb607f
    // Doesn't work at all
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      print("WidgetsBinding");
      // _getSizes();
    });
    SchedulerBinding.instance!.addPostFrameCallback((_) {
      print("SchedulerBinding");
      // _getSizes();
    });
  }

  @override
  Widget build(BuildContext context) {
    Image image = Image.asset('assets/images/landscape_big.jpg');
    Completer<ui.Image> completer = Completer<ui.Image>();
    image.image
      .resolve(const ImageConfiguration())
      .addListener(ImageStreamListener((ImageInfo info, bool synchronousCall) {
        completer.complete(info.image);
        print("completer: w ${info.image.width} h ${info.image.height}");
      }));

    return MaterialApp(
      title: 'Image Load',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Image Load'),
        ),
        body: Center(
          child: Container(
            key: _imgKey,
            child: Image.asset(
              'assets/images/landscape_big.jpg',
              frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
                if (wasSynchronouslyLoaded) {
                  print('wasSynchronouslyLoaded');
                }
                if (frame != null) {
                  _getSizes();
                  Future.delayed(Duration.zero, () {
                    print('zero delay');
                    _getSizes(); // <--- ONLY THIS WORKS
                  });
                }
                return child;
              }
            ),
          )
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            _getSizes();
          },
          child: const Icon(Icons.update),
          backgroundColor: Colors.green,
        ),
      )
    );
  }
}

因此,我在DEBUG CONSOLE 中得到了这个执行序列:

flutter: WidgetsBinding
flutter: SchedulerBinding
flutter: completer: w 3840 h 2160
flutter: width: 0.0, height: 0.0
flutter: zero delay
flutter: width: 375.0, height: 210.9375

归根结底,只有一种方法对我有用 - Future.delayedframeBuilderframeBuilderImage.asset 小部件中的持续时间为零。

我不知道这个解决方案有多可靠,为什么在 Flutter 中实现如此简单的任务如此困难。

【讨论】:

    猜你喜欢
    • 2020-12-19
    • 2012-09-01
    • 2015-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    相关资源
    最近更新 更多