Bluesky投稿専用クライアントをつくった

今年、春に「SkyThrow」というソーシャルメディアBluesky（ブルースカイ）へ投稿するだけのクライアントアプリをつくりました。
マルチアカウントでハッシュタグを便利に投稿したいと作ったのですが、共有による他のSNS（主に𝕏）への投稿ができることからマルチポストにも活用してもらっています。

https://skythrow.com/

Blueskyクライアントを作るのに利用したpackageを`pubspec.yaml`から棚卸しします。

## Bluesky公式クライアント
https://github.com/bluesky-social/social-app

棚卸しといいつつFlutterのパッケージではありません、公式クライアントのリポジトリです。
Reactで作られています。
ドキュメントやリポジトリよりもこちらのコードで実装を確認することの方が多いです。
新しい機能も、こちらのコミット状況から予測することができます。

## 利用しているパッケージ

### [atproto / atproto_code / bluesky / bluesky_text](https://pub.dev/packages/atproto)
[Shinya Katoさん](https://bsky.app/profile/shinyakato.dev)がメンテされているatproto/bluesky Dartライブラリ。
ぶっちゃけこのライブラリがあったからこそ、慣れたFlutterでクライアントを作る気になれました（なかったら公式クライアント同様、知見がないReactで作らざるをえなかった or 作らなかった）

以下のBlueskyフルクライアントもこのライブラリを元に作られています。

- [deck.blue](https://bsky.app/profile/deck.blue)
- [Ucho-ten Bluesky Client](https://bsky.app/profile/ucho-ten.net)

### [flutter_localizations / intl](https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization)
i18n、国際化対応。
日本語と英語だけ対応しました。訳は公式クライアントを踏襲しつつDeepLとかに頼っています。

### [go_router](https://pub.dev/packages/go_router)
ルーティングと画面遷移。機能は少ないので以下のツリーしかありません。
- startup
- root
- signin
- post
- tags
- taggroup
- settings
    - linkcard
    - afterpost
    - theme

### [provider](https://pub.dev/packages/provider)
状態管理。全体管理と各アカウントの2系統を管理。
最近は`riverpod`の方がよく使われると聞くので試してみようと思いつつまだ使ってみたことすらありません。

### [url_launcher](https://pub.dev/packages/url_launcher)
外部URLラウンチ、`launchUrlString`だけ使ってます。

### [shared_preferences](https://pub.dev/packages/shared_preferences)
設定の保存。
独自のサーバは立てておらずデバイスのローカルにしか保存されません。
BlueskyのAT ProtocolにはユーザのPDSに独自データを保存できるので、それに対応することを検討しています。

### [image_picker](https://pub.dev/packages/image_picker)
画像・ビデオ選択、春に画像の選択枚数制限ができるようになりました。
既に選択している画像を渡したり、「❶、❷...」と番号付きの選択にしたいなと改造を検討しつつまだなにもやってません。

### [video_player](https://pub.dev/packages/video_player)
ビデオ再生パッケージ、プレビューのため。
なぜかWindowsだけ非対応です。

### [flutter_video_info](https://pub.dev/packages/flutter_video_info)
ビデオのアスペクト比取得のため。

### [mime](https://pub.dev/packages/mime)
`lookupMimeType`、ファイルとバイナリのmimeタイプを取得。

### [image](https://pub.dev/packages/image)
投稿制限に収まるよう画像のリサイズ、jpeg再圧縮の処理。

### [flutter_native_splash](https://pub.dev/packages/flutter_native_splash)
起動時のスプラッシュスクリーンを表示。

### [dart_jsonwebtoken](https://pub.dev/packages/dart_jsonwebtoken)
Blueskyのセッション管理、アクセストークンのデコード。

### [characters](https://pub.dev/packages/characters)
UNICODEの文字列長や末尾、1文字削除等の文字列処理。

### [flutter_svg](https://pub.dev/packages/flutter_svg)
BlueskyアイコンのSVGファイル表示。

### [emoji_picker_flutter](https://pub.dev/packages/emoji_picker_flutter)
ブラウザ、デスクトップ版（未公開）でのemoji入力。

### [flutter_slidable](https://pub.dev/packages/flutter_slidable)
リストの左右スライドアクション機能。
動かない!? と思ったらメジャーアップデートで仕様が変わってマイグレートが必要だったり。

### [sealed_languages](https://pub.dev/packages/sealed_languages)
言語選択メニューを作るため。

### [string_validator](https://pub.dev/packages/string_validator)
文字列のURL判定、…にしか使ってません。
`Uri.tryParse`でもいいのではとも。

### [collection](https://pub.dev/packages/collection)
Listの便利コレクション。確認したら以下の4つを利用していました。
- lastWhereOrNull
- firstWhereOrNull
- compareAsciiUpperCaseNatural
- slices

### [package_info_plus](https://pub.dev/packages/package_info_plus)
アプリ名、バージョン、ビルド番号を取得。

### [share_plus](https://pub.dev/packages/share_plus)
投稿内容（テキストと画像、ビデオ）を他のアプリへ共有。
共有できたら便利かなと対応したところ、𝕏アプリがきっちり共有の受け取りに対応していました。そのため冒頭に書いたように、マルチポストに有効だと使ってくれている方が多いです。

### [app_settings](https://pub.dev/packages/app_settings)
OSのアプリ設定画面（SkyThrowの場合は写真アクセス許可）を開きます。

### [flutter_settings_ui](https://pub.dev/packages/flutter_settings_ui)
設定メニューを簡易に構築することができます。

### [in_app_review](https://pub.dev/packages/in_app_review)
OS各ストアのアプリレビューを表示してくれます。

### [quick_actions](https://pub.dev/packages/quick_actions)
アプリアイコン長押しメニュー（iOSのクイックアクション、Androidのアプリショートカット）への対応。
「投稿」ができるように登録してます。

### [croppy](https://pub.dev/packages/croppy)
画像クリッピングパッケージ。
切り抜くだけではなく拡大縮小、回転、傾きとかなり高機能です。
ただHeroアニメーションへの対応がうまくいかず、フォークして修正した上で利用しています。

### [http](https://pub.dev/packages/http)
OGP対応でウェブページ読み込みのため。

### [metadata_fetch_plus](https://pub.dev/packages/metadata_fetch_plus)
OGP対応でHTMLのMETAデータを取得。
JSON-LDへの対応が甘かったのでフォークして修正した上で利用しています。

### [flutter_charset_detector](https://pub.dev/packages/flutter_charset_detector)
UNICODE以外の文字コードから自動で変換してくれます。
OGP対応でUTF8以外の文字コードのサイトへ対応するため。

### [receive_sharing_intent](https://pub.dev/packages/receive_sharing_intent)
他のアプリからの共有を受けるために利用。
特にiOSでは手順がいくつかあったりバージョンアップで仕様が変わることがありました。

### [keep_keyboard_popup_menu](https://pub.dev/packages/keep_keyboard_popup_menu)
ソフトウェアキーボードを出しつつポップアップメニューを表示するため。
最新のFlutterでは動かず、既にアップデートもされていませんが、フォークして動くように修正して利用しています。

### [super_clipboard](https://pub.dev/packages/super_clipboard)
テキスト以外、特に画像のクリップボードからのペーストに対応するため。

### [flex_color_picker](https://pub.dev/packages/flex_color_picker)
アカウントごとのテーマカラーを選択するため。

### [kana_kit](https://pub.dev/packages/kana_kit)
日本語の判定と変換。
ハッシュタグはひらがな・カタカナをローマ字に変換して検索するようにしてます。

### [pretty_qr_code](https://pub.dev/packages/pretty_qr_code)
QRコード生成モジュール。
アカウントのプロフィールへのQRコードを生成しています。

## 気になっているパッケージ

### [flutter_image_compress](https://pub.dev/packages/flutter_image_compress)
画像のリサイズ、圧縮ライブラリ。
標準のImageでも機能は事足りるのですが、より効率的だと謳ってます。
あと、いま共有するのに画像をそのまま`share_plus`に渡しているので、最低限必要なサイズに落とした方がいいかなとも考えてます。

### [appinio_social_share](https://pub.dev/packages/appinio_social_share)
各サービスSDKベースの共有ライブラリ。
Facebook、Instagram等、OSの共有では思ったように画像が渡らないアプリへの共有に使えないかなと。

## あとがき

しかしフルクライアントを作られている方が日本でも何人もいるのですが、投稿のみのクライアントだけで作るのも仕様を追うのも大変なのに、フルクライアントなんてその比じゃないはず。
かつてTwitter向けのクライアントを作られていた方々もそうですけど、改めてみんな凄いなと感心しました。

そしてもうひとつ、Flutterで作ったBlueskyアプリを紹介しておきます。

### bskylog
冬コミの同人誌を書くのに、Blueskyへの過去の投稿を[twilog](https://twilog.togetter.com/)のようにログ検索したいと作ったFlutter製デスクトップアプリ（macOS、Windows）です。
11月頭から作り始めて月末に公開しました。
twilogと違いサーバに溜めるのではなくローカルのSQLite（[drift](https://pub.dev/packages/drift)を利用）に保存、検索するつくりになってます。

https://github.com/hidea/app_bskylog/releases/tag/v0.1.1

macOS版のFlutter、テキストフィールドでの日本語変換が二重に入力されたり不具合があります。
同様の不具合は、iOSでATOKから日本語を入力すると確認出来るのですが、こちらはATOK側の設定で回避可能です。
修正される気配もないので、Flutterのエンジンを追って調査しないと…と考えているのですがなかなか時間が取れません。