キカガク プラットフォームブログ

株式会社キカガクのプラットフォームブログです。エンジニアやデザイナー、プロダクトマネージャーなどが記事を書いています。

普段Next.jsしか触ってないエンジニアがFlutter触ってみて感動したこと&つまづいたこと

はじめに

はじめまして。キカガクプラットフォーム部のずんだです。

業務ではほぼ Next.js しか触ることがないのですが、最近なんとなく Flutter を触ってみていました。
どちらもモダンなフレームワークなので似ている点も多いのですが、結構違う点も多く面白かったです。
せっかくなので、Next.js ユーザー視点から見た違いや感動したところ、つまづいたところなど思ったことを書いていきます。

※ 環境構築方法や具体的なコードの書き方については書きません。

対象読者

  • Next.js で開発したことがある

Flutter とは

Flutter は Google が開発したクロスプラットフォームの開発フレームワークです。
1 つのコードベースで iOSAndroid はもちろん、 Web アプリやデスクトップアプリケーションも同時に出力できます。
(何気にデスクトップアプリケーションは作るのがかなり楽なので個人的オススメです)

いまさら説明するまでもないかもしれませんが、世界中で人気があり GitHub でのスター数は 2023 年 1 月現在で 159k にもなっているオバケ・リポジトリです。
StackOverflow Survey 2023 では「最も使われている技術」の「その他フレームワークとライブラリ」部門で ReactNative や SwiftUI を抑えて 9 位となっています。(参考

感動したところ

flutter doctor が神

Flutter、初手の環境構築から体験が非常に良いです。
キモはインストールと同時に使用できる flutter doctor というコマンドで、実行すると必要なものが足りているかどうか一括チェックしてくれます。

クロスプラットフォームだけあって必要なもの自体は多いのですが、これのおかげで環境構築に詰まることはなかったです。
環境構築が親切なフレームワーク、嫌いな人はいませんよね?
(新たに技術を学ぶときにここで心折れたことのなんと多いことか...)

公式がなんでも揃えてくれてる

個人的ベスト感動ポイントです。Flutter はリンター、フォーマッター、テストフレームワークなど開発に必要なツールを公式が用意してくれていて、新規プロジェクトを作成するとすぐに使える状態になっています。 公式が用意してくれているだけあってかなり最適化してくれていて、例えばリンターであれば自動で直してくれる範囲もかなり広く、手動修正が必要な警告もかなり修正手順が分かりやすい印象です。 (リンターの警告をどうやって修正すればいいか分からずググった覚えはほぼないくらいです。ちなみに業務ではほぼ毎日...)

Next.js の場合、公式ドキュメントに従ってインストールした場合には ESLint こそ使えるようにはなっていますが、ほぼルールは導入されていません。
さらに、Prettier などのフォーマッタや Jest などのテストフレームワークは自分で導入&設定する必要があります。 それらはあくまで Next.js 公式とは無関係の汎用的なツールです。なので Next.js 向けの設定こそありますが、Flutter ほどフレームワークに最適化されているわけではありません。

開発ツールまで公式が用意してくれていることに関しては一長一短あるとは思われますが、個人的には

  • 安定している
  • 設定の必要がほぼなく、デフォルトに近い状態で使える
  • 最適化されていて、エラーメッセージが分かりやすい

の 3 点からかなり気に入っています。

UI パーツが揃っている

Flutter は UI パーツも公式が用意してくれていて、ボタンやテキストフィールドなどの基本的なものはもちろん、かなりの数のウィジェットが用意されています。ドキュメントざっと見ただけでは把握できないくらいあります。

公式がコンポーネントを用意してくれていると導入に迷うこともないですし、Flutter 開発者はほぼ全員同じウィジェットを使っているので問題もググりやすく、個人的には最高です。

(YouTubeFlutter 公式チャンネルには「Flutter Widget of the Week」というシリーズがあり、短い動画でウィジェットとその使い方が把握できるので、暇なときに観てみると面白いです)

Next.js の場合は様々な選択肢がありますが、弊社の場合は UI コンポーネントライブラリとして Chakra UI を使っています。 簡単に導入できて、デザインも良く十分実用的ではあるのですが、サードパーティのライブラリのため公式の機能への追従という点では少し不安があります。
また Next.js 公式のコンポーネントと名称が被っているコンポーネントもあり、どちらを使うかが実装箇所によってバラバラになってしまいがちです。これもサードパーティのライブラリならではの問題ですね。

UI 系のライブラリは一度導入してしまうとなかなか乗り換えるのが難しいので、ここが安定していると嬉しいですね。

つまづいたところ

ウィジェットコンポーネント

これが一番難しかったです。 Flutter の UI はウィジェットという単位で構成されており、React のコンポーネントと似ている部分もあるのですが、違う部分も多いです。

Next.js の場合、コンポーネントを作る場合には以下のように書きます。

import { useState } from "react";

export default function Test() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Flutter でウィジェットを書く場合、まずは StatelessWidget か StatefulWidget かを選択します。 StatelessWidget は状態を持たないウィジェットで、StatefulWidget は状態を持つウィジェットです。

// StatelessWidget
import 'package:flutter/material.dart';

class Stateless extends StatelessWidget {
  const Stateless({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const Text("Hello, Flutter!");
  }
}
// StatefulWidget
import 'package:flutter/material.dart';

class Stateful extends StatefulWidget {
  const Stateful({Key? key}) : super(key: key);

  @override
  State<Stateful> createState() => _StatefulState();
}

class _StatefulState extends State<Stateful> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: _incrementCounter,
      child: Text('$_counter'),
    );
  }
}

こんな感じで状態の有無で書き方が結構変わるので、あらかじめ実装するウィジェットの責務を認識しておかないと地味に面倒なことになります。

ここでStateful ウィジェットになにか値を与えて、それを_StatefulState内で使いたい場合はどうすればいいでしょう? Stateful ウィジェットのコンストラクタに引数を渡して、_StatefulState内で変数 widget から取得する必要があります。

// StatefulWidget
import 'package:flutter/material.dart';

class Stateful extends StatefulWidget {
  const Stateful({super.key, required this.prefix});
  final String prefix;

  @override
  State<Stateful> createState() => _StatefulState();
}

class _StatefulState extends State<Stateful> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return TextButton(
      onPressed: _incrementCounter,
      child: Text('${widget.prefix}$_counter'),
    );
  }
}

// こう呼ぶ
// Stateful(prefix: "Counter: ")

引数に与えた値をそのまま使える Next.js と比べると、すこし複雑ですね。
このように UI パーツを作成する分には Next.js に比べると意識することが増えて大変な印象でした。

(大型)ライブラリを入れると書き方が結構変わる

Flutter はライブラリももちろん豊富で、その中でも状態管理系の RiverpodFleezeFlutter Hooks は汎用的に使えることから人気が高めです。

ただ、これらはどの程度の規模から導入するのが良いのか分からず... 書き方が結構変わってしまうので、最初は使わず必要になったら入れる、というのもすこし難しそうな印象でした。 さらに、コード例を検索すると導入している or していないコードが混在しており、応用しにくいときもありました。

これが 1 つのライブラリならいいのですが、上記 3 つを使うパターンと一部のみを使うパターン、別の代替ライブラリを使うパターンもあり、それぞれ書き方が結構違います... 比較してみると Next.js は小規模なプロジェクトであればライブラリによって書き方が大きく変わることは少ないかなーと思いました。(React Hook Form あたりは結構変わりますが...)

おわりに

Flutter、開発体験がとても良いと感じました。
まず環境構築が最高で、なにかに挑戦するたびに心が折れている自分にとってはこれだけで好きになりました(ちょろい)

開発中の体験もよく、リンタやフォーマッターも高機能で警告の直し方も分かりやすいので 個人開発にありがちなザクッと動くものを作ってからリファクタリング、という開発スタイルにとても合っていると思いました。

ウィジェットの設計などまだまだ勉強不足な部分が多いので、今後も勉強していきたいです。
もっと勉強したい、と思える技術に出会えたのはとても幸せなことだなぁと思いました。

以上、最後まで読んでいただきありがとうございました!

余談

せっかくなのでなにかアプリをリリースしてからこの記事を書こうと思っていました。
私用の Mac を持っていないので、Windows でも開発できる Android アプリの線で考えていたのですが、以下の記事を見つけてしまいました。

Androidアプリの個人開発者へ新たに課される「Google Play」要件が結構厳しいと話題に

2023 年 11 月より Android アプリ開発の要件がかなり厳しくなり、 「20 人以上のテスターを集めて、14 日間のクローズドテスト」を課されるようになったようです。

(´;ω;`) そんなに知り合いいない...

そんなこんなでリリースは諦めました。
(そのうち Mac mini でも買おうかな...)