Qiita Advent Calendar に出稿するのも今日が最後、ここに来て箸休めです。
これまでの技術記事はこちらより確認いただけます。
今年は 2 つの大きな技術カンファレンスを裏で支援していました。
そのうちのひとつ FlutterKaigi について書きます。
なお、今年は仕事とプライベート双方でお世話になった Vite の知見を発信させていただいた。
スケールに挑戦の 2022 年だった
まずは、これまでのふりかえりを下に示してみます。
中でも一番の飛躍を遂げた 2021 年と違って今年、個人として Flutter コミュニティの大きいニュースは 3 つありました。
- Flutter カンファレンス FlutterKaigi 2022 のオーガナイズとそのスケール
- FlutterKaigi 2022 ウェブサイトへのコントリビュート
- Flutter ハンズオン企画への高い精度
私自身は今年、現地より FlutterKaigi の配信を見守りました。
中でも FlutterKaigi の運営に携わるスタッフをスケールした経験こそ、一番価値がありました。
2021 年、携わったスタッフ一覧 (計 13 名) を下に示してみます。
そして今年 2022 年、携わったスタッフ一覧 (計 23 名) を下に示してみます。
率直に数だけで 2 倍近くの意欲あるスタッフに来ていただきました。
これも 6 月末、スタッフ募集を正式に 告知 させていただいた結果、これほどの方々に来ていただいたお陰となっています。
いま一度このタイミングに、感謝を申し上げます。
数字で見る FlutterKaigi 2023
今年の FlutterKaigi 2022 ウェブサイトでは、新たに Google アナリティクス 4 を導入しています。
07 月 19 日 (火) から 11 月 29 日 (火) までのデータを計測しました。
- 訪問者数 4932 名
- 閲覧回数について
page_view
というイベント名を観測したところ 11000 回 - スクロール回数について
scroll
というイベント名を観測したところ 11000 回
それ以外の具体的な数字については、FlutterKaigi 公式の開催報告をご確認いただければ。
- YouTube 配信情報
- 事前登録(申込)、参加者数
- アンケート
- FlutterKaigi 2022 ウェブサイト閲覧件数
Flutter for Web で Google アナリティクス 4 を利用する
Google アナリティクス 4 の設定は、至ってシンプルとなります。
ここより先は Web 開発者の一参考資料として、聞いてください。
web/index.html
に G-XXXXXXXXXX
を設定します。
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || []
function gtag() {
dataLayer.push(arguments)
}
gtag('js', new Date())
gtag('config', 'G-XXXXXXXXXX')
</script>
2022 年 Flutter 技術動向
FlutterKaigi より大きく話題は変わって、今年のふりかえりでは Flutter の技術動向にも触れていきたい。
主な Flutter SDK の更新を下に示してみました。
いつもながら stable バージョンのとびとび感が際立っていますね。
具体的には 2.10 の Windows 向けデスクトップアプリ、3 の macOS 並びに Linux 向けデスクトップアプリのサポートがありました。
そんな中今月 8 日、来年 2023 年の中頃にも Dart 3 がリリースされる予定の旨と アナウンス がなされています。
ざっくり以下項目を挙げており、その詳細は同じく来年 1 月 23 日の Flutter Festival で公表されます。
- 完全な Null-Safety をサポート
- Web Assembly (WASM) のコンパイルをサポート
- Macros (静的メタプログラミング)
- その他
このうち、Dart 2.12 のリリースより 3 年を経て Null-Safety のサポートが完結されると明言したのは大変意義深いでしょう。
実際に静的型付の筆頭 TyoeScript を 例 に、そうした Null-Safety の恩恵を開発者が受けられるのは大変幸せになります。
Dart 3 の概要について、Dart 公式 Medium で述べられているので、そちらも合わせてご確認いただきたい。
昨年の Google I/O で発表された Material 3 の対応について、Flutter 開発チームで多くのコンポーネントを Flutter へ移行し続けています。
この Material 3 進捗状況は GitHub issue をご確認いただきたい。
FlutterKaigi 2022 ウェブサイトでは
FlutterKaigi 2022 ウェブサイトでも、いくつかお世話になっています。
- ルーティングで go_router
- 状態管理で riverpod
go_router について
昨年の FlutterKaigi 2021 ウェブサイトに引き続き、アプリ内で完結させるためのルーティングを設定できるようにしました。
ただし昨年ほど、そのルーティングを利用するには至っていません。
ちなみに、昨年は以下に示す通りのページを各種準備し、それに伴いルーティングを設定しました。
- トップ
- スタッフ一覧
- 採択セッション一覧
- (厳密に言えばページでは無い) ライセンス
- ティーザー (幕間用)
- インタールード (幕間用)
しかし、今年はトップのみページを準備する仕様となっています。
昨年の FlutterKaigi 2021 ウェブサイトについて、詳細が気になる方は以下の 記事 をご確認いただきたい。
実際、今年はトップのみページを準備する仕様となっていますが、製作前の設計段階ではルーティングを使用する前提でした。
結果的にルーティングライブラリとして、今回初めてお世話になった go_router が一番、使いやすいのでは (と、私感)
というのも、基本的に path
と builder
(ページのウィジェットなど) を設定するだけで、アプリケーションのルーティングを管理できます。
import 'package:go_router/go_router.dart';
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (_, __) => const TopPage(),
),
],
);
内部的には ChangeNotifier という 仕組み の下で動いています。
ルーティングの変更検知用に、合わせて ChangeNotifierProvider を使用して、その変化に対応することも可能となります。
まず Flutter には、命令型 API (Navigator 1.0)、宣言型 API (Navigator 2.0) 2 種類のルーティングメカニズムが提供されています。
この Navigator 2.0 は少々癖もの (私感) の一方で、Navigator 1.0 と一緒に使用でき、必ずしもそれらを置き換えるものでもありません。
riverpod について
今年の FlutterKaigi 2022 ウェブサイトでは、状態管理ツールとして riverpod も合わせて使用しています。
基本的には、main.dart などの最上位ルートへ ProviderScope
を利用することで、全ての Provider が global に宣言されるようなります。
void main() {
runApp(ProviderScope(
child: MyApp(),
));
}
Provider で go_router を囲んで参照できるようにします。
import 'package:confwebsite2022/pages/index.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
final routerProvider = Provider(
(ref) => GoRouter(
debugLogDiagnostics: true,
routes: [
GoRoute(
path: '/',
builder: (_, __) => const TopPage(),
),
],
errorPageBuilder: (context, state) => MaterialPage(
key: state.pageKey,
child: Scaffold(
body: Center(
child: Text(state.error.toString()),
),
),
),
),
);
そんな Riverpod も、フロントエンドビルドツールのひとつ Vite と同じく、リリースサイクルは長くありません。
いつの間にか Riverpod が v1 さらに v2 へ、テンポよく更新されており、中でも v2 のリリースが、今年 2022 年の大きな目玉のひとつとなります。
では、早速 pubspec.yaml の flutter_riverpod を 2 系に更新します。
dependencies:
flutter:
sdk: flutter
flutter_riverpod: ^2.0.2
そもそも Riverpod が求められる場面でそれが必要とされる理由については、Provider パッケージの欠点があります。
Provider の設計上、改良した InheritedWidget に原因があり、それが起因して ウィジェット ツリーに依存するという悪循環を引き起こしています。
🗂 MyApp
└ 🗂 Provider
└ 🗂 MaterialApp
└ 🗂 Widget
└ 🗂 SignIn <- Provider をラップしない場合に ProviderNotFoundException を引き起こす
└ 🗂 Provider
- Signed
上に示したウィジェットツリーのようにウィジェットを Provider でラップしていない限り、ProviderNotFoundException を引き起こしてしまいます。
この問題を解決するため登場したのが Riverpod で、全ての Provider が global に宣言されます。
では、その Riverpod についてマイグレーションガイドを中心に確認します。
まず Riverpod 0.14 を 1.0 へ更新する際に、Provider を利用する構文に変更があります。
// Riverpod 0.14
Widget build(BuildContext context, ScopedReader watch) {
MyModel state = watch(provider);
}
ref.watch(provider)
のひとつに統一されました。
// Riverpod 1.0
Widget build(BuildContext context, WidgetRef ref) {
MyModel state = ref.watch(provider);
}
また ConsumerWidget の build
と、Consumer の builder
のシグネチャも変更されました。
続いて Riverpod 2.0 へ更新する際は、新しく riverpod_generator パッケージが公開されています。
riverpod_generator の @riverpod
は、ソースコード内のクラスとメソッドの Provider を自動的に生成するために使用できる注釈 API となります。
Web の書き心地を実感する
当初 url_launcher の launch()
を利用した際に、Web ブラウザ上でそのリンクを確認できません。
永らく FlutterKaigi のウェブサイトにおいては、この手の Web 体験が損なわれてしまっていました。
import 'package:url_launcher/url_launcher.dart';
ElevatedButton.icon(
onPressed: () async => {
await launch(
'https://twitter.com/intent/tweet?hashtags=FlutterKaigi',
webOnlyWindowName: '_blank',
);
},
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
),
icon: SvgPicture.asset(Assets.twitterWhite, width: 20),
label: Text(appLocalizations.tweet),
),
一般的な Web サイトのように、マウスオーバーすることで、ユーザーにアンカーリンクを示すことにします。
url_launcher の Link
ウィジェットを利用することで、それを実現させられるようになります。
import 'package:url_launcher/link.dart';
Link(
uri: Uri.parse(
'https://twitter.com/intent/tweet?hashtags=FlutterKaigi'),
target: LinkTarget.blank,
builder: (BuildContext ctx, FollowLink? openLink) {
return ElevatedButton.icon(
onPressed: openLink,
style: ElevatedButton.styleFrom(
shape: const StadiumBorder(),
),
icon: SvgPicture.asset(Assets.twitterWhite, width: 20),
label: Text(appLocalizations.tweet),
);
},
),
target
に LinkTarget.blank
を指定することで、独立のブラウザとして指定のリンクが開かれます。
さらに Web 体験を向上させる努力を惜しまない
一般的な Web サイトでは当たり前のように、ハッシュ (#
) 付き URL を実装できます。
しかし Flutter (Web) では、その実装が大変難しいと判明、他の方法を模索します。
そこで Scrollable.ensureVisible
を利用すれば、特定の ID に対し自動でスクロールさせることができるようになりました。
Future<void> animationScroll(Object anchor) async {
final context = GlobalObjectKey(anchor).currentContext;
if (context == null) {
throw ErrorDescription(
"An unregistered object was passed in animationScroll.\nYou should register GlobalObjectKey for some widget.");
}
await Scrollable.ensureVisible(context,
duration: const Duration(milliseconds: 1000),
curve: Curves.fastOutSlowIn);
}
実装予定だった、ハッシュ付き URL の ID を引数のひとつとして受け入れます。
あらかじめ、この ID 一覧を enum で定義することとします。
enum MenuItem { staff, timetable, sponsor }
animationScroll()
に個別の ID を受け入れることで、自動スクロールを実現しています。
String urlString;
switch (result) {
case MenuItem.staff:
await animationScroll(MenuItem.staff);
return;
case MenuItem.timetable:
await animationScroll(MenuItem.timetable);
return;
case MenuItem.sponsor:
await animationScroll(MenuItem.sponsor);
return;
}
一般的な Web サイトのような体験は難しい (実現できるといえば) が、それに近付ける努力を惜しみません。
<video controls playsinline width="100%" autoplay loop muted="true" type="video/mp4" src="https://i.imgur.com/bQ6tvWK.mp4">
Sorry, your browser doesn't support embedded videos.
</video>
最後に
Flutter コミュニティに限らず、来年 2023 年も「挑戦」の姿勢を忘れること無く、コミュニティの運営を邁進していければ幸いです。