【问题标题】:Flutter Widget test cannot emulate different screen size properlyFlutter Widget 测试无法正确模拟不同的屏幕尺寸
【发布时间】:2020-10-08 09:18:08
【问题描述】:

在部署我的 Flutter 应用之前,我想在多种屏幕尺寸上对其进行测试,以检查是否有适用于较小屏幕的 Renderflex overflow

但是当我第一次在小部件测试期间修改屏幕尺寸以匹配我在开发期间使用的设备时,我意识到小部件测试已经抛出 Render overflow 错误,即使它在实际中没有这样的错误设备。所以我问了这个问题How to fix A RenderFlex overflowed during Widget Test

但我在进一步调查并使用从小部件测试中捕捉 png 的 Flutter 黄金功能测试后,我将问题缩小到 文本大小 的差异。

您可以在下面的可重现步骤中清楚地看到,小部件文本中的文本WAY BIGGER(右侧)比真实设备中的实际文本(左侧)要大。 p>

小部件测试期间较大的文本大小会导致我的应用中出现RenderFlex error

重现步骤:

  1. 现在连接一个真实设备并使用flutter run 运行此代码

lib/main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: TextScaleComparaison(),
    ),
  );
}

class TextScaleComparaison extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final widget = Scaffold(
      body: LayoutBuilder(
        builder: (BuildContext context, BoxConstraints constraints) {
          final width = MediaQuery.of(context).size.width;
          final height = MediaQuery.of(context).size.height;
          final dpr = MediaQuery.of(context).devicePixelRatio;
          final textScale = MediaQuery.of(context).textScaleFactor;
          final vi = MediaQuery.of(context).viewInsets;
          final vip = MediaQuery.of(context).viewPadding;
          final font = DefaultTextStyle.of(context).style.fontFamily;
          print("width is $width and height is $height and dpi is $dpr txtScale is $textScale vi is $vi vip is $vip font is $font");
          return Center(child: Text("This cannot be that long!!"));
        },
      ),
    );
    return widget;
  }
}
  1. 检查日志,您应该会看到设备屏幕信息:

对我来说,我得到了:

I/flutter (27450): width is 411.42857142857144 and height is 797.7142857142857 and dpi is 2.625 txtScale is 1.1 vi is EdgeInsets.zero vip is EdgeInsets(0.0, 24.0, 0.0, 0.0) font is Roboto

将屏幕widthheight复制到textScaledevicePixelRatio到下面代码中的下一步。

  1. 编辑下面的代码以添加上述设置,因为我们想在测试中模拟这个精确的屏幕尺寸。

test/test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';
void main() {

  testWidgets(
    "Emulate real screen size",
    (WidgetTester tester) async {
      // Adjust these to match your actual device screen specs
      final width = 414;
      final height = 846;
      tester.binding.window.devicePixelRatioTestValue = (2.625);
      tester.binding.window.textScaleFactorTestValue = (1.1);
      final dpi = tester.binding.window.devicePixelRatio;
      tester.binding.window.physicalSizeTestValue = Size(width * dpi, height * dpi);
      await tester.pumpWidget(
        MediaQuery(
          data: MediaQueryData(),
          child: MaterialApp(
            home: TextScaleComparaison(),
          ),
        ),
      );
      await expectLater(
        find.byType(TextScaleComparaison),
        matchesGoldenFile("text.png"),
      );
    },
  );
}

运行 test.dartflutter test --update-goldens test/test.dart

这将在test/text.png创建一个png文件

检查日志:对我来说是打印的:

width is 414.0 and height is 846.0 and dpi is 2.625 txtScale is 1.1 vi is EdgeInsets.zero vip is EdgeInsets.zero font is Roboto

我错过了什么?为什么文字显示的不能和真机一模一样?

【问题讨论】:

    标签: unit-testing flutter testing dart


    【解决方案1】:

    正如 Curly 已经提到的here,您可以用较低的值覆盖tester.binding.window.textScaleFactorTestValue0.8 适用于我使用的字体,但根据您的设置,您可能会使用更低的值。这样,您无需在每个测试用例中等待字体的加载。

    【讨论】:

      【解决方案2】:

      这是因为flutter testflutter run使用的字体不同。

      Flutter 的默认字体是Roboto for Android 如果你没有改变它的其他字体。

      1. Android 默认:Roboto 字体和 iOS:San Francisco 字体
      2. 自定义https://flutter.dev/docs/cookbook/design/fonts

      默认情况下,flutter test 无法使用 1) 或 2) 这些字体。 Flutter 测试特意使用了一种名为Ahem 的字体,它由您在屏幕截图中看到的方块组成。

      这是预览:

      Ahem 字体正方形比您使用的正常字体要大。因此,它会导致RenderFlex overflow error

      解决方案

      要在flutter test 中实现近乎完美的设备仿真,您必须下载字体数据,然后加载您正在使用的确切字体。

      要在小部件测试中加载字体,您应该在testWidgets 函数或setUp 中执行:

      final flamante = rootBundle.load('assets/fonts/Flamante-Roma-Medium.ttf');
            await FontLoader('FlamanteRoma')
              ..addFont(flamante)
              ..load();
      

      然后在抽小部件之前将此字体添加到ThemeData

       theme: ThemeData(
                    fontFamily: 'FlamanteRoma',
       ),
      

      最终的test.dart代码为:

      import 'package:flutter/material.dart';
      import 'package:flutter/services.dart';
      import 'package:flutter_test/flutter_test.dart';
      import 'package:example/test/compare_test_size.dart';
      
      void main() {
        testWidgets(
          "Emulate real screen size",
          (WidgetTester tester) async {
            final flamante = rootBundle.load('assets/fonts/Flamante-Roma-Medium.ttf');
            await FontLoader('FlamanteRoma')
              ..addFont(flamante)
              ..load();
      
            // Adjust these to match your actual device screen specs
            final width = 411.4;
            final height = 797.7;
            tester.binding.window.devicePixelRatioTestValue = (2.625);
            tester.binding.window.textScaleFactorTestValue = (1.1);
            final dpi = tester.binding.window.devicePixelRatio;
            tester.binding.window.physicalSizeTestValue = Size(width * dpi, height * dpi);
            await tester.pumpWidget(
              MediaQuery(
                data: MediaQueryData(),
                child: MaterialApp(
                  home: TextScaleComparaison(),
                  theme: ThemeData(
                    fontFamily: 'FlamanteRoma',
                  ),
                ),
              ),
            );
            await expectLater(
              find.byType(TextScaleComparaison),
              matchesGoldenFile("text.png"),
            );
          },
        );
      }
      

      现在重新生成黄金测试并检查 png。您将看到真实的文本,不再是框:

      test/test.png

      别忘了在你的 main.dart 中添加相同的字体

      runApp(
          MaterialApp(
            home: TextScaleComparaison(),
            theme: ThemeData(
              fontFamily: 'FlamanteRoma',
            ),
          ),
        );
      

      别忘了更新pubspec.yaml并运行flutter pub get

      - family: FlamanteRoma
        fonts:
          - asset: assets/fonts/Flamante-Roma-Medium.ttf
      

      【讨论】:

      • 作为一个快速的技巧,您可以更改字体缩放以克服由测试字体引起的更通用的 RenderFlex 错误,例如tester.binding.window.textScaleFactorTestValue = (0.8);
      • 我无法让它在 Flutter 1.17.5(也不是 1.17.4,也不是 1.20.0-7.3.pre-beta)上工作。我可以做flutter build bundle,如果我将pubspec.yaml 更改为不存在的路径,它会失败,所以我相信那部分是正确的。运行 flutter test 失败并显示 Unable to load asset: assets/fonts/Flamante-Roma-Medium.ttf。这是在PlatformAssetBundle.load 中抛出的,我认为这就是问题所在,它不应该是 PlatformAssetBundle 吗?还有什么我需要做的吗?
      猜你喜欢
      • 2017-08-06
      • 1970-01-01
      • 2017-11-15
      • 1970-01-01
      • 2019-05-11
      • 2013-01-05
      • 1970-01-01
      相关资源
      最近更新 更多