🏛️

【Flutter】国会図書館のAPIで国会議事録アプリを作った話

2024/10/02に公開1

こんにちは。
株式会社ココナラ Web開発グループ フロントエンド開発チームの加藤です。

普段はWebのフロントエンドの開発をしていますが、最近プライベートでFlutterの勉強も兼ね、アプリの個人開発をしました。
Flutterの開発言語のDartはJavaScriptに似ているため、フロントエンドエンジニアでも馴染みやすいと思います。
今回はそこで使ったAPIやパッケージの紹介・解説をします。

作ったもの

チャットアプリのようなUIで気軽に国会の発言を見ることができるアプリです。
議員をフォローしてその議員の全発言を見たり、ワードをフォローしてそのワードが含まれる発言を抽出したり、会議を指定して議事録を見たりすることができます。
Androidアプリと、iOS用にWebアプリがあります。(Macを持っておらず、iOSアプリのビルドはできなかったため)

https://tiger4th.com/lp/giin/

API

概要

国立国会図書館が公開している「国会会議録検索システム」というサービスがあります。
ここでは1947年の第1回国会から最新の国会まで、すべての議事録を閲覧できます。

https://kokkai.ndl.go.jp/

また、「国会会議録検索システム 検索用API」というAPIが外部公開されており、今回のアプリではこのAPIを使いました。

https://kokkai.ndl.go.jp/api.html

ちなみに1890年~1947年の帝国議会の議事録を閲覧できる「帝国議会会議録検索システム」もあります。
ただし、戦後の約2年分以外は文字起こしされておらず、原本をスキャンした画像の閲覧のみで、APIも公開されていません。

https://teikokugikai-i.ndl.go.jp/

使用例

発言単位出力

https://kokkai.ndl.go.jp/api/speech

検索条件に当てはまる発言を取得できるAPIです。
speaker(発言者名)、any(発言に含まれるワード)、issueID(会議録ID)といったパラメータで検索することができます。
speaker は議員だけでなく、参考人等も含む国会で発言したすべての人で検索できます。
アプリ内で発言を表示するために使っています。

speaker any issueID
speakerspeaker=さかなクン anyany=チャットGPT issueIDissueID=100115254X00119470520

会議単位出力

https://kokkai.ndl.go.jp/api/meeting (会議単位出力)
https://kokkai.ndl.go.jp/api/meeting_list (会議単位簡易出力)

検索条件に当てはまる会議録の情報(回次、院、会議名、号、開催日、ID、URL等)を取得できるAPIです。
会議単位簡易出力には発言の本文が含まれません。
nameOfMeeting(会議名)、from(開会日付始点)、until(開会日付終点)といったパラメータで検索することができます。
アプリ内で会議一覧を表示するために使っています。

nameOfMeeting from, until
nameOfMeetingnameOfMeeting=本会議 from&untilfrom=2024-01-01&until=2024-01-31

問題点

ソートできない

APIの最大の問題が、ソート条件が指定できないことです。
そして仕様に記載はありませんが、会議単位出力のソート条件が独特です。

発言単位出力は会議の開会日時が新しい順に返ってきますが、会議単位出力では会議名ごとに優先度があり、会議名が第一条件、会議の開会日時の新しい順が第二条件になっているようです。

会議名の優先度は、
本会議 > 予算委員会 > ○○委員会 > ✕✕委員会 > ...
と決まっているように見えますが、多種多様な会議があるため全貌は分かりません。

たとえば until=2024-01-31 とだけ指定すると、本会議が最優先のため、2024/01/31までに開かれた本会議だけが新しい順に30件返ってきます。

https://kokkai.ndl.go.jp/api/meeting_list?until=2024-01-31

今回のアプリではデフォルトの会議一覧は単純に新しい順に表示しようと思っていましたが、それでは本会議しか表示されないため、デフォルトは「本会議一覧」と表示し、それ以外の会議は年月を指定して表示する仕様にしました。

本会議一覧

余分な文字列が入っている

speaker が発言者名ですが、発言内容の speech の最初にも発言者名が含まれています。
それは発言ではないため削除するようにしました。

また原本で全角スペースによってインデントや文字の位置調整がされているのか、それがAPIでもそのまま返ってきます。
横幅が狭いスマートフォンの画面では改行位置が違っていて、そのままでは逆に表示位置がおかしくなってしまうため、削除するようにしました。

発言内容に区切り線も含まれますが、種類がいくつもあることと、残してもそこまで読みにくくならないことから今回は残しました。

trimSpeech({required String speech}) {
  // 発言内容の最初の発言者名を削除
  if (speech.startsWith('○')) {
    List speechList = speech.split(' ');
    if (speechList.length > 1) {
      speechList[0] = '';
    }
    speech = speechList.join();
  }

  // 位置合わせ用の全角スペースを削除
  speech = speech.replaceAll(" ", "");

  return speech;
}
元データ 削除後
元データ 削除後

元データのURL:

https://kokkai.ndl.go.jp/api/meeting?issueID=121305254X00320240131

一文が長く読みにくい

1回の発言が長く、そのままだと読みにくいことも多いです。
読みやすくなるように、句点で吹き出しを分割して表示するようにしました。

元データ 分割後
元データ 分割後

元データのURL:

https://kokkai.ndl.go.jp/api/speech?speaker=さかなクン

Dart Package

使用したDart Packageと使用例を紹介します。
APIから情報を取得して表示するシンプルなアプリなので、いくつかのパッケージを使うだけで簡単に作ることができました。
パッケージはこちらから検索できます。

https://pub.dev/

http

http通信をするためのパッケージです。
APIを叩くために必要です。

https://pub.dev/packages/http

url_launcher

アプリ内ブラウザや外部ブラウザでURLを開くパッケージです。
人名やアイコンをタップしたとき、名前をGoogle検索して調べる機能を付けるために使いました。

https://pub.dev/packages/url_launcher

flutter_chat_bubble

吹き出しを作るパッケージです。
色々な形の吹き出しが用意されています。

吹き出し

https://pub.dev/packages/flutter_chat_bubble

flutter_datetime_picker_plus

iOSのようにドラムロールで日付選択をするUIのパッケージです。

日付選択

https://pub.dev/packages/flutter_datetime_picker_plus

adaptive_dialog

色々な種類のダイアログのパッケージです。

ダイアログ

https://pub.dev/packages/adaptive_dialog

shared_preferences

データをストレージに保存するためのパッケージです。
フォローした人名やワードを保存するために使っています。
Flutter WebでもLocalStorageに保存されて問題なく動作します。

フォロー

https://pub.dev/packages/shared_preferences

flutter_launcher_icons

以下のような設定を書いて実行すると、色々なOS向けのアプリアイコン画像を自動で生成してくれるパッケージです。

pubspec.yaml
flutter_launcher_icons:
  android: "launcher_icon"
  ios: true
  image_path: "assets/icon/icon.png"
  adaptive_icon_foreground: "assets/icon/icon_adaptive_foreground.png"
  adaptive_icon_background: "#06C755"
flutter pub run flutter_launcher_icons

アイコン

https://pub.dev/packages/flutter_launcher_icons

flutter_native_splash

以下のような設定を書いて実行すると、色々なOSや端末サイズ用のスプラッシュスクリーンの画像を自動で生成してくれるパッケージです。

pubspec.yaml
flutter_native_splash:
  color: "#06C755"
  image: "assets/icon/icon.png"
  android_12:
    image: "assets/icon/icon_adaptive_foreground.png"
    icon_background_color: "#06C755"
dart run flutter_native_splash:create

スプラッシュスクリーン

https://pub.dev/packages/flutter_native_splash

Flutter Web

FlutterはWebアプリを作ることもできます。

https://flutter.dev/multi-platform/web

FlutterではDartで書かれた1つのソースコードからAndroidアプリとiOSアプリのどちらもビルドできますが、iOSアプリのビルドにはMacが必要です。
今回は、Macを持っていなかったためiOS用にはWebアプリを用意しました。

Webアプリも同じソースコードからWeb向けにビルドし、出力されたファイルをサーバーに置くだけで簡単にできます。
上述のshared_preferencesやflutter_native_splash等のパッケージもWebに対応しているため、機能はネイティブアプリと遜色なく、PWAにも対応しているので「ホーム画面に追加」すればネイティブアプリとほぼ見分けが付かないレベルです。

パフォーマンスは劣りますが、FlutterはWasmにも対応しており、Wasmでビルドすることによりパフォーマンスを向上させることができます。(ただしSafariがまだWasmGCに対応しておらず動かないため、今回はWasmを使用していません)

https://docs.flutter.dev/platform-integration/web/wasm#chrome-119-or-later

もしWebのとき(またはそれ以外のとき)の処理が必要な場合には、 foundation パッケージの kIsWeb というフラグで分岐させることができます。

import 'package:flutter/foundation.dart';
if (kIsWeb) {
  // Webの場合
} else {
  // Web以外の場合
}

今後

今回は最低限の機能と対応OSでリリースしましたが、機能追加のほか、FlutterはPC用のデスクトップアプリ(Windows / macOS / Linux)も作ることができるので、今後試してみたいと思っています。

https://flutter.dev/multi-platform/desktop

最後に

ココナラではエンジニアを募集しています。
よろしければぜひ以下のページもご覧ください。

https://coconala.co.jp/recruit/engineer/

Discussion

sankharasankhara

大変楽しく読ませていただきました!
おもしろく・かつためになる、素晴らしいアプリだと思います👏