前言
該文章只是自己這兩天看電子書的總結,如果想看一手知識的可以轉到電子書book.flutterchina.club/chapter7/th…。前端開發無非就兩步第一是佈局、第二是請求資料重新整理介面,這兩天看完感覺至少寫一些簡單的介面是沒問題了。剩下的請求以及一些第三方庫有時間再繼續。對於沒有打過Flutter程式碼的朋友我建議看完至少還要自己動手打一下,不用全打,因為打跟看是兩回事,有時候看懂了打的時候還是要看一下。
StatefulWidget
動態元件,後期需要改變顯示狀態的需要用動態元件
動態元件的定義
快捷鍵
- stful 會自動生成相應最簡單的想要程式碼
- stanim 會生成帶有生命週期的程式碼
- 還有其他快捷鍵可以自己去嘗試
生成的程式碼
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Container();
}
}
複製程式碼
- State是一個儲存負責控制元件生命週期的類,還有以下一些方法。包括初始化、構建、銷燬等等使用setState方法時會重新整理對應的回撥
@override
void initState() {
_controller = AnimationController(vsync: this);
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
...
複製程式碼
StatelessWidget
靜態元件定義後不會再改變,一般很少用到。
Widget與Element
Widget(元件)是Element(元素)的封裝,flutter最後渲染的是Element。不涉及到原理時瞭解一下即可
元件按功能分類
分為四種,分別是基礎類元件、佈局類元件、容器類元件、滾動類元件
基礎類元件
基礎元件分為**文字(Text)、圖片(Image)、按鈕(xxButton)、輸入框(TextField)、單選框(Switch)與核取方塊(CheckBox)、表單(Form)**等等。基本格式為:
//文字
Text("Hello world! I'm Jack. "*4,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
複製程式碼
//按鈕的種類有很多
RaisedButton(
child: Text("normal"),
onPressed: () => {},
);
複製程式碼
可以發現格式都是類似的,具體不一一講述,屬性型別跟移動端的控制元件是類似的。唯一需要看的是表單元件
表單:為了方便上傳資料時不用對每個輸入框都做判斷。flutter提供了表單元件。
簡單理解:表單給每個輸入框提供一個判斷是否驗證同的屬性validator。並且提供提交按鈕一個出發的方法觸發回撥具體程式碼可以下面地址的介紹 book.flutterchina.club/chapter3/in…
佈局類元件
水平線性佈局(Row)、垂直線性佈局(Column)、彈性佈局(Flex)、流式佈局(Wrap|Flow)、層疊佈局(Stack|Positioned)
線性佈局
水平跟垂直都是基層與Flex。基本格式如下
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max, //有效,外層Colum高度為整個螢幕
children: <Widget>[
Container(
color: Colors.red,
child: Column(
mainAxisSize: MainAxisSize.max,//無效,內層Colum高度為實際高度
children: <Widget>[
Text("hello world "),
Text("I am Jack "),
],
),
)
],
)
複製程式碼
基本上跟Android的線性佈局一樣
- crossAxisAlignment:覺得子控制元件的對齊
- mainAxisSize:自身的大小,如果巢狀裡面的會沒效
- children:子控制元件
- 還有其他可以看看連結book.flutterchina.club/chapter4/ro…
彈性佈局
- Flex屬性與線性佈局類似
- Expanded。在Flex容器中可以設定比例
例如下面的程式碼會按1:2比例佔據螢幕
Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
height: 30.0,
color: Colors.red,
),
),
Expanded(
flex: 2,
child: Container(
height: 30.0,
color: Colors.green,
),
),
],
)
複製程式碼
流式佈局
- Wrap直接使用,超過螢幕自動換行
- Flow需要自己計算,但是效能較好。同時由於是自己計算的,所以換行規則可以自己定。
層疊佈局
- Stack類似於Android裡面的FrameLayout、Web中的絕對定位
- Position 結合Stack使用可以實現絕對定位的效果
容器類元件
容器類與佈局類不同的地方在於一般容器類只接受一個子元件。用於修飾、變換、限制大小、設定邊距等等
Padding
跟移動端不一樣的是,flutter的Padding也是單獨抽出來的元件。格式如下
Padding(
//上下左右各新增16畫素補白
padding: EdgeInsets.all(16.0),
child: Column()
複製程式碼
限制類容器(ConstrainedBox、SizedBox等等)
用於限制元件的最大最小值,格式如下,一個是限制條件的屬性、一個是child放的內容
ConstrainedBox(
constraints: BoxConstraints(minWidth: 60.0, minHeight: 100.0), //父
child: UnconstrainedBox( //“去除”父級限制
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子
child: redBox,
),
)
)
複製程式碼
裝飾器DecoratedBox
類似於Android的shape,可以設定圓角、漸變、陰影等等。格式如下
DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(colors:[Colors.red,Colors.orange[700]]), //背景漸變
borderRadius: BorderRadius.circular(3.0), //3畫素圓角
boxShadow: [ //陰影
BoxShadow(
color:Colors.black54,
offset: Offset(2.0,2.0),
blurRadius: 4.0
)
]
),
child: Padding(padding: EdgeInsets.symmetric(horizontal: 80.0, vertical: 18.0),
child: Text("Login", style: TextStyle(color: Colors.white),),
)
)
複製程式碼
變換Transform
旋轉(rotate)、平移(translate)、縮(scale)
DecoratedBox(
decoration:BoxDecoration(color: Colors.red),
child: Transform.rotate(
angle:90 ,
child: Text("Hello world"),
),
);
複製程式碼
這種方式的旋轉不會執行build方法,所以背景不會改變效能也較好一些,我的理解是它僅僅改變了child的值,而如果要改變全部則使用RotatedBox
RotatedBox
DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
//將Transform.rotate換成RotatedBox
child: RotatedBox(
quarterTurns: 1, //旋轉90度(1/4圈)
child: Text("Hello world"),
),
複製程式碼
這裡沒有講的一個是透明度的變換
Opacity(
opacity: 0.1,
child: new Container(
width: 250.0,
height: 100.0,
decoration: new BoxDecoration(
backgroundColor: const Color(0xff000000),
),
)
複製程式碼
Container容器
這個容器比較強大的是它有padding跟margin以及變換等等不過底層也是用上面的控制元件實現的
Container({
this.alignment,
this.padding, //容器內補白,屬於decoration的裝飾範圍
Color color, // 背景色
Decoration decoration, // 背景裝飾
Decoration foregroundDecoration, //前景裝飾
double width,//容器的寬度
double height, //容器的高度
BoxConstraints constraints, //容器大小的限制條件
this.margin,//容器外補白,不屬於decoration的裝飾範圍
this.transform, //變換
this.child,
padding,
margin,
})
複製程式碼
Scaffold
該容器應該不陌生,專案一建立是就有。它是一個腳手架容器,就是很多容器都定義好了。只要跟著寫就有相應的效果,先看看程式碼
Scaffold(
appBar: AppBar( //導航欄
title: Text("App Name"),
actions: <Widget>[ //導航欄右側選單
IconButton(icon: Icon(Icons.share), onPressed: () {}),
],
),
drawer: new MyDrawer(), //抽屜
bottomNavigationBar: BottomNavigationBar( // 底部導航
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),
BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),
],
currentIndex: _selectedIndex,
fixedColor: Colors.blue,
onTap: _onItemTapped,
),
floatingActionButton: FloatingActionButton( //懸浮按鈕
child: Icon(Icons.add),
onPressed:_onAdd
),
);
複製程式碼
從Scaffold的下一級子控制元件來看,有導航欄(appbar)、側邊欄(drawer)、底部導航欄(bottomNavigationBar)、body(內容)
Appbar
Android裡面也有appbar,效果可以說是一樣的屬性也類似。
AppBar({
Key key,
this.leading, //導航欄最左側Widget,常見為抽屜選單按鈕或返回按鈕。
this.automaticallyImplyLeading = true, //如果leading為null,是否自動實現預設的leading按鈕
this.title,// 頁面標題
this.actions, // 導航欄右側選單
this.bottom, // 導航欄底部選單,通常為Tab按鈕組
this.elevation = 4.0, // 導航欄陰影
this.centerTitle, //標題是否居中
this.backgroundColor,
... //其它屬性見原始碼註釋
})
複製程式碼
可能用的比較多的是Appbar下的TabBar。與Android中的TabBar是類似的
bottom: TabBar(
controller: _tabController,
tabs: tabs.map((e) => Tab(text: e)).toList())
複製程式碼
drawer(側邊欄)、BottomNacigationBar(底部導航欄)
跟正常使用容器元件沒什麼差別
Drawer(
child: Container(
padding: EdgeInsets.zero,
children: <Widget>[
),
複製程式碼
//中間有圓弧的效果
bottomNavigationBar: BottomAppBar(
color: Colors.white,
shape: CircularNotchedRectangle(), // 底部導航欄打一個圓形的洞
child: Row(
children: [
IconButton(icon: Icon(Icons.home)),
SizedBox(), //中間位置空出
IconButton(icon: Icon(Icons.business)),
],
mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部導航欄橫向空間
),
)
複製程式碼
滾動類元件
SingleChildScrollView、ListView、GridView、ConstomScrollView以及滾動監聽ScrollController
SingleChildScrollView
SingleChildScrollView({
this.scrollDirection = Axis.vertical, //滾動方向,預設是垂直方向
this.reverse=false,//滾動方向是否反向
this.padding,//邊距bool primary,//這是否是與父控制元件關聯的主滾動檢視 應該是是否與父控制元件一起滑動 用來解決滑動衝突
this.physics,//滑動鬆手後的滑動方式
this.controller,
this.child,//子view
})
複製程式碼
Scrollbar(
child: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Center(
child: Column(
//動態建立一個List<Widget>
children: str.split("")
//每一個字母都用一個Text顯示,字型為原來的兩倍
.map((c) => Text(c, textScaleFactor: 2.0,))
.toList(),
),
),
)
複製程式碼
SingleChildScrollView只能接收一個元件,如果在外面新增Scrollbar的話會有滾動條,不加則沒有。
physics
- const ClampingScrollPhysics():Android下微光效果。
- const BouncingScrollPhysics():iOS下彈性效果。
ListView
ListView跟Android的Listview是類似的都是用於展示列表資料的
ListView({
...
//可滾動widget公共引數
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
EdgeInsetsGeometry padding,
//ListView各個建構函式的共同引數
double itemExtent,
bool shrinkWrap = false,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
double cacheExtent,
//子widget列表
List<Widget> children = const <Widget>[],
})
複製程式碼
- 如果資料不多,直接用
ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(20.0),
children: <Widget>[
const Text('列表資料'),
],
);
複製程式碼
- 如果資料多,為了方便使用builder或者separated
- separated 跟builder類似只是可以方便定義分割線
class ListViewDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
//下劃線widget預定義以供複用。
Widget divider1=Divider(color: Colors.blue,);
Widget divider2=Divider(color: Colors.green);
return ListView.separated(
itemCount: 100,
//列表項構造器
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text("$index"));
},
//分割器構造器
separatorBuilder: (BuildContext context, int index) {
return index%2==0?divider1:divider2;
},
);
}
}
複製程式碼
GridView
網格佈局同樣與Android裡面的類似。用法也與ListView大致相同
class _GridViewLayoutState extends State<GridViewLayout> {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, //橫軸三個子widget
childAspectRatio: 1.0 //寬高比為1
),
children:<Widget>[
Icon(Icons.ac_unit),
Icon(Icons.airport_shuttle),
Icon(Icons.all_inclusive),
Icon(Icons.beach_access),
Icon(Icons.cake),
Icon(Icons.free_breakfast)
]
);
}
}
複製程式碼
不想以後看回來東西太多,點到為止。網格佈局只能實現規則的網格。如果要實現瀑布流效果。有個開源的庫github.com/letsar/flut…
CustomScrollView
用於把滾動控制元件放在同一個容器上做到滾動效果一致的膠水控制元件。它的子控制元件有對應的滾動控制元件實現Sliver
Sliver
Sliver有細片、小片之意,在Flutter中,Sliver通常指具有特定滾動效果的可滾動塊。可滾動widget,如ListView、GridView等都有對應的Sliver實現如SliverList、SliverGrid等貼上一段md效果的程式碼,感覺效果還不錯
import 'package:flutter/material.dart';
class CustomScrollViewTestRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
//因為本路由沒有使用Scaffold,為了讓子級Widget(如Text)使用
//Material Design 預設的樣式風格,我們使用Material作為本路由的根。
return Material(
child: CustomScrollView(
slivers: <Widget>[
//AppBar,包含一個導航欄
SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: const Text('Demo'),
background: Image.asset(
"./images/avatar.png", fit: BoxFit.cover,),
),
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: new SliverGrid( //Grid
gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //Grid按兩列顯示
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
//建立子widget
return new Container(
alignment: Alignment.center,
color: Colors.cyan[100 * (index % 9)],
child: new Text('grid item $index'),
);
},
childCount: 20,
),
),
),
//List
new SliverFixedExtentList(
itemExtent: 50.0,
delegate: new SliverChildBuilderDelegate(
(BuildContext context, int index) {
//建立列表項
return new Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: new Text('list item $index'),
);
},
childCount: 50 //50個列表項
),
),
],
),
);
}
}
複製程式碼
滾動監聽
前端開發應該都熟悉,在flutter一般監聽程式碼會放在initState中。因為initState不會多次執行。
ScrollController _controller = new ScrollController();
bool showToTopBtn = false; //是否顯示“返回到頂部”按鈕
@override
void initState() {
//監聽滾動事件,列印滾動位置
_controller.addListener(() {
print(_controller.offset); //列印滾動位置
if (_controller.offset < 1000 && showToTopBtn) {
setState(() {
showToTopBtn = false;
});
} else if (_controller.offset >= 1000 && showToTopBtn == false) {
setState(() {
showToTopBtn = true;
});
}
});
}
複製程式碼
功能型元件
在flutter中,一切皆元件。所謂的功能型元件其實指的是如事件監聽、資料儲存等。這種跟介面不是很想關,所以分開理解
1.導航返回攔截WillPopScope
有時候點選兩次過快時可能是誤點需要做處理,就用到這個。
WillPopScope(
onWillPop: () async {
if (_lastPressedAt == null ||
DateTime.now().difference(_lastPressedAt) > Duration(seconds: 1)) {
//兩次點選間隔超過1秒則重新計時
_lastPressedAt = DateTime.now();
return false;
}
return true;
},
child: Container(
alignment: Alignment.center,
child: Text("1秒內連續按兩次返回鍵退出"),
)
);
複製程式碼
onWillPop是一個回撥函式,當使用者點選返回按鈕時呼叫(包括導航返回按鈕及Android物理返回按鈕),該回撥需要返回一個Future物件,如果返回的Future最終值為false時,則當前路由不出棧(不會返回),最終值為true時,當前路由出棧退出。我們需要提供這個回撥來決定是否退出。
2.資料共享InheritedWidget
感覺就是全域性變數
didChangeDependencies這個回撥函式當數值變化時會回撥。如果封裝一下即可實現切換語言和主題等等。具體例子看book.flutterchina.club/chapter7/in…。
3.主題Theme
主題有些程式碼中會用到,記錄一下不會陌生
ThemeData({
Brightness brightness, //深色還是淺色
MaterialColor primarySwatch, //主題顏色樣本,見下面介紹
Color primaryColor, //主色,決定導航欄顏色
Color accentColor, //次級色,決定大多數Widget的顏色,如進度條、開關等。
Color cardColor, //卡片顏色
Color dividerColor, //分割線顏色
ButtonThemeData buttonTheme, //按鈕主題
Color cursorColor, //輸入框游標顏色
Color dialogBackgroundColor,//對話方塊背景顏色
String fontFamily, //文字字型
TextTheme textTheme,// 字型主題,包括標題、body等文字樣式
IconThemeData iconTheme, // Icon的預設樣式
TargetPlatform platform, //指定平臺,應用特定平臺控制元件風格
...
})
複製程式碼