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を拡張した FluroRouter
の navigateTo
で画面遷移できます。
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を使えば簡単に削除できます。