Flutter 使用Navigator進行區域性跳轉頁面

老孟Flutter發表於2020-05-26

老孟導讀:Navigator元件使用的頻率不是很高,但在一些場景下非常適用,比如區域性表單多頁填寫、底部導航一直存在,每個tab各自導航場景。

Navigator 是管理路由的控制元件,通常情況下直接使用Navigator.of(context)的方法來跳轉頁面,之所以可以直接使用Navigator.of(context)是因為在WidgetsApp中使用了此控制元件,應用程式的根控制元件通常是MaterialAppMaterialApp包含WidgetsApp,所以可以直接使用Navigator的相關屬性。

Navigator用法非常簡單,如下:

Navigator(
  initialRoute: '/',
  onGenerateRoute: (RouteSettings settings) {
    WidgetBuilder builder;
    switch (settings.name) {
      case 'home':
        builder = (context) => PageA();
        break;
      case 'user':
        builder = (context) => PageB();
        break;
    }
    return MaterialPageRoute(builder: builder, settings: settings);
  },
)複製程式碼

initialRoute表示初始化路由,onGenerateRoute表示根據RouteSettings生成路由。

那麼在什麼情況下需要使用Navigator?在需要區域性頁面跳轉的地方使用Navigator,如下面的場景:

頭條客戶端舉報場景

頭條客戶端每一個新聞下面都有一個“叉號”,點選彈出相關資訊,點選其中的區域性,會在當前小窗戶內跳轉到舉報頁面,效果如下:

此場景就是使用Navigator的典型場景,點選舉報,並不是全屏切換頁面,而是僅僅在當前彈出的頁面進行切換。

首頁程式碼如下:

@override
Widget build(BuildContext context) {
  return Center(
    child: Container(
      height: 350,
      width: 300,
      child: Navigator(
        initialRoute: '/',
        onGenerateRoute: (RouteSettings settins) {
          WidgetBuilder builder;
          switch (settins.name) {
            case '/':
              builder = (context) => PageC();
              break;
          }
          return MaterialPageRoute(builder: builder);
        },
      ),
    ),
  );
}複製程式碼

Navigator的初始化路由為PageC頁面,PageC頁面程式碼如下:

class PageC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Card(
        child: Column(
          children: <Widget>[
            _buildItem(Icons.clear, '不感興趣', '減少這類內容'),
            Divider(),
            _buildItem(Icons.access_alarm, '舉報', '標題誇張,內容質量差 等',
                showArrow: true, onPress: () {
              Navigator.of(context).push(MaterialPageRoute(builder: (context) {
                return PageD();
              }));
            }),
            Divider(),
            _buildItem(Icons.perm_identity, '拉黑作者:新華網客戶端', ''),
            Divider(),
            _buildItem(Icons.account_circle, '遮蔽', '軍事視訊、駕駛員等'),
          ],
        ),
      ),
    );
  }

  _buildItem(IconData iconData, String title, String content,
      {bool showArrow = false, Function onPress}) {
    return Row(
      children: <Widget>[
        Icon(iconData),
        SizedBox(
          width: 20,
        ),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text(
                title,
                style: TextStyle(fontSize: 18),
              ),
              Text(
                content,
                style: TextStyle(
                    color: Colors.black.withOpacity(.5), fontSize: 14),
              )
            ],
          ),
        ),
        !showArrow
            ? Container()
            : IconButton(
                icon: Icon(Icons.arrow_forward_ios),
                iconSize: 16,
                onPressed: onPress,
              ),
      ],
    );
  }
}複製程式碼

PageC頁面跳轉到PageD頁面,PageD頁面程式碼如下:

class PageD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 200,
      width: 250,
      color: Colors.grey.withOpacity(.5),
      child: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.arrow_back_ios),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              Text('返回'),
              SizedBox(
                width: 30,
              ),
              Text('舉報'),
            ],
          ),
        ],
      ),
    );
  }
}複製程式碼

Navigator_2

最終實現了區域性跳轉效果,只在中間區域變化,其他區域不變。

Tab內跳轉

還有一個典型到應用場景就Tab內跳轉,效果如下:

Navigator_3

底部導航一直存在,每個tab都有自己的導航器。

首頁程式碼如下:

class TabMain extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _TabMainState();
}

class _TabMainState extends State<TabMain> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: <Widget>[
          TabNavigator(0),
          TabNavigator(1),
          TabNavigator(2),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        onTap: (int index) {
          setState(() {
            _currentIndex = index;
          });
        },
        currentIndex: _currentIndex,
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem(title: Text('首頁'), icon: Icon(Icons.home)),
          BottomNavigationBarItem(title: Text('書籍'), icon: Icon(Icons.book)),
          BottomNavigationBarItem(
              title: Text('我的'), icon: Icon(Icons.perm_identity)),
        ],
      ),
    );
  }
}複製程式碼

首頁定義了3個tab及切換效果。

定義TabNavigator:

class TabNavigator extends StatelessWidget {
  TabNavigator(this.index);

  final int index;

  @override
  Widget build(BuildContext context) {
    return Navigator(
      initialRoute: '/',
      onGenerateRoute: (RouteSettings settins) {
        WidgetBuilder builder;
        switch (settins.name) {
          case '/':
            builder = (context) => ListPage(index);
            break;
        }
        return MaterialPageRoute(builder: builder);
      },
    );
  }
}複製程式碼

列表頁面,此頁面一般為List頁面,點選其中一個跳轉到相關詳情頁面,這裡為了簡便,只放了一個跳轉按鈕:

class ListPage extends StatelessWidget {
  ListPage(this.index);

  final int index;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: RaisedButton(
          child: Text('$index'),
          onPressed: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return DetailPage();
            }));
          },
        ),
      ),
    );
  }
}複製程式碼

詳情頁面

class DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text('DetailPage'),
      ),
    );
  }
}複製程式碼

雖然Navigator控制元件不是特別常用,但在一些場景下非常適用。

交流

老孟Flutter部落格地址(近200個控制元件用法):laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:

相關文章