LoginSignup
27
35

Flutterを知らない人がFlutterでアプリを公開するまでにどのような情報が必要でどのようなエラーを解消したか

Last updated at Posted at 2021-03-30

内容が古くなってきたので新しい記事を作成しています。↓
Flutter キャッチアップ

記事の趣旨

普段iOSで開発をしておりAndroidはほぼ経験がない者が、Flutterでアプリを作り公開するまで、どのようなことを調べなければいけなかったかを記載します。

アプリ

うつとか診断

iOS https://apps.apple.com/jp/app/うつとか診断/id1559362485
Android https://play.google.com/store/apps/details?id=jp.co.SoLaMiSmile.depressionDiagnosis

リポジトリ GitHub https://github.com/Satoru-PriChan/Depression_Diagnosis
CI/CD CodeMagic https://blog.codemagic.io/getting-started-japanese/

必要時間 測ってませんが50~150時間の間?(1日平均30分~1時間半ほど作業して3ヶ月強かかったため)

全体的な参考 flutter create してから5日で iPhone / Androidアプリを公開した話

収穫

  • Android、Android Studio, Google Play Storeのことも少し分かった 
  • FlutterではUIの作り方がSwiftUIと似ているやり方(正式な名称が分かりませんが、命令的ではなく宣言的にUI部品を定義し、親部品を書いた後インデントを一つ挿入して子部品を書いていくというような、直感的なやり方)のため、それに慣れてきた 
  • エラー発生->調査->解消の流れを何度も繰り返したためあたかも「エラー解消1000本ノック」のようになり問題解決力が向上した。

本題

Flutter 基本

Flutter入門 - 簡単なアプリを作ってUI宣言やホットリロードなど便利機能の使い方を理解しよう

Flutter でモバイルアプリを作ってみる 入門編① 〜ヘッダーとフッター〜

export PATH="$PATH:/Users//development/flutter/bin"

インストール後、flutter doctorでさらに環境をととのえる
https://flutter.dev/docs/get-started/install/macos#run-flutter-doctor
https://qiita.com/mkosuke/items/7957e71968aefc6558be

Android studio のflutter extension とdirt extensionが必要だった。

Flutter plugin not installed this adds Flutter specific functionality Dart plugin not installed this adds Dart specific functionality

-> stack over flow
ln -s ~/Library/Application\ Support/Google/AndroidStudio4.1/plugins ~/Library/Application\ Support/AndroidStudio4.1
↑を叩くとうまくいった。単に、flutter doctorが探す場所が分かるようにシンボリックリンクを追加しただけである。

とりあえずサンプルを作成

Flutter入門 - 簡単なアプリを作ってUI宣言やホットリロードなど便利機能の使い方を理解しよう

外部ライブラリ追加

http request用ライブラリ
https://pub.dev/packages/http

Android Studio ショートカット

Command + Shift + f 検索
https://developer.android.com/studio/intro/keyboard-shortcuts?hl=ja

変数・関数の名前を右クリック -> find usage: その変数・関数が呼ばれている箇所の検索

シングルトン

class MyStore {
  static final Map<String, dynamic> _items = <String, dynamic>{};
  static final MyStore _cache = MyStore._internal();

  MyStore._internal();

  factory MyStore() {
    return _cache;
  }

  set(String key, dynamic data) => _items[key] = data;
  get(String key) => _items[key];
}

if文、for文

Futures, async, await

Dynamic

動的型付け
https://note.com/hatchoutschool/n/n767701b099b0

APIやSQLiteからデータを取得するときよく使う。

Android Studio のショートカットキー

Shift 2連打 -> プロジェクト全体の検索

Ctrl + E -> 最近開いたファイル

Command + shift + A -> 全ショートカットキー一覧

Underscore

_のついた変数、クラス、メソッドは、それが宣言されたdartファイル内でのみアクセスできる。

クラス名._(); とやれば、コンストラクタがプライベートとなり、そのクラスは外から初期化できなくなる。

Codable 的なもの

Json <-> map, list

Map, list <-> class object

ない 自分で逐次関数を作るしかない

定数のリスト

クラスなどの中でStatic const

またはトップレベルでconst

const String TABLE_NAME_CAT = 'cat’;   など

Constはコンパイル時に値が必要な定数で、finalは必要ない定数(動作時に一回だけ値を入れられる)
こうした用途で使うならconstがいいだろう。

文字列へ変数を埋め込み

String embed = "Moco";
print("${embed}'s kitchen"); // ${変数名}
print("$embed's kitchen"); // {} は省略可

Generics 的なもの

Dynamic

Protocol 的なもの

Abstract class

Abstract getterって?

String get tableNameなどとするとget-onlyの扱いにできる。

implement時は

@override
// TODO: implement tableName
String get tableName => TABLE_NAME_CAT;

のように書く。

Class などのタイプそれ自体を表すには

DB

Flutterでのデータ永続化 https://flutter.dev/docs/cookbook/persistence

DB:

【Flutter】sqfliteでローカルDBを実装する
FlutterでローカルDBを扱う方法
Persist data with SQLite

FlutterでのDBClient例 ただしプロトコルは使っていない

Enum

そのほかのtips

画面遷移

[Flutter]画面遷移のやり方  https://qiita.com/kono-hiroki/items/b1a8f19dfab371e7816d
公式 https://flutter.dev/docs/cookbook/navigation/navigation-basics

画像素材

イラストレイン(商用フリー) http://illustrain.com

うつ病診断

curl -v -H "x-rapidapi-host: onlinecounselling-online-counselling-v1.p.rapidapi.com" -H "x-rapidapi-key: db95ac19b8mshab0ad98f2d9c12dp19fd80jsnbb2707773797" https://onlinecounselling-online-counselling-v1.p.rapidapi.com/docs-anxiety-treatment

うつ病チェック https://utsu.ne.jp/self_check/

簡易よく鬱症状尺度 https://www.mhlw.go.jp/bunya/shougaihoken/kokoro/dl/02.pdf https://www.mdcalc.com/quick-inventory-depressive-symptomatology-qids

エラー Target of URI doesn't exist: 'package:flutter/material.dart'.

プロジェクトフォルダでflutter pub getを叩く https://stackoverflow.com/questions/44909653/visual-studio-code-target-of-uri-doesnt-exist-packageflutter-material-dart

=> とは

The fat arrow syntax is simply a short hand for returning an expression and is similar to (){ return expression; }.

エラー The following assertion was thrown resolving an image codec Unable to load asset

画像の名前は、pubspec.ymlに記載したものを正確に記載する必要がある。

エラー Vertical viewport was given unbounded height

shrinkWrap: true を追加し、ListViewの高さがその中身のWidgetの高さによって決定されるようにした。

端末サイズ取得

https://note.com/hatchoutschool/n/n223ba8f1f3d7
final double deviceHeight = MediaQuery.of(context).size.height;

デバッグ

コンソールログ出力
print(“hello world”);

ブレークポイント
ブレークポイントの設置後、debugボタン(runボタンの隣)を押す。

UI階層構造
Open Flutter DevToolsボタンを開くとブラウザが開き、Widgetの階層構造がチェックできる
https://flutter.dev/docs/development/tools/devtools/inspector

UIの基本

公式解説 https://flutter.dev/docs/cookbook/design/drawer

Creating Reusable Custom Widgets in Flutter https://www.raywenderlich.com/10126984-creating-reusable-custom-widgets-in-flutter

-> 使い回しにはWidgetを使う
Stateful widget は自分で自分自身の外見を変える Stateless Widgetは自分では変えない。どちらもbuildメソッドにより外見を定義して返す。

Glidview https://flutter.dev/docs/cookbook/design/orientation

質問の答えなので上からしたに並んでるだけの方が良さそう。

Create a horizontal list https://flutter.dev/docs/cookbook/lists/horizontal-list
Flutter: Displaying Dynamic Contents using ListView.builder
https://medium.com/@DakshHub/flutter-displaying-dynamic-contents-using-listview-builder-f2cedb1a19fb

Item Selection in List View on Tap in flutter using ListView.Builder https://medium.com/@gadepalliaditya1998/item-selection-in-list-view-on-tap-in-flutter-using-listview-builder-612f6608505a

Selectable List View In Flutter https://vermahitesh.medium.com/select-list-items-in-flutter-21f58765c19b

要素の大きさを決定・制限する

Understanding constraints
https://flutter.dev/docs/development/ui/layout/constraints

SizedBox, ConstrainedBox

https://itome.team/blog/2019/12/flutter-advent-calendar-day9/ FlutterのBoxConstraintsを理解する
https://nzigen.com/flutter-reference/2018-05-01-constrained-box.html 要素の大きさを制限する

iOSの設定

iosフォルダにあるRunner.xcworkspaceをXCodeで開いて設定を編集する。バージョンとDeployment Targetをいじった場合は、Flutter側の設定ファイルも更新

cp Users/development/flutter/bin/cache/artifacts/engine/ios/Flutter.podspec: No such file or directory

プロジェクトフォルダでflutter precacheを実行

Error: Error when reading 'lib/main.dart': No such file or directory package main.dart: Error: No 'main' method found. Try adding a method named 'main' to your program.

main.dartファイルがlib直下になかったのでlib直下に移動

A RenderFlex overflowed by 134 pixels on the bottom.

ウィジェットがデカすぎて画面をはみ出す、よくあるケース。単純にはみ出している全体をListViewで囲えばいい。

https://stackoverflow.com/questions/49480051/flutter-dart-exceptions-caused-by-rendering-a-renderflex-overflowed
全体をSingleChildScrollViewで囲うことも考えられる。
https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html

test

Unit Test, Widget Test, Integration Testがある。
https://flutter.dev/docs/testing
https://flutter.dev/docs/testing/integration-tests
https://flutter.dev/docs/cookbook/testing/integration/introduction

Automatically assigning platform iOS with version 12.1 on target Runner because no platform was specified. Please specify a platform for this target in your Podfile.

platform :ios, '12.0'などをios/podfileで指定する

Cocoapods: LoadError - dlsym(0x7fc10fbfc9c0, Init_ffi_c): symbol not found

Big Surで起きる。arch -x86_64 sudo gem install ffiを叩く。
https://github.com/flutter/flutter/wiki/Developing-with-Flutter-on-Apple-Silicon

Unhandled Exception: type 'Future' is not a subtype of type

awaitをつけてなかった

[VERBOSE-2:profiler_metrics_ios.mm(184)] Error retrieving thread information: (ipc/send) invalid destination port

ios シミュレータを再起動
https://github.com/flutter/flutter/issues/63025

flutter: ignore recovered database ROLLBACK error DatabaseException(Error Domain=FMDatabase Code=1 "cannot rollback - no transaction is active" UserInfo={NSLocalizedDescription=cannot rollback - no transaction is active}) sql 'ROLLBACK' args []}

openDatabase 関数が終わる前(databaseの生成が終わる前)に、改めてそのdatabaseにアクセスして初期データをinsertしようとしてしまっていた。その代わり、openDatabaseのonCreateクロージャの引数として渡されるdatabaseを用いて、insertをすると上手くいった。

The default value of an optional parameter must be constant.

デフォルト値を定数にしろ
関数内でデフォルト値を与えるのもあり

void f([int value]) {
  value ??= defaultValue;
}

flutter アプリの公式の例

widgetのライフサイクル

initState()はviewDidLoadやonCreateに相当する。

https://medium.com/flutter-community/flutter-lifecycle-for-android-and-ios-developers-8f532307e0c7
https://qiita.com/sekitaka_1214/items/b087f9e9fc13424a64bb

map 関数

明らかにコーディングはおかしくないのに意味不明なエラーがいっぱい出る

Android Studioの再起動、PC再起動
https://android-java.hatenablog.jp/entry/2016/10/01/080806

集計したい時

reduce関数

日付関係

DateTime
toString()
toString()で取得したstringはparseで元に戻すことができる。
parse()
https://api.dart.dev/stable/2.12.1/dart-core/DateTime-class.html

DateFormatter
使い方
https://stackoverflow.com/questions/58337796/how-to-remove-time-from-date-flutter

Undefined class 'DateFormat'

intlパッケージをpub getし、目的のファイルでimportする。
https://pub.dev/packages/intl/install

DB insert idについて

DARTには関係ないが、SQLiteでカラムに対して INTEGER PRIMARY KEY を設定した場合、データを追加した時に INTEGER PRIMARY KEY を設定したカラムの値を指定しないと自動的に値が格納される。ので、そのカラムに特に値は指定せずinsertすればいい。
https://www.dbonline.jp/sqlite/table/index9.html

日付でソート

products.sort((a,b) {
    return a.compareTo(b);
 });

ForEachで複数の非同期処理を行い、全て終わってから下の処理に進みたい

  Future.forEach(list, (num) async {
    // do something
  });

.initState() returned a Future. State.initState() must be a void method without an async keyword. Rather than awaiting on asynchronous work directly inside of initState, call a separate method to do this work without awaiting it.

ウィジェット表示前に非同期処理をしたい場合、initState()メソッドをasyncにするのではなくFutureBuilderを使う。

Instead, you need to have your widget build normally and then have a way to notify your widget to update when the Future has returned. This is most easily done with a FutureBuilder:

@override
Widget build(BuildContext context) {
  return FutureBuilder(
    future: doSomeAsyncStuff(),
    builder: (context, snapshot) {
      if (!snapshot.hasData) {
        // Future hasn't finished yet, return a placeholder
        return Text('Loading');
      }
      return Text('Loading Complete: ${snapshot.data}');
    }
  );
}

ListTile widgets require a Material widget ancestor.

Scafold, Materialで囲う。
https://stackoverflow.com/questions/51772910/no-material-widget-found-textfield-widgets-require-a-material-widget-ancestor/51773529

デフォルト実装を提供するには

extends, またはmixinを使う。
https://stackoverflow.com/questions/17789399/providing-default-implementation-for-method-in-abstract-class

複数のウィジェットの横幅を同じサイズとしたい

それらのウィジェットをExpandedの中に入れ、それらをRowの中に入れる。
https://stackoverflow.com/questions/52583856/make-buttons-in-a-row-have-the-same-width-in-flutter

アプリアイコン

https://qiita.com/rkowase/items/e0f3f8aec207ed8567aa
https://pub.dev/packages/flutter_launcher_icons
flutter_launcher_iconsを使う。

iphone実機で見た時アイコンが反映されていない箇所があるように思ったが、iphoneを再起動したら治った。おそらくspringboardのバグ。

Android アダプティブアイコン

説明 https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive
ジェネレータ https://easyappicon.com

Android キーストア

https://flutter.dev/docs/deployment/android#create-a-keystore
https://qiita.com/rkowase/items/f1012ef0738791dd6084

code magicを使うならこちら https://blog.codemagic.io/the-simple-guide-to-android-code-signing/

This operation couldnt be completed. Unable to locate a Java Runtime. [macOS]

Java Runtimeを導入する
https://code2care.org/howto/this-operation-couldnt-be-completed-unable-to-locate-a-java-runtime-maos

android studio cannot resolve symbol 'GradleException'

FileNotFoundException()をGradleException()の代わりに使う。
https://stackoverflow.com/questions/55575122/android-studio-cannot-resolve-symbol-gradleexception

Failed to read key from store "/Users/builder/keystore/key.jks": No key with alias 'upload' found in keystore /Users/builder/keystore/key.jks

コマンドでkeyを生成する時に引数としてaliasを渡しているはずだが、ここで指定したaliasとkey.propertiesで書いているaliasが一致していることを確認。

building for iOS-armv7 but attempting to link with file built for iOS-arm64 Undefined symbols for architecture armv7:

armv7(iPhone3~5)の古いアーキテクチャ向けにビルドしようとすると、sqfliteが対応していないためにバグが起きる模様。色々解決策はありそうだが、そのような古いモデル向けにはビルドせず、アーキテクチャをarm64のみに設定したら通った。

Incorrect use of ParentDataWidget. The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type FlexParentData to a RenderObject, which has been set up to accept ParentData of incompatible type ParentData. Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. Typically, Expanded widgets are placed directly inside Flex widgets. The offending Expanded is currently placed inside a SizedBox widget

ExpandedはRow, Column, flexの配下のみで使うようにした。
https://stackoverflow.com/questions/54905388/incorrect-use-of-parent-data-widget-expanded-widgets-must-be-placed-inside-flex

App-specific password does not match required pattern (xxxx-xxxx-xxxx-xxxx)

Codemagicでは、Apple IDのパスワードそのものではなくApp Specific Passwordを入力する。https://support.apple.com/en-us/HT204397

Only releases with status draft may be created on draft app

google play consoleで一回手動でアプリをアップロードしてから出ないとコマンドでのアップロードはできない。

27
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
35