Flutterの技術検証用に簡単なアプリケーションを実装。
個人的には
- HTTP通信
- 非同期
当たりを理解することができれば、ある程度の開発が行えると判断し、「日経平均株価のチャートを表示する」アプリを開発。
※ React NativeでS&P500, Xamarinでダウ平均のチャートを表示するアプリケーションを開発する予定
成果物

初めに
Flutterとは
Flutterとは、GoogleによってDartで開発されたフリーかつオープンソースのUIのSDK。
単一のコードから、Android、iOS、Linux、macOS、Windows、Google Fuchsia向けのクロスプラットフォームアプリケーションを開発することができる。
Dartとは
Dartとは、Webアプリやモバイルアプリクライアント開発向けに設計されたプログラミング言語で、Googleによって開発された。
オブジェクト指向、クラスベース、ガベージコレクション備えたCベースの言語で、ネイティブコードまたは、JavaScriptにコンパイルすることができる。
構文はJavaに近い。
環境構築
Flutter
$ mkdir ~/local
$ cd ~/local
# @see: https://docs.flutter.dev/get-started/install/macos
$ curl -OL https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_2.8.1-stable.zip
$ unzip flutter_macos_2.8.1-stable.zip
$ echo ‘export PATH=“$PATH:$HOME/local/flutter/bin”’ >> ~/.bash_profile
$ source ~/.bash_profile
$ flutter --version
Android Studio
Install
公式サイトからInstall.
https://developer.android.com/studio/index.html
Plugin Install
Android Studio上で
preference > Plugins > Flutter Install > Android Studio Restart
と実行。
Create Flutter App
- Start a new Flutter projectを選択
- Flutter Applicationを選択してNextをクリック
- Project nameは任意の名前で入力しNextをクリック
- Company domainは任意の名前で入力しFinishをクリック
Device
適当にInstall.
Android License
Android Studio上で
Android SDK > SDK Tools > Android SDK Command-line Tools(latest) にチェック > Apply
と実行し、
$ flutter doctor --android-licenses
Xcode
Install
Mac App StoreからInstall.
https://apps.apple.com/jp/app/xcode/id497799835?mt=12
CocoaPods
$ ruby --version
$ sudo gem install -v1.8.4 cocoapods -n /usr/local/bin
Coding
Tree
$ tree -L 1
.
├── README.md
├── analysis_options.yaml
├── android // Android用のソース
├── build
├── ios // iOS用のソース
├── lib // クロスプラットフォーム(Dart)←今回はここ
├── nikkei_heikin.iml
├── pubspec.lock
├── pubspec.yaml
├── sample.png
└── test
pubspec.yaml
pubspec.yamlの依存関係にライブラリを追記。
※ pubspec.yamlは環境変数、依存関係等を記載するファイル。
公式ドキュメント
dependencies:
flutter:
sdk: flutter
+ charts_flutter: ^0.12.0 # チャートを描画するのに必要なライブラリ
+ charts_common: ^0.12.0 # チャートを描画するのに必要なライブラリ
+ flutter_sparkline: ^0.1.0 # チャートを描画するのに必要なライブラリ
+ http: ^0.13.4 # HTTPクライアント
main.dart
import 'package:flutter/material.dart';
import 'chart.dart';
// アプリ起動時に、実行
void main() {
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Chart());
}
}
chart.dart
各Class単位に分解。
Chart.class
class Chart extends StatefulWidget {
const Chart({Key? key}) : super(key: key);
@override
_ChartState createState() => _ChartState();
}
StatefulWidget継承。
FlutterにはStatelessWidget
とStatefulWidget
の二種類、Widget Classが存在しており、それぞれ下記のような特徴がある。
Widget | 特徴 |
---|---|
StatelessWidget | Viewの状態を保持しないWidget。 インスタンス化時にWidgetが決定するため変数等定義しても値が更新されることがない。 所謂、静的なWidget |
StatefulWidget | 状態を保持しているWidget。 所謂、動的なWidget StatelessWidgetとは異なり、状態を管理するState Classを実装する必要がある。 ライフサイクルについてはこちら。 |
_ChartState.class
class _ChartState extends State<Chart> {
late List<dynamic>? response = null;
getData() async {
final parameters = {
'api_key': 'xxxxxxxxxx',
'start_date': '2021-01-01',
'end_date': '2021-03-31',
};
final url = Uri.https('www.quandl.com', '/api/v3/datasets/CHRIS/CME_NK2/data.json', parameters);
final result = await http.get(url);
setState(() {
response = json.decode(result.body)['dataset_data']['data'];
});
}
@override
void initState() {
super.initState();
getData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('日経平均'),
backgroundColor: Colors.orange
),
body: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text("2021-01-01 ~ 2021-03-31 日足"),
Expanded(flex: 1,
child: Card(
child: Container(
padding: const EdgeInsets.all(10),
child: response == null ? null : createChart(response!), // const CircularProgressIndicator()
)
)
),
],
),
),
);
}
Widget createChart(List<dynamic> response) {
List<TimeSeriesSales> data = [];
for (var element in response) {
List date = element[0].split('-');
final settle = element[6];
data.add(TimeSeriesSales(DateTime(int.parse(date[0]), int.parse(date[1]), int.parse(date[2])), settle));
}
List<Series<TimeSeriesSales, DateTime>> seriesList = [
charts.Series<TimeSeriesSales, DateTime>(
id: 'Sales',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (TimeSeriesSales sales, _) => sales.time,
measureFn: (TimeSeriesSales sales, _) => sales.sales,
data: data,
)
];
return charts.TimeSeriesChart(
seriesList,
animate: false,
dateTimeFactory: const charts.LocalDateTimeFactory(),
);
}
}
ステップ数が多いので、関数単位に分解。
build
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('日経平均'),
backgroundColor: Colors.orange
),
body: Container(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text("2021-01-01 ~ 2021-03-31 日足"),
Expanded(flex: 1,
child: Card(
child: Container(
padding: const EdgeInsets.all(10),
child: response == null ? null : createChart(response!), // const CircularProgressIndicator()
)
)
),
],
),
),
);
}
描画するWidgetを実装。
レイアウトなどはここに実装。
initState
@override
void initState() {
super.initState();
getData();
}
StateClassのイニシャライズ時に実行。
ここでAPIを実行。
getData
getData() async {
final parameters = {
'api_key': 'xxxxxxxxxx',
'start_date': '2021-01-01',
'end_date': '2021-03-31',
};
final url = Uri.https('www.quandl.com', '/api/v3/datasets/CHRIS/CME_NK2/data.json', parameters);
final result = await http.get(url);
setState(() {
response = json.decode(result.body)['dataset_data']['data'];
});
}
APIを実行して、日経平均株価の情報を取得。
APIはNaddaq Data LinkのAPIを使用。
登録を行い、API Keyを発行する必要がある。
API実行後、必要なデータを取得し、setStateを実行。
setStateを実行することで、再度Widgetがbuildされる。
createChart
Widget createChart(List<dynamic> response) {
List<TimeSeriesSales> data = [];
for (var element in response) {
List date = element[0].split('-');
final settle = element[6];
data.add(TimeSeriesSales(DateTime(int.parse(date[0]), int.parse(date[1]), int.parse(date[2])), settle));
}
List<Series<TimeSeriesSales, DateTime>> seriesList = [
charts.Series<TimeSeriesSales, DateTime>(
id: 'Sales',
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
domainFn: (TimeSeriesSales sales, _) => sales.time,
measureFn: (TimeSeriesSales sales, _) => sales.sales,
data: data,
)
];
return charts.TimeSeriesChart(
seriesList,
animate: false,
dateTimeFactory: const charts.LocalDateTimeFactory(),
);
}
APIのレスポンスをChart描画ように整形。
本当にJavaっぽい。
まとめ
今回、簡易アプリケーションを実装してみたが、思ったより悪くない
その心は、Android Studioを使用すればそこまでストレスなく開発することもでき、AndroidJavaの経験がある人であればほぼキャッチアップ不要で開発が行える。
しかし、lib
配下の階層が自由度が高いことが若干マイナス。(デフォルトだと空。)
PJや人に依存してしまうため、しっかりと設計方針を決めないと負債となりかねない。
https://blog.dalt.me/2941
もし、私がスクラッチで実装するのであればMVVM方式を使用するかな・・
./lib
├── use_cases
├── utils
├── models
├── views
└── view_models
次は、ディレクトリ構成やCI/CD、OSのハンドリングの検証を行なっていきたい。