Flutterで動的にOGPを変えられるようにした話

はじめに

こちらのブログはFlutterでできています。スタックに関してはこちらをご覧ください!

https://idonuntius.com/blogs/introduction_flutter_blog

今回はブログ詳細ページをページ毎にOGPを変えられるようにしたので、その実装方法を紹介できればと思います。

前提

firebase.jsonの編集

firebase.jsonの rewrites を使っていきます。
source にはブログ詳細ページのそれぞれのpathを記載。
destination には次で説明する各ブログ詳細ページ用のHTMLを記載。

{
  "hosting": {
    "public": "build/web",
    // ~~~~~
    "rewrites": [
      // ↓↓↓ 追加
      {
        "source": "/blogs/introduction_flutter_blog",
        "destination": "/html/introduction_flutter_blog.html"
      },
      {
        "source": "/blogs/first_commit",
        "destination": "/html/first_commit.html"
      },
      // ↑↑↑ 追加
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

各ブログ詳細ページのHTMLを作成

/web ディレクトリに html ディレクトリを作成し各HTMLを追加します。
og:image に関しては /web ディレクトリに images ディレクトリを作成し、そこに画像を入れています。

/web/html/first_commit.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <link rel="icon" type="image/png" href="./../favicon.png" />
  <title>First commit</title>
  <meta name="description" content="ブログ始めました">

  <meta property="og:title" content="First commit">
  <meta property="og:description" content="ブログ始めました">
  <meta property="og:image" content="https://idonuntius.com/images/first_commit.png" />
  <meta property="og:type" content="article">
  <meta property="og:site_name" content="idonun.blog">

  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@idonuntius">
  <meta name="twitter:title" content="First commit">
  <meta name="twitter:description" content="ブログ始めました">
</head>

<body>
  <script type="text/javascript">window.location = "/blogs/first_commit_";</script>
</body>

</html>

重要なのが <script type='text/javascript'>window.location = '/blogs/first_commit_';</script> です。
こうすことで /blogs/first_commit とURLを打っても /blogs/first_commit_ にリダイレクトされます。 _を最後につけることで、ループすることを防いでいます。

ref) https://zenn.dev/flutteruniv_dev/articles/8ee86f7b4b9c61#%E2%91%A0firebase-functions%E3%81%AB%E5%8B%95%E7%9A%84%E3%81%AB%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6ogp%E8%A8%AD%E5%AE%9A%E3%82%92%E8%BF%94%E3%81%99function%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B

Flutter web側の対応

pathが /blogs/${slug}_なURLが返ってくるため、Flutter web側でそれに対応していく必要があります。
自身のブログではgo_router + go_router_builderを使用しているため、そのライブラリでの実装方法を記載していきます。

go_routerにはredirectがあります。URLが叩かれるとGoRouterクラスのredirectが反応します。 /blogs/${slug}_が叩かれた際もここが呼ばれるので、最後の_を削除して返すようにします。

final goRouter = GoRouter(
  routes: $appRoutes,
  redirect: (context, state) {
    if (state.fullPath == '/${BlogDetailRoute.path}') {
      final value = state.pathParameters['slug'];
      if (value != null && value.isNotEmpty && value.substring(value.length - 1, value.length) == '_') {
        return '/blogs/${value.substring(0, value.length - 1)}';
      }
    }
    return null;
  },
  errorBuilder: (context, state) => ErrorScreenRoute().build(context, state),
);

// ~~~

class BlogDetailRoute extends GoRouteData {
  const BlogDetailRoute({required this.slug});

  static const path = 'blogs/:slug';

  final String slug;

  @override
  Widget build(BuildContext context, GoRouterState state) {
    return BlogDetailScreen(slug: slug);
  }
}

結果

X(旧Twitter) でURLを入力したところ、問題なく表示されました!!

最後に

OGPの情報がちゃんと表示されました。実際はブログ投稿時にいちいち firebase.json やHTMLの作成しているのは辛いので、コマンドを叩けば生成されるようにしています。
しかしまだ画像だけは自動化できていないので、どこかでできたらと思っています。