LoginSignup
5
2

【爆速】Flutterで日経平均株価のチャートを表示するアプリケーションを作る

Last updated at Posted at 2022-03-31

Flutterの技術検証用に簡単なアプリケーションを実装。
個人的には

  • HTTP通信
  • 非同期

当たりを理解することができれば、ある程度の開発が行えると判断し、「日経平均株価のチャートを表示する」アプリを開発。
※ React NativeでS&P500, Xamarinでダウ平均のチャートを表示するアプリケーションを開発する予定

成果物

初めに

Flutterとは

image.png
Flutterとは、GoogleによってDartで開発されたフリーかつオープンソースのUIのSDK。
単一のコードから、Android、iOS、Linux、macOS、Windows、Google Fuchsia向けのクロスプラットフォームアプリケーションを開発することができる。

Dartとは

image.png
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

  1. Start a new Flutter projectを選択
  2. Flutter Applicationを選択してNextをクリック
  3. Project nameは任意の名前で入力しNextをクリック
  4. 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は環境変数、依存関係等を記載するファイル。
公式ドキュメント

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

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にはStatelessWidgetStatefulWidgetの二種類、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のハンドリングの検証を行なっていきたい。

5
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2