4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Flutter for Webで可変パスとブラウザからの直接アクセスへの対応

Last updated at Posted at 2022-06-30

Flutter for Webではルーティングを設定することで、そのパスへアクセスした時に特定のウィジェットを呼び出せます。

シンプルな方法

via flutter-webでURLをつける方法 - Qiita

class HomePage extends StatelessWidget {
  //このClassのURLを以下で定義する
  static const String route = '/mypage';

そして MaterialApp の初期化時に routes でパスを指定します。

MaterialApp(
    initialRoute: HomePage.route,
    routes: {
      HomePage.route: (context) => HomePage(),
      LoginPage.route: (context) => LoginPage(),

この方法の場合、 /users/100 とか /users/100/products といった可変パスに対応できません。

fluroを使う

そこでfluroを使います。例えば以下のようにして /github/ユーザ名/リポジトリ名 みたいなパスに対応します。詳細は使い方は 【Flutter】ルーティングライブラリFluroの使い方 - 中安拓也のブログが参考になります。

import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import '../pages/home_page.dart';
import '../pages/learning.dart';

Handler createBasicHandler(Widget targetWidget) {
  return Handler(
      handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
    return targetWidget;
  });
}

Handler learningPageHandler = Handler(
    handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
  return LearningPage(
      userName: params['userName']!.first,
      repository: params['repository']!.first);
});

class Routes {
  static void configureRoutes(FluroRouter router) {
    router
      ..define('/', handler: createBasicHandler(const HomePage()))
      ..define('/github/:userName/:repository', handler: learningPageHandler);
  }
}

後はNavigatorを拡張した FluroRouternavigateTo で画面遷移できます。

MyApp.router!.navigateTo(context, '/github/goofmint/test-repo')

アプリの初期化時には routes ではなく onGenerateRoute で指定します。

class MyApp extends StatelessWidget {
  static FluroRouter? router;
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Manabiya',
        debugShowCheckedModeBanner: false,
        theme: ThemeData.light(),
        darkTheme: ThemeData.dark(),
        initialRoute: '/',
        onGenerateRoute: MyApp.router!.generator;
    );
  }
}

Webブラウザから直アクセスするとエラーが出る

ここまでは良いのですが、画面遷移した /github/goofmint/test-repo にブラウザから直接アクセスするとエラーが出ました。

Expected a value of type 'Handler', but got one of type 'Null'
See also: https://flutter.dev/docs/testing/errors

このエラーが出る原因が全然分からなかったのですが、 onGenerateRoute を実行した結果、HandlerではなくNullが返ってくるのでエラーになったのだろうと推測しました。そこで、 dart - Difference between onGenerateRoute and routes in Flutter - Stack Overflow を参考にonGenerateRouteの内容を見てみました。

class MyApp extends StatelessWidget {
  static FluroRouter? router;
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Manabiya',
        debugShowCheckedModeBanner: false,
        theme: ThemeData.light(),
        darkTheme: ThemeData.dark(),
        initialRoute: '/',
        onGenerateRoute: (setting) {
          // settingは文字列ではないのでdebugPrintではなくprintにしています
          print(setting);
          return MyApp.router!.generator(setting);
        });
  }
}

その結果、こんなログが確認できました。

RouteSettings("/", null)
RouteSettings("/github", null)

パスを / で区切って、順番にアクセスしているようです。 /github は元々想定していなかったので、この設定が必要とのことです。

さらに /github/:userName も必要なので、次のようにルーティング設定を追加します。不要なルーティングはすべてHomePageを表示するようにしていますが、本当な何らか用意すべきかも知れません。

router
  ..define('/', handler: createBasicHandler(const HomePage()))
  ..define('/github', handler: createBasicHandler(const HomePage()))
  ..define('/github/:userName',
      handler: createBasicHandler(const HomePage()))
  ..define('/github/:userName/:repository', handler: learningPageHandler);

ちなみに /github/goofmint/test-repo/ に画面遷移するようにした場合、最後の / がない場合とある場合、両方のルーティングが必要です。

router
  ..define('/', handler: createBasicHandler(const HomePage()))
  ..define('/github', handler: createBasicHandler(const HomePage()))
  ..define('/github/:userName',
      handler: createBasicHandler(const HomePage()))
  ..define('/github/:userName/:repository', handler: learningPageHandler)
  ..define('/github/:userName/:repository/', handler: learningPageHandler);

面倒な場合は最後の / はない画面遷移にするのが良さそうです。

まとめ

Flutter for WebはSEO対策さえ無視できれば(認証必須のWebアプリの場合など)、かなり使いやすいWebフロントエンドフレームワークではないかと思います。SelextableTextを使うことでテキストのコピペもできます。URLのルーティングもfluroを使えば(ハマりましたが)自由度高くできそうです。

もしURLの /#/ を消したい場合にはurl_strategyを使えば簡単に削除できます。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?