Flutter Widget测试无法正确模拟其他屏幕尺寸 [英] Flutter Widget test cannot emulate different screen size properly
问题描述
在部署Flutter应用之前,我想在多种屏幕尺寸上对其进行测试,以检查较小屏幕上是否存在 Renderflex溢出
。
但是,当我第一次在小部件测试期间修改屏幕尺寸以匹配开发过程中使用的设备时,我意识到小部件测试抛出渲染溢出
错误即使它在实际设备上没有此类错误,也已经存在。所以我问了这个问题
在Widget测试期间较大的文本大小导致我的应用程序中出现 RenderFlex错误
。
要重现的步骤:
- 现在连接真实设备并使用
fl运行此代码完全运行
lib / main.dart
import'package:flutter / material.dart';
void main(){
runApp(
MaterialApp(
home:TextScaleComparaison(),
),
);
}
类TextScaleComparaison扩展了StatelessWidget {
@override
Widget build(BuildContext context){
final Widget = Scaffold(
body: LayoutBuilder(
builder:(BuildContext context,BoxConstraints约束){
最终宽度= MediaQuery.of(context).size.width;
最终高度= MediaQuery.of(context).size。高度;
final dpr = MediaQuery.of(context).devicePixelRatio;
final textScale = MediaQuery.of(context).textScaleFactor;
final vi = MediaQuery.of(context).viewInsets;
最终VIP = MediaQuery.of(context).viewPadding;
最终字体= DefaultTextStyle.of(context).style.fontFamily;
print( width is $ width and height is $高度和dpi是$ dpr txtScale是$ textScale vi是$ vi vip是$ vip字体是$ font);
return Center(child:Text(这不能那么久!));
},
),
);
返回小部件;
}
}
- 检查日志并且您应该看到设备屏幕信息:
对我来说,我得到了:
I / flutter(27450):宽为411.42857142857144,高为797.7142857142857,dpi为2.625 txtScale是1.1 vi是EdgeInsets.zero vip是EdgeInsets(0.0,24.0,0.0,0.0)字体是Roboto
将屏幕宽度
和高度
复制到 textScale
和 devicePixelRatio
转到下面的代码中的下一步。
- 编辑下面的代码以添加上述设置,因为我们要在测试中模拟此确切的屏幕尺寸。
test /test.dart
import'package:flutter / material.dart';
import‘package:flutter_test / flutter_test.dart’;
导入 package:my_app / main.dart;
void main(){
testWidgets(
模拟实际屏幕尺寸,
(WidgetTester测试器)异步{
//将其调整为匹配您的实际设备屏幕规格
的最终宽度= 414;
的最终高度= 846;
tester.binding.window.devicePixelRatioTestValue =(2.625);
tester.binding.window。 textScaleFactorTestValue =(1.1);
最终dpi = tester.binding.window.devicePixelRatio;
tester.binding.window.physicalSizeTestValue = Size(width * dpi,height * dpi);
等待测试人员.pumpWidget(
MediaQuery(
data:MediaQueryData(),
子对象:MaterialApp(
home:TextScaleComparaison(),
),
),
);
等待ExpectLater(
find.byType(TextScaleComparaison),
matchesGoldenFile( text.png),
);
},
);
}
使用<$运行 test.dart
c $ c> flutter test --update-goldens test / test.dart
这将在 test / text创建一个png文件.png
检查日志:对我来说,它打印出来:
宽度为414.0,高度是846.0和dpi是2.625 txtScale是1.1 vi是EdgeInsets.zero vip是EdgeInsets.zero字体是Roboto
我缺少什么?为什么文本不能显示与真实设备完全相同的文本?
这是因为 flutter测试
和 flutter运行
。
Flutter的默认字体是 Roboto
for Android,如果您未更改其他字体。
- 默认Android:
Roboto
字体和iOS操作系统:旧金山
字体 - 自定义
Ahem
字体方块比您使用的普通字体大得多。因此,它会导致RenderFlex溢出错误
解决方案
要在
flutter测试
中实现对设备的近乎完美的仿真,必须下载字体数据,然后加载所使用的确切字体。
要在小部件测试中加载字体,应在
testWidgets
函数或setUp
:最终flamante = rootBundle.load('assets / fonts / Flamante-Roma-Medium.ttf');
等待FontLoader(’FlamanteRoma’)
..addFont(flamante)
..load();
然后将该字体添加到
ThemeData
之前主题:ThemeData(
fontFamily:'FlamanteRoma',
),
最终测试.dart代码为:
import'package:flutter / material.dart';
导入的 package:flutter / services.dart;
import‘package:flutter_test / flutter_test.dart’;
import‘package:example / test / compare_test_size.dart’;
void main(){
testWidgets(
模拟实际屏幕大小,
(WidgetTester测试仪)异步{
final flamante = rootBundle.load ('assets / fonts / Flamante-Roma-Medium.ttf');
等待FontLoader('FlamanteRoma')
..addFont(flamante)
..load();
//调整这些值以匹配您实际的设备屏幕规格
最终宽度= 411.4;
最终高度= 797.7;
tester.binding.window.devicePixelRatioTestValue =(2.625);
tester.binding.window.textScaleFactorTestValue =(1.1);
最终dpi = tester.binding.window.devicePixelRatio;
tester.binding.window.physicalSizeTestValue = Size(width * dpi,height * dpi);
等待tester.pumpWidget(
MediaQuery(
data:MediaQueryData(),
child:MaterialApp(
home:TextScaleComparaison(),
主题:ThemeData(
fontFamily: FlamanteRoma,
),
),
),
);
等待ExpectLater(
find.byType(TextScaleComparaison),
matchesGoldenFile( text.png),
);
},
);
}
现在重新生成黄金测试并检查png。您将看到真实的文本,而不再是框:
test / test.png
并且不要忘记在main.dart中添加相同的字体
runApp(
MaterialApp(
主页:TextScaleComparaison(),
主题:ThemeData(
fontFamily:'FlamanteRoma',
),
),
);
也不要忘记更新
pubspec.yaml
并运行flutter pub get
-家庭:FlamanteRoma
字体:
-资产:assets / fonts / Flamante-Roma-Medium.ttf
Before deploying my Flutter app, I wanted to test it on multiple screen sizes to check if there is any
Renderflex overflow
for smaller screens.But I when first modified the screen size during widget testing to match the device I was using during the development, I realized that the widget test is throwing
Render overflow
errors already, even though it did not have such errors on the real device. So I asked this questions How to fix A RenderFlex overflowed during Widget TestBut I after further investigation and using Flutter golden feature test which snaps png out of widget tests, I narrowed down the problem to a discrepancy in text size.
You can see clearly in the reproducible step below that the text during the widget text is WAY BIGGER (on the right) than the actual text in the real device (on the left).
The bigger text size during Widget test causes the
RenderFlex error
in my app.Steps to reproduce:
- Now connect a real device and run this code with
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; } }
- Check the logs and you should see device screen info:
For me I got :
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
Copy the screen
width
andheight
to andtextScale
anddevicePixelRatio
to the next step in the code below.- Edit the code below to add the above setting because we want to simulate this exact screensize in the test.
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"), ); }, ); }
Run
test.dart
withflutter test --update-goldens test/test.dart
This will create a png file at
test/text.png
Check the logs: For me it printed:
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
What I am missing ? Why can't the text show exactly the same as the real device?
解决方案That is because of the font difference used by
flutter test
andflutter run
.Flutter's default font is
Roboto
for Android if you did not change it other font.- Default Android:
Roboto
font and for iOS:San Francisco
font - Customize https://flutter.dev/docs/cookbook/design/fonts
Either 1) or 2) these fonts are not available to
flutter test
by default. Flutter test purposely uses a font calledAhem
which is made out of square blocks that you see on your screenshot.This is a preview:
Ahem
font square are wayyy bigger than the normal that you are using. Therefore, it causes theRenderFlex overflow error
Solution
To achieve a near perfect emulation of your device in
flutter test
you have to download the font data then load the exact font that you are using.To load a font in widget test, you should do inside the
testWidgets
function orsetUp
:final flamante = rootBundle.load('assets/fonts/Flamante-Roma-Medium.ttf'); await FontLoader('FlamanteRoma') ..addFont(flamante) ..load();
Then add this font to the
ThemeData
before pumping the widget.theme: ThemeData( fontFamily: 'FlamanteRoma', ),
The final test.dart code is:
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"), ); }, ); }
Now re generate the golden test and check the png. You will see real text, not boxes anymore:
test/test.png
And don't forget to add the same font in your main.dart
runApp( MaterialApp( home: TextScaleComparaison(), theme: ThemeData( fontFamily: 'FlamanteRoma', ), ), );
And also don't forget to update
pubspec.yaml
and runflutter pub get
- family: FlamanteRoma fonts: - asset: assets/fonts/Flamante-Roma-Medium.ttf
这篇关于Flutter Widget测试无法正确模拟其他屏幕尺寸的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- Now connect a real device and run this code with