以前環境構築できなくて始まる前に投げてしまったFlutterについて最近再挑戦しています。
まだまだ勉強することだらけですがFlutter+sqliteを使った株の資産管理アプリがとりあえず完成してGooglePlayのストアに並ぶところまではできたので調べたことや使った技術についてメモ。
ストアへの直リンクは以下になります。公開したてだからなのか検索に引っかかりません。。;;

環境構築、エディタについて
環境構築は以下のページを参考に行いました。


エディタはVSCodeにFlutter開発用のプラグインをインストールしたものを使っています。
上記リンク内に必要なプラグインも書いてあったのでその辺は全て指示に従ってセットアップしました。
環境変数Pathの設定内容が多すぎて編集できなかったり、既存のJAVA_HOMEが指してるJDKが古すぎたりで上記のリンクの通りに行かない箇所もありましたがその辺はググったりして解決。
インストールが終わったタイミングでバージョン確認したところFlutter2.5.3、Dart 2.14.4でした。
>flutter --version Flutter 2.5.3 • channel stable • https://github.com/flutter/flutter.git Framework • revision 18116933e7 (6 weeks ago) • 2021-10-15 10:46:35 -0700 Engine • revision d3ea636dc5 Tools • Dart 2.14.4
チュートリアルする
FlutterもDartも何もわからない状態なので、最初は日本語訳されたチュートリアルをやりました。

チュートリアルの時点でStatelessWidgetとStatefulWidgetが出てくるのですがここ最初は理解できませんでした^q^
チュートリアルは無事に終わった。 pic.twitter.com/O2pWSzVkcn
— ぼこちょ (@imo_tikuwa) November 16, 2021
ネット上のほとんどのプログラムがコピペで動かない件
最初のうちはネットの情報を元にソースコピーしたりしてFlutterの開発言語(Dart)の勉強をしていたのですがめちゃくちゃエラーが出るし、ビルドもできない。。
調べたところ、Dartが途中からNull-Safetyな言語になったのが原因みたい。
割と最近のこと(2021年3月3日?)みたい??;;

ネットのコードをコピペすると主に以下のような指摘が出ます。
- 変更が加わらないウィジェットにconstつけてって指摘が入る
- 付けなくても動くけどエディタ上の主張が激しい
- newの宣言はいらないって指摘が出る
- これは宣言を消すだけ
- nullableでない型にnullがセットされる場所でエラーが出る
- nullableな型に変える(型の後ろにはてな「?」をつける)ことでそこのエラーは止まりますが、参照してる箇所で別のエラーが出るので辿って修正する必要がある
たくさん指摘が出ますが、エディタの補完がしっかり効いているということともいえる?
アラート0個を目指して修正するとだいたい動くようになってるし結構勉強になる(気がする)。
慣れてきたのでアプリを自作する
数日触っているうちになんとなくDartの読み書きができるようになってきたので、以前PHP8+MySQLで作成した日々の株資産の情報を入力してチャート表示するシステムを、Flutter製のアプリ化してみました。
一度作ったものがあるとロジック自体は考えなくていいので結構楽。
パッケージはpubspec.yamlに追加
pubspec.yaml = PHPでいうところのcomposer.json、javascriptでいうところのpackage.json、RubyでいうところのGemfile、Pythonでいうところのrequirement.txtみたいなパッケージ管理のファイル…と思いきや、非推奨のアラートを握りつぶす設定をしたり、画像や音のようなアセットのパスを通したり、リリースビルドの際のバージョン番号を設定したりするのにも使うみたい。
今回は作ったアプリでは最終的に以下のような感じになりました。
dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter sqflite: ^2.0.0 path_provider: intl: ^0.17.0 table_calendar: charts_flutter: charts_common: horizontal_data_table: month_picker_dialog: persistent_bottom_nav_bar: ^4.0.2 flutter_colorpicker: ^1.0.2 provider: ^6.0.1 fluttertoast: ^8.0.8 google_mobile_ads: ^0.13.2+1
VSCodeだと保存したタイミングで通信が始まり指定したパッケージを取得してくれて便利です。
稀にパッケージのインストールが終わった後もVSCode内で、読み込み対象のファイルが無い旨のエラーが出たりしました。ウィンドウ読み込みなおせば解決しますがこれは謎。
簡単なCRUDから作成
![]() | ![]() |
最初は簡単なところからということで、入力項目の少ない口座情報や入金情報の登録/更新/一覧/削除などの機能から作りました。
アップデート以外のCRUDはなんとなーくできた。
登録画面と編集画面のクラスは共通化したいのでいったん登録できる状態までをコミットして続きは明日やるー pic.twitter.com/qcDC3P2bMF— ぼこちょ (@imo_tikuwa) November 18, 2021
入力した情報を記録するのにsqliteデータベースを使用ました。
sqfliteというパッケージを使用してsqliteデータベースの接続を作成するのですが、ここで非同期処理(Future async/await)の知識が必要となりました。始めたてのときはエラーの解消がやや難しかった。。
具体的なデータベース接続のサンプルはネットで探すとたくさん見つかるのですが私は以下のブログの情報が分かりやすかったので参考にさせていただきました。

データベースへの接続が出来るようになったら概ね以下のような作業
- CREATE TABLEのクエリを作成
- sqliteから取得したデータを取り扱いやすいようにする入れ物(モデル)を作成
- モデルを元にデータベースとのやり取りを行う処理insert/update/delete処理を作成
- 画面作成
「設定」みたいな画面を作っといてボタン押したらデータベースのレコード全削除みたいな初期化機能を作っておくと動作確認が簡単でよかった気がします。
カレンダー表示、クリックした日付の情報表示機能を作成
![]() | ![]() | ![]() |
table_calendarというパッケージを使用してカレンダーを表示、クリックした日付で登録されている資産情報を取得、そこから登録/更新のダイアログ表示などの機能を作ってみました。
table_calendarの使い方は以下の記事で概ねつかめました。

出来たものが以下。
カレンダー表示→カレンダーの日付クリック→口座ごとのデータ一覧表示→編集ボタンクリックでアラートダイアログ表示→登録 or 更新→更新後データ一覧のステート更新って流れが出来た!
flutterのステートちょっとわかってきたかも。 pic.twitter.com/WKWa6hCpXy— ぼこちょ (@imo_tikuwa) November 21, 2021
登録したデータを元にホーム画面にチャート&テーブル表示
チャートはcharts_flutter、テーブル表示はhorizontal_data_tableパッケージを使用しました。
チャート表示を行うためのパッケージは他にもいくつか見つかりましたが、Googleが提供していて一番入門によさそうだったものを使いました。
基本的な使い方はパッケージのギャラリーページがあるので参考にしました。
詳細な使い方を調べるのはGitHubのIssueを漁るのが良かった気がします。
テーブル表示について最初はFlutter標準のDataTableというクラスを使用してたのですが、日付列とヘッダー行を固定化したいなーと考え、調べたところ良さげなパッケージが見つかったため作り直しました。
標準のDataTableと比べると表示と動作が少し重くなりましたがなかなかに満足のいく表示が出来るようになった気がします( ・`ー・´)
flutter使った資産管理アプリ、最初の頃に作った画面少し直すのと、設定機能作りこんでいったん完成でよさそう。
horizontal_data_tableパッケージを使用したヘッダーと先頭列を固定化したデータテーブルは無事に動くところまで作れた pic.twitter.com/JDHP48mIVV— ぼこちょ (@imo_tikuwa) November 26, 2021
フッターに固定のナビゲーションを追加
スマホアプリによくあるフッターに固定されたボタンを追加しました。
最初は各ページのScaffoldのbottomNavigationBarに共通化したBottomNavigationBarウィジェットを表示して、クリックの度にpushReplacementな遷移をしてましたが遷移の度にフッターも含めたページが再描画されるため見栄えが悪い状態でした。。
「Flutter ナビゲーション 固定」とかで調べると色々やり方は見つかるのですが、今回はpersistent_bottom_nav_barというパッケージを導入しての解決を行いました。
フッターの固定にあたってのタブビュー導入が最初はイメージがつかめず苦戦しました。以下の2ページを見たりプログラム書いたりして、やっとのことで理解できました。。


変更内容の抜粋
class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( + home: Home(), + initialRoute: '/', - home: const MainPage(), - initialRoute: '/', - routes: <String, WidgetBuilder> { - '/main': (BuildContext context) => const MainPage(), - '/account': (BuildContext context) => const AccountPage(), - } ); } }
import 'package:persistent_bottom_nav_bar/persistent-tab-view.dart'; import 'package:stock_performance_flutter/pages/account_page.dart'; import 'package:stock_performance_flutter/pages/main_page.dart'; class Home extends StatefulWidget { const Home({Key? key}) : super(key: key); @override _HomeState createState() => _HomeState(); } class _HomeState extends State<Home> { late PersistentTabController _controller; final _pages = <Widget>[ MainPage(), const AccountPage(), ]; @override void initState() { super.initState(); _controller = PersistentTabController(initialIndex: 0); } @override Widget build(BuildContext context) { return Scaffold( body: PersistentTabView( context, controller: _controller, screens: _pages, items: [ PersistentBottomNavBarItem( icon: const Icon(Icons.home), title: "ホーム", ), PersistentBottomNavBarItem( icon: const Icon(Icons.account_balance), title: "口座", ), ], navBarStyle: NavBarStyle.style3, backgroundColor: Colors.white, ) ); } }
別ページの状態を更新するためにproviderパッケージ導入
これまでStatefulWidgetのsetStateを使用してページ内の状態を更新してきましたが、口座画面や入出金画面でデータを登録/更新/削除したタイミングでホーム画面のチャートとテーブル表示を更新するにはどうすればいいのかを調べた結果、プロバイダと呼ばれる仕組みを導入する必要があることがわかりました。
プロバイダー導入前の状態について
プロバイダーを導入する前の状態でも各種データを登録/更新/削除したタイミングでhome.dartに初期表示したいタブのインデックスを持たせpushReplacementな画面遷移を行い、home.dartでは渡された引数にくっついてるインデックスを元に表示するタブを更新をするという方法を取って、一応目的とした動作はできていました。
ここに載せて供養。
class HomeArguments { int initTabIndex; HomeArguments(this.initTabIndex); }
@override Widget build(BuildContext context) { + // インデックス引数付きのpushがあった場合タブインデックスを更新 + if (ModalRoute.of(context)!.settings.arguments != null) { + final args = ModalRoute.of(context)!.settings.arguments as HomeArguments; + setState(() { + _controller.index = args.initTabIndex; + }); + } return Scaffold( body: PersistentTabView( ....
// 何らかのボタンをクリックしたときの完了処理 ElevatedButton( child: Text(addOrEdit), onPressed: () { if (_formKey.currentState!.validate()) { saveRecord().then((_) { // 登録/更新画面を閉じる Navigator.of(context).pop(); + // 元に戻れない形のページ遷移(pushReplacement)を行う + Navigator.of(context, rootNavigator: true).pushReplacement( + MaterialPageRoute( + builder: (context) => const Home(), + settings: RouteSettings( + arguments: HomeArguments(AccountPage.tabIndex) + ) + ) + ); }); } }, )
プロバイダー導入
プロバイダーを理解するのに以下の参考になりました。


ざっくり以下のような作業を行いました。
- providerパッケージインストール
- ChangeNotifierを継承したプロバイダークラス作成
- main.dartに作成したクラスを登録
- ページ内の状態を持つ変数をプロバイダークラスに移植
- ページ内でsetStateで状態更新していた箇所をプロバイダーに移植
- プロバイダーでは状態の更新を通知するのにnotifyListeners()を呼び出す
- StatefulWidgetをStatelessWidgetに書き換え
StatefulWidgetをStatelessWidget化する過程では以下の記事にあるようなStatefulWidgetのラッパークラスを作りました。


出来上がった状態が以下
実機のAndroid11に更新したのでスクリーンキャプチャしてみた。
設定画面の変更内容がproviderの状態管理によってホーム画面のチャートとテーブル表示にリアルタイムに反映されるようになった。
月ごと表示OFFのときはスワイプで月切り替えられない代わりに任意の日付期間指定しての表示ができる。 pic.twitter.com/vuo28JgtCk— ぼこちょ (@imo_tikuwa) December 4, 2021
出来上がったアプリをGooglePlayで公開 2021/12/14追記
- GooglePlayConsoleのアカウント作成、25$の登録料払う
- コンソール上でアプリの登録情報を作成する
- AppBundleを作成してアップロードする
初めてってのもありますがめちゃくちゃ苦戦しました。。いろんなサイトを参考にしたり一生懸命ドキュメント読んだり。
上の手順からは端折ってますが実際にはAdMobの広告掲載をしてみたかったのでそちらも登録、広告ユニットIDをプログラム内に組み込んだりとか色々やりました。
AppBundleをアップロード後、GooglePlay側で審査が入るのですがこれが即時で行われるわけではなく3日ほど待ちが発生しました;;
出来上がったアプリが以下になります。
もしよろしかったら株やFXの資産管理に使ってみてください。

メモ
FlutterのSDK内を見ると中身はGitのリポジトリなのでもしかしたらバージョンは任意のものにダウングレードとかできるのかもしれない??
実機デバッグで使用してる手持ちのAndroid端末が縦に長い(たぶん21:9)のでもしかしたら普通の16:9のスマホで使ったらウィジェットが衝突とか起きてそうなのが心配。。iPhoneはそもそも持ってないので動作確認一切できていない。。
その他の参考リンク
vscode StatelessWidgetからStatefulWidgetに自動変換するショートカットキー – Qiita
Flutterで中国語フォントになってしまうときの設定 – Qiita
How to refresh an AlertDialog in Flutter? – Stack Overflow
Flutterで画面の向きを固定する – Qiita
FlutterでAdMobを表示(iOS/Android) – Qiita
flutterアプリでfirebase_admobからgoogle_mobile_adsに切り替えてみた – Qiita
Flutterで開発したアプリアイコンの変更方法! iOS・Android別に紹介
Flutter StrutStyleで日本語と英語のTextの高さを揃える – Qiita
Google PlayにAndroidアプリを公開する | Flutterで始めるアプリ開発
Flutter製のAndroidアプリをGooglePlayにリリースする
Flutter端末の文字サイズを参照しないようにする | がーみブログ
まとめ
今回のアプリ制作を通して以下のことが出来るようになりました。
- Flutter2.5.3、Dart2.14.4を使用したアプリ開発
- sqliteを使用したデータ管理
- providerを使用した状態管理
- persistent_bottom_nav_barを使用したタブビュー表示
- table_calendarを使用したカレンダー表示
- charts_flutterを使用したラインチャート表示(ツールチップ、凡例は独自の描画クラス作成)
- horizontal_data_tableを使用したデータテーブル表示
- データベースのバックアップ/リストア
- アプリの日本語化(table_calendar、DatePicker、MonthPicker)
- google_mobile_adsを使用した広告表示
など
次にアプリ開発をする場合は
- 認証機能(FirebaseのAuthentication?)
- FirebaseのCloudFirestoreを使用したデータ管理
- riverpodを使用した状態管理
の辺りをやってみたいと考え中。