Flutterをやってみる

/

「スマホアプリが動かなくなった」という友人の投稿を見て、居住自治体が配布しているアプリをインストールし、複数の不具合等を見つけたのがきっかけでした。アプリに関する知識が全く無い状態で自治体に報告するのも・・・と思ったので、Flutter を触ってみることにしました。

ちなみに、このアプリは Apache Cordova 製でした。

Image from Gyazo

感想・まとめ

  • RAM 16GB では足りない
  • やる前に何度か見かけていた『Flutter はクロスプラットフォーム開発 FW であるが、Android と iOS アプリ開発に要する労力が半分になる訳ではない』という言説の理由が何となく分かりました
    • Material Components を使用するならある程度は楽できるが、独自デザインになると難易度が急に上がる
    • Dart と Flutter だけなら学習コストは大したことが無いが、必要に応じて Kotolin そして Android 自体をも把握して作成するのは大変・・・iOS だと Swift も

not yet / do it later

did it

Flutter Learn を手を動かしながら読み終えましたが、Widget に何があるか、Flutter/Dart 特有の書き方といったものを更に理解するため、Zenn や Google Codelab 等のチュートリアルをやりました。

notes

commands

flutter create の後に-e を付ける事で最小構成のプロジェクトを作成できる。

terminal
1
flutter create -e [project_name] --platforms=ios,android

run app

terminal
1
flutter devices
2
flutter run -d [device_id]

codelabs/google-maps-in-flutter

terminal
1
# deprecated
2
flutter pub run build_runner build --delete-conflicting-outputs
3
# use
4
dart run build_runner build --delete-conflicting-outputs
5
dart run build_runner build -d

codelabs/flutter-boring-to-beautiful

flutter_bloc

Flutter アプリケーションにおけるビジネスロジックの管理を簡素化し、UI とビジネスロジックの分離を促進するためのデザインパターン。状態管理が容易になり、コードのテストがしやすくなる。らしい。

part of

undefined
1
part of 'playback_bloc.dart';

ライブラリや大規模なコードベースを分割するために使用されるキーワード。複数の Dart ファイルを1つのライブラリとして扱い、モジュール化やコードの整理が容易になる。らしい。

class A with B

learn_flutter/boring_to_beautiful/lib/src/shared/playback/bloc/playback_event.dart at main · oriverk/learn_flutter

  • part of
  • @Freezed()
  • class A with B
    • class A extends B とは違う様子
undefined
1
part of 'playback_bloc.dart';
2
3
@Freezed()
4
class PlaybackEvent with _$PlaybackEvent {}
  • class A extends B
    • 単一継承
    • クラスAはクラスBのすべての機能を継承し、オーバーライドできる
    • 強い継承関係を示す
  • class A with B
    • 複数のミックスインを使用可能
    • クラスAはミックスインBの機能を利用でき、他の継承関係に影響を与えない
    • 再利用可能な機能を追加するために使用する

extension A on B

learn_flutter/boring_to_beautiful/lib/src/shared/extensions.dart at main · oriverk/learn_flutter

undefined
1
extension DurationString on String {
2
/// Assumes a string (roughly) of the format '\d{1,2}:\d{2}'
3
Duration toDuration() => switch (split(':')) {
4
[var minutes, var seconds] => Duration(
5
minutes: int.parse(minutes.trim()),
6
seconds: int.parse(seconds.trim()),
7
),
8
[var hours, var minutes, var seconds] => Duration(
9
hours: int.parse(hours.trim()),
10
minutes: int.parse(minutes.trim()),
11
seconds: int.parse(seconds.trim()),
12
),
13
_ => throw Exception('Invalid duration string: $this'),
14
};
15
}

learn_flutter/boring_to_beautiful/lib/src/shared/providers/songs.dart at main · oriverk/learn_flutter

undefined
1
final _songs = <RankedSong>[
2
RankedSong(
3
1,
4
'Before You',
5
ArtistsProvider.shared.getArtist('jmo')!,
6
'2:45'.toDuration(),
7
const MyArtistImage(
8
image: 'assets/images/albums/artist6-album1.jpg',
9
sourceLink: 'https://unsplash.com/photos/cTKGZJTMJQU',
10
sourceName: 'Drew Dizzy Graham',
11
),
12
),
13
]

Dart 言語の機能で、既存のクラスに新しいメソッドやプロパティを追加する方法です。この機能を使用することで、既存のクラスの定義を変更することなく、そのクラスに新しい機能を追加できます。Flutter でもこの機能を利用して、標準ウィジェットや他のクラスにカスタムメソッドを追加できます。(ChatGPT)

undefined
1
<!-- 文字列クラスに新しいメソッドを追加する例 -->
2
<!-- Palindrome means 回文 -->
3
extension StringExtension on String {
4
bool get isPalindrome {
5
String normalized = this.toLowerCase().replaceAll(RegExp(r'[\W_]+'), '');
6
return normalized == normalized.split('').reversed.join('');
7
}
8
}
9
10
void main() {
11
String text = 'A man a plan a canal Panama';
12
print(text.isPalindrome); // true
13
}

codelabs/material-motion-flutter

InkWell VS GestureDetector

SharedAxisTransition; アニメーション パッケージ内の共有軸移行

  • アニメーション パッケージ内の共有軸移行は、SharedAxisTransition と呼ばれます。このウィジェットには次のプロパティがあります
    • fillColor: 移行中の背景に使用される色
    • animation: 子の出入りを操作するアニメーション
    • secondaryAnimation: 新しいコンテンツが前面にプッシュされたときに子を移行させるアニメーション
    • transitionType: 共有軸移行のタイプを、スケーリング、水平、垂直から選択します
    • child: 移行しながら出入りするウィジェット

慣れの問題だと思うが、css と比較して非常に複雑だと感じた。個人的には boring_to_beautifulのtheme.dart のページ遷移時アニメーションぐらいで十分。

undefined
1
import 'package:flutter/material.dart';
2
3
class ThemeProvider extends InheritedWidget {
4
<!-- ... -->
5
6
final pageTransitionsTheme = const PageTransitionsTheme(
7
builders: <TargetPlatform, PageTransitionsBuilder>{
8
TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(),
9
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
10
TargetPlatform.linux: NoAnimationPageTransitionsBuilder(),
11
TargetPlatform.macOS: NoAnimationPageTransitionsBuilder(),
12
TargetPlatform.windows: NoAnimationPageTransitionsBuilder(),
13
},
14
);
15
16
<!-- ... -->
17
}

battery level app

Which AndroidManifest.xml to edit in Flutter project?

Main AndroidManifest.xml file is located at ‘Project’ > app > src > main > AndroidManifest.xml. Other AndroidManifest.xml which are in debug folder and profile folder are systems generated when you are running a flutter app directly to mobile it will generate or while creating a flutter project. E.g: It is used for hot reload when the device is connected.

電池残量と充電状態を監視する

充電状態の変化を監視する

undefined
1
val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
2
context.registerReceiver(null, ifilter)
3
}
undefined
1
val status: Int = batteryStatus?.getIntExtra(BatteryManager.EXTRA_STATUS, -1) ?: -1
2
val isCharging: Boolean = status == BatteryManager.BATTERY_STATUS_CHARGING
3
|| status == BatteryManager.BATTERY_STATUS_FULL
4
5
// How are we charging?
6
val chargePlug: Int = batteryStatus?.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ?: -1
7
val usbCharge: Boolean = chargePlug == BatteryManager.BATTERY_PLUGGED_USB
8
val acCharge: Boolean = chargePlug == BatteryManager.BATTERY_PLUGGED_AC

現在の電池残量を特定する

undefined
1
private fun getBatteryLevel(): Int {
2
val batteryLevel: Int
3
4
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
5
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
6
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
7
} else {
8
// val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
9
// val level: Int = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
10
// val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
11
// batteryLevel = level * 100 / scale
12
13
// https://developer.android.com/training/monitoring-device-state/battery-monitoring?hl=ja#kotlin
14
val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
15
context.registerReceiver(null, ifilter)
16
}
17
val batteryPct: Float? = batteryStatus?.let { intent ->
18
val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
19
val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
20
level * 100 / scale.toFloat()
21
}
22
batteryLevel = batteryPct as Int
23
}
24
25
return batteryLevel
26
}
  • 条件式 VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
    • デバイスが Android Lollipop(API レベル21)以上のバージョンで動作しているかどうかをチェック
  • VERSION.SDK_INT
    • デバイスで実行されている Android のバージョンを取得するための定数
  • VERSION_CODES.LOLLIPOP
    • Android のバージョン Lollipop(API レベル21)を示す定数

codelabs/firebase-get-to-know-flutter

firebase setup on Windows

terminal
1
dart pub global activate flutterfire_cli

install through coreybutler/nvm-windows: A node.js version management utility for Windows. Ironically written in Go.

terminal
1
# run codes in administrator mode in powershell
2
nvm install lts
3
nmv use blah blah
4
5
npm install -g firebase-tools
6
firebase login
7
8
flutterfire configure