30日後に始まるFlutter
0日目:自己ステータス
Android開発5年ぐらい
Flutter2年弱前に副業でちょっと
以上。
1日目:2024年9月のFlutter事情調査
Flutter 3.24 and Dart 3.5
自分がやってた当時はFlutter 2だった記憶
- Flutter3での変化
- 2022年5月リリース
- 全てのデスクトッププラットフォームへの対応
- モバイルアップデート
- 折りたたみ式携帯対応
- 簡素化されたiOSリリース
- Gradleバージョンの更新
- ウェブ
- ツールのアプデ
- lint
- パフォーマンス
- インぺラー
- パフォーマンス向上とジャンク軽減を目指すFlutterの新エンジン
- Dart3の変化
- 2023年5月リリース
- 100%Nullセーフティ
- Record:https://dart.dev/language/records
- 他の言語でいうところの タプル型
- 構造化データを構築する方法を簡略化
- 固定サイズ、異なる型でもOK
- 他の言語でいうところの タプル型
- Patterns:https://dart.dev/language/patterns
- 構造化データを個々の要素に分解して扱いたい
- クラス修飾子:https://dart.dev/language/class-modifiers
- 「abstract class」のabstractみたいな感じでinterface、base、final を使用する
- 2023年5月リリース
2日目:古サンプルアプリのビルド/pubspec.yaml
最終変更半年前ぐらいの自分のSampleAppを開いてみたら案の定ビルドできなかったので対応していく
ElevatedButton.styleFrom(primary: colorName)
primaryがなくなってるらしい。
「primary」は、「backgroundColor」に
「onPrimary」は、「foregroundColor」に
一旦ビルドできた
pubspec.yamlに関して
チートシートに丁寧に書かれてる
- publish_to:パッケージを公開する場所を指定します。
'none'
とすることでパッケージの公開を防止できます。 - environment:ビルド環境に関する設定
- sdk:dartのバージョン
- flutter:Flutterのバージョン
- dependencies
- cupertino_icons:iosアプリスタイルのUI作成するためにCupertinoIconsを使う場合必要
- dev_dependencies:開発時に利用するライブラリ。リリースビルドには含まれない。テスト・Lint系
- flutter:アプリのアセットとして含むファイルやフォントの指定
バージョン指定
^
を毎回忘れてしまう
フィールド | 説明 |
---|---|
>=0.1.2 <1.0.0 | 0.1.2以上、1.0.0未満のバージョンを許可 |
^0.1.2 | 0.1.2以上、1.0.0未満のバーションを許可。指定されたバージョン以上でメジャーバージョンが同じであれば許可 |
any | バージョン指定なし |
空白 | any同様 |
参考
doc
3~8日目:ポケモン
こちらやってみます
Widget系
-
Image.network()
- ネットの画像読み込み
- 画像表示に関して色々したい場合は、
BoxDecoration
&DecorationImage
でやりそう
-
Spacer()
- 残りのスペース埋める
-
Chip
- M2は円形寄り, M3はRadius8
-
Stack
- 重ねるWidget。ComposeでいうBox
-
ListView
- リスト作るやつ
- 各アイテムは
ListTile
で作ると簡単- 複雑なUIは無理では?業務で使うことあるのかな?
- leading:先頭につくやつ
-
withOpacity
- 色の透過を指定
-
circular
- Radiusの指定
Theme
MaterialAppのthemeで指定する
ThemeMode mode = ThemeMode.system; // デバイスのシステムを利用
MaterialApp(
theme: ThemeData.light(
useMaterial3: true, // M3の利用
),
darkTheme: ThemeData.dark(), // ダークテーマ
themeMode: mode, // テーマのモードを指定。
);
-
computeLuminance
- 暗ければ 0 に近い値が、明るければ 1 に近い値
- テキストの色の出しわけ等で使える
- デザインシステムがある場合は使わないかな
State
StatefulWidget
受け取った値をState側で使う場合はinitStateの中でwidget.xxxという形式で指定
@override
void initState() {
super.initState();
_current = widget.themeMode;
}
ChangeNotifier - ChangeNotifierProvider
非推奨になったらしい
stateの変更があったときに自動で更新される
StateNotifier - StateNotifierProvider
これもRiverpod2.0からちょっと非推奨。AsyncNotifierとNotifierが最近らしい
// Providerの作成
final pokemonThemeProvider =
StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
(ref) => ThemeModeNotifier());
class ThemeModeNotifier extends StateNotifier<ThemeMode> {
// 初期値セット
ThemeModeNotifier() : super(ThemeMode.system) {
_init();
}
void _init() async {
// stateがThemeModeになってる
state = await getThemeMode();
}
void update(ThemeMode nextMode) {
state = nextMode;
}
}
// 利用側
// ConsumerWidgetのbuildの中で
final themeState = ref.watch(pokemonThemeProvider); // 状態管理
final themeNotifier = ref.read(pokemonThemeProvider.notifier); // メソッドなど呼ぶ
SharedPreferences
データを端末に保存
const _KEY_THEME_MODE = "theme_mode";
// 保存
Future<void> saveThemeMode(ThemeMode mode) async {
final pref = await SharedPreferences.getInstance(); // Instances作成
pref.setString(_KEY_THEME_MODE mode.name); //
}
// 取得
Future<ThemeMode> getThemeMode() async {
final pref = await SharedPreferences.getInstance();
return toMode(pref.getString(_KEY_THEME_MODE) ?? ThemeMode.system.name);
}
Classで渡す引数に関して
const ThemeModeSelectionPage({
Key? key,
required this.mode,
}) : super(key: key);
final ThemeMode mode;
なみかっこ{}
にはOptional なパラメーターーという扱いになり、Widget を使用する側は名前付きで値を渡す必要があります。一方で{}
なしだと、必須パラメーターになる。名前は不要で順番が大切になる。
実践的には名前付きなので{}
があった方が良さそう。
required
は名前付きの必須のパラメーターを表す。
関数を引数で
final Function(int) callback;
途中です
9日目 factory
Factory Constructor
ロジックをクライアントに公開することなくオブジェクトを作成し、共通のインターフェイスを用いて新しく作成されたオブジェクトを参照する。
Named Constructor
名前付きコンストラクタがあり、似てる
Named Constructor は、一つのクラスが複数のコンストラクタを持てるようにしたものであり、様々な用途に応じたインスタンスを使い分けることができる。
Named Constructor
class Human {
String name;
int age;
// 通常のコンストラクタ
Human({required this.name, required this.age});
// Named Constructor①
Human.sample() : this.name = 'sample', this.age = 10;
// Named Constructor②
Human.test() : this.name = 'test', this.age = 99;
// factoryコンストラクタ①
factory Human.sample() {
return Human(name: 'sample', age: 10);
}
// factoryコンストラクタ②
factory Human.test() {
return Human(name: 'test', age: 99);
}
}
factoryはサブクラスを返却できる
10日目 StateNotifierProviderの更新
class SampleStateNotifier
extends StateNotifier<Map<int, SmapleModel?>> {
SampleStateNotifier(this.ref) : super(Map());
}
このようなMapの状態を見るStateNotifierを定義した
Map内のあるアイテムを更新する処理にて以下の現象が起きた
state[id] = hogeData; // 更新されない
state = {...state, id: hogeData}; // 更新された
Mapの更新は注意が必要そうだ
11日目 SQLITE
テーブル作成
static Future<Database> openDb() async {
return await openDatabase(
join(await getDatabasesPath(), favFileName),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE $favTableName(id INTEGER PRIMARY KEY)',
);
},
version: 1,
);
}
onCreateでは初期定義を行います。基本的にはこの中でテーブルを作成
CREATE
static Future<void> create(Favorite fav) async {
var db = await openDb();
await db.insert(
favTableName,
fav.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
conflictAlgorithmに関して
- rollback:OR ROLLBACK
- SQLステートメントを中止して現在のトランザクションをロールバック
- abort:OR ABORT
- fail:OR FAIL
- SQLステートメントを中止sルウが、変更を取り消すこともトランザクションを終了することもしない
- ignore:OR IGNORE
- SQLステートメントを中止するが、後続処理を何も問題がないように継続する。
- replace:OR REPLACE
- 競合している対象を削除し、SQLステートメントを実行する
READ
static Future<List<Favorite>> read() async {
var db = await openDb();
final List<Map<String, dynamic>> maps = await db.query(favTableName);
return List.generate(maps.length, (index) {
return Favorite(
pokeId: maps[index]['id'],
);
});
}
Listの生成でList.generateというものがある
DELETE
static Future<void> delete(int pokeId) async {
var db = await openDb();
await db.delete(
favTableName,
where: 'id = ?',
whereArgs: [pokeId],
);
}
whereの?がパラメータを示してそう
whereArgsはListなのでまとめて削除もできそう
12日目 dynamic?
Dartは基本的に静的型付け言語ですが、dynamic型を使用すれば動的な型宣言になる
var a;
a = 0;
a = ''; // エラー
dynamic a;
a = 0;
a = ''; // エラーが起きない
このdynamicをよく使うケースとしては、APIやSQLiteからデータを取得するとき。
13日目
リップルに関して
- InkResponse
- InkWell
- Ink
これらがある
InkResponse
ウィジェット中央に円形のsplashが表示される
アイコンボタンなどに利用
InkWell
ウィジェット全体の探検
ボタンに利用
Ink
InkResponseとInkWellがタッチエフェクトを描画するウィジェットだったのに対し、Ink単体ではエフェクトの描画はできない
14日目
IndexedStack
複数の子ウィジェットをスタック上に積み重ね、指定したインデックスのウィジェットのみを表示する機能を持ってる。
StackとIndexedStackは、どちらも複数のウィジェットを管理するためのツールですが、その動作には重要な違いがあります。Stackは、その子ウィジェットを重ねて表示することができ、ウィジェットはStackのエッジに対して相対的に配置されます。一方、IndexedStackは一度に一つの子ウィジェットだけを表示し、表示されるウィジェットは指定されたインデックスのものです。
ウィジェットの切り替えなどに利用されます。
ウィジェットの再描画を避けることが挙げる
IndexedStackはすべての子ウィジェットをメモリ上に保持しますが、その分だけメモリを消費する