Day 4. 首頁、路由
本系列同步發表在 第11屆鐵人賽
中秋節到拉~ 先祝大家中秋快樂~
–
好了,廢話不多說,進入今天正題囉~
主頁面 - 首頁
lib/pages/home.dart
import 'package:flutter/material.dart';
// 主頁面
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
titleSpacing: 0.0,
title: TabBar(
labelPadding: EdgeInsets.zero,
tabs: <Widget>[
Tab(text: "Home"),
Tab(text: "Repo"),
Tab(text: "Activity"),
Tab(text: "Issues"),
],
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {},
)
],
),
body: TabBarView(
children: <Widget>[
HomePage(),
Text("Repo"),
Text("Activity"),
Text("Issues"),
],
),
drawer: Drawer(
...(略)
),
),
);
}
}
// 首頁
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: ListView(
children: <Widget>[
Container(
child: Divider(
height: 8.0,
color: Colors.grey[200],
),
color: Colors.grey[200],
),
ListTile(
dense: true,
title: Text("Hackernews Top"),
trailing: Icon(Icons.chevron_right),
onTap: () {},
),
Divider(
height: 0.0,
),
ListTile(
title: Text(
"Pre-industrial workers had a shorter workweek than today's"),
subtitle: Text("by jammygit 2 hours ago | 18 comments"),
onTap: () {},
),
...(中間省略)
ListTile(
title: Text("MisterBooo / LeetCodeAnimation"),
subtitle: Text("Java 38,443 6,483"),
onTap: () {},
),
],
),
);
}
}
這邊程式碼有點多,沒關係,先來簡單看個分解圖~
這邊登入後會進入主畫面(MainPage),根據 TabBar 四個 Tab 分成四個頁面,首頁(HomePage)、倉庫頁(RepoPage)、近況頁(ActivityPage)、議題頁(IssuePage)
可以注意到,主畫面跟登入頁一樣使用 Scaffold
這個 Widget,主要搭配 MaterialApp
作使用,它包含了
- Material Design 的元件(如屬性 appBar, bottomAppBar, floatingActionButton…)
- 互動性的元素(如 drawers, snack bars, and bottom sheets)
- 一定程度的可客製化(如 theme, l10n)
對我這個沒什麼設計感的工程師可說是一大便利呢~
一樣起手都是 StatelessWidget
,上圖特別我用紅色框起來都是可以對應程式碼裡寫的 Widget,
目前都只是再拉排版的動作,程式碼看起來不免看起來臃腫
之後都是需要慢慢重構,讓程式碼好維護管理
–
欸!? 怪怪的
眼尖(PM?)的同學可能看到有些地方 怪怪的…
對,沒錯上面的 TabBar 每個 Tab 根本文字展示不完全,而且離兩邊圖示有些落差…
這時候就可以開啟 Flutter debug 工具或 Devtools
(在 VSCode 中可以 Ctrl+Shift+p 直接找 Flutter: Toggle Debug Painting 或 Dart: Open DevTools)
可以清楚看到 Layout 長什麼樣子,加上查詢文件後發現有兩處可調整
- Tab 根本文字展示不完全 =>
TabBar.labelPadding: EdgeInsets.zero
, - 離兩邊圖示有些落差 =>
AppBar.titleSpacing: 0.0
修改完結果
路由 (Route)
好了,到現在我們有兩個頁面了,那要如何作跳轉呢?
稍微用簡單的圖來表示
LoginPage (/login) <== [Navigator] ==> MainPage (/home)
Flutter 裡在 Route
切換時會有個中間人 Navigator
來作導覽。
而導覽機制跟 Stack 一樣用 pop/push 操作,詳細可以參考這篇 Medium 文章(Flutter: Push, Pop, Push)
routes.dart
class GitmeRebornRoutes {
static const root = "/";
static const login = "/login";
static const home = "/home";
}
個人會習慣另外定義一個 Routes 類別,來對應路由名稱。
接下來簡單談一下 MaterialApp
裡是怎麼設定路由的。從上圖 紅框 中可以看到是影響頁面轉換的屬性。
主要判斷頁面如何轉換的步驟為
- 若有設定 home 屬性,便認之為 / ([root]根路由)
- 否則查找 routes 屬性,有無對應路由設定。
- 否則會調用 onGenerateRoute,來判斷是否有提供對應路由的回傳。
- 若上述都沒成立,則會調用到 onUnknownRoute。
(翻譯自 https://api.flutter.dev/flutter/material/MaterialApp-class.html )
接下來,我修改了些原本的程式碼
主要將 home
屬性移除,加上 routes
對應表格並且用 onGenerateRoute
來跳轉根路由到登入畫面(LoginPage)。
lib/pages/home.dart
& lib/pages/login.dart
再來,登入畫面中的登入按鈕和主畫面中的登出按鈕,當點擊他們時會使用 Navigator
導覽至新的頁面。
今日成果
–
好了,今日就先到這邊吧
看完可以去吃烤肉囉~