使用具名路由


Navigator.push 需要指定 Route 實例,若要切換的頁面變多時,會造成 Route 的指定,分散在程式碼的多個位置,這時可以使用具名路由(Named routes),為每個路由命名、集中管理。

來直接看看〈Navigator 與 MaterialPageRoute〉中的第一個範例,如何改用具名路由:

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
  title: 'Openhome',
  // 設定初始路由,initialRoute 預設值其實就是 '/'
  initialRoute: '/',
  // 路由表
  routes: {
    '/'       : (_) => MainPage(),
    '/detail' : (_) => DetailPage()
  }
));

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主畫面'),
      ),
      body: GestureDetector(
        onTap: () {
          // 使用 pushNamed 指定路由名稱
          Navigator.pushNamed(context, '/detail');
        },
        child: Image.asset('images/caterpillar.png'),
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: () => Navigator.pop(context),
        child: Center(
          child: Image.asset('images/caterpillar.png'),
        ),
      ),
    );
  }
}

MaterialApp 可以設定 routes,路由表是藉由 Map<String, Widget Function(BuildContext)> 來指定,initialRoute 可以指定初始時要顯示的路由,若想指定某個路由置入 Navigator 的堆疊,可以使用 Navigator.pushNamed 指定路由名稱。

問題來了,若路由表集中在 routes 指定了,該怎麼在操作時,傳遞特定資料給頁面呢?Navigator.pushNamed 可以指定 arguments,在後續建構 Widget 的相關場合中,若可以取得 BuildContext,就可以透過 ModalRoute.of(context).settings.arguments,來取得各路由被置入堆疊時指定的 arguments

例如,若要將〈Navigator 與 MaterialPageRoute〉中第一個範例,改為使用具名路由的話,可以如下:

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
  title: 'Openhome',
  routes: {
    '/'       : (_) => MainPage(),

    // 取得 pushNamed 傳入的 arguments
    '/detail' : (context) => DetailPage(ModalRoute.of(context).settings.arguments)
  }
));

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主畫面'),
      ),
      body: GestureDetector(
        // 指定的 arguments
        onTap: () => Navigator.pushNamed(context, '/detail', arguments: '說明'),
        child: Image.asset('images/caterpillar.png'),
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
  String title;

  DetailPage(this.title);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: GestureDetector(
        onTap: () => Navigator.pop(context, '結果值'),
        child: Center(
          child: Image.asset('images/caterpillar.png'),
        ),
      ),
    );
  }
}

為了不修改 DetailPage,在建立 DetailPage 實例時,透過 ModalRoute.of(context).settings.arguments 取得了 Navigator.pushNamed 指定的 arguments;你也可以修改一下 DetailPage,不透過建構式傳入 title

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
  title: 'Openhome',
  routes: {
    '/'       : (_) => MainPage(),
    '/detail' : (_) => DetailPage()
  }
));

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('主畫面'),
      ),
      body: GestureDetector(
        onTap: () => Navigator.pushNamed(context, '/detail', arguments: '說明'),
        child: Image.asset('images/caterpillar.png'),
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // 透過 ModalRoute.of(context).settings.arguments 取得 title
        title: Text(ModalRoute.of(context).settings.arguments),
      ),
      body: GestureDetector(
        onTap: () => Navigator.pop(context, '結果值'),
        child: Center(
          child: Image.asset('images/caterpillar.png'),
        ),
      ),
    );
  }
}