🏆
【Flutter】Sizer対応したGoldenTestを実装する
はじめに
Flutterでアプリを開発する時のResposive対応でSizerライブラリを使う方多いんじゃないでしょうか。
今回はそのSizerライブラリに対応したGoldenTestを実装していきます。
実装そのものは簡単ですが、詰まったところがあったので書き留めておきます。
GoldenTestなにそれ?
って方は以下記事を参考にしてください。
今回のゴール
GoldenTestのマスタースクリーンショットで、実機と同じレイアウトにすることを目標とします。以下画像のようになればOKとする。
FirstPageはIconとTextだけのシンプルなWidgetでSizerを使っているのでデバイスサイズに応じて変化します。FirstPageの中身は最後の方にのせておきます。
実機の画面
FirstPage(小) | FirstPage(大) |
---|---|
GoldenTestのスクリーンショット
FirstPage(小) | FirstPage(大) |
---|---|
Sizer対応
ただ単にtestでSizerを入れるだけです。
void main() {
testGoldens('golden test', (WidgetTester tester) async {
await loadAppFonts();
//テストしたいWidgetをSizerで包んであげる。
final widget = Sizer(builder: (context, orientation, deviceType) {
//任意のWidget
return FirstPage();
});
await tester.pumpWidgetBuilder(widget);
await multiScreenGolden(tester, 'golden_test');
});
}
もし、Sizerで包んでいない場合は以下のようなエラーが出てくると思います。
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following LateError was thrown building FirstPage(dirty):
LateInitializationError: Field 'width' has not been initialized.
The relevant error-causing widget was:
FirstPage
When the exception was thrown, this was the stack:
#0 SizerUtil.width (package:sizer/util.dart)
#1 SizerExt.w (package:sizer/extension.dart:12:36)
#2 FirstPage.build (package:sizer_golden_test_demo/main.dart:35:25)
#3 StatelessElement.build (package:flutter/src/widgets/framework.dart:4739:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4665:15)
#5 Element.rebuild (package:flutter/src/widgets/framework.dart:4355:5)
#6 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4643:5)
#7 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4638:5)
... Normal element mounting (211 frames)
#218 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#219 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6333:36)
#220 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6344:32)
... Normal element mounting (362 frames)
#582 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3673:14)
#583 Element.updateChild (package:flutter/src/widgets/framework.dart:3422:20)
#584 RenderObjectToWidgetElement._rebuild (package:flutter/src/widgets/binding.dart:1198:16)
#585 RenderObjectToWidgetElement.update (package:flutter/src/widgets/binding.dart:1175:5)
#586 RenderObjectToWidgetElement.performRebuild (package:flutter/src/widgets/binding.dart:1189:7)
#587 Element.rebuild (package:flutter/src/widgets/framework.dart:4355:5)
#588 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2620:33)
#589 AutomatedTestWidgetsFlutterBinding.drawFrame (package:flutter_test/src/binding.dart:1139:19)
#590 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:319:5)
#591 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1143:15)
#592 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1080:9)
#593 AutomatedTestWidgetsFlutterBinding.pump.<anonymous closure> (package:flutter_test/src/binding.dart:1006:9)
#596 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
#597 AutomatedTestWidgetsFlutterBinding.pump (package:flutter_test/src/binding.dart:993:27)
#598 WidgetTester.pumpWidget.<anonymous closure> (package:flutter_test/src/widget_tester.dart:554:22)
#601 TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:71:41)
#602 WidgetTester.pumpWidget (package:flutter_test/src/widget_tester.dart:551:27)
#603 _pumpAppWidget (package:golden_toolkit/src/testing_tools.dart:128:16)
詰まったところ
ちゃんとSizerで包んでやったのでもう大丈夫だと思っていたら、なんだかレイアウトが実機と全然違う現象がおきました。
その原因はconst
でした!
テストしたいWidgetにconst
がついている場合、multiScreenGolden
を実行しても、デバイスサイズに応じてレイアウトが変わってくれません。
模擬的にtest内でWidgetにconstをつけてflutter test --update-goldens
してみると、、
void main() {
testGoldens('golden test', (WidgetTester tester) async {
await loadAppFonts();
final widget = Sizer(builder: (context, orientation, deviceType) {
//任意のWidgetにconstがついているとする
return const FirstPage();
});
await tester.pumpWidgetBuilder(widget);
await multiScreenGolden(tester, 'golden_test');
});
}
GoldenTestのスクリーンショット(const有りパターン)
Head | Head |
---|---|
見ての通りIconもTextもサイズが変わっておらず、Sizer対応していないです。
FirstPage
今回テストしているWidgetです。
import 'package:flutter/material.dart';
import 'package:sizer/sizer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Sizer(builder: (context, orientation, deviceType) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: FirstPage(),
);
});
}
}
class FirstPage extends StatelessWidget {
const FirstPage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 20.w,
height: 30.h,
child: Icon(
Icons.flutter_dash_rounded,
size: 40.sp,
),
),
Text('Hello world', style: TextStyle(fontSize: 12.sp)),
],
),
),
);
}
}
終わりに
参考になれば幸いです。
Discussion