如何在不同屏幕尺寸上测试Flutter小部件?

42

我有一个Flutter小部件,根据屏幕大小显示额外的数据。 有人知道在多个不同的屏幕大小上测试此小部件的方法吗?

我已经查看了widget_tester源代码,但没有找到任何信息。


你所说的测试是指单元测试/类似测试还是手动测试? - Rémi Rousselet
通过测试,我指的是小部件测试,例如测试特定小部件是否可见,类似于单元测试。https://flutter.io/docs/cookbook/testing/widget - Jordan Davies
9个回答

67
你可以使用WidgetTester来指定自定义的界面尺寸。
下面的代码将以42x42的屏幕尺寸运行测试。
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets("foo", (tester) async {
    tester.binding.window.physicalSizeTestValue = Size(42, 42);

    // resets the screen to its original size after the test end
    addTearDown(tester.binding.window.clearPhysicalSizeTestValue);

    // TODO: do something
  });
}

默认尺寸是多少?我设置了1200和2400,但它仍然比默认尺寸小。 - Luke Pighetti
它的宽度为800,高度为600。 - Rémi Rousselet
您还可以使用devicePixelRatioTestValue属性设置像素密度。 - Ber
6
现在正确的答案是使用tester.binding.setSurfaceSize(size);(请参见https://github.com/flutter/flutter/issues/12994#issuecomment-902964066)。 - Dominik Roszkowski
从3.9.0版本开始,tester.binding.window已被弃用。这似乎是有效的:tester.view.devicePixelRatio = 1; tester.view.physicalSize = const Size(1200, 800); - manish kiranagi
显示剩余4条评论

37

我不确定为什么,但是@rémi-rousselet的解决方案对我没有起作用。我必须使用binding.window.physicalSizeTestValuebinding.window.devicePixelRatioTestValue指定屏幕尺寸,以便输出是完全确定的。

我为像我这样的Flutter初学者添加了更多代码。请查看以下内容:

void main() {

  final TestWidgetsFlutterBinding binding =
    TestWidgetsFlutterBinding.ensureInitialized();

  testWidgets("Basic layout test (mobile device)", (tester) async {
    binding.window.physicalSizeTestValue = Size(400, 200);
    binding.window.devicePixelRatioTestValue = 1.0;

    await tester.pumpWidget(new MyApp());

    expect(find.byType(MyHomePage), findsOneWidget);
    // etc.
  });
}

1
谢谢,这个对我起作用了(setSurfaceSize没有)。 - divan
1
我也是,setSurfaceSize() 对我没用,但你的解决方案行了。谢谢。 - svprdga
@VizGhar 我按照您的建议操作,但在处理文本时出现了问题,请查看 https://dev59.com/zbvoa4cB1Zd3GeqP8q8d 和 https://github.com/flutter/flutter/issues/59755。 - TSR
添加 devicePixelRatioTestValue 很好用! - Moses Aprico
对于任何在尝试此操作时出现类型转换错误的人,请添加 as TestWidgetsFlutterBinding。因此,整个代码如下:final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized() as TestWidgetsFlutterBinding; - Loren.A

15

有一个叫做 device_preview 的软件包,可以模拟你的 Flutter 应用在不同设备上运行。


8

@rémi-rousselet的解决方案非常完美!

此外,如果您想测试方向更改,请尝试以下操作:

const double PORTRAIT_WIDTH = 400.0;
const double PORTRAIT_HEIGHT = 800.0;
const double LANDSCAPE_WIDTH = PORTRAIT_HEIGHT;
const double LANDSCAPE_HEIGHT = PORTRAIT_WIDTH;

final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();

await binding.setSurfaceSize(Size(PORTRAIT_WIDTH, PORTRAIT_HEIGHT));
await tester.pumpWidget(MyWidget());

// test in portrait

await binding.setSurfaceSize(Size(LANDSCAPE_WIDTH, LANDSCAPE_HEIGHT));
await tester.pumpAndSettle();

// OrientationBuilder gets triggered

// test in landscape

7

目前最安全的方法是使用setSurfaceSize

import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets("foo", (tester) async {
    tester.binding.setSurfaceSize(Size(400, 400));

    // reset
    tester.binding.setSurfaceSize(null);

    // continue
  });
}

相关的 Github 问题请参见此处


他们计划废弃这个功能。请参考:https://github.com/flutter/flutter/issues/123881 - Hrishikesh Kadam
1
此时的正确答案应该是 https://dev59.com/LFQJ5IYBdhLWcg3wg2JP#76415174 - Hrishikesh Kadam

6
自从3.10.0版本开始,窗口单例已被弃用(https://docs.flutter.dev/release/breaking-changes/window-singleton)。因此,现在必须按照以下方式设置大小:
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets("foo", (tester) async {
    tester.view.physicalSize = Size(42, 42);

    // resets the screen to its original size after the test end
    addTearDown(tester.view.resetPhysicalSize);

    // TODO: do something
  });
}


1
我还需要添加 tester.view.devicePixelRatio = 1.0; - Hrishikesh Kadam
这应该是Flutter 3.10.0以上版本的接受答案,谢谢! - undefined

3

你可以尝试使用这个小部件来测试你的小部件在实时更改屏幕大小时的效果。

屏幕大小测试

https://pub.dev/packages/screen_size_test

预览

enter image description here

演示 https://dartpad.dartlang.org/43d9c47a8bf031ce3ef2f6314c9dbd52

代码示例

import 'package:screen_size_test/screen_size_test.dart';
...
MaterialApp(
  title: 'Demo',
  builder: (context, child) => ScreenSizeTest(
    child: child,
  ),
  home: Scaffold(
    body: ListView(
      children: List.generate(
          20,
          (index) => Container(
                padding: EdgeInsets.all(10),
                child: Placeholder(),
              )),
    ),
  ),
)

滑块不工作。 - S A Saharukh

3
尽管@Rémi Rousselet的答案非常有帮助,但它并没有完全解决我的问题。事实证明,我只需将要测试的小部件包装在MediaQuery小部件中并设置大小即可。
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  Widget makeTestableWidget({Widget child, Size size}) {
    return MaterialApp(
      home: MediaQuery(
        data: MediaQueryData(size: size),
        child: child,
      ),
    );
  }

  testWidgets("tablet", (tester) async {
    final testableWidget = makeTestableWidget(
      child: WidgetUnderTest(),
      size: Size(1024, 768),
    );

    ...
  });

  testWidgets("phone", (tester) async {
    final testableWidget = makeTestableWidget(
      child: WidgetUnderTest(),
      size: Size(375, 812),
    );

    ...
  });
}

1
这并不完全正确。它并没有真正改变大小,而是模拟MediaQuery。像LayoutBuilderRenderObjects,或者golden tests仍然基于默认大小。 - Rémi Rousselet
啊,好的。当我尝试使用TestWidgetsFlutterBinding时,它没有任何影响,因为在我的小部件中,我从MediaQuery获取屏幕大小,而MediaQuery是由MaterialApp小部件创建的。如果我删除MaterialApp小部件,它会抛出一个关于textDirection为空的错误,所以这似乎是最好的选择,但显然不是每种情况下的最佳选择。 - Jordan Davies
我建议将我的答案标记为解决方案(除非你不同意),因为嘲弄MediaQuery很少是期望的解决方案。 - Rémi Rousselet

0
我建议将testWidgets进行封装,这样当您想要为屏幕添加平台或尺寸时,就不需要对所有测试进行更改。以下是一个测试一些尺寸和平台的矩阵:
const List<Size> _testedSizes = [
  Size(800, 600),
  Size(600, 800),
];

void testWidgetInPlatformMatrix(
  String description,
  Future<void> Function(WidgetTester) callback,
) {
  for (var size in _testedSizes) {
    testWidgets(
      '$description ($size)',
      (widgetTester) async {
        await widgetTester.binding.setSurfaceSize(size);
        return callback(widgetTester);
      },
      variant: TargetPlatformVariant.mobile(),
    );
  }
}


如果你要使用testWidgetInPlatformMatrix而不是testWidgets
testWidgetInPlatformMatrix('Counter increments smoke test',
          (WidgetTester tester) async {
  //...
});

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