文章目錄
-
- 1、輸出Hello World
- 2、文字、容器
- 3、遠端圖片、本地圖片、實現圓形圖片,實現圓角圖片
- 4、基礎列表元件、 水平列表元件 、 圖示元件
- 5、列表、動態列表
- 6、網格佈局
- 7、底部導航
- 8、頁面跳轉、跳轉傳值(普通路由、普通路由傳值)
- 9、頁面跳轉、跳轉傳值(命名路由、命名路由傳值)
- 10、替換路由、返回到根路由
- 11、自定義AppBar 定義頂部Tab切換、底部Tab結合頂部Tab實現類似頭條頁面佈局
- 12、TabController定義頂部tab切換,並介紹生命週期函式
- 13、Drawer側邊欄、以及側邊欄內容佈局
- 14、按鈕元件 RaisedButton、FlatButton、OutlineButton、IconButton、ButtonBar以及自定義按鈕元件
- 15、類似閒魚App底部導航凸起按鈕
- 16、單行文字框、多選框
- 17、多行文字框、開關按鈕、多選框、單選按鈕、RadioListTile、Radio、表單
- 18、呼叫原生時間選擇器、日期選擇器、時間戳、Future非同步
- 19、呼叫第三方時間選擇器、日期選擇器、時間戳
- 20、輪播圖 flutter_swiper
- 21、普通對話方塊、列表對話方塊、單選對話方塊、Toast提示
1、輸出Hello World
1.1、 效果圖
1.2、輸出一句Hello World
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
//等價於:
/*
void main(){
runApp(MyApp());
}
*/
class MyApp extends StatelessWidget{ //抽離頁面全部內容
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp( //頂層 widget
home:Scaffold( //Material Design 佈局結構的基本實現
appBar: AppBar( //顯示在介面頂部的一個 AppBar。
title: Text('Flutter Demo'), //標題
),
body: HomeContent(), //當前介面所顯示的主要內容 Widget。
),
theme: ThemeData( //主題
primarySwatch: Colors.yellow //修改主題顏色
),
);
}
}
class HomeContent extends StatelessWidget{ //抽離主介面
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center( //居中
child: Text("Hello Flutter", //Text元件用來顯示字串內容到頁面中
textDirection:TextDirection.ltr, //表示文字的顯示方式
style: TextStyle( //樣式
fontSize: 40.0, //大小
color: Colors.yellow, //顏色
),
),
);
}
}
複製程式碼
1.3、知識點
1 、MaterialApp
MaterialApp 是一個方便的 Widget,它封裝了應用程式實現 Material Design 所需要的
一些 Widget。一般作為頂層 widget 使用。
常用的屬性:
home(主頁)
title(標題)
color(顏色)
theme(主題)
routes(路由)
2、Scaffold
Scaffold 是 Material Design 佈局結構的基本實現。此類提供了用於顯示 drawer、snackbar 和底部 sheet 的 API。
Scaffold 有下面幾個主要屬性:
appBar - 顯示在介面頂部的一個 AppBar。
body - 當前介面所顯示的主要內容 Widget。
drawer - 抽屜選單控制元件。
1.4、筆記
1.
所有的元件都是類
2.
在Dart中,允許例項化類的時候,不寫關鍵詞new,例如:
var p = new Persion();
可以寫成:
var p = Persion();
3.在Flutter裡面,我們把Center放在runApp裡面是顯然不合理的!
因為:當我們的一個元件功能需求很多的時候,我們的程式碼就會很多,那麼我們放到runApp裡,程式碼就會變得很雜亂冗餘
所以:我們可以把它單獨抽離成一個元件,相當於Java裡的封裝,減輕程式負擔,節儉程式碼質量
4.問題:為什麼例項化runApp(MyApp());的時候, Widget build(BuildContext context) {
// TODO: implement build
return Center( //居中
child: Text('Hello Flutter',
textDirection: TextDirection.ltr, //表示文字的顯示方式
)
);
}抽象方法會自動執行?
答:build 是用來建立 Widget 的,build 在每次介面重新整理的時候都會呼叫
5.child: Text是例項化Text的意思嗎?
答:不,我們來看下面這個程式碼自然就能明白了!
class MyApp extends StatelessWidget{
@override
//build 是用來建立 Widget 的,build 在每次介面重新整理的時候都會呼叫
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: HomeContent(),
),
theme: ThemeData(
primarySwatch: Colors.yellow //修改主題顏色
),
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center( //居中
child: Text('Hello Flutter',
textDirection: TextDirection.ltr, //表示文字的顯示方式
style: TextStyle( //字型樣式
fontSize: 40.0,
//設定顏色的兩種方式
// 方式一
color: Colors.yellow,
// 方式二:
//引數依次的含義為:紅色,綠色,藍色,透明度 ,顏色取前三個的混合值,透明度取第四個引數
// color: Color.fromRGBO(244, 233, 321, 0.5)
),
)
);
}
}
我們主要分析body就行了其他的不用管,可以看到body是主頁內容,他直接 body: HomeContent(),
等價於 body: new HomeContent(),那麼意思已經很明顯了,就是在要實現哪些功能的時候,就例項化哪些元件即可!
複製程式碼
2、文字、容器
2.1丶 效果圖
2.2、程式碼+註釋
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Fullter Demo'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container( //是一個容器,相當於前端的div,用於佈局
child: Text('我是一個文字我是一個文字我是一個文字我是一個文字我是一個文字我是一個文字',
textAlign: TextAlign.center, //居中顯示
overflow: TextOverflow.ellipsis, //表示溢位後用...代替
maxLines: 1, //最多顯示一行
textScaleFactor: 1.8, //字型放大兩倍
style: TextStyle(
fontSize: 16.0 , //設定字型大小
color: Colors.red , //字型顏色
fontWeight:FontWeight.w800, //字型加粗
fontStyle: FontStyle.italic, //字型傾斜
decoration: TextDecoration.lineThrough , //穿過文字的線
decorationColor: Colors.white, //穿過線的顏色
decorationStyle: TextDecorationStyle.dashed, //虛線
letterSpacing: 5.0 //字型間距
),
),
height: 300.0, //設定容器高度
width: 300.0, //設定容器寬度
decoration: BoxDecoration(
color: Colors.yellow, //背景顏色
border: Border.all(
color: Colors.blue, //邊框顏色
width: 2.0 //邊框寬度
),
borderRadius: BorderRadius.all(
Radius.circular(20), //設定圓角邊框
// Radius.circular(150), //設定圓形邊框
)
),
// padding: EdgeInsets.all(20), //表示與上下左右四邊都有20的內邊距
padding: EdgeInsets.fromLTRB(10, 30, 5, 0), //設定左上右下內邊距
margin: EdgeInsets.fromLTRB(10, 30, 5, 0), //設定左上右下外邊距
// transform: Matrix4.translationValues(100, 0, 0) //X軸位移100
// transform: Matrix4.rotationZ(0.3), //Z軸旋轉,正值是順時針旋轉,負值是逆時針旋轉
// transform: Matrix4.diagonal3Values(1.2, 1, 1), //縮放
alignment: Alignment.bottomLeft, //讓邊框內的元素居左側底部
)
);
}
}
複製程式碼
2.3丶 知識點
Text 元件
[1]
textAlign 文字對齊方式(center 居中,left 左對齊,right 右對齊,justfy 兩端對齊)
textDirection 文字方向(ltr 從左至右,rtl 從右至左)
overflow 文字超出螢幕之後的處理方式(clip裁剪,fade 漸隱,ellipsis 省略號)
textScaleFactor 字型顯示倍率
maxLines 文字顯示最大行數
style 字型的樣式設定
[2]下面是 TextStyle 引數 :
名稱 功能
decoration 文字裝飾線(none 沒有線,lineThrough 刪除線,overline 上劃線,underline 下劃線)
decorationColor 文字裝飾線顏色
decorationStyle 文字裝飾線風格([dashed,dotted]虛線,
double 兩根線,solid 一根實線,wavy 波浪線)
wordSpacing 單詞間隙(如果是負值,會讓單詞變得更緊湊
letterSpacing 字母間隙(如果是負值,會讓字母變得更緊湊)
fontStyle 文字樣式(italic 斜體,normal 正常體)
fontSize 文字大小
color 文字顏色
fontWeight 字型粗細(bold 粗體,normal 正常體)
更多引數:https://docs.flutter.io/flutter/painting/TextStyle-class.html
複製程式碼
3、遠端圖片、本地圖片、實現圓形圖片,實現圓角圖片
3.1、效果圖
【1】引用遠端圖片
【2】引用本地圖片
【3】實現圓形圖片(方法一)、圓角圖片(受弧度影響)
【4】實現圓形圖片(方法二)
3.2、程式碼+註釋
【1】引用遠端圖片
import 'package:flutter/material.dart';
void main()=> runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: HomeCentent(),
),
);
}
}
class HomeCentent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.network( //引入一張遠端圖片
"https://www.itying.com/images/flutter/2.png", //遠端圖片連結
alignment: Alignment.topLeft, //設定圖片的方位在左上角
// color: Colors.yellow, //設定這張圖片的顏色
// colorBlendMode: BlendMode.luminosity, //設定顏色的混合模式
fit:BoxFit.cover, //設定圖片的顯示模式,cover—全屏顯示,最常用!
// repeat: ImageRepeat.repeatX, //橫向平鋪,縱向不變
// repeat: ImageRepeat.repeat, //橫向縱向都平鋪,
),
width: 300,
height: 300,
decoration: BoxDecoration(
color: Colors.yellow //設定方框背景顏色
),
)
);
}
}
複製程式碼
【2】引用本地圖片
先做準備工作:(三步)
1.專案根目錄下,建立Images資料夾,如下圖:
2.分別建立2.0x,3.0x,4.0x並且在這些資料夾裡面放圖片,然後再在外面放一張圖片,如圖:
3.在pubspec.yaml 檔案裡,增加如下配置:
點選右上角 Packages get並且要Ctrl+s儲存
然後就可以開始敲程式碼了:
import 'package:flutter/material.dart';
void main()=> runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: HomeCentent(),
),
);
}
}
class HomeCentent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Container(
child: Image.asset('images/aa.png', //匯入本地圖片
fit: BoxFit.cover,
),
height: 300,
width: 300,
)
);
}
}
複製程式碼
【3】實現圓形圖片(方法一)、圓角圖片(根據弧度變化)
return Center(
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
color: Colors.yellow , //設定方框背景顏色
// borderRadius: BorderRadius.all(
// Radius.circular(150) //變成圓形,不過一般不這麼實現圓形圖片
// ),
borderRadius: BorderRadius.circular(150), //實現圓形圖片的方式一: 有些麻煩
image: DecorationImage(
image: NetworkImage("https://www.itying.com/images/flutter/2.png"),
fit: BoxFit.cover //鋪滿全屏
),
),
)
);
複製程式碼
【4】實現圓形圖片(方法二):
return Center(
child: Container(
child: ClipOval( //實現圓形圖片方式二,最簡單的方式
child: Image.network('https://www.itying.com/images/flutter/2.png',
height: 100,
width: 100,
fit: BoxFit.cover,
),
),
)
);
複製程式碼
3.3、知識點
圖片元件是顯示影像的元件,Image 元件有很多建構函式,這裡我們只給大家講兩個
Image.asset, 本地圖片
Image.network 遠端圖片
Image 元件的常用屬性:
名稱 型別 說明
alignment Alignment 圖片的對齊方式
color 和 colorBlendMode 設定圖片的背景顏色,通常和 colorBlendMode 配合一起使用,這樣可以是圖片顏色和背景色混合。上面的圖片就是進行了顏色的混合,綠色背景和圖片紅色的混合
fit BoxFit fit 屬性用來控制圖片的拉伸和擠壓,這都是根據父容器來的。
BoxFit.fill:全圖顯示,圖片會被拉伸,並充滿父容器。
BoxFit.contain:全圖顯示,顯示原比例,可能會有空隙。
BoxFit.cover:顯示可能拉伸,可能裁切,充滿(圖片要充滿整個容器,還不變形)。
BoxFit.fitWidth:寬度充滿(橫向充滿),顯示可能拉伸,可能裁切。
BoxFit.fitHeight :高度充滿(豎向充滿),顯示可能拉伸,可能裁切。
BoxFit.scaleDown:效果和 contain 差不多,但是此屬性不允許顯示超過源圖片大小,可小不可大。
repeat 平鋪
ImageRepeat.repeat : 橫向和縱向都進行重複,直到鋪滿整個畫布。
ImageRepeat.repeatX: 橫向重複,縱向不重複。
ImageRepeat.repeatY:縱向重複,橫向不重複。
width 寬度 一般結合 ClipOval 才能看到效果
height 高度 一般結合 ClipOval 才能看到效果
更多屬性參考:https://api.flutter.dev/flutter/widgets/Image-class.html
複製程式碼
4、基礎列表元件、 水平列表元件 、 圖示元件
4.1、效果圖
【1】基礎列表
【2】基礎列表+圖示元件
【3】基礎列表+引用遠端圖片
【4】垂直列表
【5】水平列表
4.2、程式碼+註釋
【1】基礎列表
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Hello Flutter'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView( //列表
children: <Widget>[ //表示配置它的子元素
ListTile( //每個item一般都是寫在ListTile裡,這是規範
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就',
style: TextStyle(
fontSize: 18
),
), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
)
],
);
}
}
複製程式碼
【2】基礎列表+圖示元件
...
...
...
return ListView( //列表
children: <Widget>[ //表示配置它的子元素
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Icon(Icons.settings,color: Colors.yellow), //在左側放置一個圖示,修改圖示顏色
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Icon(Icons.home,size: 30), //在左側放置一個圖示
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
trailing: Icon(Icons.settings), //在右側放置一個圖示
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Icon(Icons.settings), //在左側放置一個圖示
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Icon(Icons.home), //在左側放置一個圖示
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
)
],
);
複製程式碼
【3】基礎列表+引用遠端圖片
...
...
...
return ListView( //列表
children: <Widget>[ //表示配置它的子元素
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Image.network("https://www.itying.com/images/flutter/1.png"),
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Image.network("https://www.itying.com/images/flutter/2.png"),
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
trailing: Image.network("https://www.itying.com/images/flutter/5.png")
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Image.network("https://www.itying.com/images/flutter/3.png"),
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
),
ListTile( //每個item一般都是寫在ListTile裡,這是規範
leading: Image.network("https://www.itying.com/images/flutter/4.png"),
title: Text('床前明月光,按資料庫裡大家都燒錄機按時考慮到就開始拉到就'), //標題
subtitle: Text('阿斯利康大神解答控制元件阿斯科利大家酷啦四大皆空拉絲機的酷啦資料庫了多久啊上課了多久啊上課了經典款了'), //內容
)
],
);
複製程式碼
【4】垂直列表
...
...
return ListView(
scrollDirection: Axis.vertical, //垂直列表
padding: EdgeInsets.all(10),
children: <Widget>[
Image.network('https://www.itying.com/images/flutter/1.png'),
Container(
child: Text('我是一個標題',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/2.png'),
Container(
child: Text('我是一個標題',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/3.png'),
Container(
child: Text('我是一個標題',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/4.png'),
Container(
child: Text('我是一個標題',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/5.png'),
Container(
child: Text('我是一個標題',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/6.png'),
Container(
child: Text('我是一個標題',
textAlign: TextAlign.center, //居中
style: TextStyle(
fontSize: 28,
),
),
height: 60,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
),
Image.network('https://www.itying.com/images/flutter/2.png'),
Image.network('https://www.itying.com/images/flutter/3.png'),
Image.network('https://www.itying.com/images/flutter/4.png'),
Image.network('https://www.itying.com/images/flutter/1.png'),
Image.network('https://www.itying.com/images/flutter/2.png'),
Image.network('https://www.itying.com/images/flutter/3.png'),
Image.network('https://www.itying.com/images/flutter/1.png'),
Image.network('https://www.itying.com/images/flutter/2.png'),
Image.network('https://www.itying.com/images/flutter/3.png'),
],
);
複製程式碼
【5】水平列表
...
...
...
return Container(
height: 180,
child: ListView(
// scrollDirection: Axis.vertical, //垂直列表
scrollDirection: Axis.horizontal, //水平列表,
children: <Widget>[
Container(
width: 180.0,
height: 180.0,
color: Colors.yellow,
),
Container(
width: 180.0,
height: 180.0,
color: Colors.blue,
child: ListView(
children: <Widget>[
Image.network("https://www.itying.com/images/flutter/2.png"),
Text('我是一個文字')
],
),
),
Container(
width: 180.0,
height: 180.0,
color: Colors.red,
),
Container(
width: 180.0,
height: 180.0,
color: Colors.green,
)
],
),
);
複製程式碼
4.3、知識點
- Flutter 列表元件概述
列表佈局是我們專案開發中最常用的一種佈局方式。Flutter 中我們可以通過 ListView 來定義
列表項,支援垂直和水平方向展示。通過一個屬性就可以控制列表的顯示方向。列表有一下
分類:
1、垂直列表
2、垂直圖文列表
3、水平列表
4、動態列表
5、矩陣式列表 - Flutter 列表引數
名稱 型別 說明
scrollDirection Axis Axis.horizontal 水平列表
Axis.vertical 垂直列表
padding EdgeInsetsGeometry 內邊距
resolve bool 元件反向排序
children List<Widget> 列表元素
複製程式碼
4.4、筆記
- 在ListView的children: 裡可以放置任何的元件
- 我們在寫完每一個ListTile(android裡的item)後,它會自動的生成Widget陣列,這就相當於是一個adapter管理資料後的一個陣列
5、列表、動態列表
5.1、 效果圖
5.2、程式碼+註釋
實現動態列表:
【1】方式一:抽離元件
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
//方式一:通過建立一個一個的ListTile來實現列表
List<Widget> _getData(){
return [
ListTile(
title: Text("我是一個列表"),
),
ListTile(
title: Text("我是一個列表"),
),
ListTile(
title: Text("我是一個列表"),
),
ListTile(
title: Text("我是一個列表"),
)
];
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: this._getData(),
);
}
}
複製程式碼
【2】方式二:通過List集合,來動態生成ListTile資料
...
...
class HomeContent extends StatelessWidget{
//方式二:通過List集合,來動態生成ListTile資料
List<Widget> _getData(){
List<Widget> list = new List();
for(var i=0;i<20;i++){
list.add(ListTile(
title: Text("我是$i列表"),
));
}
return list;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: this._getData( ),
);
}
}
複製程式碼
【3】方式三:通過Map集合,來動態生成ListTile資料
現在外部建立好資料:就拿我當前是在lib/res/listData.dart
List listData=[
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/1.png',
},
{
"title":'Childhood in a picture',
"author":'Google',
"imageUrl":'https://www.itying.com/images/flutter/2.png',
},
{
"title":'Alibaba Shop',
"author":'Alibaba',
"imageUrl":'https://www.itying.com/images/flutter/3.png',
},
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/4.png',
},
{
"title":'Tornado',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/5.png',
}
];
複製程式碼
記得引入:import ‘res/listData.dart’;
class HomeContent extends StatelessWidget{
//方式三:通過Map集合,來動態生成ListTile資料
List<Widget> _getData(){
var tempList = listData.map((value){ //首先我要說明一下,因為它返回的是一個map型別的值,所以我們就用var讓它自動判斷來接收值
return ListTile( //其次再來分析這個return,這裡它的作用是,每次返回改變後的值賦給tempList
leading: Image.network(value["imageUrl"]),
title: Text(value["title"]),
subtitle: Text(value["author"]),
);
});
return tempList.toList(); //最後再返回這個值,但是要記住,他不是一個集合陣列,所以我們要讓它轉換下型別,通過toList
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return ListView(
children: this._getData( ),
);
}
}
複製程式碼
【4】方式四:通過builder規範,讓ListView自動迴圈遍歷 假資料
...
...
class HomeContent extends StatelessWidget{
//注意這裡是在例項化構造方法的時候就執行了,所以是先建立好資料,再通過下面的方式四來遍歷獲取資料
List list = new List();
HomeContent(){
for(var i=0;i<20;i++){
this.list.add('我是第$i條');
}
}
@override
Widget build(BuildContext context) {
// TODO: implement build
//方式四:通過builder規範,讓ListView自動迴圈遍歷資料
return ListView.builder(
itemCount: this.list.length, //這裡必須要指定List的長度
itemBuilder:(context,index){ //需要傳入兩個引數,然後Builder會自動從0一直迴圈到最大長度
return ListTile(
title: Text(this.list[index]), //每次取出index的索引對應的資料返回
);
},
);
}
}
複製程式碼
【5】方式五:通過builder規範,讓ListView自動迴圈遍歷 動態真實資料
class HomeContent extends StatelessWidget{
//自定義方法
Widget _getListData(context,index){
return ListTile(
title: Text(listData[index]["title"]), //每次取出index的索引對應的資料返回
leading: Image.network(listData[index]["imageUrl"]),
subtitle: Text(listData[index]["author"])
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
//方式四:通過builder規範,讓ListView自動迴圈遍歷資料
return ListView.builder(
itemCount: listData.length, //這裡必須要指定List的長度
itemBuilder:this._getListData //☆這裡要注意寫法,我們只是把方法名後面的東西賦值給itemBuilder,並不是要執行方法,所以一定要搞清楚!
//賦值的目的是在於將程式碼抽離出去,簡潔明瞭
);
}
複製程式碼
5.3、知識點
1、Flutter 列表元件概述
列表佈局是我們專案開發中最常用的一種佈局方式。Flutter 中我們可以通過 ListView 來定義
列表項,支援垂直和水平方向展示。通過一個屬性就可以控制列表的顯示方向。列表有一下
分類:
1、垂直列表
2、垂直圖文列表
3、水平列表
4、動態列表
5、矩陣式列表
2、 Flutter 列表引數
名稱 型別 說明
scrollDirection Axis Axis.horizontal 水平列表
Axis.vertical 垂直列表
padding EdgeInsetsGeometry 內邊距
resolve bool 元件反向排序
children List<Widget> 列表元素
複製程式碼
6、網格佈局
6.1、效果圖
【1】簡單的網格佈局
【2】迴圈遍歷假資料,實現網格佈局
【3】GridView.count 動態獲取資料並實現網格佈局
【4】GridView.builder 動態獲取資料並實現網格佈局
6.2、 程式碼+註釋
【1】簡單的網格佈局
import 'package:flutter/material.dart';
void main()=>runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: HomeContent(),
)
);
}
}
class HomeContent extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count( //網格元件
crossAxisCount: 2, //控制網格列數
children: <Widget>[ //控制網格元素
Text('這是一個文字'), //孩子元素
Text('這是一個文字'),
Text('這是一個文字'),
Text('這是一個文字'),
Text('這是一個文字'),
Text('這是一個文字'),
Text('這是一個文字'),
Text('這是一個文字'),
Text('這是一個文字')
],
);
}
}
複製程式碼
【2】迴圈遍歷假資料,實現網格佈局
...
...
class HomeContent extends StatelessWidget{
List<Widget> _getlistData(){
List<Widget> list = new List();
for(var i=0;i<20;i++){
list.add(Container(
alignment: Alignment.center,
child: Text(
'這是第$i條資料',
style: TextStyle(color: Colors.white,fontSize: 20),
),
color: Colors.blue,
// height: 400, //設定高度沒有反應,
));
}
return list;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count( //網格元件
crossAxisSpacing: 20.0, //水平子Widget 之間的間距
mainAxisSpacing: 20.0, //垂直子Widget 之間的間距
padding: EdgeInsets.all(10), //與View上下左右間隔10
crossAxisCount: 2, //控制網格列數
childAspectRatio: 0.7, //寬度和高度的比例
children: this._getlistData() //返回的是Widget
);
}
}
複製程式碼
【3】GridView.count 動態獲取資料並實現網格佈局
在開頭記得匯入外部資料的dart檔案,如下:
List listData=[
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/1.png',
},
{
"title":'Childhood in a picture',
"author":'Google',
"imageUrl":'https://www.itying.com/images/flutter/2.png',
},
{
"title":'Alibaba Shop',
"author":'Alibaba',
"imageUrl":'https://www.itying.com/images/flutter/3.png',
},
{
"title":'Candy Shop',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/4.png',
},
{
"title":'Tornado',
"author":'Mohamed Chahin',
"imageUrl":'https://www.itying.com/images/flutter/5.png',
}
];
複製程式碼
main.dart
import 'package:flutter/material.dart';
import 'res/listData.dart';
...
...
class HomeContent extends StatelessWidget{
List<Widget> _getlistData(){
var tempList = listData.map((value){
return Container(
child: Column( //注意,這裡也可以用ListView來獲取資料,但是ListView的不足在它會自適應寬度,也就是平鋪整個寬度
children: <Widget>[ //但是,Column不會,他只會顯示元素多大就是多大,而且是自動垂直排列
Image.network(value['imageUrl']),
SizedBox(height: 10,), //讓Text與Image有10的間距
Text(value['title'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15
),
)
],
),
decoration: BoxDecoration( //邊框類
border: Border.all( //設定邊框樣式
color: Color.fromRGBO(233, 233, 233, 0.9), //顏色
width: 1 //邊框寬度
)
),
);
});
return tempList.toList();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.count( //網格元件 — 列表佈局
crossAxisSpacing: 20.0, //水平子Widget 之間的間距
mainAxisSpacing: 20.0, //垂直子Widget 之間的間距
padding: EdgeInsets.all(10), //與View上下左右間隔10
crossAxisCount: 2, //控制網格列數
// childAspectRatio: 0.7, //寬度和高度的比例
children: this._getlistData() //返回的是Widget
);
}
}
複製程式碼
【4】GridView.builder 動態獲取資料並實現網格佈局
import 'package:flutter/material.dart';
import 'res/listData.dart';
...
...
class HomeContent extends StatelessWidget{
Widget _getlistData(context,index){
return Container(
child: Column( //注意,這裡也可以用ListView來獲取資料,但是ListView的不足在它會自適應寬度,也就是平鋪整個寬度
children: <Widget>[ //但是,Column不會,他只會顯示元素多大就是多大,而且是自動垂直排列
Image.network(listData[index]['imageUrl']),
SizedBox(height: 10,), //讓Text與Image有10的間距
Text(listData[index]['title'],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15
),
)
],
),
decoration: BoxDecoration( //邊框類
border: Border.all( //設定邊框樣式
color: Color.fromRGBO(233, 233, 233, 0.9), //顏色
width: 1 //邊框寬度
)
),
);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return GridView.builder( //網格元件 — 列表佈局
/**
* 說明一下,這裡寫這個方法的作用是,因為當我們使用GridView.builder時,我們不能再將元素的樣式放在外邊,而是
* 應該要藉助下面這個類,才能去填寫樣式,否則會報錯
*/
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 20.0, //水平子Widget 之間的間距
mainAxisSpacing: 20.0, //垂直子Widget 之間的間距
crossAxisCount: 2, //控制網格列數
),
itemCount: listData.length, //資料長度
itemBuilder: this._getlistData
);
}
}
複製程式碼
6.3、 知識點
GridView 元件的常用引數
當資料量很大的時候用矩陣方式排列比較清晰。此時我們可以用網格列表元件 GridView 實
現佈局。
GridView 建立網格列表有多種方式,下面我們主要介紹兩種。
1、可以通過 GridView.count 實現網格佈局
2、通過 GridView.builder 實現網格佈局
常用屬性:
名稱 型別 說明
scrollDirection Axis 滾動方法
padding EdgeInsetsGeometry 內邊距
resolve bool 元件反向排序
crossAxisSpacing double 水平子 Widget 之間間距
mainAxisSpacing double 垂直子 Widget 之間間距
crossAxisCount int 一行的 Widget 數量
childAspectRatio double 子 Widget 寬高比例
children <Widget>[ ]
gridDelegate SliverGridDelegateWithFixedCrossAxisCount(常用)SliverGridDelegateWithMax
CrossAxisExtent 控制佈局主要用在GridView.builder 裡面
複製程式碼
6.4、筆記
1、Container 不是佈局,是容器!
2、網格元件,也叫列表佈局
7、底部導航
效果圖:
Home.dart
Category.dart
Setting.dart
main.dart:
import 'package:flutter/material.dart';
import 'pages/Tabs.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home: Tabs() //將程式碼全部抽離出去成一個Tabs元件
);
}
}
複製程式碼
在lib根目錄下,新建pages,再新建Tabs.dart檔案
Tabs.dart
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
@override
_TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex=0;
List _pageList = [ //先將所有頁面放到List集合內
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: this._pageList[this._currentIndex], //再根據下標獲取對應頁面設定到body 裡
bottomNavigationBar: BottomNavigationBar( //自定義底部導航條
currentIndex: this._currentIndex, //配置對應的索引值選中
onTap: (int index){
setState(() { //改變狀態
this._currentIndex=index; //更改選中的Tab座標
});
},
iconSize: 45.0, //Icon的大小,預設在20左右
fixedColor: Colors.red, //選中的顏色,預設是藍色
type: BottomNavigationBarType.fixed, //配置底部tabs可以有多個按鈕,預設是隻能至多隻能放三個,三個以上時,需要加上這句程式碼
items:[
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.home),
title: Text('首頁')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.category),
title: Text('分類')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.settings),
title: Text('設定')
),
]
),
);
}
}
複製程式碼
分別新建三個頁面,在pages根目錄下新建tabs資料夾,在這個資料夾裡,分別新建三個檔案:Category.dart、Home.dart、Setting.dart
Category.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Text('分類');
}
}
複製程式碼
Home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Text('我是首頁元件');
}
}
複製程式碼
Setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
@override
_SettingPageState createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
)
],
);
}
}
複製程式碼
8、頁面跳轉、跳轉傳值(普通路由、普通路由傳值)
效果圖:
Home.dart
Search.dart
Category.dart
Form.dart
底部導航基本框架在:flutter實現底部導航
好的,做好準備工作,我們就開始進入正軌,開始實現頁面跳轉
Home.dart
import 'package:flutter/material.dart';
import '../Search.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳轉到搜尋頁面'),
onPressed: (){ //監聽器
//路由跳轉
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>SearchPage()
)
);
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
SizedBox(height: 20,),
],
);
}
}
複製程式碼
Category.dart
import 'package:flutter/material.dart';
import '../Form.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start, //次軸居左
mainAxisAlignment: MainAxisAlignment.center, //主軸居中
children: <Widget>[
RaisedButton(
child: Text('跳轉到表單頁面'),
onPressed: () { //監聽器
Navigator.of(context).push(
MaterialPageRoute(
builder: (context)=>FormPage(title: '我是跳轉傳值',)
/**
* 相當於:builder:(context){
* return FormPage();
* }
*/
)
);
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
)
],
);
}
}
複製程式碼
在lib→pages目錄下新建這兩個檔案:
Search.dart
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('搜尋頁面'),
),
body: Text('搜尋頁面內容區域'),
);
}
}
複製程式碼
Form.dart
import 'package:flutter/material.dart';
class FormPage extends StatelessWidget {
String title;
FormPage({this.title='表單'}); //引數是一個可選引數,預設值是表單,當接收到值時,表單值直接被覆蓋
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton( //懸浮按鈕
child: Text('返回'),
onPressed: (){ //監聽器
Navigator.of(context).pop(); //退出當前堆,返回上一級頁面
},
),
appBar: AppBar(
title: Text(this.title),
),
body: ListView(
children: <Widget>[
ListTile(
title: Text('我是表單頁面'),
),
ListTile(
title: Text('我是表單頁面'),
),
ListTile(
title: Text('我是表單頁面'),
),
ListTile(
title: Text('我是表單頁面'),
),
],
),
);
}
}
複製程式碼
9、頁面跳轉、跳轉傳值(命名路由、命名路由傳值)
效果圖:
Home.dart
Product.dart
ProductInfo.dart
底部導航基本框架參考:flutter實現底部導航
其他按鈕介面參考上一節:flutter實現頁面跳轉、跳轉傳值(普通路由、普通路由傳值)
我先把所有相關的資料夾與檔案頁面位置截圖給你們看,方便你們理解,其他重複程式碼你們參考上面兩個章節去學習一下,就會了,加油,各位!
然後,我這裡就直接貼命名路由的相關程式碼了:
main.dart 修改
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
initialRoute: '/', //初始化的時候載入的路由
// home: Tabs(), //將程式碼全部抽離出去成一個Tabs元件,再抽離成一個初始化路由元件,在上方 initialRoute: '/',
//配置命名路由
onGenerateRoute: oonGenerateRoute //將抽離出去的路由傳值規範賦值給左邊,而不是執行,記住喔!
);
}
}
複製程式碼
Home.dart
import 'package:flutter/material.dart';
import '../Search.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳轉到搜尋頁面'),
onPressed: (){ //監聽器
//命名路由跳轉
Navigator.pushNamed(context, '/search',arguments: { //我們必須要用arguments 工具來攜帶id這個鍵,才能在那邊取出值
"id":123
});
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('跳轉到商品頁面'),
onPressed: (){ //監聽器
//命名路由跳轉
Navigator.pushNamed(context, '/product');
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
],
);
}
}
複製程式碼
Routes.dart
import '../pages/Form.dart';
import '../pages/Search.dart';
import '../pages/Tabs.dart';
import '../pages/Product.dart';
import '../pages/ProductInfo.dart';
import 'package:flutter/material.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由傳值 arguments工具是必須的
'/form':(context)=>FormPage(),
'/product':(context)=>ProductPage(), //命名路由傳值 arguments工具是必須的
'/productInfoPage':(context,{arguments})=>ProductInfoPage(arguments:arguments), //命名路由傳值 arguments工具是必須的
'/search':(context,{arguments})=>SearchPage(arguments:arguments) //命名路由傳值 arguments工具是必須的
};
//固定寫法
var oonGenerateRoute=(RouteSettings settings) {
// 統一處理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的鍵去獲取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
複製程式碼
Tabs.dart不變
Product.dart
import 'package:flutter/material.dart';
class ProductPage extends StatefulWidget {
@override
_ProductPageState createState() => _ProductPageState();
}
class _ProductPageState extends State<ProductPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品詳情'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳轉到商品詳情'),
onPressed: (){ //監聽器
Navigator.pushNamed(context, '/productInfoPage',arguments: {
'pid':456
});
},
color: Theme.of(context).accentColor,
textTheme: ButtonTextTheme.primary,
),
],
),
);
}
}
複製程式碼
ProductInfo.dart
import 'package:flutter/material.dart';
class ProductInfoPage extends StatefulWidget {
final Map arguments;
ProductInfoPage({Key key,this.arguments}) : super(key: key);
@override
_ProductInfoPageState createState() => _ProductInfoPageState(arguments:this.arguments);
}
class _ProductInfoPageState extends State<ProductInfoPage> {
Map arguments;
_ProductInfoPageState({this.arguments});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('商品詳情'),
),
body: Container(
child: Text('pid=${arguments['pid']}'),
),
);
}
}
複製程式碼
10、替換路由、返回到根路由
效果圖:
Setting.dart
RegisterFirst.dart
RegisterSecond.dart
RegisterThird.dart
無相關的頁面程式碼架構請參考:flutter實現頁面跳轉、跳轉傳值(命名路由、命名路由傳值)
主要變動:
這裡負責貼主要程式碼:
Login.dart
import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('登入'),
),
body: Center(
child: Column(
children: <Widget>[
SizedBox(height: 40,),
Text('這是一個登入頁面,點選登入會執行登入操作'),
RaisedButton(
child: Text('登入'),
onPressed: (){
},
)
],
),
),
);
}
}
複製程式碼
RegisterFirst.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RegisterFirstPage extends StatelessWidget {
const RegisterFirstPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第一步-輸入手機號")
),
body:Column(
children: <Widget>[
SizedBox(height: 40),
Text("這是註冊的第一步,請輸入您的手機號 然後點選下一步"),
SizedBox(height: 40),
RaisedButton(
child: Text('下一步'),
onPressed: (){
// Navigator.pushNamed(context, '/registerSecond');
//替換路由
// 表示點選按鈕時跳轉到第二個頁面,並且是一種替換,簡單來說可以理解為被銷燬的意思
Navigator.of(context).pushReplacementNamed('/registerSecond');
},
)
],
)
);
}
}
複製程式碼
RegisterSecond.dart
import 'package:flutter/material.dart';
class RegisterSecondPage extends StatelessWidget {
const RegisterSecondPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二步-驗證碼")
),body:Column(
children: <Widget>[
SizedBox(height: 40),
Text("輸入驗證碼完成註冊"),
SizedBox(height: 40),
RaisedButton(
child: Text('下一步'),
onPressed: (){
Navigator.pushNamed(context, '/registerThird');
//替換路由
// Navigator.of(context).pushReplacementNamed('/registerThird');
},
)
],
)
);
}
}
複製程式碼
RegisterThird.dart
import 'package:flutter/material.dart';
import 'package:flutter_app15/pages/Tabs.dart';
class RegisterThirdPage extends StatelessWidget {
const RegisterThirdPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第三步-完成註冊")
),body:Column(
children: <Widget>[
SizedBox(height: 40),
Text("輸入密碼完成註冊"),
SizedBox(height: 40),
RaisedButton(
child: Text('確定'),
onPressed: (){
/**
* 返回根 pushAndRemoveUntil
* 實現原理:
* 1、首先先將所有的路由全部置為空 (route) => route == null
* 2、然後再跳轉回根路由 new MaterialPageRoute(builder: (context) => new Tabs()),
*/
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new Tabs(index:1)),
(route) => route == null
);
},
)
],
)
);
}
}
複製程式碼
Setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
@override
_SettingPageState createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Column(
children: <Widget>[
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
),
ListTile(
title: Text('我是一個文字'),
)
],
),
RaisedButton(
child: Text('跳轉到登入頁面'),
onPressed: (){
Navigator.pushNamed(context, '/login');
},
),
RaisedButton(
child: Text('跳轉到註冊頁面'),
onPressed: (){
Navigator.pushNamed(context, '/registerFirst');
},
)
],
);
}
}
複製程式碼
Routes.dart
import 'package:flutter_app15/pages/user/RegisterThird.dart';
import '../pages/Form.dart';
import '../pages/Search.dart';
import '../pages/Tabs.dart';
import '../pages/Product.dart';
import '../pages/ProductInfo.dart';
import '../pages/user/Login.dart';
import '../pages/user/RegisterFirst.dart';
import '../pages/user/RegisterSecond.dart';
import '../pages/user/RegisterThird.dart';
import 'package:flutter/material.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由傳值 arguments工具是必須的
'/form':(context)=>FormPage(),
'/product':(context)=>ProductPage(), //命名路由傳值 arguments工具是必須的
'/productInfoPage':(context,{arguments})=>ProductInfoPage(arguments:arguments), //命名路由傳值 arguments工具是必須的
'/search':(context,{arguments})=>SearchPage(arguments:arguments), //命名路由傳值 arguments工具是必須的
'/login':(context)=>LoginPage(),
'/registerFirst':(context)=>RegisterFirstPage(),
'/registerSecond':(context)=>RegisterSecondPage(),
'/registerThird':(context)=>RegisterThirdPage()
};
//固定寫法
var oonGenerateRoute=(RouteSettings settings) {
// 統一處理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的鍵去獲取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
複製程式碼
11、自定義AppBar 定義頂部Tab切換、底部Tab結合頂部Tab實現類似頭條頁面佈局
效果圖:
Home.dart
AppBarDemo.dart
Categroy.dart
底部導航基本架構參考:flutter實現底部導航
main.dart
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
debugShowCheckedModeBanner: false, //去掉debug圖示
initialRoute: '/', //初始化的時候載入的路由
// home: Tabs(), //將程式碼全部抽離出去成一個Tabs元件,再抽離成一個初始化路由元件,在上方 initialRoute: '/',
//配置命名路由
onGenerateRoute: oonGenerateRoute //將抽離出去的路由傳值規範賦值給左邊,而不是執行,記住喔!
);
}
}
複製程式碼
Routes.dart
import '../pages/Tabs.dart';
import 'package:flutter/material.dart';
import '../pages/AppBarDemo.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由傳值 arguments工具是必須的
'/appBarDemo':(context)=>AppBarDemoPage()
};
//固定寫法
var oonGenerateRoute=(RouteSettings settings) {
// 統一處理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的鍵去獲取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
複製程式碼
Tabs.dart
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
final index; //用來作為返回根路由時,判斷要顯示第幾個底部導航項的座標
Tabs({Key key,this.index=0}) : super(key: key); //可選引數,預設是0
@override
_TabsState createState() => _TabsState(this.index); //把當前座標通過_TabsState構造方法傳給_TabsState類
}
class _TabsState extends State<Tabs> {
int _currentIndex;
_TabsState(index){ //接收Tabs 類呼叫時,傳過來的引數,賦值給 _currentIndex
this._currentIndex=index;
}
List _pageList = [ //先將所有頁面放到List集合內
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: this._pageList[this._currentIndex], //再根據下標獲取對應頁面設定到body 裡
bottomNavigationBar: BottomNavigationBar( //自定義底部導航條
currentIndex: this._currentIndex, //配置對應的索引值選中
onTap: (int index){
setState(() { //改變狀態
this._currentIndex=index; //更改選中的Tab座標
});
},
// iconSize: 45.0, //Icon的大小,預設在20左右
fixedColor: Colors.red, //選中的顏色,預設是藍色
items:[
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.home),
title: Text('首頁')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.category),
title: Text('分類')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.settings),
title: Text('設定')
)
]
),
);
}
}
複製程式碼
AppBarDemo.dart
import 'package:flutter/material.dart';
class AppBarDemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController( //頂部導航切換
length: 2, //必須配置:頂部圖示一共多少個
child: Scaffold(
appBar: AppBar(
title: Text('AppBarDemoPage'),
backgroundColor: Colors.red, //設定導航上的背景顏色
centerTitle: true, //設定:無論是在Android 還是 ios 上,標題都是居中顯示
// leading: Icon(Icons.menu), //給導航左邊新增圖示,預設是返回圖示,無法監聽
// leading: IconButton(
// icon: Icon(Icons.menu), //給導航左邊新增圖示,預設是返回圖示,可以監聽
// onPressed: (){
// print('menu');
// },
// ),
// actions: <Widget>[ //右側新增圖示按鈕
// IconButton(
// icon: Icon(Icons.search),
// onPressed: (){
// print('search');
// },
// ),
// IconButton( //右側新增第二個圖示按鈕
// icon: Icon(Icons.settings),
// onPressed: (){
// print('settings');
// },
// )
// ],
bottom: TabBar(
tabs: <Widget>[ //配置Tabs選單,系統會根據這裡配置的是順序對應下邊body:TabBarView 裡元素的順序進行顯示
Tab(text: '熱門',),
Tab(text: '推薦',)
],
),
),
body: TabBarView(
children: <Widget>[
ListView(
children: <Widget>[
ListTile(
title: Text('第一個tab'),
),
ListTile(
title: Text('第一個tab'),
),
ListTile(
title: Text('第一個tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第二個tab'),
),
ListTile(
title: Text('第二個tab'),
),
ListTile(
title: Text('第二個tab'),
)
],
)
],
),
),
);
}
}
複製程式碼
Home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳轉到appBar'),
onPressed: (){
//路由跳轉
Navigator.pushNamed(context, '/appBarDemo');
},
)
],
),
);
}
}
複製程式碼
Categroy.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
@override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold( //在Scaffold 裡再巢狀Scaffold
appBar: AppBar(
backgroundColor: Colors.black26,
/**
* 將頂部導航寫在title裡是為了防止,當Scaffold巢狀Scaffold時,發生兩個頂部的bug,為了修復這個bug,
* 我們就可以把頂部導航條寫在title裡
*/
title: Row(
children: <Widget>[
Expanded(
child: TabBar(
indicatorColor: Colors.blue, //設定指示器的顏色
labelColor: Colors.blue, //設定:選中顏色
unselectedLabelColor: Colors.white, //設定:未選中顏色
indicatorSize: TabBarIndicatorSize.label, //選中時,底部指示條與文字一樣長,預設是tab
tabs: <Widget>[
Tab(text: '熱銷'),
Tab(text: '推薦',),
Tab(text: '三',),
Tab(text: '四',)
],
),
)
],
),
),
body: TabBarView(
children: <Widget>[
ListView(
children: <Widget>[
ListTile(
title: Text('第一個tab'),
),
ListTile(
title: Text('第一個tab'),
),
ListTile(
title: Text('第一個tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第二個tab'),
),
ListTile(
title: Text('第二個tab'),
),
ListTile(
title: Text('第二個tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第三個tab'),
),
ListTile(
title: Text('第三個tab'),
),
ListTile(
title: Text('第三個tab'),
)
],
),
ListView(
children: <Widget>[
ListTile(
title: Text('第四個tab'),
),
ListTile(
title: Text('第四個tab'),
),
ListTile(
title: Text('第四個tab'),
)
],
)
],
),
),
);
}
}
複製程式碼
Demo目錄結構:
12、TabController定義頂部tab切換,並介紹生命週期函式
效果圖:
Home.dart
TabBarController.dart
Home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('跳轉到appBar'),
onPressed: (){
//路由跳轉
Navigator.pushNamed(context, '/appBarDemo');
},
),
SizedBox(width: 10,),
RaisedButton(
child: Text('TabController定義頂部tab切換 '),
onPressed: (){
//路由跳轉
Navigator.pushNamed(context, '/tabBarController');
},
)
],
),
);
}
}
複製程式碼
TabBarController.dart
import 'package:flutter/material.dart';
class TabBarControllerPage extends StatefulWidget {
@override
_TabBarControllerPageState createState() => _TabBarControllerPageState();
}
class _TabBarControllerPageState extends State<TabBarControllerPage> with SingleTickerProviderStateMixin {
TabController _tabController; //第二種配置頂部導航的方式、
@override
void dispose() { //宣告周期函式,銷燬時呼叫
// TODO: implement dispose
super.dispose();
_tabController.dispose(); //銷燬時,把_tabController也給銷燬
}
@override
void initState() { //生命週期函式,初始化時,自動呼叫
// TODO: implement initState
super.initState();
_tabController=new TabController(length: 2, vsync: this); //呼叫構造方法初始化時,進行例項化,引數一是長度,引數二是固定寫法
_tabController.addListener((){
print(_tabController.index); //監聽改變時的下標
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TabBarControllerpage'),
bottom: TabBar(
controller: this._tabController, //注意,這裡是不一樣的地方,要讓controller=上面的_tabController
tabs: <Widget>[
Tab(text:"熱銷"),
Tab(text:"推薦"),
],
),
),
body: TabBarView(
controller: this._tabController, //注意這裡也需要配置
children: <Widget>[
Center(child: Text('熱銷'),),
Center(child: Text('推薦'),),
],
),
);
}
}
複製程式碼
還有記得配置命名路由喲:
Routes.dart
import '../pages/Tabs.dart';
import 'package:flutter/material.dart';
import '../pages/AppBarDemo.dart';
import '../pages/TabBarController.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由傳值 arguments工具是必須的
'/appBarDemo':(context)=>AppBarDemoPage(),
'/tabBarController':(context)=>TabBarControllerPage()
};
//固定寫法
var oonGenerateRoute=(RouteSettings settings) {
// 統一處理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的鍵去獲取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
複製程式碼
13、Drawer側邊欄、以及側邊欄內容佈局
效果圖:
Tabs.dart
User.dart
Tabs.dart
專案結構:
main.dart
import 'package:flutter/material.dart';
import 'routes/Routes.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
debugShowCheckedModeBanner: false, //去掉debug圖示
initialRoute: '/', //初始化的時候載入的路由
// home: Tabs(), //將程式碼全部抽離出去成一個Tabs元件,再抽離成一個初始化路由元件,在上方 initialRoute: '/',
//配置命名路由
onGenerateRoute: oonGenerateRoute //將抽離出去的路由傳值規範賦值給左邊,而不是執行,記住喔!
);
}
}
複製程式碼
Routes.dart
import '../pages/Tabs.dart';
import 'package:flutter/material.dart';
import '../pages/User.dart';
//配置路由
final routes={ //配置命名路由
'/':(context)=>Tabs(), //命名路由傳值 arguments工具是必須的
'/user':(context)=>UserPage()
};
//固定寫法
var oonGenerateRoute=(RouteSettings settings) {
// 統一處理
final String name = settings.name; //得到命名路由的名字,例如:'/form'
final Function pageContentBuilder = routes[name]; //得到命名路由的鍵去獲取值,例如:(context)=>FormPage(),
if (pageContentBuilder != null) {
if (settings.arguments != null) {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
return route;
} else {
final Route route = MaterialPageRoute(
builder: (context) => pageContentBuilder(context));
return route;
}
}
};
複製程式碼
User.dart 跳轉到使用者中心程式碼
import 'package:flutter/material.dart';
class UserPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('使用者中心'),
),
);
}
}
複製程式碼
Tabs.dart 側邊欄主要程式碼
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
final index; //用來作為返回根路由時,判斷要顯示第幾個底部導航項的座標
Tabs({Key key,this.index=0}) : super(key: key); //可選引數,預設是0
@override
_TabsState createState() => _TabsState(this.index); //把當前座標通過_TabsState構造方法傳給_TabsState類
}
class _TabsState extends State<Tabs> {
int _currentIndex;
_TabsState(index){ //接收Tabs 類呼叫時,傳過來的引數,賦值給 _currentIndex
this._currentIndex=index;
}
List _pageList = [ //先將所有頁面放到List集合內
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
body: this._pageList[this._currentIndex], //再根據下標獲取對應頁面設定到body 裡
bottomNavigationBar: BottomNavigationBar( //自定義底部導航條
currentIndex: this._currentIndex, //配置對應的索引值選中
onTap: (int index){
setState(() { //改變狀態
this._currentIndex=index; //更改選中的Tab座標
});
},
// iconSize: 45.0, //Icon的大小,預設在20左右
fixedColor: Colors.red, //選中的顏色,預設是藍色
items:[
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.home),
title: Text('首頁')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.category),
title: Text('分類')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.settings),
title: Text('設定')
)
]
),
drawer: Drawer( //左側邊欄
child: Column(
children: <Widget>[
Row( //這裡使用Row和Expanded進行配合是為了,讓底部的線水平鋪滿螢幕
children: <Widget>[
Expanded(
child: UserAccountsDrawerHeader(
accountName: Text('睿少帥哥'), //使用者名稱
accountEmail: Text('ruishao@qq.com'), //郵箱
currentAccountPicture: CircleAvatar( //設定圓形頭像
backgroundImage: NetworkImage('https://www.itying.com/images/flutter/3.png'),
),
decoration: BoxDecoration( //設定背景圖片
image: DecorationImage(
image: NetworkImage('https://www.itying.com/images/flutter/2.png'),
fit: BoxFit.cover
)
),
otherAccountsPictures: <Widget>[ //將圖片顯示在頭像右邊
Image.network('https://www.itying.com/images/flutter/4.png'),
Image.network('https://www.itying.com/images/flutter/5.png'),
],
)
)
],
),
ListTile(
leading: CircleAvatar(
child: Icon(Icons.home),
),
title: Text('我的空間'),
),
Divider(),
ListTile(
leading: CircleAvatar(
child: Icon(Icons.people),
),
title: Text('使用者中心'),
onTap: (){ //側邊欄監聽器
Navigator.of(context).pop(); //隱藏側邊欄
Navigator.pushNamed(context, '/user');
},
),
Divider(),
ListTile(
leading: CircleAvatar(
child: Icon(Icons.settings),
),
title: Text('設定中心'),
),
Divider(),
],
),
),
endDrawer: Drawer( //右側邊欄
child: Text('右側側邊欄'),
),
);
}
}
複製程式碼
剩餘:Category.dart、Home.dart、Setting.dart 屬於與本章無關的程式碼,但是還是給你們貼出來一起學學,順便整理成完整Demo,請參考:
Flutter實現TabController定義頂部tab切換,並介紹生命週期函式
14、按鈕元件 RaisedButton、FlatButton、OutlineButton、IconButton、ButtonBar以及自定義按鈕元件
效果圖:
import 'package:flutter/material.dart';
class ButtonDemoPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('按鈕演示頁面'),
actions: <Widget>[
IconButton( //圖示按鈕
icon: Icon(Icons.settings),
onPressed: (){
},
)
],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center, //垂直居中
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center, //水平居中
children: <Widget>[
RaisedButton(
child: Text('普通按鈕'),
onPressed: (){
print('普通按鈕');
},
),
SizedBox(width: 10,),
RaisedButton(
child: Text('顏色按鈕'),
color: Colors.blue, //背景顏色
textColor: Colors.white, //字型白色
onPressed: (){
print('顏色按鈕');
},
),
SizedBox(width: 10,),
RaisedButton(
child: Text('陰影按鈕'),
color: Colors.blue, //背景顏色
textColor: Colors.white, //字型白色
elevation: 10, //設定陰影效果,值越大陰影效果越好
onPressed: (){
print('陰影按鈕');
},
),
],
),
RaisedButton.icon(
// onPressed: null,
icon: Icon(Icons.search),
label: Text('圖示按鈕'),
color: Colors.blue,
textColor: Colors.white,
onPressed: (){
print('圖示按鈕');
},
),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container( //利用容器來設定按鈕的寬度和高度
height: 50,
width: 300,
child: RaisedButton(
child: Text('寬度高度'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('寬度高度');
},
),
)
],
),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //鋪滿螢幕寬度
child: Container( //利用容器設定高度
height: 80,
margin: EdgeInsets.all(10), //設定:左右間距10
child: RaisedButton(
child: Text('自適應按鈕'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('自適應按鈕');
},
),
)
)
],
),
SizedBox(height: 10,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('圓角按鈕'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
shape: RoundedRectangleBorder( //圓角按鈕
borderRadius: BorderRadius.circular(10) //圓角弧度
),
onPressed: (){
print('圓角按鈕');
}),
SizedBox(width: 10,),
Container(
height: 80, //設定:高度、也可以理解為直徑
child: RaisedButton(
child: Text('圓形按鈕'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
splashColor: Colors.red, //設定長按按鈕時,水波紋顏色
shape: CircleBorder( //圓角按鈕
side: BorderSide(
color: Colors.white
)
),
onPressed: (){
print('圓形按鈕');
}),
),
FlatButton( //扁平按鈕,預設是沒有陰影,而且預設也沒有背景顏色
child: Text('扁平按鈕'),
color: Colors.blue,
textColor: Colors.yellow,
onPressed: (){
print('扁平化按鈕');
},
)
],
),
SizedBox(height: 10,),
OutlineButton( //帶邊框按鈕
child: Text('邊框按鈕'),
// color: Colors.red, 沒有效果,這就是邊框按鈕的特性,不僅自帶邊框,還無法設定它的背景顏色,我猜可能是作者怕設定的背景顏色跟邊框顏色一致
// textColor: Colors.yellow, //有效果
onPressed: (){
print('邊框按鈕');
},
),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded( //自適應水平平鋪
child: Container(
margin: EdgeInsets.all(20), //上下左右分別間距 20
height: 50,
child: OutlineButton(
child: Text('註冊'),
onPressed: (){
},
),
),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ButtonBar(
children: <Widget>[
RaisedButton(
child: Text('登入'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('登入');
},
),
RaisedButton(
child: Text('註冊'),
color: Colors.blue,
textColor: Colors.white,
elevation: 20,
onPressed: (){
print('註冊');
},
),
MyButton(text:'自定義按鈕',height: 60.0,width: 100,pressed: (){
print('自定義按鈕');
})
],
)
],
)
],
),
);
}
}
//自定義按鈕元件
class MyButton extends StatelessWidget {
final text;
final pressed;
final double width;
final double height;
const MyButton({this.text="",this.pressed=null,this.width=80.0,this.height=30.0});
@override
Widget build(BuildContext context) {
return Container(
height: this.height,
width: this.width,
child: RaisedButton(
child: Text(this.text),
onPressed: this.pressed,
),
);
}
}
複製程式碼
15、類似閒魚App底部導航凸起按鈕
效果圖:
底部導航欄基本架構請參考:
Flutter實現底部導航
我這裡主要貼如何將中間的圖示變成: 類似閒魚App底部導航凸起按鈕
Tabs.dart
import 'package:flutter/material.dart';
import 'tabs/Category.dart';
import 'tabs/Home.dart';
import 'tabs/Setting.dart';
class Tabs extends StatefulWidget {
final index; //用來作為返回根路由時,判斷要顯示第幾個底部導航項的座標
Tabs({Key key,this.index=0}) : super(key: key); //可選引數,預設是0
@override
_TabsState createState() => _TabsState(this.index); //把當前座標通過_TabsState構造方法傳給_TabsState類
}
class _TabsState extends State<Tabs> {
int _currentIndex;
_TabsState(index){ //接收Tabs 類呼叫時,傳過來的引數,賦值給 _currentIndex
this._currentIndex=index;
}
List _pageList = [ //先將所有頁面放到List集合內
HomePage(),
CategoryPage(),
SettingPage()
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FlutterDemo'),
),
floatingActionButton: Container(
height: 70,
width: 70,
padding: EdgeInsets.all(8), //設定:內邊距8
margin: EdgeInsets.only(top: 2), //設定:外邊距2, 這樣就可以讓浮動按鈕下來,貼近分類Tab文字
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
color: Colors.white
),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
setState(() { //可以實現重新渲染頁面,因為_currentIndex變成了1,所以頁面會跳轉到分類頁面
this._currentIndex=1; //點選浮動按鈕時,切換到分類頁面
});
},
backgroundColor: this._currentIndex==1?Colors.red:Colors.yellow //利用三目運算子,實現選中時,浮動按鈕背景顏色變化
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, //浮動按鈕居中並且位於介面底部
body: this._pageList[this._currentIndex], //再根據下標獲取對應頁面設定到body 裡
bottomNavigationBar: BottomNavigationBar( //自定義底部導航條
currentIndex: this._currentIndex, //配置對應的索引值選中
onTap: (int index){
setState(() { //改變狀態
this._currentIndex=index; //更改選中的Tab座標
});
},
// iconSize: 45.0, //Icon的大小,預設在20左右
fixedColor: Colors.red, //選中的顏色,預設是藍色
items:[
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.home),
title: Text('首頁')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.category),
title: Text('分類')
),
BottomNavigationBarItem( //設定導航項
icon:Icon(Icons.settings),
title: Text('設定')
)
]
),
);
}
}
複製程式碼
16、單行文字框、多選框
效果圖:
TextField.dart
CheckBox.dart
TextField.dart
import 'package:flutter/material.dart';
class TextFieldDemoPage extends StatefulWidget {
@override
_TextFieldDemoPageState createState() => _TextFieldDemoPageState();
}
class _TextFieldDemoPageState extends State<TextFieldDemoPage> {
var _username = new TextEditingController(); //初始化時給表單賦值
var _password; //初始化時不賦值
@override
void initState() {
// TODO: implement initState
super.initState();
_username.text='初始值'; //呼叫text賦值
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('表單演示頁面'),
),
body: Padding(
padding: EdgeInsets.all(20),
// child: TextDemo(),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: '請輸入使用者名稱'
),
controller: _username, //通過唯一標識物件,初始化時賦值
onChanged: (value){ //文字框變化時的觸發事件 , 如果發生改變,系統就會自動將輸入框的值賦給value
setState(() {
_username.text = value;
});
},
),
SizedBox(height: 20,),
TextField(
obscureText: true, //開啟密碼模式
decoration: InputDecoration(
hintText: '請輸入密碼'
),
onChanged: (value){ //文字框變化時的觸發事件 , 如果發生改變,系統就會自動將輸入框的值賦給value
setState(() {
this._password = value; //注意,這裡沒有寫.text,所以下面獲取值的時候,也不需要寫.text,直接寫變數名即可
});
},
),
SizedBox(height: 40,),
Container(
width: double.infinity, //表示Container的寬度變成自適應寬度
height: 40,
child: RaisedButton(
child: Text('登入'),
onPressed: (){
print(this._username.text); //列印使用者名稱
print(this._password); //列印密碼
},
color: Colors.blue,
textColor: Colors.white,
),
)
],
),
),
);
}
}
//特殊效果舉例,不過這裡沒有呼叫,要想看效果的話直接在body:TextDemo 即可
class TextDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
TextField(), //輸入框表單
SizedBox(height: 20,),
TextField(
decoration: InputDecoration(
hintText: '請輸入搜尋的內容', //提示文字,相當於Android裡的hint
border: OutlineInputBorder() //給表單四周新增邊框
),
),
SizedBox(height: 20,),
TextField(
maxLines: 4, //設定對最大行數
decoration: InputDecoration(
hintText: '多行文字框', //多行文字框
border: OutlineInputBorder() //給表單四周新增邊框
),
),
SizedBox(height: 20,),
TextField(
obscureText: true, //開啟密碼模式
decoration: InputDecoration(
hintText: '密碼框', //多行文字框
border: OutlineInputBorder() //給表單四周新增邊框
),
),
SizedBox(height: 20,),
TextField( //特殊效果,輸入資訊時,使用者名稱動態跑到邊框屆提示
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: '使用者名稱'
),
),
SizedBox(height: 20,),
TextField( //特殊效果,輸入資訊時,使用者名稱動態跑到邊框屆提示
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: '密碼'
),
),
SizedBox(height: 20,),
TextField( //在文字框前面加上圖示
decoration: InputDecoration(
icon: Icon(Icons.people),
hintText: '請輸入使用者名稱'
),
)
],
),
);
}
} //列舉文字框的樣式與模式,要使用時在body裡呼叫TextDemo元件即可
複製程式碼
CheckBox.dart
import 'package:flutter/material.dart';
class CheckBoxPage extends StatefulWidget {
@override
_CheckBoxPageState createState() => _CheckBoxPageState();
}
class _CheckBoxPageState extends State<CheckBoxPage> {
var flag = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('checkbox'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(children: <Widget>[
Checkbox( //多選框元件
value: this.flag,
onChanged: (v){ //多選框值變化時,觸發
setState(() {
this.flag=v;
});
},
activeColor: Colors.red, //設定:選中時的顏色
),
],),
Row(
children: <Widget>[
Text(this.flag?'選中':'未選中') //顯示:checkBox當前是否勾選的值
],
),
SizedBox(height: 40,),
CheckboxListTile( //多選框元件
value: this.flag,
onChanged: (v){ //多選框值變化時,觸發
setState(() {
this.flag=v;
});
},
title: Text('標題'),
subtitle: Text('這是二級標題'),
),
Divider(),
CheckboxListTile( //多選框元件
value: this.flag,
onChanged: (v){ //多選框值變化時,觸發
setState(() {
this.flag=v;
});
},
title: Text('標題'),
subtitle: Text('這是二級標題'),
secondary: Icon(Icons.help),
)
],
),
);
}
}
複製程式碼
17、多行文字框、開關按鈕、多選框、單選按鈕、RadioListTile、Radio、表單
效果圖:
Radio.dart
FormDemo.dart
Radio.dart
import 'package:flutter/material.dart';
class RadioPage extends StatefulWidget {
@override
_RadioPageState createState() => _RadioPageState();
}
class _RadioPageState extends State<RadioPage> {
int sex=1;
bool flag = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Radio'),
),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
// Row(
// children: <Widget>[
// Text('男: '),
// Radio( //類似單選按鈕組
// value: 1,
// onChanged: (v){ //Radio改變時,觸發事件
// setState(() { //重新渲染介面
// this.sex=v;
// });
// },
// groupValue: this.sex, //如果這裡的值是一樣的,說明都是屬於同一個人單選按鈕組
// ),
// SizedBox(width: 20,),
// Text('女: '),
// Radio( //類似單選按鈕組
// value: 2,
// onChanged: (v){ //Radio改變時,觸發事件
// setState(() { //重新渲染介面
// this.sex=v;
// });
// },
// groupValue: this.sex, //如果這裡的值是一樣的,說明都是屬於同一個人單選按鈕組
// )
// ],
// ),
// Row(
// children: <Widget>[
// Text('${this.sex}'), //列印下標
// Text(this.sex==1?'男':'女') //利用三目運算子,列印值
// ],
// ),
SizedBox(height: 40,),
RadioListTile(
value: 1,
onChanged: (v){ //Radio改變時,觸發事件
setState(() { //重新渲染介面
this.sex=v;
});
},
groupValue: this.sex, //如果這裡的值是一樣的,說明都是屬於同一個人單選按鈕組
title: Text('標題'),
subtitle: Text('這是二級標題'),
secondary: Icon(Icons.help), //設定: 圖示
selected: this.sex==1, //選中時,文字發亮
),
RadioListTile(
value: 2,
onChanged: (v){ //Radio改變時,觸發事件
setState(() { //重新渲染介面
this.sex=v;
});
},
groupValue: this.sex, //如果這裡的值是一樣的,說明都是屬於同一個人單選按鈕組
title: Text('標題'),
subtitle: Text('這是二級標題'),
secondary: Image.network('https://www.itying.com/images/flutter/1.png'), //載入遠端圖片
selected: this.sex==2, //選中時,文字發亮
),
SizedBox(height: 40,),
Switch( //Android裡的開關按鈕
value: this.flag,
onChanged: (v){
setState(() {
print((v));
this.flag=v;
});
},
)
],
),
),
);
}
}
複製程式碼
FormDemo.dart
import 'package:flutter/material.dart';
class FormDemoPage extends StatefulWidget {
@override
_FormDemoPageState createState() => _FormDemoPageState();
}
class _FormDemoPageState extends State<FormDemoPage> {
String username;
int sex=1; //預設1是男
String info = '';
List hobby=[
{
'checked':true,
'title':'吃飯'
},
{
'checked':false,
'title':'睡覺'
},
{
'checked':true,
'title':'寫程式碼'
},
];
List<Widget> _getHobby(){ //返回多個checkBox集合
List<Widget> tempList=[];
for(var i=0;i<this.hobby.length;i++){
//寫法一:
tempList.add(Text(this.hobby[i]['title']+":"));
tempList.add(
Checkbox(
value: this.hobby[i]['checked'],
onChanged: (value){
setState(() {
this.hobby[i]['checked']=value;
});
},
)
);
//寫法二:
// tempList.add(
// Row(
// children: <Widget>[
// Text(this.hobby[i]['title']+":"),
// Checkbox(
// value: this.hobby[i]['checked'],
// onChanged: (value){
// setState(() {
// this.hobby[i]['checked']=value;
// });
// },
// )
// ],
// )
// );
}
return tempList;
}
void _sexChanged(value){ //將單選組按鈕的監聽器抽離出來
setState(() {
this.sex=value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('學員資訊登記系統'),
),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: '輸入使用者資訊'
),
onChanged: (value){
setState(() {
this.username=value;
});
},
),
SizedBox(height: 10,),
Row(
children: <Widget>[
Text('男'),
Radio(
value: 1,
onChanged: this._sexChanged,
groupValue: this.sex,
),
SizedBox(width: 20,),
Text('女'),
Radio(
value: 2,
onChanged: this._sexChanged,
groupValue: this.sex,
)
],
),
//愛好
SizedBox(height: 40,),
Wrap(
children: this._getHobby(),
),
SizedBox(height: 20,),
TextField(
maxLines: 4,
decoration: InputDecoration(
hintText: '描述資訊',
border: OutlineInputBorder()
),
onChanged: (value){
setState(() {
this.info=value;
});
},
),
SizedBox(height: 40,),
Container(
width: double.infinity, //表示Container的寬度變成自適應寬度
height: 40,
child: RaisedButton(
child: Text('提交資訊'),
onPressed: (){
print(this.sex); //獲取單選按鈕的值
print(this.username); //獲取文字框的值
print(this.hobby); //獲取多選框的值
print(this.info); //獲取描述資訊
},
color: Colors.blue,
textColor: Colors.white,
),
)
],
),
),
);
}
}
複製程式碼
18、呼叫原生時間選擇器、日期選擇器、時間戳、Future非同步
效果圖:
參考第三方庫:pub.dev/packages/da…
在pubspec.yaml,引入最新的依賴,我這裡是
在相關類引入包名:
import ‘package:date_format/date_format.dart’;
時間戳轉化演示:
時間戳就是時間在伺服器儲存的值,他跟正常我們看得的值是不一樣的
//日期轉化成時間戳:
var now = new DateTime.now();
print(now.millisecondsSinceEpoch);//單位毫秒,13 位時間戳
//時間戳轉化成日期:
var now = new DateTime.now();
var a=now.millisecondsSinceEpoch; //時間戳
print(DateTime.fromMillisecondsSinceEpoch(a));
複製程式碼
DatePicker.dart
import 'package:flutter/material.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart' as prefix0;
class DatePickerPage extends StatefulWidget {
@override
_DatePickerPageState createState() => _DatePickerPageState();
}
class _DatePickerPageState extends State<DatePickerPage> {
DateTime _nowDate = DateTime.now();
var _nowTime = TimeOfDay(hour: 12,minute: 20); //初始化的時候時間值
_showDatePicker() async{ //Flutter自帶的日期元件 //獲取非同步資料方式二:通過async非同步方法獲取
//獲取非同步資料方式一:通過then獲取
// showDatePicker(
// context:context,
// initialDate:_nowDate, //當前日期
// firstDate:DateTime(1980), //起始日期
// lastDate:DateTime(2100) //結束日期
// ).then((result){
// print(result);
// });
/**
* await 配合 async完成非同步流程
* await表示等待非同步請求,請求完後就把值賦給result
* 我們再列印這個result即可拿到資料
*/
var result = await showDatePicker(
context:context,
initialDate:_nowDate, //當前日期
firstDate:DateTime(1980), //起始日期
lastDate:DateTime(2100), //結束日期
locale: prefix0.Locale('zh') // 非必須,如果作業系統是中文的話可以不加,如果不是的話,就要加上才能變成中文
);
// print(result);
setState(() {
this._nowDate = result; //將選擇日期後的值賦給_nowDate
});
}
_showTimePicker() async{ //Flutter自帶的時間元件
var result = await showTimePicker(
context:context,
initialTime: _nowTime
);
setState(() {
this._nowTime = result;
});
}
@override
void initState() {
// TODO: implement initState
super.initState();
// var now = DateTime.now();
// print(now); //2019-11-17 12:23:06.811117
// print(now.millisecondsSinceEpoch); //1573964623294
/* print(DateTime.fromMicrosecondsSinceEpoch(1573964623294)); //1970-01-19 13:12:44.623294*/
print(formatDate(DateTime.now(), [yyyy, '年', mm, '月', dd]));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DatePickerDemo'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
InkWell( //讓元件可以點選
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Text('${_nowDate}'),
Text('${formatDate(_nowDate, [yyyy, '年', mm, '月', dd])}'), //使用第三方庫按照指定形式顯示資料
Icon(Icons.arrow_drop_down)
],
),
onTap: _showDatePicker,
),
InkWell( //讓元件可以點選
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Text('${_nowDate}'),
// Text('${_nowTime}'), //這樣顯示的值是:TimeOfDay(07:26) 並不是我們想要的,所以要改成下面
Text('${_nowTime.format(context)}'),
Icon(Icons.arrow_drop_down)
],
),
onTap: _showTimePicker,
)
],
)
],
),
);
}
}
複製程式碼
這裡可能會遇到關於呼叫後,時間還為英文的情況,針對這種情況,解決辦法為:
flutter showDatePicker顯示中文日期_Flutter時間控制元件顯示中文
如果看不懂教程,我還提供了一些程式碼變動截圖,你們照著改就行:
pubspec.yaml
main.dart
匯入國際化的包 flutter_localizations
import ‘package:flutter_localizations/flutter_localizations.dart’;
DatePicker.dart
_showDatePicker() async{ //Flutter自帶的日期元件 //獲取非同步資料方式二:通過async非同步方法獲取
//獲取非同步資料方式一:通過then獲取
// showDatePicker(
// context:context,
// initialDate:_nowDate, //當前日期
// firstDate:DateTime(1980), //起始日期
// lastDate:DateTime(2100) //結束日期
// ).then((result){
// print(result);
// });
/**
* await 配合 async完成非同步流程
* await表示等待非同步請求,請求完後就把值賦給result
* 我們再列印這個result即可拿到資料
*/
var result = await showDatePicker(
context:context,
initialDate:_nowDate, //當前日期
firstDate:DateTime(1980), //起始日期
lastDate:DateTime(2100), //結束日期
locale: prefix0.Locale('zh') // 非必須,如果作業系統是中文的話可以不加,如果不是的話,就要加上才能變成中文
);
// print(result);
setState(() {
this._nowDate = result; //將選擇日期後的值賦給_nowDate
});
}
複製程式碼
19、呼叫第三方時間選擇器、日期選擇器、時間戳
效果圖:
如果會遇到英文問題,請參考:
Flutter實現呼叫原生時間選擇器、日期選擇器、時間戳、Future非同步
我在此篇博文已對解決方法進行了詳細講解
那麼下面我們就開始這一章相關的知識
參考第三方庫:
配置:pubspec.yaml
在相關類引入包名:
import ‘package:flutter_cupertino_date_picker/flutter_cupertino_date_picker.dart’;
DatePickerPub.dart
import 'package:flutter/material.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter_cupertino_date_picker/flutter_cupertino_date_picker.dart'; //第三方時間元件庫
class DatePickerPubPage extends StatefulWidget {
@override
_DatePickerPubPageState createState() => _DatePickerPubPageState();
}
class _DatePickerPubPageState extends State<DatePickerPubPage> {
DateTime _dateTime = DateTime.now(); //獲取當前日期
_showDatePicker(){
DatePicker.showDatePicker(
context,
pickerTheme: DateTimePickerTheme(
showTitle: true,
confirm: Text('確定', style: TextStyle(color: Colors.red)),
cancel: Text('取消', style: TextStyle(color: Colors.cyan)),
),
minDateTime: DateTime.parse("1980-05-12"), //起始日期
maxDateTime: DateTime.parse('2100-05-12'), //終止日期
initialDateTime: DateTime.now(), //獲取當前時間
dateFormat: 'yyyy年M月d日 EEE,H時:m分', //設定:年-月-日的格式
pickerMode: DateTimePickerMode.datetime, // show TimePicker
locale: DateTimePickerLocale.zh_cn, //設定:語言-中文
onClose: () => print("----- onClose -----"), //取消監聽器
// onCancel: () => print('onCancel'),
// onChange: (dateTime, List<int> index) { //滑動時間元件時,觸發
// setState(() {
// _dateTime = dateTime;
// });
// },
onConfirm: (dateTime, List<int> index) { //確定監聽器
setState(() {
_dateTime = dateTime;
});
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DatePickerPubDemo'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
InkWell(
child: Row(
children: <Widget>[
Text('${formatDate(_dateTime, [yyyy, '年', mm, '月', dd," ",HH,":",nn])}'),
Icon(Icons.arrow_drop_down)
],
),
onTap: _showDatePicker
)
],
)
],
),
);
}
}
複製程式碼
20、輪播圖 flutter_swiper
效果圖:
圖1
圖2
圖3
圖4
圖5
第三方庫參考:pub.dev/packages/fl…
配置 pubspec.yaml
在需要輪播的類引入,包名:
import ‘package:flutter_swiper/flutter_swiper.dart’;
圖1、圖2程式碼:
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class SwiperPage extends StatefulWidget {
@override
_SwiperPageState createState() => _SwiperPageState();
}
class _SwiperPageState extends State<SwiperPage> {
List<Map> imgList = [ //設定: 輪播圖的圖片素材
{
"url":"https://www.itying.com/images/flutter/1.png"
},
{
"url":"https://www.itying.com/images/flutter/2.png"
},
{
"url":"https://www.itying.com/images/flutter/3.png"
},
{
"url":"https://www.itying.com/images/flutter/4.png"
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('輪播圖元件演示'),
),
body: Column(
children: <Widget>[
/**
* 必須要套Container,才能使用輪播圖,否則會報錯
*/
Container(
// height: 150,
/**
* 用AspectRatio套住輪播圖元件是為了讓圖片不變形,根據手機螢幕寬高比自適應,效果最佳
*/
// width: double.infinity, 還是會變形的話就加上這句
child: AspectRatio(
aspectRatio: 16/9,
child: Swiper(
itemBuilder: (BuildContext context,int index){ //每次迴圈遍歷時,將i賦值給index
return new Image.network(
imgList[index]['url'],
fit: BoxFit.fill,);
},
itemCount: imgList.length,
pagination: new SwiperPagination(), //設定:分頁器.
loop: true, //無限迴圈
autoplay: true, //圖片自動輪播
// control: new SwiperControl(), //設定:左右的箭頭
),
),
)
],
)
);
}
}
複製程式碼
圖3程式碼
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class SwiperPage extends StatefulWidget {
@override
_SwiperPageState createState() => _SwiperPageState();
}
class _SwiperPageState extends State<SwiperPage> {
List<Map> imgList = [ //設定: 輪播圖的圖片素材
{
"url":"https://www.itying.com/images/flutter/1.png"
},
{
"url":"https://www.itying.com/images/flutter/2.png"
},
{
"url":"https://www.itying.com/images/flutter/3.png"
},
{
"url":"https://www.itying.com/images/flutter/4.png"
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('輪播圖元件演示'),
),
body: Column(
children: <Widget>[
/**
* 必須要套Container,才能使用輪播圖,否則會報錯
*/
Container(
// height: 150,
/**
* 用AspectRatio套住輪播圖元件是為了讓圖片不變形,根據手機螢幕寬高比自適應,效果最佳
*/
// width: double.infinity, 還是會變形的話就加上這句
child: AspectRatio(
aspectRatio: 16/9,
child: new Swiper(
layout: SwiperLayout.CUSTOM,
customLayoutOption: new CustomLayoutOption(
startIndex: -1,
stateCount: 3
).addRotate([
-45.0/180,
0.0,
45.0/180
]).addTranslate([
new Offset(-370.0, -40.0),
new Offset(0.0, 0.0),
new Offset(370.0, -40.0)
]),
itemWidth: 300.0,
itemHeight: 200.0,
itemBuilder: (context, index) {
return new Container(
// color: Colors.grey, 設定:輪播圖背景
child: new Center(
child: Image.network(imgList[index]['url'],fit: BoxFit.contain,),
),
);
},
itemCount: imgList.length)
),
)
],
)
);
}
}
複製程式碼
圖4、圖5程式碼
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
class SwiperPage extends StatefulWidget {
@override
_SwiperPageState createState() => _SwiperPageState();
}
class _SwiperPageState extends State<SwiperPage> {
List<Map> imgList = [ //設定: 輪播圖的圖片素材
{
"url":"https://www.itying.com/images/flutter/1.png"
},
{
"url":"https://www.itying.com/images/flutter/2.png"
},
{
"url":"https://www.itying.com/images/flutter/3.png"
},
{
"url":"https://www.itying.com/images/flutter/4.png"
},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('輪播圖元件演示'),
),
body: Swiper(
itemBuilder: (BuildContext context,int index){ //每次迴圈遍歷時,將i賦值給index
return new Image.network(
imgList[index]['url'],
fit: BoxFit.fill,);
},
itemCount: imgList.length,
// pagination: new SwiperPagination(), 設定:分頁器
// control: new SwiperControl(), 設定:左右的箭頭
),
);
}
}
複製程式碼
21、普通對話方塊、列表對話方塊、單選對話方塊、Toast提示
效果圖:
Dialog.dart
這裡我把所有的程式碼都寫在了一個類裡,不過要想實現Toast的功能,我們必須要引入第三方庫,可參考:pub.dev/packages/fl…
配置:pubspec.yaml
引入包名:import ‘package:fluttertoast/fluttertoast.dart’;
Dialog.dart
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
class DialogPage extends StatefulWidget {
@override
_DialogPageState createState() => _DialogPageState();
}
class _DialogPageState extends State<DialogPage> {
_alertDialog() async{
var result = await showDialog( //通過非同步在外面獲取值
context:context,
builder: (context){
return AlertDialog( //系統自帶: 普通對話方塊
title: Text('提示資訊!'),
content: Text('您確定要刪除嗎?'),
actions: <Widget>[ //監聽器
FlatButton( //確定監聽
child: Text('取消'),
onPressed: (){
print('取消');
Navigator.pop(context,'Cancle');
},
),
FlatButton( //取消監聽
child: Text('確定'),
onPressed: (){
print('確定');
Navigator.pop(context,'OK');
},
)
],
);
}
);
print(result); //在外部獲取資料並列印
}
_simpleDialog() async{
var result = await showDialog(
context: context,
builder: (context){
return SimpleDialog(
title: Text('選擇內容'),
children: <Widget>[
SimpleDialogOption(
child: Text('Option A'),
onPressed: (){
print('Option A');
Navigator.pop(context,'A');
},
),
Divider(),
SimpleDialogOption(
child: Text('Option B'),
onPressed: (){
print('Option B');
Navigator.pop(context,'B');
},
),
Divider(),
SimpleDialogOption(
child: Text('Option C'),
onPressed: (){
print('Option C');
Navigator.pop(context,'C');
},
),
Divider(),
],
);
}
);
print(result);
}
_modelBottomSheet() async{
var result = await showModalBottomSheet(
context: context,
builder: (context){
return Container(
height: 250, //配置底部彈出框高度
child: Column(
children: <Widget>[
ListTile(
title: Text('分享 A'),
onTap: (){
Navigator.pop(context,'分享A');
},
),
Divider(),
ListTile(
title: Text('分享 B'),
onTap: (){
Navigator.pop(context,'分享B');
},
),
Divider(),
ListTile(
title: Text('分享 C'),
onTap: (){
Navigator.pop(context,'分享C');
},
),
],
),
);
}
);
print(result);
}
_toast(){
Fluttertoast.showToast(
msg: "提示資訊",
toastLength: Toast.LENGTH_SHORT, //跟Android一樣,短時間提示
gravity: ToastGravity.CENTER, //居中
timeInSecForIos: 1, //Android無效果,ios有效果文件上已經說明:timeInSecForIos int (only for ios)
backgroundColor: Colors.red, //背景顏色
textColor: Colors.white, //字型顏色
fontSize: 16.0 //字型大小
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DialogDemo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text('alert彈出框-AlerDialog'),
onPressed: _alertDialog,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('select彈出框-SimpleDialog'),
onPressed: _simpleDialog,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('ActionSheet底部彈出框-showModalBottomSheet'),
onPressed: _modelBottomSheet,
),
SizedBox(height: 20,),
RaisedButton(
child: Text('toast-fluttertoast第三方庫'),
onPressed: _toast,
)
],
),
),
);
}
}
複製程式碼