本系列同步發表在 個人部落格,歡迎大家關注~
經過兩天的努力,終於能來修改頁面拉~
第二部份的更改頁面我想大家應該可以猜得到,一樣用 FutureBuilder 那一套囉~
lib/pages/trending/project.dart
import "package:flutter/material.dart";
import 'package:gitme_reborn/components/github_trending_tiles.dart';
import 'package:gitme_reborn/services/github_trending_api.dart';
import 'package:gitme_reborn/services/models/project.dart';
class TrendingProjects extends StatefulWidget {
@override
_TrendingProjectsState createState() => _TrendingProjectsState();
}
class _TrendingProjectsState extends State<TrendingProjects> {
Future<List<Project>> projectList;
@override
void initState() {
super.initState();
projectList = githubTrendingClient.listProjects();
}
@override
Widget build(BuildContext context) {
return Scrollbar(
child: RefreshIndicator(
child: FutureBuilder(
future: projectList,
builder:
(BuildContext context, AsyncSnapshot<List<Project>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
if (!snapshot.hasError) {
return ListView.separated(
padding: EdgeInsets.all(0.0),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ProjectTile(
name: snapshot.data[index].fullName,
description: snapshot.data[index].description,
stars: snapshot.data[index].stars,
currentStars: snapshot.data[index].currentPeriodStars,
language: snapshot.data[index].language,
languageColor: snapshot.data[index].languageColor,
builtBy: snapshot.data[index].builtBy,
onPressed: () {},
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(height: 0.0),
);
} else {
return Center(child: Text("No Data"));
}
break;
case ConnectionState.none:
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
}
},
),
onRefresh: () async {
setState(() {
projectList = githubTrendingClient.listProjects();
});
},
),
);
}
}
lib/services/github_trending_api.dart 中宣告一個全域變數 GitHubTrendingApiClient githubTrendingClient = GitHubTrendingApiClient(); 方便作使用。GitHubTrendingApiClient 方法 listProjects 和 listDevelopers 就可以了。ProjectTile 是自己封裝的 Widget,方便維護用。(程式碼在 lib/components/github_trending_tiles.dart)不知道大家是否在 Day 10 時有注意到,其中畫面右上角的 PopupMenuButton 我完全沒提到是作什麼用。

在 GitHubTrendingApiClient 方法中有一個參數 since,是這 API 可選填的參數之一。

那麼這邊就先很簡潔地用 PopupMenuButton 的方式作呈現

程式碼中,宣告 enum TrendingDateRange { daily, weekly, monthly },
在 AppBar.actions 中填入 PopupMenuButton。
class TrendingPage extends StatefulWidget {
@override
_TrendingPageState createState() => _TrendingPageState();
}
class _TrendingPageState extends State<TrendingPage> {
TrendingDateRange _dateRange = TrendingDateRange.daily;
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: TabBar(...),
actions: <Widget>[
PopupMenuButton(
itemBuilder: (BuildContext context) {
return [
PopupMenuItem<TrendingDateRange>(
child: Row(
children: <Widget>[
Icon(Icons.date_range),
SizedBox(width: 8.0),
Text("Date range"),
],
),
enabled: false,
),
CheckedPopupMenuItem<TrendingDateRange>(
value: TrendingDateRange.daily,
checked: _dateRange == TrendingDateRange.daily,
child: Text("daily"),
),
CheckedPopupMenuItem<TrendingDateRange>(
value: TrendingDateRange.weekly,
checked: _dateRange == TrendingDateRange.weekly,
child: const Text("weekly"),
),
CheckedPopupMenuItem<TrendingDateRange>(
value: TrendingDateRange.monthly,
checked: _dateRange == TrendingDateRange.monthly,
child: const Text("monthly"),
),
];
},
onSelected: (TrendingDateRange range) {
setState(() {
_dateRange = range;
});
},
),
],
),
body: TabBarView(
children: <Widget>[
TrendingProjects(dateRange: _dateRange),
TrendingDevelopers(dateRange: _dateRange),
],
),
),
);
}
}
將 TrendingPage 切換成 StatefulWidget。
用 CheckedPopupMenuItem 讓每個選項前面有勾勾。
在 PopupMenuButton.onSelected 屬性裡使用 setState 來切換 _dateRange。
TrendingProjects 與 TrendingDevelopers 新增屬性 dateRange。
TrendingProjects 與 TrendingDevelopers 需作些微調。
主要注意紅框內的改變, initState 裡的東西轉移到 build 裡,這樣才能切換趨勢區間時,重新撈取資料與渲染頁面。
--
今日成果

好了,到今天本系列的第二部份 - API 串接篇進入第八天
差不多也要進入尾聲囉~