You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WidgetTester中调整屏幕尺寸?响应式布局测试遇异常

Flutter HomeLayout 尺寸适配测试问题

问题背景

我实现了一个首页布局Widget,逻辑很简单:

  • 移动设备(宽度<600)显示底部导航栏(BottomNavigationBar)
  • Web/平板设备(宽度≥600)显示侧边导航栏(NavigationRail)

需要验证不同尺寸下导航栏的渲染类型,但测试时遇到了问题。

实现代码

class HomeLayout extends StatelessWidget {
  const HomeLayout({
    Key? key,
    this.child,
  }) : super(key: key);

  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (_, constraints) {
        if (constraints.maxWidth >= 600) {
          return _WideDeviceLayout(child: child); // 渲染NavigationRail
        }

        return _ThinDeviceLayout(child: child); // 渲染BottomNavigationBar
      },
    );
  }
}

测试问题

我尝试用tester.view.physicalSize模拟视图尺寸,结果:

  • 窄屏测试(宽度412)正常通过,能正确渲染BottomNavigationBar
  • 宽屏测试(宽度1024)始终失败,组件还是渲染BottomNavigationBar

如果不设置physicalSize,直接调用pumpWidget断言:

  • 窄屏测试失败,宽屏测试成功,说明测试器默认视图宽度大于600。

测试代码如下:

group('HomeLayout widget', () {
  group('on Thin Device (width < 600)', () {
    testWidgets('should render BottomNavigationBar', (tester) async {
      await tester.pumpWidget(testWidget);

      tester.view.physicalSize = const Size(412, 915);
      await tester.pumpAndSettle();

      expect(
        tester.widget(_WidgetFinders.navBar),
        isA<BottomNavigationBar>(),
      );
    });
  });

  group('on Wide Device (width >= 600)', () {
    testWidgets('should render NavigationRail', (tester) async {
      await tester.pumpWidget(testWidget);

      tester.view.physicalSize = const Size(1024, 600);
      await tester.pumpAndSettle();

      expect(tester.widget(_WidgetFinders.navBar), isA<NavigationRail>());
    });
  });
});

问题原因

核心问题是设置视图尺寸的时机错误:
你先调用了pumpWidget,此时组件已经用测试器默认的宽屏约束完成了初始渲染;之后修改tester.view.physicalSize,虽然更新了视图尺寸,但LayoutBuilder并没有收到新的约束触发重建——因为LayoutBuilder的约束依赖于父组件的布局上下文,初始渲染后单纯修改视图尺寸不会自动触发它的重建逻辑。

解决方案

1. 先设置尺寸,再渲染组件

把设置physicalSize的代码放到pumpWidget之前,让组件初始化时就使用指定的尺寸约束。

2. 测试后恢复视图尺寸

为了避免当前测试的尺寸设置影响其他测试,建议在测试结束后恢复默认尺寸。

修正后的测试代码:

group('HomeLayout widget', () {
  group('on Thin Device (width < 600)', () {
    testWidgets('should render BottomNavigationBar', (tester) async {
      // 先设置视图尺寸
      tester.view.physicalSize = const Size(412, 915);
      // 固定像素密度,避免尺寸计算偏差
      tester.view.devicePixelRatio = 1.0;
      
      await tester.pumpWidget(testWidget);
      await tester.pumpAndSettle();

      expect(
        tester.widget(_WidgetFinders.navBar),
        isA<BottomNavigationBar>(),
      );

      // 测试结束后恢复默认尺寸
      addTearDown(() => tester.view.resetPhysicalSize());
    });
  });

  group('on Wide Device (width >= 600)', () {
    testWidgets('should render NavigationRail', (tester) async {
      tester.view.physicalSize = const Size(1024, 600);
      tester.view.devicePixelRatio = 1.0;
      
      await tester.pumpWidget(testWidget);
      await tester.pumpAndSettle();

      expect(tester.widget(_WidgetFinders.navBar), isA<NavigationRail>());

      addTearDown(() => tester.view.resetPhysicalSize());
    });
  });
});

补充说明

  • 设置devicePixelRatio是为了避免因像素密度差异导致的实际显示尺寸计算偏差,确保测试中的尺寸和预期一致。
  • addTearDown会在测试完成后执行,保证视图尺寸恢复到默认状态,不干扰其他测试用例。

内容的提问来源于stack exchange,提问作者Keids

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 16:05:05