depression_diagnosis
A new Flutter application.
Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter, view our online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
Flutter 基本
Flutter入門 - 簡単なアプリを作ってUI宣言やホットリロードなど便利機能の使い方を理解しよう Flutter でモバイルアプリを作ってみる 入門編① 〜ヘッダーとフッター〜
export PATH="$PATH:/Users/a999-503/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通信を行う」「リストビューによって多数の項目を表示する」といった機能を実装します。本記事ではGitHubのFlutterリポジトリにあるIssuesを一覧表示するアプリを作っていきます。
外部ライブラリ追加
http request用ライブラリ https://pub.dev/packages/http
Android Studio ショートかっと
Command + Shift + f 検索 https://developer.android.com/studio/intro/keyboard-shortcuts?hl=ja
シングルトン
https://sbfl.net/blog/2015/01/04/implementing-factory-and-singleton-pattern-in-dart/
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文
https://www.cresc.co.jp/tech/java/Google_Dart2/language/control_flow/control_flow.html
Futures, async, await
https://dart.dev/codelabs/async-await
Dynamic
動的型付け https://note.com/hatchoutschool/n/n767701b099b0
APIやSQLiteからデータを取得するときよく使う。
Android Studio のショートカットキー
Shift 2連打 -> プロジェクト全体の検索
Ctrl + E -> 最近開いたファイル
Command + shift + A -> 全ショートカットキー一覧
Underscore
_のついた変数、クラス、メソッドは、それが宣言されたdartファイル内でのみアクセスできる。
クラス名._(); とやれば、コンストラクタがプライベートとなり、そのクラスは外から初期化できなくなる。
https://www.366service.com/jp/qa/530a2dae7c68f1f136dec8cbe4350bd7
Codable 的なもの
Json <-> map, list
Map, list <-> class object
ない
定数のリスト
クラスなどの中でStatic const
またはトップレベルでconst
const String TABLE_NAME_CAT = 'cat’; など
Constはコンパイル時に値が必要な定数で、finalは必要ない定数(動作時に一回だけ値を入れられる) こうした用途で使うならconstがいいだろう。
文字列へ変数を埋め込み
https://sites.google.com/site/dartmedart/string-kurasuni-guansurumemo
String embed = "Moco"; print("${embed}'s kitchen"); // ${変数名} print("$embed's kitchen"); // {} は省略可
Generics 的なもの
https://dart.academy/generics-in-dart-and-flutter/
Dynamic
https://dart.dev/guides/language/language-tour#using-generic-methods
普通にとかやってクラスやメソッドに指定するもの
Protocol 的なもの
Abstract class
https://dart.dev/guides/language/language-tour#abstract-classes
https://stackoverflow.com/questions/52854383/flutter-protocols-do-they-exist
https://medium.com/dev-genius/flutter-protocol-oriented-programming-c27082af9bfc
Abstract getterって?
String get tableNameなどとするとget-onlyの扱いにできる。
implement時は
@override // TODO: implement tableName String get tableName => TABLE_NAME_CAT;
のように書く。
Implements と extends の違い??
Static はabstract classに書けないようだ・・・ なので、クラスの名前をテーブルの名前として使うしかないかもしれない。
Class などのタイプそれ自体
https://api.dart.dev/stable/2.10.4/dart-core/Type-class.html
DB
Flutterでのデータ永続化 https://flutter.dev/docs/cookbook/persistence
DB:
【Flutter】sqfliteでローカルDBを実装する FlutterでローカルDBを扱う方法 Persist data with SQLite
Swiftではラッパーを以下のように作っていたので、dartでも以下のように便利なやつを作りたい。
import Foundation import RealmSwift import RxSwift
final class RealmUtil {
/// query: 検索
/// - Parameters:
/// - type: Objectのtype
/// - predicate: 検索するNSPredicate, 全取得の場合はnilを設定
/// - sortedKey: ソート KeyPath
/// - ascending: bool
/// - limit: 取得数, 全取得の場合は0以下を指定
/// - Returns:
/// Observable<G> (検索結果は一件ずつ)
static func query<G: RealmObject>(type: G.Type, predicate: NSPredicate?, sortedKey: String = "", ascending: Bool = true, limit: Int = 0) -> Observable<G> {
Observable.create { observer in
do {
let realm = try Realm()
var objects: Results<G>
if let predicate = predicate {
objects = realm.objects(type).filter(predicate)
} else {
objects = realm.objects(type)
}
let items = Array(sortedKey.isEmpty ? objects :
objects.sorted(byKeyPath: sortedKey, ascending: ascending))
if items.isEmpty != true {
let limitCount = (limit < 1 || limit > items.count) ? items.count : limit
for i in 0 ..< limitCount {
observer.onNext(items[i])
}
}
observer.onCompleted()
} catch {
Logger.debug("RealmUtil.query() error:\(error)")
observer.onError(AppError.noData)
}
return Disposables.create()
}
}
/// add: 新規追加 or 更新
/// - Parameters:
/// - object: 追加または更新するRealmObject
/// - updatePolicy: What to do if an object with the same primary key alredy exists. Must be `.error` for objects without a primary key.
/// - Returns:
/// Observable<Void>
static func add<G: RealmObject>(object: G, updatePolicy: Realm.UpdatePolicy = .modified) -> Observable<Void> {
Observable.create { observer in
do {
let realm = try Realm()
try realm.safeWrite {
realm.add(object, update: .modified)
observer.onNext(Void())
observer.onCompleted()
}
} catch {
Logger.debug("RealmUtil.add() error:\(error)")
observer.onError(AppError.generic)
}
return Disposables.create()
}
}
/// delete: 削除
/// - Parameters:
/// - objects: 削除するRealmObject Sequence
/// - Returns:
/// Observable<Void>
static func delete<G: RealmObject>(objects: [G]) -> Observable<Void> {
Observable.create { observer in
do {
let realm = try Realm()
try realm.safeWrite {
realm.delete(objects)
observer.onNext(Void())
observer.onCompleted()
}
} catch {
Logger.debug("RealmUtil.delete() error:\(error)")
observer.onError(AppError.generic)
}
return Disposables.create()
}
}
/// delete object data: 特定のRealm Objectを削除
/// - Parameters:
/// - objects: 削除するRealmObjectのtype
/// - predicate: 絞り込み条件。全削除の場合はnilを指定
/// - Returns:
/// Observable<Void>
static func deleteObjectData<G: RealmObject>(type: G.Type, predicate: NSPredicate? = nil) -> Observable<Void> {
Observable.create { observer in
do {
let realm = try Realm()
try realm.safeWrite {
var objects: Results<G>
if let predicate = predicate {
objects = realm.objects(type).filter(predicate)
} else {
objects = realm.objects(type)
}
realm.delete(objects)
observer.onNext(Void())
observer.onCompleted()
}
} catch {
Logger.debug("RealmUtil.deleteObjectData() error:\(error)")
observer.onError(AppError.generic)
}
return Disposables.create()
}
}
}
extension RealmUtil { static func setRealmConfigration() { var config = Realm.Configuration() config.schemaVersion = RealmConfig.schemaVersion Realm.Configuration.defaultConfiguration = config } }
*/
FlutterでのDBClient例 ただしプロトコルは使っていない
https://qiita.com/popy1017/items/7ada79b07281f4a8e544
Generics として指定したクラスそれ自体を中で使うには・・・genericメソッドではなくgenericクラスでないとダメかもしれない。
https://qiita.com/dracrowa6539/items/6534740b3a917f8fbb0d
https://camellabs.com/flutter-sqlite-crud-example-form/
Enum
https://stackoverflow.com/questions/38908285/add-methods-or-values-to-enum-in-dart
そのほかのtips
https://logmi.jp/tech/articles/303939
画面遷移
[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
=> とは
https://stackoverflow.com/questions/51868395/flutter-dart-difference-between-and
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
https://stackoverflow.com/questions/50252569/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の設定
https://flutter.dev/docs/deployment/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
iOS
with version 12.1
on target Runner
because no platform was specified. Please specify a platform for this target in your Podfile.
Automatically assigning platform 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 シミュレータを再起動 flutter/flutter#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 アプリの公式の例
https://flutter.github.io/samples/#
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 関数
https://stackoverflow.com/questions/49941361/dart-mapping-a-list-list-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);
});
https://stackoverflow.com/questions/57000166/how-to-sort-order-a-list-by-date-in-dart-flutter
ForEachで複数の非同期処理を行い、全て終わってから下の処理に進みたい
Future.forEach(list, (num) async {
// do something
});
https://qiita.com/hisw/items/2df0052a400263d5863e
async
keyword. Rather than awaiting on asynchronous work directly inside of initState, call a separate method to do this work without awaiting it.
.initState() returned a Future. State.initState() must be a void method without an ウィジェット表示前に非同期処理をしたい場合、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で一回手動でアプリをアップロードしてから出ないとコマンドでのアップロードはできない。