- 投稿日:2019-11-25T23:12:22+09:00
Unity + DockerでUnityプロジェクトからAARファイルを自動生成
はじめに
UnityのSceneをAndroidのSubView(FrameLayout)として埋め込むまで
https://qiita.com/_nonono/items/253aa15d6027ecc8ad66
このような記事があり、AndroidにUnityのシーンをFrameLayoutに埋め込むことができます(便利Unityで開発せずビルド成果物(AARファイル)のみ必要な場合
1.指定のバージョンのUnityをインストール
2.Unityを立ち上げAndroidプロジェクトをExport
3.Android Studioでビルド
上の3つの作業を手作業で行うので一苦労だと思います
Unity、Android Studioのバージョンを意識したりしなければならいので良い方法が必要です「なんとか良い方法を!」と思って
Unityの入ったDockerイメージを使ってAARの生成を自動化
してみたので、作業を下に記していきますTL;DR
いきなり一連の作業をスクリプトにできないので、dockerの中で作業を1つずつして、最後にスクリプトにまとめます
やったこと
docker pull gableroux/unity3d
- Dockerコンテナに入りライセンスファイルを用意
- ライセンスファイルをコピー
- ビルド用クラスを作成
- Unityプロジェクトをビルド
- build.gradleとAndroidManifest.xmlを編集
- GradleでAndroidプロジェクトをビルド
- build.shを書いて完全自動化
実際の環境
・MacBook Pro (15-inch, 2016) macOS 10.13.6(17G10007)
・docker desktop ver. 2.1.0.5(40693)1. Unityの入ったDockerイメージをpullする
まずは、Unityの入っているDockerイメージをインターネットから落としてきます
次のコマンドを実行すると最新版のコンテナが自分のローカルに保存されます
Dockerイメージをpulldocker pull gableroux/unity3dしかし、このイメージではAndroidプロジェクトを生成/ビルドができないので、
Unityバージョン
とUnityプロジェクトのビルドターゲット
を指定しなければなりません。この記事では、
Unity 2018.4.5f1
でビルドターゲットをAndroid
とするので、Dockerイメージのtagに2018.4.5f1-android
を指定します
※目的のバージョンのイメージが無い場合があるので、 DockerHub のページで確認しておきましょう目的のDockerイメージをpulldocker pull gableroux/unity3d:2018.4.5f1-android
docker images
を実行するとpullしてきたイメージが確認できますDockerImageの確認cha84rakanal$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE gableroux/unity3d 2018.4.5f1-android 3cc6afa5c2bd 3 months ago 6.7GB
docker run -it {image name} bash
を実行すればイメージからコンテナが作成され、Dokcer内のターミナルに接続できる
なので、接続してUnity
とAndroid SDK
があるかを確認しておきますUnityとAndroidSDKの確認cha84rakanal$ docker run -it --rm gableroux/unity3d:2018.4.5f1-android bash root@7462f939f4d0:/# echo $ANDROID_HOME /opt/android-sdk-linux root@7462f939f4d0:/# ls /opt/ android-sdk-linux Unity Unity-2018.4.5f1 root@7462f939f4d0:/#2. Dockerコンテナに入ったUnity用のライセンスを生成する(1回のみ・手作業)
普段使うmacOSやWindowsでUnityを一番最初に起動すると次の画面が出てアクティベーションをする必要があります
次の作業は、DockerコンテナにインストールされているUnityのアクティベーションを行います
まず、Unityプロジェクトジェクトのディレクトリに移動してdockerコンテナを立ち上げます
※ パスワードを平打ちするので気をつけましょうDockerコンテナの立ち上げcd /path/to/project docker run -it --rm \ -e "UNITY_USERNAME=username@example.com" \ -e "UNITY_PASSWORD=example_password" \ -e "TEST_PLATFORM=linux" \ -e "WORKDIR=/root/project" \ -v "$(pwd):/root/project" \ gableroux/unity3d:gableroux/unity3d:2018.4.5f1-android \ bash次に、Dockerコンテナ内で以下のコマンドを実行します
アクティベーションをするxvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \ /opt/Unity/Editor/Unity \ -logFile /dev/stdout \ -batchmode \ -username "$UNITY_USERNAME" -password "$UNITY_PASSWORD"すると、出力に次のようなXMLが表示されるので、表示されたXMLを
unity3d.alf
というファイル名で保存しますターミナルでの出力LICENSE SYSTEM [2017723 8:6:38] Posting <?xml version="1.0" encoding="UTF-8"?><root><SystemInfo><IsoCode>en</IsoCode><UserName>[...]もし、XMLではなく
401
エラーが出た場合は、2段階認証を切りましょう
Googleでサインイン
やFaceBookでサインイン
でUnity IDを作成している場合でも401
エラーがでるので、メールアドレスから作成したUnityIDを使いましょう401エラーCan't activate unity: No sufficient permissions while processing request HTTP error code 401
unity3d.alf
が用意できたら https://license.unity3d.com/manual にアクセスします。アクセスしたらサイトの指示に従ってunity3d.alf
をアップロード、質問に答えていきましょう。すべて終わるとUnity_v2018.x.ulf
かUnity_v2019.x.ulf
がダウンロードできるので保存しておきます。
このUnity_v*.x.ulfファイル
は今後必要になるので大切に保管しておきますプロジェクトチームの誰かがこの作業をやれば、Unityのバージョンを変えない限り、
3以降の作業でUnity_v2018.x.ulf
を使いまわすことができます(多分※アクティベーションの詳しい手順は このページ に書いてありますが、まとめとしてこの記事にも記しています
3. Dockerコンテナに入ったUnityにライセンスを当てる
2の手順でダウンロードしてきたファイル
Unity_v2018.x.ulf
をUnityプロジェクトのルートディレクトにおきますUnity_v2018.x.ulfの設置/path/to/project ├── Assets ├── Library ├── Logs ├── Packages ├── ProjectSettings ├── README.md └── Unity_v2018.x.ulf次に、Unityプロジェクトジェクトのディレクトリに移動してdockerコンテナを立ち上げます
Dockerコンテナの立ち上げcd /path/to/project docker run -it --rm \ -v "$(pwd):/root/project" \ gableroux/unity3d:gableroux/unity3d:2018.4.5f1-android \ bashここから先の作業7.が終わるまでは、dockerコンテナ内のターミナルで
exit
をしてはいけませんライセンスファイルをUnityプロジェクトのルートディレクトに設置できたら、dockerコンテナ内のターミナルで次のコマンドを実行します。これでライセンスファイルの設置は完了です
Unityのライセンスファイルを設置set -e set -x mkdir -p /root/.cache/unity3d mkdir -p /root/.local/share/unity3d/Unity/ set +x cp ~/project/Unity_v2018.x.ulf /root/.local/share/unity3d/Unity/Unity_lic.ulf4. ビルド用クラスを作成してAssets/Scripts/Editorに配置
Unityプロジェクトをコマンドラインからビルドできるように次のC#ソースを
Assets/Scripts/Editor
に設置しますApplicationBuild.csusing UnityEngine; using System; using System.Linq; public static class ApplicationBuild { private static string[] GetAllScenePaths() { return EditorBuildSettings.scenes .Where(scene => scene.enabled) .Select(scene => scene.path) .ToArray(); } public static void AndroidBuild() { string[] scenes = GetAllScenePaths(); BuildPipeline.BuildPlayer(scenes, "./Build/", BuildTarget.Android, BuildOptions.AcceptExternalModificationsToPlayer); } }公式ドキュメント を確認すると、 ビルドのパラメータを色々設定できます。
今回は、プロジェクトのエクポートパスは./Build
、ビルドターゲットはAndroidなのでBuildTarget.Android
、AndroidのビルドはExternalで行うのでオプションにBuildOptions.AcceptExternalModificationsToPlayer
を指定していますBuildPipeline.BuildPlayer
BuildPlayer(EditorBuildSettingsScene[] levels, string locationPathName, BuildTarget target, BuildOptions options)
パラメータ levels The Scenes to be included in the build. If empty, the currently open Scene will be built. Paths are relative to the project folder (Assets/MyLevels/MyScene.unity). locationPathName 成果物の保存先のパス target ビルドする BuildTarget options ビルドしたプレイヤーを実行するか、などの追加の BuildOptions 5. コマンドラインからUnityプロジェクトをビルド
次のコマンドでUnityプロジェクトをコマンドラインからビルドできます
コマンドラインからUnityプロジェクトをビルド/opt/Unity/Editor/Unity -batchmode -quit -nographics -logFile ./build.log -projectPath . -executeMethod ApplicationBuild.AndroidBuildログは
./build.log
に保存されるので、エディタ等で確認するとビルドが進んでるのがわかります
ビルド中は、ターミナルに何かこれといって表示されるものは無いです(無視できるエラーとかは出るビルドが完了するとログに次の行が記録されます
build.logExiting batchmode successfully now!※成果物の保存先のパス
./Build
ディレクトリがすでにあるとビルドが停止してしまうので消しておきましょう6. APKではなくAARを作成するようにbuild.gradleとAndroidManifest.xmlを編集
5の作業が終わり、ビルドが成功すると、
./Build
ディレクトリにAndroidプロジェクトが生成されますコマンドラインでのビルド後/path/to/project ├── Assets ├── Build │ └── {Project Name} │ ├── build.gradle │ ├── gradle.properties │ ├── libs │ ├── local.properties │ ├── proguard-unity.txt │ └── src ├── Library ├── Logs ├── Packages ├── ProjectSettings ├── README.md └── Unity_v2018.x.ulfデバイスにインストールする
.apk
の作成であれば、gradle
コマンドでビルドして終了です
今回は、.aar
の作成なので、build.gradle
とAndroidManifest.xml
を編集する必要があります
build.gradle
apply plugin: 'com.android.application'
をapply plugin: 'com.android.library'
に変更applicationId 'com.project.unitytest'
を削除bundle
をコメントアウト(Unity2018.3.x以降??)build.gradle--- apply plugin: 'com.android.application' +++ apply plugin: 'com.android.library' --- applicationId 'com.project.unitytest' --- bundle { +++ /*bundle { language { enableSplit = false } density { enableSplit = false } abi { enableSplit = true } --- } +++ }*/
AndroidManifest.xml
- 該当のintent-filterタグとその子をすべてコメントアウト
AndroidManifest.xml<!--<intent-filter>--> <!--<action android:name="android.intent.action.MAIN" />--> <!--<category android:name="android.intent.category.LAUNCHER" />--> <!--<category android:name="android.intent.category.LEANBACK_LAUNCHER" />--> <!--</intent-filter>-->UnityプロジェクトからAARファイルの生成を行うには、この作業も自動化しておく必要があるので、
UnityのPostProcessの機構を使って自動化しておきます[Unity] PostProcessでビルド後に処理を差し込む
https://qiita.com/edo_m18/items/346439f7678218e85e69※
build.gradle
の文字コードはUTF-8
で書き出すこと、 C#のでのUTF8Encoding
はUTF-8 with BOM
であるので注意が必要。UTF-8 with BOM
のbuild.gradle
をgradle
に渡すとエラーになるので注意
※UnityのPostProcessBuildにはこの記事ではふれません
※build.gradle
のbundle
のコメントアウトだが このページ によれば次に示す置換でも問題ないbuild.gradle--- bundle { +++ splits { language { --- enableSplit = false +++ enable false } density { --- enableSplit = false +++ enable false } abi { --- enableSplit = true +++ enable true } }bundle部分を処理するC#の例build_text = build_text.Replace("bundle {", "splits {"); build_text = build_text.Replace("enableSplit = false", "enable false"); build_text = build_text.Replace("enableSplit = true", "enable true");7. コマンドラインでAndroidプロジェクトをビルド
残る作業は
./Build
ディレクトリに生成されたAndroidプロジェクトをビルドしてAARファイルを生成するだけです
生成されたAndroidプロジェクトのディレクトリ(build.gradle
があるディレクトリ)に移動して次のコマンドを実行するだけですAARファイルを生成gradle bundleDebugAarAARファイルを生成gradle bundleReleaseAarデバッグとリリースがあるので必要に応じて使い分けましょう
ビルドが成功するとプロジェクトのディレクトリに
build
ディレクトリが生成されて、
その中に{PROJECT_NAME}-debug.aar
{PROJECT_NAME}-release.aar
が生成されますビルド後/path/to/project ├── Assets ├── Build │ └── {Project Name} │ ├── build │ │ └── outputs │ │ └── aar │ │ ├── {PROJECT_NAME}-debug.aar │ │ └── {PROJECT_NAME}-release.aar │ ├── build.gradle │ ├── gradle.properties │ ├── libs │ ├── local.properties │ ├── proguard-unity.txt │ └── src ├── Library ├── Logs ├── Packages ├── ProjectSettings ├── README.md └── Unity_v2018.x.ulf8. 作業をシェルスクリプトにまとめる
4から7の作業をまとめて、コマンド一行でAARファイルの生成できるようにします
ライセンスファイルはUnityプロジェクトのルートディレクトリに設置して、6の作業はUnityのPostProcessBuildで自動化しておきましょうそうして作業をまとめたスクリプトが次になります
build.sh#!/usr/bin/env bash set -e set -x mkdir -p /root/.cache/unity3d mkdir -p /root/.local/share/unity3d/Unity/ set +x cp ~/project/Unity_v2018.x.ulf /root/.local/share/unity3d/Unity/Unity_lic.ulf cd ~/project/ && rm -r ./Build && /opt/Unity/Editor/Unity -batchmode -quit -nographics -logFile ./build.log -projectPath . -executeMethod ApplicationBuild.AndroidBuild cd ~/project/Build/{Project Name} && gradle bundleDebugAarUnityプロジェクトのルートディレクトリに移動して、dockerコンテナ内で
build.sh
を実行しますAARの自動ビルドcd /path/to/project chmod 777 build.sh docker run -it --rm \ -v "$(pwd):/root/project" \ gableroux/unity3d:gableroux/unity3d:2018.4.5f1-android \ /bin/bash -c "/root/project/build.sh"さいごに
これでコマンドを実行してビルドされるのを待つだけになりました!
これをBitbucket Pipeline
やCircleCI
、GitHub Actions
とかのCIツールに載せていけるといいですね
まとめている間に、既にmacOSやWindowsにUnityがインストールされていて、かつ、Android SDKがインストールされているなら、Docker上じゃなくていいかなと思いました余談
実際の出力cha84rakanal$ time docker run -it --rm -e "WORKDIR=/root/project" -v "$(pwd):/root/project" gableroux/unity3d:2018.4.5f1-android /bin/bash -c "/root/project/build.sh" + mkdir -p /root/.cache/unity3d + mkdir -p /root/.local/share/unity3d/Unity/ + set +x ALSA lib confmisc.c:767:(parse_card) cannot find card '0' ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory ALSA lib conf.c:5007:(snd_config_expand) Evaluate error: No such file or directory ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM default ALSA lib confmisc.c:767:(parse_card) cannot find card '0' ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory ALSA lib conf.c:5007:(snd_config_expand) Evaluate error: No such file or directory ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM default /home/builduser/buildslave/unity/build/Editor/Platform/Linux/UsbDevices.cpp:UsbDevicesQuery Welcome to Gradle 5.1.1! Here are the highlights of this release: - Control which dependencies can be retrieved from which repositories - Production-ready configuration avoidance APIs For more details see https://docs.gradle.org/5.1.1/release-notes.html Starting a Gradle Daemon (subsequent builds will be faster) > Task :help Welcome to Gradle 5.1.1. To run a build, run gradle <task> ... To see a list of available tasks, run gradle tasks To see a list of command-line options, run gradle --help To see more detail about a task, run gradle help --task <task> For troubleshooting, visit https://help.gradle.org BUILD SUCCESSFUL in 5s 1 actionable task: 1 executed debugger-agent: Unable to listen on 28 Starting a Gradle Daemon (subsequent builds will be faster) Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0. Use '--warning-mode all' to show the individual deprecation warnings. See https://docs.gradle.org/5.5.1/userguide/command_line_interface.html#sec:command_line_warnings BUILD SUCCESSFUL in 2m 7s 21 actionable tasks: 21 executed real 33m9.065s user 0m0.162s sys 0m0.178s cha84rakanal$
time
コマンドで実行時間を図ったら30分もビルドしてるので、dockerコンテナ内よりホスト側でビルドした方がはやいやんけw
- 投稿日:2019-11-25T21:40:43+09:00
Flutter desktop embedding を Ubuntu にインストール&試してみる
Flutterで書いたアプリケーションをデスクトップ環境でも動かせるようにする、Flutter desktop embeddingをUbuntu 18.04環境で試してみたので、その手順をまとめておきます。
環境構築の手順
Flutter SDKインストール
リポジトリからcloneした後は、適当なパスに配置して、パスを通しておきます。
$ git clone https://github.com/flutter/flutter $ sudo mv flutter /usr/local/ $ export PATH=$PATH:/usr/local/flutter/binAndroid Studioインストール
https://developer.android.com/studioからLinux版をダウンロードして適当な場所に置いて、インストールスクリプトを叩きます。
Android Studioのインストールで基本的には次へを押していく感じです。
$ wget https://developer.android.com/studio](https://developer.android.com/studio $ sudo tar -xzvf android-studio-ide-191.5977832-linux.tar.gz -C /usr/local/ $ /usr/local/android-studio/bin/studio.sh でAndroid Studioをセットアップその他のインストール
サンプル実行(flutter run)時に必要になるので、clang++をインストールしておきます。
$ sudo apt install clangFlutterセットアップ
ターゲット環境をHost PC (Ubuntu) にする設定を行います。参考
$ flutter config --enable-linux-desktopここまでの状況の確認
以下のコマンドを使うと、flutterを動かすためのインストール状況を診断してくれます。今回はサンプルを動かせればいいので、以下の状態でも大丈夫です。
$ flutter doctor Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel master, v1.12.3-pre.49, on Linux, locale en_US.UTF-8) [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2) [✓] Linux toolchain - develop for Linux desktop [!] Android Studio (version 3.5) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [!] VS Code (version 1.40.1) ✗ Flutter extension not installed; install from https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter [!] Proxy Configuration ! NO_PROXY does not contain 127.0.0.1 [✓] Connected device (1 available) ! Doctor found issues in 3 categories.サンプルを実行してみる
flutter-desktop-embedding取得
$ git clone https://github.com/google/flutter-desktop-embedding.git
サンプルを実行してみる
$ cd flutter-desktop-embedding/example $ flutter runもしくは、事前にビルドしておくなら以下。
$ flutter build linux $ flutter runソースコードは./lib/main.dartみたいです。
- 投稿日:2019-11-25T17:53:08+09:00
Android Builderの使い方
本内容は以下のサイトを参照して作ったです。
https://blog.csdn.net/androidxiaogang/article/details/80586631// static 内部クラスの使い方
外部クラスの属性は多いし、可変し、絶対必須ではない。
そして、使用者によって非必須の属性を設定するのは使用者次第なので、
普通のやり方で、各属性のset関数でやります。
或いは、いっぱいのコンストラクタ関数を用意する。Builderのコンストラクタ関数は可変で、使い方は便利である。
個人的な感じはBuilderパータンの方法は普通の使い型より少しだけを簡単になる。以下のクラスの使い方法は
NutritionFacts nutritionFacts = new NutritionFacts.Builder(240, 8)
.calories(100)
.sodium(35)
.carbohydrate(27)
.build();サンプルコード:
public class TestStaticClass {
private final int servingSize; // サイズ private final int servings;//食べ物 private final int calories;//カロリー private final int fat;//脂肪 private final int sodium;//Na private final int carbohydrate;//炭水化物 @Override public String toString() { return "NutritionFacts{" + "servingSize=" + servingSize + ", servings=" + servings + ", calories=" + calories + ", fat=" + fat + ", sodium=" + sodium + ", carbohydrate=" + carbohydrate + '}'; } public static class Builder { //必須属性 private int servingSize; private int servings; //可選の属性 private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; //必須の属性を追加 public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int calories) { this.calories = calories; return this; } public Builder fat(int fat) { this.fat = fat; return this; } public Builder carbohydrate(int carbohydrate) { this.carbohydrate = carbohydrate; return this; } public Builder sodium(int sodium) { this.sodium = sodium; return this; } //外部クラスにオブジェクトを作成 public TestStaticClass build() { return new TestStaticClass(this); } } private TestStaticClass(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; }}
- 投稿日:2019-11-25T16:06:36+09:00
[ Android ] バージョンを指定する方法
- 投稿日:2019-11-25T15:34:51+09:00
【Flutter】VSCode上でIntegration Testを動かす
FlutterのIntegration Testは、公式のcookbookに載っている方法をそのまま利用すると、いくつか不便な点があります。
- 毎回コマンドラインから実行する必要がある
- 実行するたびにアプリのビルドが走って時間がかかる
- ブレイクポイントを打ってデバッグすることができない
ということでなんとかしましょう。
対象IDEはVSCodeです。
参考元
こちらのMedium記事を大いに参考にします。
https://medium.com/flutter-community/hot-reload-for-flutter-integration-tests-e0478b63bd54内容をザッッッッックリと要約すると
- 通常
flutter drive
は次の2つのプロセスを動かして相互通信させる
- アプリ自体(シミュレーター上)
- テストコード(単なるDartプログラム)
- ならば、それぞれを自分でIDE上で動かして通信させれば良い
- 結果、Hot Reload/Hot Restart、ブレイクポイントなど自由自在
まあ、ザックリこういうことです。
リンク先の記事内ではAndroid Studioでの例が載っているのですが、
これを、VSCodeでやりたい!!というのが今回の目的です。
なお、こちらのQiita記事も参考にさせていただいたのでリンクを貼っておきます。
https://qiita.com/sensuikan1973/items/c8b56dfaf780e61af567
ではやり方を見ていきましょう。
使うアプリ
アプリ自体は何でもいいです。今回はこうしました。
main.dartimport 'package:flutter/material.dart'; void main() => runApp(MaterialApp(home: Scaffold(appBar: AppBar(), body: MyApp()))); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final Hoge _hoge = Hoge(); @override Widget build(BuildContext context) { return Column(children: <Widget>[ Text(_hoge.text, key: const Key('text')), RaisedButton( key: const Key('button'), child: const Text('button'), onPressed: () { setState(() { _hoge.change(); }); }, ) ]); } } class Hoge { String text = 'hogehoge'; void change() { if (text == 'hogehoge') { text = 'fugafuga'; } else { text = 'hogehoge'; } } }ボタンを押すたびに、hogehogeとfugafugaが切り替わります。それだけ。
テストコードから中身にアクセスするため、Keyが設定してあります。
テスト用にアプリを起動するためのコードは次のような感じ。
test_driver/app.dartimport 'package:flutter_driver/driver_extension.dart'; import 'package:stepbystep/main.dart' as app; void main() { // app_test.dart の方とやりとりしたい場合はこの引数にhandlerを追加 enableFlutterDriverExtension(); // runAppに好きなWidgetを渡しても良い app.main(); }アプリ自体(シミュレーター上)を動かす
launch.json
に、configurationを追加して次のようにします。追加したconfigurationの名前と、ポート番号は適当です。
launch.json{ "version": "0.2.0", "configurations": [ { "name": "App of Integration Test", "type": "dart", "request": "launch", "program": "test_driver/app.dart", "args": ["--observatory-port", "8888", "--disable-service-auth-codes"] }, { "name": "Flutter", "request": "launch", "type": "dart" } ] }この状態で、VSCode上から今追加したconfigurationを指定してデバッグを開始すると、
test_driver/app.dart
に記述したアプリがシミュレーター上で走ります。いつものようにHotReloadやHotRestartも可能です。ブレイクポイントも機能します。
そして重要な点は、ポート番号8888でこのアプリにアクセス可能になるということです。
試しにブラウザを開いて、アドレスバーに
http://localhost:8888/と打ち込んでみましょう。
こんな感じの画面が出れば、確かにこのポート番号でFlutterが動いていることがわかります。
細かい注意(DEBUG CONSOLEの出力)
configurationを指定してデバッグ開始した場合、DEBUG CONSOLEの出力がconfigurationごとに別々になるので注意してください。DEBUG CONSOLE内の右の方にプルダウンリストがあるのでそこから選択すればOK。
テストコードの実行
Integration Testの公式cookbookでは、テストコードは
/test_driver/
ディレクトリに入れることになっていますが、VSCode上でテストコードをテストとして実行するためには、
_test.dart
で終わるファイル名がついたコードを/test/
ディレクトリ内に配置する必要があります。(参考:v3.6 - Dart Code - Dart & Flutter support for Visual Studio Code)
test/integration_test.dartimport 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; void main() { FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(dartVmServiceUrl: 'http://localhost:8888/') .timeout(Duration(seconds: 10)); }); tearDownAll(() async { if (driver != null) { await driver.close(); } }); test('the button changes the text from hogehoge to fugafuga', () async { expect(await driver.getText(find.byValueKey('text')), equals('hogehoge')); driver.tap(find.byValueKey('button')); expect(await driver.getText(find.byValueKey('text')), equals('fugafuga')); }); }
FlutterDriver.connect()
にdartVmServiceUrl
引数を与えると、そのURLを使ってアプリに接続しようとします。なので、先程ブラウザで開いたのと同じ
http://localhost:8888/
を指定しましょう。(
timeout
については「問題点」の所で後述します)あとは、VSCode上でこのファイルを開いた状態で、configurationにいつもの
Flutter
を指定し、デバッグを実行すればOKです。Integration Testが実行されます。
テストコード内にブレイクポイントを打つと、きちんと機能します。
アプリを起動したまま、テストコードだけ繰り返し実行することもできます。
プロセスを終了させる
これでテストはできましたが、実はテストを終了しても裏でプロセスが残っています。
メモリ・CPUの観点からもよくないし、port 8888も占有されたままです。
そのまま同じアプリの開発を続けるなら恐らく問題ないのですが、
違うアプリの開発に移ってから同様にIntegration Testをすると次のエラーが出ます。
flutter: Could not start Observatory HTTP server: SocketException: Failed to create server socket (OS Error: Address already in use, errno = 48), address = 127.0.0.1, port = 8888なので、このプロセスを終了させましょう。
次のコマンドを実行します。
lsof -i :8888すると、port 8888を使用しているプロセスの一覧が出るので、PIDを指定してkillします。
kill (PIDを指定)テスト終了時に自動でプロセスも終了したらいいんですけどね。そのような方法をご存知の方は教えていただけると嬉しいです!!!
問題点
実はこの方法ですが、けっこうでかい問題点が含まれております。
何か解決案をお持ちの方は教えていただけると嬉しいです!
アプリ側の状況によってテスト結果が変わる
「アプリを1から起動し、テストを実行して、アプリを閉じる」という一連の流れを手動で断ち切ってしまっていますので、
アプリを操作したり、テストコードを連続で実行したりすると、結果に影響します。
例えばこの記事の例だと、Integration Testを一度実行するとtextが
fugafuga
に切り替わっている(初期値はhogehoge
)ので、そのままもう一度テストを実行すると2回目は失敗します。テストの独立性を保証する工夫が必要です。
とりあえず毎回アプリをHot Restartするのが一番ラクな気がします。
Dart: Run All Tests
でIntegration Testも実行されてしまうUnit TestとWidget Testだけ全て実行したい場面でも、
/test/
に配置したIntegration Testも一緒に実行されてしまいます。その際にテスト用アプリが実行されていなければ、当然エラーになります。
上記コードではとりあえず
FlutterDriver.connect
にタイムアウトを設定してありますので、アプリが実行されていない場合はここでタイムアウトしてIntegration Testが終了します。でもそうすると、「Integration Test 以外のテストが全て通れば良しとする」という風に結果を自分で判断しなければいけません。
自動テストや自動デプロイを組んでいると影響が出そうです。
ただ、自動テストの場合はVSCode上で実行するわけではないので、自動で実行するテストをうまく指定できれば問題ないかもしれません。
Unit TestとWidget Testを
/test/unit_and_widget_test/
のようなディレクトリにまとめて、launch.json
でこのディレクトリを指定してまとめて実行することも考えましたが、この方法だと、一つのファイルを実行したらシェルが終了してしまうらしく、次のファイルを実行する際にエラーになりました。残念。
一応解決策
FlutterDriver.connect
が失敗した場合にフラグ立てをして、テストケースの中身をif文で回避する手があります。test/integration_test.dartimport 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; void main() { FlutterDriver driver; bool doTest = true; setUpAll(() async { driver = await FlutterDriver.connect(dartVmServiceUrl: 'http://localhost:8888/') .timeout(Duration(seconds: 3), onTimeout: () { print('Flutter driver connection failed!!!!!'); doTest = false; return null; }); }); tearDownAll(() async { if (driver != null) { await driver.close(); } }); test('the button changes the text from hogehoge to fugafuga', () async { if (doTest) { expect(await driver.getText(find.byValueKey('text')), equals('hogehoge')); driver.tap(find.byValueKey('button')); expect(await driver.getText(find.byValueKey('text')), equals('fugafuga')); } }); }無理やり回避した感がすごいですが、一応、
Run All Tests
の際も、Integration Test以外がすべて通ればOKを出してくれます。(setUpAllはテストケースの実行が決定してから呼ばれるため、test関数のskip引数はうまく使えない)
おわり
この記事は以上です!
Integration Testでもアプリコードやテストコードにブレイクポイントを打ってデバッグできるのでとっても便利です!
これでストア申請に必要なスクショも撮れますね。
でも問題点には注意が必要です。
最後に。記事に載せようと思ってUnit TestやWidget Testも書きましたが、載せるタイミングがありませんでした。何が誰の参考になるかもわからないので載せておきます。
test/unit_test.dartimport 'package:test/test.dart'; import 'package:stepbystep/main.dart'; void main() { test('change method changes text', () { final Hoge hoge = Hoge(); expect(hoge.text, equals('hogehoge')); hoge.change(); expect(hoge.text, equals('fugafuga')); hoge.change(); expect(hoge.text, equals('hogehoge')); }); }test/widget_test.dartimport 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:stepbystep/main.dart'; void main() { testWidgets('button changes text', (WidgetTester tester) async { await tester.pumpWidget(MaterialApp(home: MyApp())); expect(find.text('hogehoge'), findsOneWidget); await tester.tap(find.byKey(const Key('button'))); await tester.pumpAndSettle(); expect(find.text('fugafuga'), findsOneWidget); await tester.tap(find.byKey(const Key('button'))); await tester.pumpAndSettle(); expect(find.text('hogehoge'), findsOneWidget); }); }
- 投稿日:2019-11-25T13:59:04+09:00
adb - Android debug tool
Enable Device Debug
- Enable developer mode on your Android device
- Settings ⇒ System ⇒ About Phone ⇒ Tap "Build number" over 7 times and quickly
- Then you will see "You are already developer"
- Enable debugging
- Go to "Developer Options" ⇒ Enable "ADB debugging"
- Connect Android phone to your Windows PC, then you will see a pop-up ( some phone will not show pop-up, please check notification center)
- Choose "Transfer files"
adb Setup
Windows
- Download adb package for windows. https://developer.android.com/studio/releases/platform-tools.html
- Unzip the package, then put it to any path
cd C:\{adb folder path}Mac
- Download adb package for mac. https://developer.android.com/studio/releases/platform-tools.html
- Install adb
- Open terminal
Check installation success
- Type
adb
- If you can see below message, means adb is able to use
Android Debug Bridge version 1.0.40 Version 4986621 Installed as C:\{adb folder path}\adb\adb.exe global options: -a listen on all network interfaces, not just localhost -d use USB device (error if multiple devices connected) -e use TCP/IP device (error if multiple TCP/IP devices available) -s SERIAL use device with given serial (overrides $ANDROID_SERIAL) -t ID use device with given transport id -H name of adb server host [default=localhost] -P port of adb server [default=5037] -L SOCKET listen on given socket for adb server [default=tcp:localhost:5037]adb command
- Connect your Android phone to PC/MAC, then type
adb devices
If you see below message, means your Android phone already connected to PC, and able to use adb command
List of devices attached 35PDU18712000000 device
- Send text to device
adb shell input text "textttt"
- Send key to device. You can use key name or key code. eg: TAB(61), HOME(3), ENTER(66) https://developer.android.com/reference/android/view/KeyEvent
adb shell input keyevent TAB adb shell input keyevent 61
- Log ( logcat) https://developer.android.com/studio/command-line/logcat
- 投稿日:2019-11-25T12:36:47+09:00
トラブルシューティング of AVD @ Ubuntu18.04
PCのBIOSで「Virtualization Technology」 をEnableに変更する
sudo apt install qemu-kvm sudo adduser $USER kvmとしてリブート
起動に時間がかかる
コマンドラインで一旦起動したら次回以降OKとなりました。
https://developer.android.com/studio/run/emulator-commandline
コマンドラインで起動
~/Android/Sdk/emulator$ ./emulator -avd Nexus_5X_API_28
- 投稿日:2019-11-25T10:32:15+09:00
[Android]TextView の文字の大きさを自動的に調整する(Autosizing TextViews)
はじめに
Android の TextView では View サイズに応じて、
文字の大きさ(TextSize)を自動的に調整する機能が実装されています。
その機能について調べたので、簡単に使い方をまとめたいと思います。View サイズに応じて TextSize を自動的に調整する
次のように TextView の autoSizeTextType を uniform に設定すると、
TextView の View サイズに応じて TextSize が自動的に調整されるようになります。activity_main.xml<TextView android:id="@+id/size_type_uniform_view" android:layout_width="match_parent" android:layout_height="1dp" android:text="Uniform" android:background="#aaffaa" app:autoSizeTextType="uniform"/>次のような感じで View サイズの変化に応じて TextSize を自動的に調整してくれます。
TextSize を大きくするステップ数の調整をする
次のように autoSizeStepGranularity を設定すると、
TextSize を大きくするステップ数の調整ができます。activity_main.xml<TextView android:id="@+id/granularity_view" android:layout_width="match_parent" android:layout_height="1dp" android:text="Granularity" android:background="#aaffaa" app:autoSizeTextType="uniform" app:autoSizeStepGranularity="10dp"/>次のような感じで TextSize を徐々に大きくするのではなく段階的に大きくしてくれます。
TextSize の最小値と最大値を調整する
次のように autoSizeMinTextSize と autoSizeMaxTextSize を設定すると、
View サイズに応じて調整される TextSize の最小値と最大値を調整できます。activity_main.xml<TextView android:id="@+id/min_max_view" android:layout_width="match_parent" android:layout_height="1dp" android:text="MinMax" android:background="#ffaaaa" app:autoSizeTextType="uniform" app:autoSizeMinTextSize="25dp" app:autoSizeMaxTextSize="75dp"/>次のような感じで、ある View サイズよりも小さくなったら TextSize が小さくならず
ある View サイズよりも大きくなったら TextSize を大きくしないようにできます。プリセットで ステップ数、最小値、最大値を定義する
autoSizeStepGranularity や autoSizeMinTextSize 、autoSizeMaxTextSize を
設定すればTextSize をどのように大きさを調整するか大まかに設定できました。もっと詳細に調整したい場合は autoSizePresetSizes を利用します。
次のような配列(プリセット)を渡すと、TextSize をそのステップ数、最小値、最大値で調整してくれます。arrays.xml<?xml version="1.0" encoding="utf-8"?> <resources> <array name="autosize_text_sizes"> <item>10dp</item> <item>50dp</item> <item>100dp</item> </array> </resources>activity_main.xml<TextView android:id="@+id/size_preset_sizes_view" android:layout_width="match_parent" android:layout_height="1dp" android:text="PresetSizes" android:background="#aaaaff" app:autoSizeTextType="uniform" app:autoSizePresetSizes="@array/autosize_text_sizes"/>次のような感じでプリセットのステップ数、最小値、最大値となるよう TextSize を調整してくれます。
おわりに
TextView の文字の大きさを自動的に調整したいときは、
次の attributes にて どのように自動的に調整するか設定できる。
attributes 説明 autoSizeTextType uniform であれば自動的に調整する。
none であれば自動的に調整しない。autoSizeStepGranularity 自動的に調整する際の TextSize を大きくするステップ数を設定する。 autoSizeMinTextSize 自動的に調整する際の TextSize の最小値を設定する。 autoSizeMaxTextSize 自動的に調整する際の TextSize の最大値を設定する。 app:autoSizePresetSizes 自動的に調整する際の プリセット を設定する。
プリセットに定義した内容で ステップ数、最小値、最大値が決まる参考文献
- 投稿日:2019-11-25T00:59:37+09:00
【Unity】これ以上セーフエリア対応で消耗しないためのアセットを作った【AutoScreen】
AutoScreen(GitHub)
はじめに
Unityにおけるセーフエリアの対応に関しては
Screen.safeArea
というAPIがあるのですが、ビルドして実機で呼ばないと端末ごとの値が取れない=エディタ上ではレイアウトの確認ができないという辛さがあります。最近だとUnite Tokyo 2019でも紹介されたDevice Simulatorが記憶に新しいところですが、このパッケージはUnity2019.3からでないと使用できず、まだプレビュー版となっています。
- 新機能「Device Simulator」でモバイル開発のイテレーションを加速させよう(Unity公式ブログ)
- 【Unity】Device Simulatorでノッチとセーフエリアの対策(テラシュールブログ)
個人的な感覚ではUnityはたとえLTS版であってもある程度時間が経つまでは本番で使用するのが怖いので、自分が実際にDevice Simulatorを使えるようになるのは2020年の後半になる気がしています。
そんなに待ってられないよ!ということでUniSafeAreaAdjusterをありがたく使わせてもらってたのですが
- 解像度に合わせて端末を選択するのが面倒
- 特に複数の
GameObject
にSafeAreaAdjuster
コンポーネントをつけてるとき- デバイスのフレーム表示機能がほしい
- 手軽に対応端末を追加・拡張できるようにしたい
という気持ちがピークに達して自分でアセットを作ってしまったのでご紹介します。
便宜上「アセット」と書いてますがアセットストアに公開するのはけっこう面倒なのでGitHubでだけ公開しています。
↓↓↓↓↓↓↓
AutoScreen(GitHub)READMEがまだないのですが、そのうち本記事を元に英語で書くと思います。
機能
- モバイル実機上でセーフエリアに応じて
RectTransform
のアンカーを自動調整するコンポーネント
SafeArea
:セーフエリア用(適用したい方向を上下左右自由に組み合わせ可)UnsafeArea
:非セーフエリア用(上下左右どれか選択)RuntimeSafeAreaUpdater
:画面の回転を自動検知してSafeArea
,UnsafeArea
を更新- エディタ上でセーフエリアをリアルタイムプレビュー
- 既存のGameウィンドウでセーフエリアあり端末の解像度を選択するだけ(追加作業なし)
- 再生/非再生の状態に関係なく即時反映
- on/off可能なオプション
- デバイスのフレームを表示
- セーフエリアの境界線表示
対応状況
- Unity2018.3以降対応(Device Simulatorは未対応)
- iOS:実機・エディタともに対応
- iPhone X/XS/11 Pro
- iPhone XS Max/11 Pro Max
- iPhone XR
- iPad Pro (第3世代, 11インチ)
- iPad Pro (第3世代, 12.9インチ)
- Android:実機のみ対応
Android端末のエディタプレビューは以下の理由でオミットしています。
- 個人的にAndroid端末にビルドする必要性がない
- フレーム画像と解像度・セーフエリアデータの収集に手間がかかる
- 対応が必要な端末数が多そう
端末のマスターデータの追加自体は簡単なので、必要に応じて後述の手順を参考に自分で足してください。
インストール方法
リポジトリ内に
*.unitypackage
ファイルがあるのでこちらを使用してください。使い方
とりあえず動作を確認してみたい場合は
Demo
シーンを用意してあるのでいじってみたりビルドしてみてください。セーフエリアの自動調整機能
Canvas
直下のGameObject
にSafeArea
/UnsafeArea
コンポーネントをAddComponent
するとRectTransform
のAnchor
が自動で調整されます。直下じゃなくてもCanvas
に至るまでの親GameObject
のRectTransform
がすべて縦横に完全にストレッチするようにしてあれば正常に動作します。
親GameObject
が存在する場合のRectTransform
の設定デバイスのフレーム表示/セーフエリアの範囲表示
セーフエリアありの端末解像度を選択するとGameウィンドウの左上に歯車アイコンが表示されるので、そこから表示・非表示を切り替え可能です。
こだわった点
基本的には以下の3点を追求しました。
- uGUIのオートレイアウトのような簡単で自然な使い心地
- 使ってて細かい挙動を含めイライラしない
- シンプルかつ高い拡張性
リアルタイムプレビュー
- エディタ上で
- 既存のGameウィンドウの解像度選択を変更するだけで
- リアルタイムに
- 再生/非再生の状態を問わず
- セーフエリアあり端末での見た目が自動で調整
されるので、ビルドしなくてもセーフエリアありの場合のレイアウトを手軽に確認できます。
設定変更がGameウィンドウから可能
通常アセットやエディタの拡張の設定は
[MenuItem]
を使用してグローバルメニュー(+ショートカットキー)から行えるようにするのですが、Gameウィンドウ上に設定のUIを配置することでon/off切り替えの煩雑さを軽減しました。ちなみにUIの配置にはUIElementsを使用しています。
高い拡張性
端末解像度とセーフエリアの情報は
ScriptableObject
を継承したアセットとして保持しているので、追加・拡張が簡単にできます。
SafeArea
,UnsafeArea
コンポーネントで満たせない複雑な要件の場合も、SafeAreaBase
クラスを継承することでエディタ上でのリアルタイムプレビューの機能が簡単に実装できます。Gitフレンドリー
セーフエリアの対応は
RectTransform
のアンカーを自動調整することで実現していますが、シーンやプレハブの保存前に必ずアンカーをリセットしています。これにより「Gameウィンドウで異なる解像度を選択して保存 → Gitでdiffが出る」ということが起きません。
実装の詳細
Gameウィンドウの情報を利用するにあたり、Gameウィンドウの実態である
GameView
クラスが公開されていないため、リフレクションを用いてそのデータにアクセスしています。実際にリフレクションを用いているのは
GameViewProxy
クラスのみで、値の変更についてはGameViewEvent
クラスが監視・イベント化しています。実機のフレーム表示は前もって用意したフレーム画像を描画していて、セーフエリアの大きさは事前にシミュレータビルドで収集した値を
ScriptableObject
を継承したGameViewScreen
アセットに保存・使用しています。デバイスのフレーム表示とセーフエリアの境界線表示はそれぞれ
DeviceFrameDrawer
コンポーネントとSafeAreaDrawer
コンポーネントが行っていて、それらのコンポーネントがアタッチされたAutoScreenManager
プレハブが自動的にHierarchyに配置されるようになっています。このプレハブインスタンスはシーンやビルドには含まれず、Hierarchyにも表示されません。対応端末追加の手順
エディタ上でのプレビューはマスターデータを追加するだけで簡単に対応機種を増やすことが可能です。
- Gameウィンドウから手作業で解像度を追加します。自動化したい場合はGameViewSizeHelperを使うと良いです。
- Projectウィンドウで右クリックし、
Create
->ScriptableObjects
->GameViewScreen
からマスターデータを保持するためのアセットを作成します。- 2.で作成したアセットの各値をInspectorウィンドウから設定します。
- 解像度やセーフエリアの値はDemoシーンをシミュレータや実機にビルドして確認すると楽です。
Base Text
は1.で追加した解像度のLabel
と同じ文字列にしてください。- 必要であればデバイスのフレーム画像を追加し、アセットの
Frame
にセットしてください。- 解像度を適当に変更すると反映されます。Unityを再起動する必要はありません。
Tips
- 自動で追加される
RuntimeSafeAreaUpdater
コンポーネントは画面の回転を許可していないアプリの場合は不要- 11インチのiPadはGameウィンドウから解像度を登録するとプレビュー可能
- デフォルトだとUnityに解像度が登録されてない
- ラベル名は「iPadPro 2388x1668 Landscape」「iPadPro 2388x1668 Portrait」で登録する
- 12インチのiPadの解像度を選択するとセーフエリアあり(第3世代)としてプレビュー表示される
- 第1〜2世代のセーフエリアなしレイアウトを確認したい場合は適当なラベル名で別途解像度を追加すればOK
- セーフエリア境界線の太さ・色は
AutoScreenManager
プレハブから調整可能SafeAreaDrawer
コンポーネントは単体で実機でも動作可能ライセンス
MIT
参考
- GameView.cs(UnityCsReference)
- GameViewSizes.cs(UnityCsReference)
- UniSafeAreaAdjuster(GitHub)
- NotchSolution(GitHub)
最後に
やりたいことや細かい挙動の調整を全部やろうとしたら結果的に
- 新しいプレハブAPIの使い方
- UIElements
[ExecuteAlways]
HideFlags
等々Unityのよく知らなかった様々な機能について詳しく知る良い機会になりました。個別の知見についてはアドベントカレンダーのときにでもまとめたいと思います。
コードにコメント書かないマンなのですがコード自体はシンプルで読みやすいと思うので、わからないことがあったらとりあえずコードを読んでみてください。
実は実践未投入なので、何か不具合があればPRかTwitterへ → @su10_dev
- 投稿日:2019-11-25T00:18:41+09:00
Scoped Model を利用した場合の画面遷移でうまくいったコード例(Flutter)
最近Flutterをいじり始めました。Scoped Modelを利用するとスッキリ書けるので、そのコードを書いたときの画面遷移時に具体的にどうコードを書くとうまくいったかをレポートします。
[Scoped Model 初めて聞いた方へ]Scoped Model とは
公式のパッケージサイトは下記です。
https://pub.dev/packages/scoped_model
基礎原理の解説について下記記事が参考になりました。
https://qiita.com/hayassh/items/690fa0d6528e056617b5
自分の言葉で書くと、数値や文字列の処理部分とデザインテンプレート部分をModel定義をすることで綺麗に分離ができます。あと、このパッケージは、GoogleのFlutter開発チームが提供してくれているVeggie Seasonsというアプリのコードにも採用されていたので、自分もやってみようと思いました。Veggie Seasonsのコードは以下です。
https://github.com/flutter/samples/tree/master/veggieseasons
ページ遷移時の試行錯誤時のエラー解決で得た知見
今回はこちらを共有したいのです。Scoped Modelを使わない場合と比較できるように書いてみました。
#main.dart//1:route定義をMaterialAppのところで行って、 void main() { runApp(MaterialApp( title: 'Demo', initialRoute: '/', routes: { '/': (context) => FirstScreen(), '/second': (context) => SecondScreen(), }, )); } //2:↓最初のページに当たるFirstScreen部分のStatelessWidget記述です。 class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Demo'), ), body: Center( // 中略 child: RaisedButton( padding: EdgeInsets.all(12.0), shape: StadiumBorder(), child: Text( "次のページへ", style: TextStyle(fontSize: 20.0, color: Colors.white), ), color: Colors.green, onPressed: () { Navigator.pushNamed(context, "/second"); //3:↑で次のページ"SecondScreen()"へ移動クラス生成です。 }, ), //次の画面(SecondScreen) class SecondScreen extends StatelessWidget { //以下略SecondScreenの表示内容などが書かれています。 }↑これをScoped Model 活用して記述する際、
下記のように書くとうまくいきました。#main.dartimport 'model.dart'; /* ↑model(今回はDemoModelという名称です)を別ファイルで定義しました。 このmodelそのものの部分の解説は、 ページ遷移の解説に戻るまでにかなり時間がかかるので割愛します。 ※本記事前述の解説などの参考になる記事が複数存在しますので、ご覧ください。 */ import 'package:scoped_model/scoped_model.dart'; // scoped_modelのパッケージをインポートします。 DemoModel demoModel = DemoModel(); //modelを生成しておきます。 void main() { runApp(MaterialApp( title: 'Demo', home: ScopedModel<DemoModel>( model: demoModel, child: new FirstScreen() ) )); } /* route指示部分がごっそり無くなっています。書いてみるとエラーになりました。 遷移時に各ボタンなどの実行時のコードに Scoped Modelの子孫のクラスとなる次のページのクラス生成を指示することになります。 */ //最初の画面(FirstScreen) class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text('Demo'), ), body: Center( // 中略 child: RaisedButton( padding: EdgeInsets.all(12.0), shape: StadiumBorder(), child: Text( "次のページへ", style: TextStyle(fontSize: 20.0, color: Colors.white), ), color: Colors.green, onPressed: () { Navigator.push( context, new MaterialPageRoute<Null>( settings: const RouteSettings(name: "/second"), builder: (BuildContext context) { return MaterialApp( home: ScopedModel<DemoModel>( model: quizBrainModel, child: new SecondScreen(), ), ); } ), ); /* ↑ScopedModelを使わない場合、 Navigator.pushNamed(context, "/second");となっていた部分です。 */ }, ), //次の画面(SecondScreen) class SecondScreen extends StatelessWidget { SecondScreen(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( title: Text("Second Page"), ), body: //中略 new ScopedModelDescendant<DemoModel>( builder: (context, child, model) => new Row( children: model.getSomeList()) ) /* ↑このページは、Scoped Model子孫クラスなので new ScopedModelDescendant<Model名称>で メソッドや変数が呼び出せます。 例としてmodelで指定しておいた、 特定のListを呼び出すメソッドを書いています。 */参考になればと思います。他にも書き方があるかもしれません。ご指摘などあればコメントいただければと思います。