前言
上一節(jié)中我們一塊學(xué)習(xí)Flutter生命周期相關(guān)的基本知識,了解到了在flutter中生命周期函數(shù)存在的意義以及各個不同生命周期函數(shù)的回調(diào)時機(jī)拓哺,到目前為止我們已經(jīng)完成了對Flutter所有入門相關(guān)的課程學(xué)習(xí),掌握了各種常用組件的使用方法以及使用路由來完成頁面切換傳遞數(shù)據(jù)荧恍,還學(xué)習(xí)了在flutter中的數(shù)據(jù)存儲菲语,網(wǎng)絡(luò)請求等一系列的相關(guān)課程拴驮。本次課程作為基礎(chǔ)到進(jìn)階到過度篇唤反,咱們來一塊利用所學(xué)知識做一個課程表View凳寺,對Flutter相關(guān)知識點加以鞏固提高,做到活學(xué)活用彤侍。
1.課程目標(biāo)
- 分析課表view組成部分肠缨,拆解繪制流程
- 課表view繪制,數(shù)據(jù)準(zhǔn)備
- 自行利用所學(xué)Widget組合課表view
2.效果圖
我們先來看下已經(jīng)繪制好的課程表View效果圖盏阶,然后對效果圖上的具體實現(xiàn)流程做拆解分析晒奕,一步步來完成Flutter課程表view的實現(xiàn)。
從上面的效果圖我們可以分析得出般哼,該課表view可分解成下面幾個部分吴汪,我用不同的顏色塊標(biāo)記出
整體可分為三個大塊
- 1 頂部藍(lán)色框框圈住的日期星期區(qū)域
- 2 左側(cè)灰色框框圈住的課程節(jié)次索引區(qū)域
- 3 中間綠色框框圈起來的課程信息區(qū)域
下面我們來追一看不同區(qū)域的具體實現(xiàn)代碼
3. View拆分
3.1 頂部日期星期View
頂部日期View可以拆解為GridView+Column組成惠窄,之所以選擇GridView是因為我們要做到Column里的數(shù)據(jù)每一個item都均分顯示蒸眠,GridView設(shè)置單行顯示,Colum設(shè)置上面view是星期杆融,下面view是日期楞卡,利用小算法計算出當(dāng)前日期,然后給當(dāng)前日期設(shè)置不同的樣式脾歇,來提示用戶蒋腮。
分析了實現(xiàn)思路,具體代碼我就不詳細(xì)講解了貼上頂部日期星期的具體實現(xiàn)代碼供讀者參考
星期日期View代碼:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/pages/custom_widget/widget/SpaceWidget.dart';
/**
* desc:
* author: xiedong
* date: 4/25/21
**/
class SyllabusPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => PageState();
}
class PageState extends State<SyllabusPage> {
var weekList = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
var dateList = [];
var currentWeekIndex = 0;
@override
void initState() {
super.initState();
var monday = 1;
var mondayTime = DateTime.now();
//獲取本周星期一是幾號
while (mondayTime.weekday != monday) {
mondayTime = mondayTime.subtract(new Duration(days: 1));
}
mondayTime.year; //2020 年
mondayTime.month; //6(這里和js中的月份有區(qū)別藕各,js中是從0開始池摧,dart則從1開始,我們無需再進(jìn)行加一處理) 月
mondayTime.day; //6 日
// nowTime.hour ;//6 時
// nowTime.minute ;//6 分
// nowTime.second ;//6 秒
for (int i = 0; i < 7; i++) {
dateList.add(
mondayTime.month.toString() + "/" + (mondayTime.day + i).toString());
if ((mondayTime.day + i) == DateTime.now().day) {
setState(() {
currentWeekIndex = i + 1;
});
}
}
// print('Recent monday '+DateTime.now().day.toString());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("我的課表"),
centerTitle: true,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
child: GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 8,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8, childAspectRatio: 1 / 1),
itemBuilder: (BuildContext context, int index) {
return Container(
color: index == this.currentWeekIndex
? Color(0xf7f7f7)
: Colors.white,
child: Center(
child: index == 0
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("星期",
style: TextStyle(
fontSize: 14, color: Colors.black87)),
SpaceWidget(height: 5),
Text("日期", style: TextStyle(fontSize: 12)),
],
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(weekList[index - 1],
style: TextStyle(
fontSize: 14,
color: index == currentWeekIndex
? Colors.lightBlue
: Colors.black87)),
SpaceWidget(height: 5),
Text(dateList[index - 1],
style: TextStyle(
fontSize: 12,
color: index == currentWeekIndex
? Colors.lightBlue
: Colors.black87)),
],
),
),
);
}),
),
],
),
);
}
}
運行代碼后激况,效果如下圖所示:
3.2 中間課表view
中間課表view跟左側(cè)課程節(jié)次指引是在一個大View里處理的作彤,考慮到有些手機(jī)可能一屏顯示不完整個課程表視圖膘魄,這里我實現(xiàn)的邏輯是
- 1 先把上圖標(biāo)號為2跟3的區(qū)域包裹在一個
SingleChildScrollView
里讓整個View支持上下滑動,- 2.然后在
SingleChildScrollView
里用Row
包裹2跟3區(qū)域竭讳,2是一個GridView
儡炼,整體布局1列10行娘荡,跟課表view保持高度一樣,- 3 .區(qū)域3又分為兩部分,一個是
背景格子
區(qū)域驶拱,另外一個帶背景顏色的課表區(qū)域
,整個3區(qū)域我還是利用GridView
實現(xiàn)昨登,- 4 在這里蕴掏,我默認(rèn)讓每個
課程View即圖中標(biāo)號為4
的區(qū)域占兩個課程格子的大小,這樣一周7天思瘟,每天有5大節(jié)課荸百,所以GridView
需要設(shè)置為5行7列,供35個item滨攻,然后讓區(qū)域3跟左側(cè)2區(qū)域高度一致够话,- 5 區(qū)域3
GridView
中的每一個Item采用Stack布局,底下的一層view用Column
包括兩個高度一樣的Container
光绕,設(shè)置好邊框女嘲,讓他呈現(xiàn)格子的樣式,頂上的一層試圖用來顯示課程信息诞帐,背景顏色利用提前設(shè)置好的顏色數(shù)組值欣尼,每次隨機(jī)取不同的值設(shè)置不同的顏色,再利用Center
組件顯示課程具體信息停蕉。- 6 左側(cè)區(qū)域2 課程指引view設(shè)置相同的背景即可愕鼓,不需要特殊處理
核心代碼如下:
Expanded(
child: SingleChildScrollView(
child: Row(
children: [
Expanded(
flex: 1,
child: GridView.builder(
shrinkWrap: true,
// physics:ClampingScrollPhysics(),
itemCount: 10,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, childAspectRatio: 1 / 2),
itemBuilder: (BuildContext context, int index) {
return Container(
// width: 25,
// height:s 80,
child: Center(
child: Text(
(index + 1).toInt().toString(),
style: TextStyle(fontSize: 15),
),
),
decoration: BoxDecoration(
color: Color(0xff5ff5),
// border: Border.all(color: Colors.black12, width: 0.5),
border: Border(
bottom: BorderSide(
color: Colors.black12, width: 0.5),
right: BorderSide(
color: Colors.black12, width: 0.5),
),
));
}),
),
Expanded(
flex: 7,
child: GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 35,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7, childAspectRatio: 1 / 4),
itemBuilder: (BuildContext context, int index) {
return Container(
child: Stack(
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
// border: Border.all(color: Colors.black12, width: 0.5),
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.5),
right: BorderSide(
color: Colors.black12,
width: 0.5),
),
)),
),
Flexible(
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
// border: Border.all(color: Colors.black12, width: 0.5),
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.5),
right: BorderSide(
color: Colors.black12,
width: 0.5),
),
)),
),
],
),
if (index % 5 == 0 || index % 5 == 1)
Container(
margin: EdgeInsets.all(0.5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: colorList[index % 7],
),
child: Center(
child: Text(
infoList[index % 2],
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 11,
letterSpacing: 1),
),
),
)
],
),
);
}),
)
],
),
),
),
運行代碼后,中間課程信息View的效果如圖
完整代碼如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app/pages/custom_widget/widget/SpaceWidget.dart';
/**
* desc:
* author: xiedong
* date: 4/25/21
**/
class SyllabusPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => PageState();
}
class PageState extends State<SyllabusPage> {
var colorList = [
Colors.red,
Colors.lightBlueAccent,
Colors.grey,
Colors.cyan,
Colors.amber,
Colors.deepPurpleAccent,
Colors.purpleAccent
];
var infoList = ["高等數(shù)學(xué)-周某某教授@綜合樓201", "大學(xué)英語-王某某講師@行政樓501"];
var weekList = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
var dateList = [];
var currentWeekIndex = 0;
@override
void initState() {
super.initState();
var monday = 1;
var mondayTime = DateTime.now();
//獲取本周星期一是幾號
while (mondayTime.weekday != monday) {
mondayTime = mondayTime.subtract(new Duration(days: 1));
}
mondayTime.year; //2020 年
mondayTime.month; //6(這里和js中的月份有區(qū)別慧起,js中是從0開始菇晃,dart則從1開始,我們無需再進(jìn)行加一處理) 月
mondayTime.day; //6 日
// nowTime.hour ;//6 時
// nowTime.minute ;//6 分
// nowTime.second ;//6 秒
for (int i = 0; i < 7; i++) {
dateList.add(
mondayTime.month.toString() + "/" + (mondayTime.day + i).toString());
if ((mondayTime.day + i) == DateTime.now().day) {
setState(() {
currentWeekIndex = i + 1;
});
}
}
// print('Recent monday '+DateTime.now().day.toString());
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
child: GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 8,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8, childAspectRatio: 1 / 1),
itemBuilder: (BuildContext context, int index) {
return Container(
color: index == this.currentWeekIndex
? Color(0xf7f7f7)
: Colors.white,
child: Center(
child: index == 0
? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("星期",
style: TextStyle(
fontSize: 14, color: Colors.black87)),
SpaceWidget(height: 5),
Text("日期", style: TextStyle(fontSize: 12)),
],
)
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(weekList[index - 1],
style: TextStyle(
fontSize: 14,
color: index == currentWeekIndex
? Colors.lightBlue
: Colors.black87)),
SpaceWidget(height: 5),
Text(dateList[index - 1],
style: TextStyle(
fontSize: 12,
color: index == currentWeekIndex
? Colors.lightBlue
: Colors.black87)),
],
),
),
);
}),
),
Expanded(
child: SingleChildScrollView(
child: Row(
children: [
Expanded(
flex: 1,
child: GridView.builder(
shrinkWrap: true,
// physics:ClampingScrollPhysics(),
itemCount: 10,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1, childAspectRatio: 1 / 2),
itemBuilder: (BuildContext context, int index) {
return Container(
// width: 25,
// height:s 80,
child: Center(
child: Text(
(index + 1).toInt().toString(),
style: TextStyle(fontSize: 15),
),
),
decoration: BoxDecoration(
color: Color(0xff5ff5),
// border: Border.all(color: Colors.black12, width: 0.5),
border: Border(
bottom: BorderSide(
color: Colors.black12, width: 0.5),
right: BorderSide(
color: Colors.black12, width: 0.5),
),
));
}),
),
Expanded(
flex: 7,
child: GridView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: 35,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7, childAspectRatio: 1 / 4),
itemBuilder: (BuildContext context, int index) {
return Container(
child: Stack(
children: [
Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Flexible(
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
// border: Border.all(color: Colors.black12, width: 0.5),
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.5),
right: BorderSide(
color: Colors.black12,
width: 0.5),
),
)),
),
Flexible(
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
// border: Border.all(color: Colors.black12, width: 0.5),
border: Border(
bottom: BorderSide(
color: Colors.black12,
width: 0.5),
right: BorderSide(
color: Colors.black12,
width: 0.5),
),
)),
),
],
),
if (index % 5 == 0 || index % 5 == 1)
Container(
margin: EdgeInsets.all(0.5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(2),
color: colorList[index % 7],
),
child: Center(
child: Text(
infoList[index % 2],
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 11,
letterSpacing: 1),
),
),
)
],
),
);
}),
)
],
),
),
),
_bottomView
],
),
);
}
@override
String pageTitle() => "我的課表";
Widget _topView = SizedBox(
height: 30,
child: Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 7,
itemBuilder: (BuildContext context, int index) {
return Text("dd");
}),
),
);
Widget _centerView = Expanded(
child: GridView.builder(
itemCount: 63,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
),
itemBuilder: (BuildContext context, int index) {
return Container(
// width: 25,
// height: 80,
child: Center(
child: Text(
(index + 1).toString(),
style: TextStyle(fontSize: 15),
),
),
decoration: BoxDecoration(
color: Color(0xff5ff5),
border: Border.all(color: Colors.black12, width: 0.5),
));
}),
);
Widget _bottomView = SizedBox(
height: 30,
child: Row(
children: [
//底部view可自行擴(kuò)充
],
),
);
}
詳細(xì)代碼已同步更新到我的Flutter入門進(jìn)階之旅專欄上蚓挤,感興趣的讀者可以自行查閱更多相關(guān)代碼:
倉庫地址:本節(jié)Flutter課程吧View課程代碼