1. 程式人生 > 其它 >路由與導航

路由與導航

路由與導航

在原生的Android中,一個Activity稱為一個路由,在iOS中指的是一個ViewController。如果需要開啟一個新的路由,在Android中可以使用startActivity()方法,並且傳入引數。在iOS中可以使用pushViewController()。

而在Flutter中,使用了Router與Navigator來組成統一的管理,Router是頁面的一個抽象概念,用這個可以建立介面、接收引數以及響應Navigator的開啟和關閉;而Navigator則是用於管理和維護路由棧,開啟路由頁面或者關閉路由頁面,即入棧出棧操作。

在Flutter中,路由分為兩種,一種是基本路由,一種是命名路由。

  • 基本路由:無需提前註冊,在切換頁面的時候需要手動構造頁面的例項。
  • 命名路由:需要提前註冊路由頁面識別符號,在頁面切換時通過路由識別符號開啟一個新的路由頁面。

基本路由

如果需要開啟一個新的頁面,需要建立一個MaterialPageRoute物件例項,然後呼叫Navigator.push(),那麼Flutter就會把新跳轉的頁面放到棧頂。如果需要關閉新的頁面,那麼使用Navigator.pop()即可。一個樣例程式碼如下:

first_page.dart

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

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("first page"),),
      body: Center(
        child: TextButton(
          child: Text("turn to second page"),
          onPressed: () => Navigator.push(
              context, MaterialPageRoute(builder: (context) => SecondPage())),
        ),
      ),
    );
  }
}

second_page.dart

import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("second page"),),
      body: Center(
        child: TextButton(
          child: Text("back to first page"),
          onPressed: () => Navigator.pop(context),
        ),
      ),
    );
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_router_page/first_page.dart';

void main(List<String> args) => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: FirstPage(),);
  }
}

這樣就可以做成一個簡單的路由跳轉了。建立新的路由物件使用的是MaterialPageRoute類,該類是PageRoute的子類,定義了路由建立及切換時過渡動畫的相關介面和屬性,並且自帶頁面切換動畫。

命名路由

為了必要頻繁的建立MaterialPageRoute例項,可以使用命名路由的方式。在命名路由中,需要提前建立好對映規則,即路由表。對應路由表中,第一個引數對應頁面的名字,第二個引數對應頁面,且需要以MaterialApp為根。

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_namerouter_page/first_page.dart';
import 'package:flutter_namerouter_page/second_page.dart';
import 'package:flutter_namerouter_page/unknown_page.dart';

void main(List<String> args) => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      /// 路由表結構
      routes: {
        'first': (context) => FirstPage(),
        'second': (context) => SecondPage(),
      },
      /// 初始化頁面
      initialRoute: 'first',
      /// 路由表中沒有的頁面會跳轉到的地方
      onUnknownRoute: (RouteSettings setting) =>
          MaterialPageRoute(builder: (context) => UnKnownPage()),
    );
  }
}

first_page.dart

import 'package:flutter/material.dart';

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("first page"),
      ),
      body: Center(
        child: TextButton(
          child: Text("turn to second page"),
          onPressed: () => {Navigator.pushNamed(context, "second")},
        ),
      ),
    );
  }
}

second_page.dart

import 'package:flutter/material.dart';

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("second page"),
      ),
      body: Center(
          child: Column(
        children: [
          TextButton(
            child: Text("back to first page"),
            onPressed: () => {Navigator.pop(context)},
          ),
          TextButton(
            child: Text("turn to first page"),
            onPressed: () => Navigator.pushNamed(context, 'first'),
          ),
          TextButton(
              onPressed: () => Navigator.pushNamed(context, 'third'),
              child: Text("turn to third page")),
        ],
      )),
    );
  }
}

unknown.dart

import 'package:flutter/material.dart';

class UnKnownPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("unknown page"),
      ),
      body: Center(
        child: TextButton(
          child: Text("unknown page"),
          onPressed: () => Navigator.pop(context),
        ),
      ),
    );
  }
}

路由傳參

在Flutter中,路由類似於Activity一樣。在Android中進行頁面傳參,使用的是Intent進行傳遞,然後使用startActivityForResult()進行傳參。

在Flutter中,使用Navigator.of(context).pushNamed('頁面名稱', arguments: 傳遞的資料).then((value){如果是StatefulWidget的話,那麼可以在此使用setState進行設定。})

在第二個頁面中,使用 ModalRoute.of(context).settings.arguments 獲取頁面傳輸過來的引數,類似於Android中的getIntent() ,然後進行轉換。在第二個頁面中,可以使用 Navigator.pop(context,'傳遞的引數')

路由棧

在Android中,Activity具有四種不同的啟動模式,standard,singleTask,singleTop,singleInstance。在Flutter中,使用了安卓中的啟動模式的管理方式。

在Flutter中,可以單獨移除路由棧中的一個特定的頁面。使用Navigator.removeRoute() & Navigator.removeRouteBelow() 進行移除。

方式1: push() & pushNamed() => pop()

這種方式類似於Android中的standard的模式,模型圖如下:

使用push或者pushNamed後的模型圖

使用pop後的模型圖

方式2: pushReplacement() & pushReplacementNamed()

這種方式用在路由棧頂頁面的替換場景,比如棧頂是b,想替換成c,那麼使用該函式即可。類似於Android中先把棧頂彈出,然後再放入c路由。這種模型的方式所展現的動畫僅僅是路由c的進入動畫,並沒有b的退出動畫。模型圖如下:

方式3: popAndPushNamed()

這種方式類似於方式2,但是在動畫方面有不一樣的地方。使用這種方式,在b彈出的時候,會顯示b的彈出動畫,而c進入的時候,又回顯示c的進入動畫。比方式2多一個彈出動畫。

方式4: pushAndRemoveUntil() & pushNamedAndRemoveUntil()

從路由棧中,新新增一個頁面,並且刪除路由棧中所有之前的路由。也就是說,使用這種方式進行路由的新增,路由棧中僅有一個路由。也可以使用這個方法,進行開啟一個新的頁面,並且制定路由棧中的某一個頁面以上的路由頁面都刪除。

比如執行這條語句Navigator.pushNamedAndRemoveUtil(context,'page_e',ModalRoute.withName('page_b')) 意思是開啟一個d頁面,然後刪除b頁面以上的頁面,示意圖如下:

方式5: popUntil()

一直彈出,直到某一個頁面位置,沒有push的操作。Navigator.popUtil(context,ModalRoute.withName('page_a'))

自定義路由

如果在兩個路由之間跳轉,需要自定義動畫,那麼會使用到自定義路由。自定義路由需要繼承PageRouteBuilder類。該類是所有自定義路由的基類。在PageRouteBuilder中,有幾個屬性比較重要,如下:

編號 名稱 作用
1 pageBuilder 用來建立所需要跳轉的路由頁面
2 opaque 是否需要遮擋整個頁面
3 transitionsBuilder 用於自定義專場動畫
4 transitionDuration 自定義專場動畫的執行時間
這是小睿的部落格,如果需要轉載,請標註出處啦~ヾ(≧▽≦*)o謝謝。