Open4

go_routerを使ってみる

YuheiNakasakaYuheiNakasaka

FlutterのNavigation2.0は少々癖があり使いにくい。そこで現状は有象無象のRouterライブラリが跋扈している最中だがgo_routerというpackageが中々良さそうだったので見てみる。

YuheiNakasakaYuheiNakasaka

Get Startedの例を見ればわかるが基本はpathとpageを設定していくだけ。直感的。

class App extends StatelessWidget {
  ...
  final _router = GoRouter(
    routes: [
      GoRoute(
        path: '/',
        pageBuilder: (context, state) => MaterialPage<void>(
          key: state.pageKey,
          child: const Page1Page(),
        ),
      ),
      GoRoute(
        path: '/page2',
        pageBuilder: (context, state) => MaterialPage<void>(
          key: state.pageKey,
          child: const Page2Page(),
        ),
      ),
    GoRoute(
      path: '/family/:fid',
      pageBuilder: (context, state) {
        // use state.params to get router parameter values
        final family = Families.family(state.params['fid']!);

        return MaterialPage<void>(
          key: state.pageKey,
          child: FamilyPage(family: family),
        );
      },
    ),
    ],
  ...
  );
}

YuheiNakasakaYuheiNakasaka

GoRouter自身がChangeNotifierなのでルーティングの変更を検知できるChangeNotifierProviderとかを使ってればルートの変化に応じてその配下が変化するとかも簡単にできそう。リダイレクトもサブディレクトリレベルでできる。mobile/Desktop/webと全てに対応してて良い。

YuheiNakasakaYuheiNakasaka

ざっと書いてみたけど難しい部分はなかった。実戦で使ってみても良いと思う。

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final _router = GoRouter(
    routes: [
      GoRoute(
        path: '/',
        pageBuilder: (context, state) => MaterialPage(
          child: HomePage(),
        ),
      ),
      GoRoute(
        path: '/page_a',
        pageBuilder: (context, state) => MaterialPage(
          child: BasicPage(title: 'Page A'),
        ),
      ),
      GoRoute(
        path: '/page_b',
        pageBuilder: (context, state) => MaterialPage(
          child: BasicPage(title: 'Page B'),
        ),
      ),
      GoRoute(
        path: '/page_b/:id',
        pageBuilder: (context, state) => MaterialPage(
          child: BasicPage(title: 'Page B / ${state.params["id"]!}'),
        ),
      ),
      GoRoute(
        path: '/redirected_page',
        pageBuilder: (context, state) => MaterialPage(
          child: BasicPage(title: 'Redirected Page'),
        ),
      ),
      GoRoute(
        path: '/admin',
        pageBuilder: (context, state) => MaterialPage(
          child: BasicPage(title: 'Admin Page'),
        ),
      ),
    ],
    errorPageBuilder: (_, state) => MaterialPage(
      key: state.pageKey,
      child: ErrorPage(),
    ),
    redirect: (state) {
      final isLoggedIn = false;
      if (!isLoggedIn && state.location == '/admin') {
        return '/redirected_page';
      }
      if (isLoggedIn && state.location == '/admin') {
        return '/admin';
      }
      return null;
    },
  );

  
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationParser: _router.routeInformationParser,
      routerDelegate: _router.routerDelegate,
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: ListView(
        children: [
          ListTile(
            onTap: () => context.go('/page_a'),
            title: Text('Page A'),
          ),
          ListTile(
            onTap: () => context.go('/page_b'),
            title: Text('Page B'),
          ),
          ListTile(
            onTap: () => context.go('/page_b/123'),
            title: Text('Page B 123'),
          ),
          ListTile(
            onTap: () => context.go('/admin'),
            title: Text('Admin'),
          ),
        ],
      ),
    );
  }
}

class BasicPage extends StatelessWidget {
  const BasicPage({
    Key? key,
    required this.title,
  }) : super(key: key);

  final String title;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        leading: IconButton(
          onPressed: () => context.go('/'),
          icon: Icon(Icons.arrow_back_ios),
        ),
      ),
      body: Center(
        child: Text(title),
      ),
    );
  }
}

class ErrorPage extends StatelessWidget {
  const ErrorPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Error occured.'),
      ),
    );
  }
}