該文章只是本身這兩天看電子書的總結,若是想看一手知識的能夠轉到電子書book.flutterchina.club/chapter7/th…。前端開發無非就兩步第一是佈局、第二是請求數據刷新界面,這兩天看完感受至少寫一些簡單的界面是沒問題了。剩下的請求以及一些第三方庫有時間再繼續。對於沒有打過Flutter代碼的朋友我建議看完至少還要本身動手打一下,不用全打,由於打跟看是兩回事,有時候看懂了打的時候仍是要看一下。html
動態組件,後期須要改變顯示狀態的須要用動態組件前端
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Container();
}
}
複製代碼
@override
void initState() {
_controller = AnimationController(vsync: this);
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
...
複製代碼
靜態組件定義後不會再改變,通常不多用到。git
Widget(組件)是Element(元素)的封裝,flutter最後渲染的是Element。不涉及到原理時瞭解一下便可github
分爲四種,分別是基礎類組件、佈局類組件、容器類組件、滾動類組件bash
基礎組件分爲**文本(Text)、圖片(Image)、按鈕(xxButton)、輸入框(TextField)、單選框(Switch)與複選框(CheckBox)、表單(Form)**等等。基本格式爲:app
//文本
Text("Hello world! I'm Jack. "*4,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
複製代碼
//按鈕的種類有不少
RaisedButton(
child: Text("normal"),
onPressed: () => {},
);
複製代碼
能夠發現格式都是相似的,具體不一一講述,屬性類型跟移動端的控件是相似的。惟一須要看的是表單組件less
表單:爲了方便上傳數據時不用對每一個輸入框都作判斷。flutter提供了表單組件。async
簡單理解:表單給每一個輸入框提供一個判斷是否驗證同的屬性validator。而且提供提交按鈕一個出發的方法觸發回調具體代碼能夠下面地址的介紹 book.flutterchina.club/chapter3/in…ide
水平線性佈局(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的線性佈局同樣
例以下面的代碼會按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,
),
),
],
)
複製代碼
容器類與佈局類不一樣的地方在於通常容器類只接受一個子組件。用於修飾、變換、限制大小、設置邊距等等
跟移動端不同的是,flutter的Padding也是單獨抽出來的組件。格式以下
Padding(
//上下左右各添加16像素補白
padding: EdgeInsets.all(16.0),
child: Column()
複製代碼
用於限制組件的最大最小值,格式以下,一個是限制條件的屬性、一個是child放的內容
ConstrainedBox(
constraints: BoxConstraints(minWidth: 60.0, minHeight: 100.0), //父
child: UnconstrainedBox( //「去除」父級限制
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: 90.0, minHeight: 20.0),//子
child: redBox,
),
)
)
複製代碼
相似於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),),
)
)
複製代碼
旋轉(rotate)、平移(translate)、縮(scale)
DecoratedBox(
decoration:BoxDecoration(color: Colors.red),
child: Transform.rotate(
angle:90 ,
child: Text("Hello world"),
),
);
複製代碼
這種方式的旋轉不會執行build方法,因此背景不會改變性能也較好一些,個人理解是它僅僅改變了child的值,而若是要改變所有則使用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),
),
)
複製代碼
這個容器比較強大的是它有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(
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(內容)
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(
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({
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的話會有滾動條,不加則沒有。
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('列表數據'),
],
);
複製代碼
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;
},
);
}
}
複製代碼
網格佈局一樣與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…
用於把滾動控件放在同一個容器上作到滾動效果一致的膠水控件。它的子控件有對應的滾動控件實現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中,一切皆組件。所謂的功能型組件其實指的是如事件監聽、數據存儲等。這種跟界面不是很想關,因此分開理解
有時候點擊兩次過快時多是誤點須要作處理,就用到這個。
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時,當前路由出棧退出。咱們須要提供這個回調來決定是否退出。
感受就是全局變量
didChangeDependencies這個回調函數當數值變化時會回調。若是封裝一下便可實現切換語言和主題等等。具體例子看book.flutterchina.club/chapter7/in…。
主題有些代碼中會用到,記錄一下不會陌生
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, //指定平臺,應用特定平臺控件風格
...
})
複製代碼