Flutterを知らない人が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 的なもの
https://dart.academy/generics-in-dart-and-flutter/
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;
}
https://dart.dev/tools/diagnostic-messages#non_constant_default_value
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で一回手動でアプリをアップロードしてから出ないとコマンドでのアップロードはできない。
Author And Source
この問題について(Flutterを知らない人がFlutterでアプリを公開するまでにどのような情報が必要でどのようなエラーを解消したか), 我々は、より多くの情報をここで見つけました https://qiita.com/satoru_pripara/items/1427b8ea2ce7df3a3e85著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .