flutter需要什么基礎。我叫王睿,對您有幫助的話,請給個點贊或關注喔,謝謝!
1.1、 效果圖
1.2、輸出一句Hello World
import 'package:flutter/material.dart';void main()=>runApp(MyApp());
//等價于:
/*
void main(){runApp(MyApp());
}
*/class MyApp extends StatelessWidget{ //抽離頁面全部內容@overrideWidget build(BuildContext context) {// TODO: implement buildreturn MaterialApp( //頂層 widgethome:Scaffold( //Material Design 布局結構的基本實現appBar: AppBar( //顯示在界面頂部的一個 AppBar。title: Text('Flutter Demo'), //標題),body: HomeContent(), //當前界面所顯示的主要內容 Widget。),theme: ThemeData( //主題primarySwatch: Colors.yellow //修改主題顏色),);}
}class HomeContent extends StatelessWidget{ //抽離主界面@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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。
flutter從入門到精通、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 buildreturn 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 buildreturn MaterialApp(home:Scaffold(appBar: AppBar(title: Text('Flutter Demo'),),body: HomeContent(),),theme: ThemeData(primarySwatch: Colors.yellow //修改主題顏色),);}
}
class HomeContent extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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.1丶 效果圖
2.2、代碼+注釋
import 'package:flutter/material.dart';void main()=>runApp(MyApp());class MyApp extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn MaterialApp(home:Scaffold(appBar: AppBar(title: Text('Fullter Demo'),),body: HomeContent(),));}
}class HomeContent extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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.1、效果圖
【1】引用遠程圖片
【2】引用本地圖片
【3】實現圓形圖片(方法一)、圓角圖片(受弧度影響)
【4】實現圓形圖片(方法二)
3.2、代碼+注釋
【1】引用遠程圖片
import 'package:flutter/material.dart';
void main()=> runApp(MyApp());class MyApp extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn MaterialApp(home: Scaffold(appBar: AppBar(title: Text('FlutterDemo'),),body: HomeCentent(),),);}
}class HomeCentent extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn MaterialApp(home: Scaffold(appBar: AppBar(title: Text('FlutterDemo'),),body: HomeCentent(),),);}
}class HomeCentent extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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.1、效果圖
【1】基礎列表
【2】基礎列表+圖標組件
【3】基礎列表+引用遠程圖片
【4】垂直列表
【5】水平列表
4.2、代碼+注釋
【1】基礎列表
import 'package:flutter/material.dart';void main()=>runApp(MyApp());class MyApp extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn MaterialApp(home:Scaffold(appBar: AppBar(title: Text('Hello Flutter'),),body: HomeContent(),));}
}class HomeContent extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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.1、 效果圖
5.2、代碼+注釋
實現動態列表:
【1】方式一:抽離組件
import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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("我是一個列表"),)];}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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;}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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,這里它的作用是,每次返回改變后的值賦給tempListleading: Image.network(value["imageUrl"]),title: Text(value["title"]),subtitle: Text(value["author"]),);});return tempList.toList(); //最后再返回這個值,但是要記住,他不是一個集合數組,所以我們要讓它轉換下類型,通過toList}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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條');}}@overrideWidget 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"]));}@overrideWidget 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.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{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn MaterialApp(home:Scaffold(appBar: AppBar(title: Text('Flutter Demo'),),body: HomeContent(),));}
}class HomeContent extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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;}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn GridView.count( //網格組件crossAxisSpacing: 20.0, //水平子Widget 之間的間距mainAxisSpacing: 20.0, //垂直子Widget 之間的間距padding: EdgeInsets.all(10), //與View上下左右間隔10crossAxisCount: 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();}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn GridView.count( //網格組件 — 列表布局crossAxisSpacing: 20.0, //水平子Widget 之間的間距mainAxisSpacing: 20.0, //垂直子Widget 之間的間距padding: EdgeInsets.all(10), //與View上下左右間隔10crossAxisCount: 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 //邊框寬度)),);}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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、網格組件,也叫列表布局
效果圖:
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{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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()];@overrideWidget 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> {@overrideWidget 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> {@overrideWidget 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> {@overrideWidget build(BuildContext context) {return ListView(children: <Widget>[ListTile(title: Text('我是一個文本'),),ListTile(title: Text('我是一個文本'),),ListTile(title: Text('我是一個文本'),),ListTile(title: Text('我是一個文本'),),ListTile(title: Text('我是一個文本'),)],);}
}
效果圖:
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> {@overrideWidget 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> {@overrideWidget 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> {@overrideWidget 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='表單'}); //參數是一個可選參數,默認值是表單,當接收到值時,表單值直接被覆蓋@overrideWidget 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('我是表單頁面'),),],),);}
}
效果圖:
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{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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> {@overrideWidget 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> {@overrideWidget 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});@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('商品詳情'),),body: Container(child: Text('pid=${arguments['pid']}'),),);}
}
效果圖:
Setting.dart
RegisterFirst.dart
RegisterSecond.dart
RegisterThird.dart
無相關的頁面代碼架構請參考:flutter實現頁面跳轉、跳轉傳值(命名路由、命名路由傳值)
主要變動:
這里負責貼主要代碼:
Login.dart
import 'package:flutter/material.dart';class LoginPage extends StatelessWidget{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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);@overrideWidget 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);@overrideWidget 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);@overrideWidget 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> {@overrideWidget 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;}}
};
效果圖:
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{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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 類調用時,傳過來的參數,賦值給 _currentIndexthis._currentIndex=index;}List _pageList = [ //先將所有頁面放到List集合內HomePage(),CategoryPage(),SettingPage()];@overrideWidget 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 {@overrideWidget 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> {@overrideWidget 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> {@overrideWidget build(BuildContext context) {return DefaultTabController(length: 4,child: Scaffold( //在Scaffold 里再嵌套ScaffoldappBar: 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, //選中時,底部指示條與文字一樣長,默認是tabtabs: <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目錄結構:
效果圖:
Home.dart
TabBarController.dart
Home.dart
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {@overrideWidget 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; //第二種配置頂部導航的方式、@overridevoid dispose() { //聲明周期函數,銷毀時調用// TODO: implement disposesuper.dispose();_tabController.dispose(); //銷毀時,把_tabController也給銷毀}@overridevoid initState() { //生命周期函數,初始化時,自動調用// TODO: implement initStatesuper.initState();_tabController=new TabController(length: 2, vsync: this); //調用構造方法初始化時,進行實例化,參數一是長度,參數二是固定寫法_tabController.addListener((){print(_tabController.index); //監聽改變時的下標});}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('TabBarControllerpage'),bottom: TabBar(controller: this._tabController, //注意,這里是不一樣的地方,要讓controller=上面的_tabControllertabs: <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;}}
};
效果圖:
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{@overrideWidget build(BuildContext context) {// TODO: implement buildreturn 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 {@overrideWidget 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 類調用時,傳過來的參數,賦值給 _currentIndexthis._currentIndex=index;}List _pageList = [ //先將所有頁面放到List集合內HomePage(),CategoryPage(),SettingPage()];@overrideWidget 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切換,并介紹生命周期函數
效果圖:
import 'package:flutter/material.dart';class ButtonDemoPage extends StatelessWidget {@overrideWidget 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), //設置:左右間距10child: 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), //上下左右分別間距 20height: 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});@overrideWidget build(BuildContext context) {return Container(height: this.height,width: this.width,child: RaisedButton(child: Text(this.text),onPressed: this.pressed,),);}
}
效果圖:
底部導航欄基本架構請參考:
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 類調用時,傳過來的參數,賦值給 _currentIndexthis._currentIndex=index;}List _pageList = [ //先將所有頁面放到List集合內HomePage(),CategoryPage(),SettingPage()];@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('FlutterDemo'),),floatingActionButton: Container(height: 70,width: 70,padding: EdgeInsets.all(8), //設置:內邊距8margin: 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('設置'))]),);}
}
效果圖:
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; //初始化時不賦值@overridevoid initState() {// TODO: implement initStatesuper.initState();_username.text='初始值'; //調用text賦值}@overrideWidget 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){ //文本框變化時的觸發事件 , 如果發生改變,系統就會自動將輸入框的值賦給valuesetState(() {_username.text = value;});},),SizedBox(height: 20,),TextField(obscureText: true, //開啟密碼模式decoration: InputDecoration(hintText: '請輸入密碼'),onChanged: (value){ //文本框變化時的觸發事件 , 如果發生改變,系統就會自動將輸入框的值賦給valuesetState(() {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 {@overrideWidget build(BuildContext context) {return Container(child: Column(children: <Widget>[TextField(), //輸入框表單SizedBox(height: 20,),TextField(decoration: InputDecoration(hintText: '請輸入搜索的內容', //提示文字,相當于Android里的hintborder: 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;@overrideWidget 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),)],),);}
}
效果圖:
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;@overrideWidget 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;});}@overrideWidget 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,),)],),),);}
}
效果圖:
參考第三方庫:https://pub.dev/packages/date_format
在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;});}@overridevoid initState() {// TODO: implement initStatesuper.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]));}@overrideWidget 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});}
效果圖:
如果會遇到英文問題,請參考:
Flutter實現調用原生時間選擇器、日期選擇器、時間戳、Future異步
我在此篇博文已對解決方法進行了詳細講解
那么下面我們就開始這一章相關的知識
參考第三方庫:
https://pub.dev/packages/flutter_cupertino_date_picker
配置: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 TimePickerlocale: 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;});},);}@overrideWidget 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)],)],),);}
}
效果圖:
圖1
圖2
圖3
圖4
圖5
第三方庫參考:https://pub.dev/packages/flutter_swiper
配置 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"},];@overrideWidget 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賦值給indexreturn 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"},];@overrideWidget 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"},];@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('輪播圖組件演示'),),body: Swiper(itemBuilder: (BuildContext context,int index){ //每次循環遍歷時,將i賦值給indexreturn new Image.network(imgList[index]['url'],fit: BoxFit.fill,);},itemCount: imgList.length,
// pagination: new SwiperPagination(), 設置:分頁器
// control: new SwiperControl(), 設置:左右的箭頭),);}
}
效果圖:
Dialog.dart
這里我把所有的代碼都寫在了一個類里,不過要想實現Toast的功能,我們必須要引入第三方庫,可參考:https://pub.dev/packages/fluttertoast
配置: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 //字體大小);}@overrideWidget 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,)],),),);}
}
我叫王睿,對您有幫助的話,請給個點贊或關注喔,謝謝!
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态