什麼是 Widget
Flutter
的核心設計思想便是一切即 Widget
,在 Flutter
的世界裡,包括views
、view
、 controllers
、layouts
等在內的概念都建立在 Widget
之上,可以理解成原生的View
。
lib/main.dart
是程式的主入口
//導包
import 'package:flutter/material.dart';
//程式入口
void main() => runApp(MyApp());
//相當於主頁面
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Home Page'),
);
}
}
複製程式碼
可以看到,這個返回了一個MaterialApp
的Widget
,作為程式的主介面。
父級元件
Widget | 介紹 |
---|---|
StatelessWidget | 無狀態widget,類似靜態頁面,不與使用者互動 |
StatefulWidget | 有狀態widget,可以改變,可以與使用者互動 |
基礎元件
Widget | 介紹 |
---|---|
MaterialApp | 一般用作APP頂層的主頁入口,可配置主題,多語言,路由等 |
Scaffold | 一般使用者頁面的承載Widget,包含appbar、snackbar、drawer等material design的設定。 |
Appbar | 一般用於Scaffold的appbar ,內有標題,二級頁面返回按鍵等,當然不止這些,tabbar等也會需要它 |
Text | 顯示文字,類似於TextView |
Image | 顯示圖片,可以載入本地資源、file、網路圖片、記憶體圖片 |
TextField | 輸入框,類似於EditText |
Text
Text(
//文字
'我是Text我是Text我是Text我是Text我是Text我是Text我是Text我是Text我是Text我是Text',
//超出螢幕 clip裁剪,fade漸隱,ellipsis省略號
overflow: TextOverflow.ellipsis,
//對齊方式
textAlign: TextAlign.center,
//文字方向
textDirection: TextDirection.rtl,
//樣式
style: TextStyle(
color: Colors.lightBlue,
fontSize: 14,
fontStyle: FontStyle.italic,
backgroundColor: Colors.black87,
//none無文字裝飾,lineThrough刪除線,overline文字上面顯示線,underline文字下面顯示線
decoration: TextDecoration.lineThrough,
//字母間隙
letterSpacing: 10,
),
)
複製程式碼
Image
方法 | 作用 |
---|---|
Image.asset 、 Image(image: new AssetImage() |
載入本地圖片 |
Image.file 、 Image(image: new FileImage() |
file |
Image.memory 、 Image(image: new MemoryImage() |
載入記憶體byte陣列 |
Image.network 、 Image(image: new NetworkImage() |
載入網路圖片 |
其中,asset
首先需要在根目錄下建立images
資料夾,然後在pubspec.yaml
檔案中新增引用才能使用
flutter:
uses-material-design: true
assets:
- images/ic_launcher.png
複製程式碼
Image.asset(
'images/ic_launcher.png',
width: 100,
height: 100,
fit: BoxFit.fitHeight,
),
Image(
image: new NetworkImage(
'http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg'),
width: 100,
height: 100,
fit: BoxFit.cover,
)
複製程式碼
TextField
TextField(
//鍵盤輸入方式
keyboardType: TextInputType.number,
decoration: InputDecoration(
//提示文字
hintText: '請輸入手機號',
//內容邊距
contentPadding: EdgeInsets.all(10),
//提示文字樣式
hintStyle:
TextStyle(color: Colors.deepOrangeAccent, fontSize: 18),
//邊框
border: OutlineInputBorder(
//圓角
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(color: Colors.deepOrange),
),
),
//文字改變時呼叫
onChanged: (String content) {
print("content=" + content);
},
//游標顏色
cursorColor: Colors.deepOrangeAccent,
//游標圓角
cursorRadius: Radius.circular(4),
//游標寬度
cursorWidth: 2,
)
複製程式碼
佈局型
Widget | 多個子Widget | 介紹 |
---|---|---|
Container | 否 | 預設充滿,包含了padding、margin、color、寬高、decoration 等配置 |
Padding | 否 | 用於設定padding,對,你沒有猜錯,絕大部分Widget是沒有padding屬性的 |
Center | 否 | 用於居中顯示 |
Column | 是 | 垂直佈局,類似於LinearLayout 的orientation="vertical" |
Row | 是 | 水平佈局,類似於LinearLayout 的orientation="horizontal" |
Stack | 是 | 類似於relativeLayout 或者FrameLayout |
ListView | 是 | 類似於ListView或者RecyclerView |
Container
Padding
Column
Row
class ContainerDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ContainerDemo'),
),
body: Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.only(left: 10, right: 20),
// color: Colors.orangeAccent,
decoration: new BoxDecoration(
//設定了decoration的color,就不能設定Container的color,否則會報錯
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(10)),
border: new Border.all(width: 10, color: Colors.blue)),
child: Column(
//Column 垂直方向,Row 水平方向
mainAxisAlignment: MainAxisAlignment.center,
//max相當於match_parent,min相當於wrap_content
mainAxisSize: MainAxisSize.max,
verticalDirection: VerticalDirection.up,
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
margin: EdgeInsets.all(20),
color: Colors.black54,
child: Text(
'1111111',
),
),
Text(
'111FFFFg',
style: TextStyle(
fontSize: 18,
backgroundColor: Colors.black54,
),
),
Text('222'),
Text('333'),
],
),
),
);
}
}
複製程式碼
Stack
Stack
類似於relativeLayout 或者FrameLayout,有2種定位方式
1.alignment
作用於是全部的子Widget
class StackDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Stack(
textDirection: TextDirection.ltr,
//以整個元件的中心為座標原點,x、y 偏移量取值範圍為 [-1,1],
// 如果 x 的偏移量大於 0,則表示向右偏移,小於 0 則向左偏移;
// 如果 y 軸的偏移量大於 0 則向下偏移,小於 0 則向上偏移。
// alignment: Alignment(0, 0),
//AlignmentDirectional.topStart:垂直靠頂部水平靠左對齊
//AlignmentDirectional.topCenter:垂直靠頂部水平居中對齊
//AlignmentDirectional.topEnd:垂直靠頂部水平靠右對齊
//AlignmentDirectional.centerStart:垂直居中水平靠左對齊
//AlignmentDirectional.center:垂直和水平居中都對齊
//AlignmentDirectional.centerEnd:垂直居中水平靠右對齊
//AlignmentDirectional.bottomStart:垂直靠底部水平靠左對齊
//AlignmentDirectional.bottomCenter:垂直靠底部水平居中對齊
//AlignmentDirectional.bottomEnd:垂直靠底部水平靠右對齊
alignment: AlignmentDirectional.centerEnd,
children: <Widget>[
Container(
color: Colors.black54,
child: Text(
'1111111',
),
),
Text('111FFFFg',
style: TextStyle(fontSize: 18, backgroundColor: Colors.black54)),
Text('22222222')
],
),
);
}
}
複製程式碼
2.Positioned
只能控制單個Widget
,主要有left
、right
、top
、bottom
、width
、height
幾個屬性,分別表示距左、右、上、下的邊距,長度和寬度
注意:
1)left
、right
並存時,left
生效;top
、bottom
並存時,top
生效
2)left
、right
、width
不能並存,top
、bottom
、height
不能並存,會報錯
class StackDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
Container(
color: Colors.black54,
child: Text(
'1111111',
),
),
Positioned(
top: 100,
child: Text(
'111FFFFg',
style: TextStyle(
fontSize: 18,
backgroundColor: Colors.black54,
),
),
),
Positioned(
child: Text('22222222'),
right: 10,
top: 200,
)
],
),
);
}
}
複製程式碼
ListView
主要有3種構造方式
ListView.builder
ListView.separated
ListView.custom
1.ListView.builder
使用自帶的item
--ListTile
class ListViewDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListViewDemo'),
centerTitle: true,
),
body: Container(
color: Colors.white,
child: ListView.builder(
//item
itemBuilder: (context, index) {
return ListTile(
//前置圖示
leading: new Icon(Icons.list),
title: new Text('標題'),
subtitle: new Text('副標題'),
//後置圖示
trailing: new Icon(Icons.arrow_forward_ios),
//內容邊距
contentPadding: EdgeInsets.all(10),
);
},
//數量
itemCount: 10,
//內容適配
shrinkWrap: true,
//內邊距
padding: EdgeInsets.only(left: 10),
//是否倒敘
reverse: false,
//item 高度 讓item載入更加高效
itemExtent: 50,
//滑動方式
//AlwaysScrollableScrollPhysics() 總是可以滑動
//NeverScrollableScrollPhysics禁止滾動
//BouncingScrollPhysics 內容超過一屏 上拉有回彈效果
//ClampingScrollPhysics 包裹內容 不會有回彈
physics: BouncingScrollPhysics(),
//預載入
cacheExtent: 10,
),
),
);
}
}
複製程式碼
2.ListView.separated
相當於原生中的多型別,核心是separatorBuilder
,與itemBuilder
是一同渲染的,可以用它來實現分割線
ListView.separated(
itemBuilder: (context, index) {
return ListTile(
//前置圖示
leading: new Icon(Icons.list),
title: new Text('標題'),
subtitle: new Text('副標題'),
//後置圖示
trailing: new Icon(Icons.arrow_forward_ios),
//內容邊距
contentPadding: EdgeInsets.all(10),
);
},
separatorBuilder: (context, index) {
return Divider(
color: Colors.black45,
height: 10,
//左邊縮排
indent: 50,
);
},
itemCount: 20,
)
複製程式碼
3.ListView.custom
前2種方式是此方式的快捷方式,雖然不常用,但還是要了解下
ListView.custom(
childrenDelegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
//前置圖示
leading: new Icon(Icons.list),
title: new Text('標題 custom'),
subtitle: new Text('副標題 custom'),
//後置圖示
trailing: new Icon(Icons.arrow_forward_ios),
//內容邊距
contentPadding: EdgeInsets.all(10),
);
}),
)
複製程式碼
效果同方式1
示例
模擬器上間隔線顯示有問題,真機正常
ListViewDemo
相關程式碼
class ListViewDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListViewDemo'),
centerTitle: true,
),
body: Container(
color: Colors.white,
child: ListView.separated(
itemBuilder: ((context, index) {
return MoveItem();
}),
separatorBuilder: (context, index) {
return Divider(
color: Colors.black45,
height: 10,
);
},
itemCount: 10,
),
),
);
}
}
複製程式碼
MoveItem
相關程式碼
class MoveItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(4),
child: Row(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.network(
'http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg',
width: 100,
height: 150,
fit: BoxFit.fill,
),
),
Padding(
padding: EdgeInsets.only(left: 15),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(
'狄仁傑之四大天王',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
Text('豆瓣評分6.6'),
Text('型別:動作、驚悚、冒險'),
Text('導演:徐克'),
Row(
children: <Widget>[
Text('主演:'),
Padding(
padding: EdgeInsets.only(left: 10),
),
Container(
width: 40,
height: 40,
child: CircleAvatar(
backgroundImage: NetworkImage(
'http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg'),
),
),
Container(
width: 40,
height: 40,
margin: EdgeInsets.only(left: 10),
child: CircleAvatar(
backgroundImage: NetworkImage(
'http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg'),
),
),
Container(
width: 40,
height: 40,
margin: EdgeInsets.only(left: 10),
child: CircleAvatar(
backgroundImage: NetworkImage(
'http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg'),
),
),
],
),
],
),
],
),
);
}
}
複製程式碼