項目案例 -- StatelessWidget
- 案例代碼如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage(),
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("商品列表"),
),
body: SFContentBody(),
);
}
}
class SFContentBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [
SFProductItem("Apple1","MacBook1","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
SFProductItem("Apple2","MacBook2","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
SFProductItem("Apple3","MacBook3","https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdl.bbs.9game.cn%2Fattachments%2Fforum%2F201507%2F29%2F154250no2g2zqiuiqvaiku.jpg&refer=http%3A%2F%2Fdl.bbs.9game.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1637072364&t=07974f838e1b49ecdecaba22f4af4fa2"),
],
);
}
}
class SFProductItem extends StatelessWidget {
final String title;
final String desc;
final String imageUrl;
final titleStyle = TextStyle(fontSize: 25,color: Colors.orange);
final descStyle = TextStyle(fontSize: 20,color: Colors.green);
//自定義構(gòu)造函數(shù)
SFProductItem(this.title,this.desc,this.imageUrl);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8), //設(shè)置內(nèi)邊距
decoration: BoxDecoration(
border: Border.all(
width: 5, //設(shè)置邊框的寬度
color: Colors.purple //設(shè)置邊框的額顏色
)
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,style: titleStyle),
SizedBox(height: 8),//設(shè)置間距
Text(desc,style: descStyle),
SizedBox(height: 8),
Image.network(imageUrl)
],
),
);
}
}
- 效果如下所示:

- 給widget添加Container的快捷鍵
Alt + Enter - widget之間
設(shè)置間距使用SizedBox(height: 8)
項目案例 -- StatefulWidget
-
StatefulWidget最大的特點是:StatefulWidget通過創(chuàng)建狀態(tài)類_SFHomeContentState,來管理自己的狀態(tài)數(shù)據(jù); - 案例代碼如下:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("商品列表")
),
body: SFHomeContent("上面是一個簡單的計數(shù)器")
);
}
}
class SFHomeContent extends StatefulWidget {
final String message;
SFHomeContent(this.message);
@override
State<StatefulWidget> createState() {
return _SFHomeContentState();
}
}
//
class _SFHomeContentState extends State<SFHomeContent>{
var _counter = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_getButtons(),
Text("當(dāng)前計數(shù): $_counter",style: TextStyle(fontSize: 20)),
Text("${widget.message}",style: TextStyle(fontSize: 18))
],
),
);
}
Widget _getButtons(){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text("+",style: TextStyle(fontSize: 20,color: Colors.white)),
color: Colors.pink,
onPressed: (){
print("點擊+");
setState(() {
_counter++;
});
},
),
RaisedButton(
child: Text("-",style: TextStyle(fontSize: 20,color: Colors.white)),
color: Colors.purple,
onPressed: (){
print("點擊-");
setState(() {
_counter--;
});
},
)
],
);
}
}
-
class _SFHomeContentState extends State<SFHomeContent>,Widget _getButtons()類名與方法名之前加下劃線表明屬于私有的; -
class _SFHomeContentState extends State<SFHomeContent>:_SFHomeContentState狀態(tài)用來管理SFHomeContent這個widget的狀態(tài)數(shù)據(jù)的,即_SFHomeContentState會綁定SFHomeContent,_SFHomeContentState中能通過widget屬性訪問SFHomeContent中的內(nèi)容; - 代碼運行效果:

StatefulWidget生命周期
- 所謂生命周期是指:目標(biāo)組件從創(chuàng)建到銷毀的整個過程,監(jiān)聽組件的生命周期以便在不同的時期執(zhí)行不同的邏輯;
- Flutter組件的生命周期:
-
StatelessWidget可以由父widget直接傳入值,調(diào)用build方法來創(chuàng)建,整個過程非常簡單,其生命周期,主要關(guān)注構(gòu)造函數(shù)與build方法; -
StatefulWidget需要通過State來管理其狀態(tài)數(shù)據(jù),并且監(jiān)聽狀態(tài)的改變重新build整個widget;
-
-
StatefulWidget生命周期的過程如下圖所示:
Snip20211018_14.png - 1.執(zhí)行
StatefulWidget的構(gòu)造函數(shù); - 2.執(zhí)行
StatefulWidget的createState方法,創(chuàng)建一個維護StatefulWidget的State對象; - 3.執(zhí)行
State的構(gòu)造方法; - 4.執(zhí)行
initState方法,我們通常在此方法中執(zhí)行一些數(shù)據(jù)初始化操作,或者發(fā)送網(wǎng)絡(luò)請求; - 5.
didChangeDependencies方法,在下面兩種情況下會調(diào)用:- 調(diào)用
initState時會調(diào)用; - 從其他對象中依賴一些數(shù)據(jù)發(fā)生改變時,比如InheritedWidget;
- 調(diào)用
- 6.執(zhí)行
build方法,渲染widget樹; - 7.當(dāng) 當(dāng)前的widget不再使用時,會調(diào)用
dispose方法進行銷毀; - 8.手動調(diào)用
setState方法,會根據(jù)最新的狀態(tài)數(shù)據(jù)來重新調(diào)用build方法,構(gòu)建對應(yīng)的widget; - 9.執(zhí)行
didUpdateWidget方法是在父widget觸發(fā)重建rebuild時,系統(tǒng)會調(diào)用didUpdateWidget方法; - 代碼案例驗證:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("商品列表")
),
body: SFHomeContent("上面是一個簡單的計數(shù)器")
);
}
}
class SFHomeContent extends StatefulWidget {
final String message;
SFHomeContent(this.message){
print("SFHomeContent 構(gòu)造方法");
}
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
var _counter = 0;
_SFHomeContentState(){
print("_SFHomeContentState 構(gòu)造方法");
}
@override
void initState() {
super.initState();
print("_SFHomeContentState initState");
}
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
child: Text("+",style: TextStyle(fontSize: 25,color: Colors.white)),
color: Colors.pinkAccent,
onPressed: (){
setState(() {
_counter++;
});
},
),
Text("${widget.message}",style: TextStyle(fontSize: 18))
],
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("_SFHomeContentState didChangeDependencies");
}
@override
void didUpdateWidget(SFHomeContent oldWidget) {
super.didUpdateWidget(oldWidget);
print("_SFHomeContentState didUpdateWidget");
}
-
@override
void dispose() {
super.dispose();
print("_SFHomeContentState dispose");
}
}
- 執(zhí)行結(jié)果如下:

-
StatefulWidget生命周期復(fù)雜版的過程如下圖所示:
Snip20211018_13.png mounded時State內(nèi)部設(shè)置的一個屬性,不需要我們手動進行修改的,其主要作用是記錄widget對應(yīng)的element是否為空;dirty state含義是臟的state,它實際是來標(biāo)記Element的,標(biāo)記為dirty的Element會等待下一次的重繪檢查,強制調(diào)用build方法來構(gòu)建我們的widget;clean state含義是干凈的state,它表示當(dāng)前build出來的widget,下一次重繪檢查不需要重新build;
Flutter的編程范式
- 命令式編程:就是一步步給計算機命令,告訴它我們想做什么事情;
- 聲明式編程:通常是描述目標(biāo)的性質(zhì),依賴哪些狀態(tài),并且當(dāng)依賴的狀態(tài)發(fā)生改變時,我們通過某些方式通知目標(biāo)做出響應(yīng),聲明式編程是依賴框架的;
- Flutter采用的是聲明式編程;
- 命令式編程的代碼案例:
final text = new Text();
var title = "Hello World";
text.setContent(title); //主動設(shè)置title
- 聲明式編程的代碼案例:
var title = "Hello World";
Text(title); //告訴Text內(nèi)部顯示的是title
基礎(chǔ)Widget
文本W(wǎng)idget
- 在Flutter中使用
Text組件來顯示文本;
普通文本
-
控制文本布局的參數(shù):在構(gòu)造函數(shù)中;-
textAlign:文本的對齊方式; -
textDirection:文本的排版方向; -
maxLines:最大顯示行數(shù); -
overflow:文本的截斷規(guī)則;
-
-
控制文本樣式的參數(shù):在構(gòu)造函數(shù)的參數(shù)style中;-
fontSize:文本大??; -
color:文本顏色; -
fontFamily:設(shè)置字體; -
shaow:文本陰影;
-
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return TextDemo();
}
}
class TextDemo extends StatelessWidget {
const TextDemo({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
"基礎(chǔ)widget \ndasd has發(fā)哈就困了打算看是的撒 \n但是開發(fā)雙卡雙待",
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 30,
color: Colors.red,
fontWeight: FontWeight.bold,
fontFamily: 'Courier'
),
);
}
}
富文本
- 富文本使用
Text.rich,其中必選參數(shù)傳入InlineSpan,是一個抽象類,我們需要傳入其子類即可,其子類有:-
TextSpan:顯示文本的; -
WidgetSpan:顯示圖片的; placeholderSpace
-
- 案例代碼如下:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return TextRichDemo();
}
}
class TextRichDemo extends StatelessWidget {
const TextRichDemo({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text.rich(
TextSpan(
children: [
TextSpan(text: "Hello World!!", style: TextStyle(fontSize: 20,color: Colors.green)),
TextSpan(text: "Hello iOS!!", style: TextStyle(fontSize: 20,color: Colors.red)),
WidgetSpan(child: Icon(Icons.favorite,color: Colors.red)),
TextSpan(text: "Hello Flutter!!", style: TextStyle(fontSize: 25,color: Colors.orange))
]
)
);
}
}
按鈕Widget
- 常見的按鈕類型有:
- RaisedButton
- FlatButton
- OutlineButton
- FloatingActionButton
RaisedButton
-
RaisedButton是帶有一定圓角和陰影以及灰色背景的按鈕,并且在點擊的時候有動畫效果,其構(gòu)造函數(shù)如下:
class RaisedButton extends MaterialButton {
/// Create a filled button.
///
/// The [autofocus] and [clipBehavior] arguments must not be null.
/// Additionally, [elevation], [hoverElevation], [focusElevation],
/// [highlightElevation], and [disabledElevation] must be non-negative, if
/// specified.
const RaisedButton({
Key key,
@required VoidCallback onPressed,
VoidCallback onLongPress,
ValueChanged<bool> onHighlightChanged,
MouseCursor mouseCursor,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
Brightness colorBrightness,
double elevation,
double focusElevation,
double hoverElevation,
double highlightElevation,
double disabledElevation,
EdgeInsetsGeometry padding,
VisualDensity visualDensity,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
FocusNode focusNode,
bool autofocus = false,
MaterialTapTargetSize materialTapTargetSize,
Duration animationDuration,
Widget child,
})
FlatButton
-
FlatButton:默認沒有背景顏色,不帶陰影,高亮狀態(tài)下有背景,構(gòu)造函數(shù)如下:
class FlatButton extends MaterialButton {
/// Create a simple text button.
///
/// The [autofocus] and [clipBehavior] arguments must not be null.
const FlatButton({
Key key,
@required VoidCallback onPressed,
VoidCallback onLongPress,
ValueChanged<bool> onHighlightChanged,
MouseCursor mouseCursor,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color disabledColor,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
Brightness colorBrightness,
EdgeInsetsGeometry padding,
VisualDensity visualDensity,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
FocusNode focusNode,
bool autofocus = false,
MaterialTapTargetSize materialTapTargetSize,
@required Widget child,
})
OutlineButton
-
OutlineButton:帶有邊框的按鈕,構(gòu)造函數(shù)如下:
class OutlineButton extends MaterialButton {
/// Create an outline button.
///
/// The [highlightElevation] argument must be null or a positive value
/// and the [autofocus] and [clipBehavior] arguments must not be null.
const OutlineButton({
Key key,
@required VoidCallback onPressed,
VoidCallback onLongPress,
MouseCursor mouseCursor,
ButtonTextTheme textTheme,
Color textColor,
Color disabledTextColor,
Color color,
Color focusColor,
Color hoverColor,
Color highlightColor,
Color splashColor,
double highlightElevation,
this.borderSide,
this.disabledBorderColor,
this.highlightedBorderColor,
EdgeInsetsGeometry padding,
VisualDensity visualDensity,
ShapeBorder shape,
Clip clipBehavior = Clip.none,
FocusNode focusNode,
bool autofocus = false,
Widget child,
})
FloatingActionButton
-
FloatingActionButton:懸浮按鈕,構(gòu)造函數(shù)如下:
class FloatingActionButton extends StatelessWidget {
/// Creates a circular floating action button.
///
/// The [mini] and [clipBehavior] arguments must not be null. Additionally,
/// [elevation], [highlightElevation], and [disabledElevation] (if specified)
/// must be non-negative.
const FloatingActionButton({
Key key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.focusColor,
this.hoverColor,
this.splashColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation,
this.focusElevation,
this.hoverElevation,
this.highlightElevation,
this.disabledElevation,
@required this.onPressed,
this.mouseCursor,
this.mini = false,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.materialTapTargetSize,
this.isExtended = false,
})
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return ButtonWidget();
}
}
class ButtonWidget extends StatelessWidget {
const ButtonWidget({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
child: Text("RaisedButton"),
textColor: Colors.white,
onPressed: (){
print("RaisedButton click");
},
),
FlatButton(
child: Text("FlatButton"),
onPressed: (){
print("FlatButton click");
},
),
OutlineButton(
child: Text("OutlineButton"),
onPressed: (){
print("OutlineButton click");
},
),
FloatingActionButton(
child: Icon(Icons.add),
onPressed: (){
print("FloatingActionButton click");
},
),
//自定義button
FlatButton(
color: Colors.green,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.favorite,color: Colors.red),
Text("喜歡作者")
],
),
onPressed: (){
print("FlatButton");
},
)
],
);
}
}
- 執(zhí)行結(jié)果如下圖所示:

-
ShapeBorder:設(shè)置邊框,是一個抽象類,其常用子類為RoundedRectangleBorder - 1.默認情況下Button上下會有一定的間距,可通過
materialTapTargetSize屬性,去除間距; - 2.Button在沒有文本的情況下,會顯示默認尺寸大小,可通過
ButtonTheme屬性,自定義任意尺寸大?。?/li> - 3.可通過
padding屬性,去除內(nèi)容邊距; - 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
//`ButtonTheme`屬性,自定義任意尺寸大小
//通過`padding`屬性,去除內(nèi)容邊距
class _SFHomeContentState extends State<SFHomeContent>{
final imageUrl = "";
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Column(
children: [
ButtonTheme(
minWidth: 30,
height: 15,
child: FlatButton(
color: Colors.red,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
child: Text("FlatButton"),
textColor: Colors.white,
padding: EdgeInsets.all(0),
onPressed: (){
print("click FlatButton");
},
),
)
],
);
}
}
class ButtonExtra01 extends StatelessWidget {
const ButtonExtra01({
Key key,
}) : super(key: key);
//`materialTapTargetSize`屬性,去除間距
@override
Widget build(BuildContext context) {
return Column(
children: [
FlatButton(
color: Colors.red,
child: Text("FlatButton"),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
textColor: Colors.white,
onPressed: (){
print("click FlatButton");
},
),
FlatButton(
color: Colors.red,
child: Text("FlatButton"),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
textColor: Colors.white,
onPressed: (){
print("click FlatButton");
},
)
],
);
}
}
圖片Widget
- Image:圖片組件,其構(gòu)造函數(shù)如下:
class Image extends StatefulWidget {
/// Creates a widget that displays an image.
///
/// To show an image from the network or from an asset bundle, consider using
/// [new Image.network] and [new Image.asset] respectively.
///
/// The [image], [alignment], [repeat], and [matchTextDirection] arguments
/// must not be null.
///
/// Either the [width] and [height] arguments should be specified, or the
/// widget should be placed in a context that sets tight layout constraints.
/// Otherwise, the image dimensions will change as the image is loaded, which
/// will result in ugly layout changes.
///
/// Use [filterQuality] to change the quality when scaling an image.
/// Use the [FilterQuality.low] quality setting to scale the image,
/// which corresponds to bilinear interpolation, rather than the default
/// [FilterQuality.none] which corresponds to nearest-neighbor.
///
/// If [excludeFromSemantics] is true, then [semanticLabel] will be ignored.
const Image({
Key key,
@required this.image,
this.frameBuilder,
this.loadingBuilder,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.isAntiAlias = false,
this.filterQuality = FilterQuality.low,
})
-
@required this.image:必選參數(shù),參數(shù)類型為ImageProvider是一個抽象類,常見子類有:NetworkImage與AssetImage
NetworkImage加載網(wǎng)絡(luò)圖片
- 案例代碼如下:
class NetWorkImage extends StatelessWidget {
const NetWorkImage({
Key key,
@required this.imageUrl,
}) : super(key: key);
final String imageUrl;
@override
Widget build(BuildContext context) {
return Image(
image: NetworkImage(imageUrl),
width: 200,
height: 200,
fit: BoxFit.fill,
alignment: Alignment.bottomLeft,
repeat: ImageRepeat.repeatY,
);
}
}
-
fit:圖片的顯示模式,等價于OC中的contentMode -
alignment:圖片的對齊方式; -
repeat:當(dāng)圖片未填充滿控件時,可設(shè)置重復(fù)填充;
AssetImage加載本地圖片
- 首先在Flutter項目中引入圖片資源;
- 然后在
pub spec.yaml文件中進行配置,然后執(zhí)行flutter pub get命令; - 最后使用圖片;
- 流程如下:

- 代碼案例:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
final imageUrl = "";
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Image(
image: AssetImage("asset/images/180.png"),
);
}
}
- 案例代碼:
class ImageExtra extends StatelessWidget {
const ImageExtra({
Key key,
@required this.imageUrl,
}) : super(key: key);
final String imageUrl;
@override
Widget build(BuildContext context) {
return FadeInImage(
placeholder: AssetImage("asset/images/180.png"), //設(shè)置占位圖
image: NetworkImage(imageUrl),//設(shè)置網(wǎng)絡(luò)圖片
fadeOutDuration: Duration(milliseconds: 1),//淡入淡出的動畫效果
fadeInDuration: Duration(milliseconds: 1),
);
}
}
-
placeholder屬性可設(shè)置占位圖片;
Icon圖標(biāo)
- 1.Icon可設(shè)置字體圖片與圖片圖標(biāo);
- 2.字體圖片與圖片圖標(biāo)是矢量圖,放大不會失真;
- 3.圖標(biāo)可設(shè)置顏色;
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
final imageUrl = "";
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
//1.Icon可設(shè)置字體圖片與圖片圖標(biāo)
//2.字體圖片與圖片圖標(biāo)是矢量圖,放大不會失真
//3.圖標(biāo)可設(shè)置顏色
//4.下面三種寫法等價
return Icon(Icons.pets,size: 200,color: Colors.red);
// return Icon(IconData(0xe91d, fontFamily: 'MaterialIcons'),size: 200,color: Colors.red);
// return Text("\ue91d",style: TextStyle(fontSize: 100,fontFamily: 'MaterialIcons',color: Colors.red));
}
}
輸入框Widget -- TextField
- TextField的構(gòu)造函數(shù):
class TextField extends StatefulWidget {
/// Creates a Material Design text field.
///
/// If [decoration] is non-null (which is the default), the text field requires
/// one of its ancestors to be a [Material] widget.
///
/// To remove the decoration entirely (including the extra padding introduced
/// by the decoration to save space for the labels), set the [decoration] to
/// null.
///
/// The [maxLines] property can be set to null to remove the restriction on
/// the number of lines. By default, it is one, meaning this is a single-line
/// text field. [maxLines] must not be zero.
///
/// The [maxLength] property is set to null by default, which means the
/// number of characters allowed in the text field is not restricted. If
/// [maxLength] is set a character counter will be displayed below the
/// field showing how many characters have been entered. If the value is
/// set to a positive integer it will also display the maximum allowed
/// number of characters to be entered. If the value is set to
/// [TextField.noMaxLength] then only the current length is displayed.
///
/// After [maxLength] characters have been input, additional input
/// is ignored, unless [maxLengthEnforced] is set to false. The text field
/// enforces the length with a [LengthLimitingTextInputFormatter], which is
/// evaluated after the supplied [inputFormatters], if any. The [maxLength]
/// value must be either null or greater than zero.
///
/// If [maxLengthEnforced] is set to false, then more than [maxLength]
/// characters may be entered, and the error counter and divider will
/// switch to the [decoration.errorStyle] when the limit is exceeded.
///
/// The text cursor is not shown if [showCursor] is false or if [showCursor]
/// is null (the default) and [readOnly] is true.
///
/// The [selectionHeightStyle] and [selectionWidthStyle] properties allow
/// changing the shape of the selection highlighting. These properties default
/// to [ui.BoxHeightStyle.tight] and [ui.BoxWidthStyle.tight] respectively and
/// must not be null.
///
/// The [textAlign], [autofocus], [obscureText], [readOnly], [autocorrect],
/// [maxLengthEnforced], [scrollPadding], [maxLines], [maxLength],
/// [selectionHeightStyle], [selectionWidthStyle], and [enableSuggestions]
/// arguments must not be null.
///
/// See also:
///
/// * [maxLength], which discusses the precise meaning of "number of
/// characters" and how it may differ from the intuitive meaning.
const TextField({
Key key,
this.controller,
this.focusNode,
this.decoration = const InputDecoration(),
TextInputType keyboardType,
this.textInputAction,
this.textCapitalization = TextCapitalization.none,
this.style,
this.strutStyle,
this.textAlign = TextAlign.start,
this.textAlignVertical,
this.textDirection,
this.readOnly = false,
ToolbarOptions toolbarOptions,
this.showCursor,
this.autofocus = false,
this.obscuringCharacter = '?',
this.obscureText = false,
this.autocorrect = true,
SmartDashesType smartDashesType,
SmartQuotesType smartQuotesType,
this.enableSuggestions = true,
this.maxLines = 1,
this.minLines,
this.expands = false,
this.maxLength,
this.maxLengthEnforced = true,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.selectionHeightStyle = ui.BoxHeightStyle.tight,
this.selectionWidthStyle = ui.BoxWidthStyle.tight,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection = true,
this.onTap,
this.mouseCursor,
this.buildCounter,
this.scrollController,
this.scrollPhysics,
this.autofillHints,
})
- 案例代碼如下:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SFHomePage()
);
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("基礎(chǔ)widget")
),
body: SFHomeContent()
);
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent>{
final usernameTextEditController = TextEditingController();
final passwordTextEditController = TextEditingController();
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Theme(
data: ThemeData(
primaryColor: Colors.red
),
child: Padding(
padding: EdgeInsets.all(8.0),
child:Column(
children: [
TextField(
controller: usernameTextEditController,
decoration: InputDecoration(
labelText: "username",
icon: Icon(Icons.people),
hintText: "請輸入用戶名",
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.red[100],
),
onChanged: (value){
print("onChanged:$value");
},
onSubmitted: (value){
print("onSubmitted:$value");
},
),
SizedBox(height: 10),
TextField(
controller: passwordTextEditController,
decoration: InputDecoration(
labelText: "password",
icon: Icon(Icons.lock),
hintText: "請輸入密碼",
border: InputBorder.none,
filled: true,
fillColor: Colors.red[100],
),
onChanged: (value){
print("onChanged:$value");
},
onSubmitted: (value){
print("onSubmitted:$value");
},
),
SizedBox(height: 20),
Container(
width: 300,
height: 35,
child: FlatButton(
child: Text("登 錄",style: TextStyle(fontSize: 20,color: Colors.white)),
color: Colors.blue,
onPressed: (){
print("login");
final username = usernameTextEditController.text;
final password = passwordTextEditController.text;
print("username = $username,password = $password");
},
),
)
],
)
)
);
}
}
- 運行結(jié)果如下:

-
Theme:設(shè)置整個UI的主題風(fēng)格; - 給目標(biāo)組件
設(shè)置寬高,通常是在目標(biāo)組件外面再包裝一層Container,然后設(shè)置width與height屬性; -
onChanged:監(jiān)聽輸入框文本變化的回調(diào); -
onSubmitted:監(jiān)聽輸入框提交時的回調(diào); - 在登錄按鈕的點擊回調(diào)中,獲取輸入框的文本是通過給輸入框綁定一個
TextEditingController,然后通過TextEditingController.text獲取輸入框的文本;
單子布局組件
- 常見的單子布局組件有:Align,Center,Padding,Container
Align組件
- Align:即對齊;
- Center組件:是繼承自Align組件的,本質(zhì)上Align與Center等價;
- 案例代碼如下:
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Container(
color: Colors.red,
child: Align(
child: Icon(Icons.pets,size: 50),
alignment: Alignment(0,0),
widthFactor: 5,
heightFactor: 3,
),
);
}
}
- Align默認占據(jù)整個屏幕大??;
- widthFactor與heightFactor 是child的尺寸的倍數(shù)大小 作為Align的尺寸大??;
Padding組件
- Padding:主要用來設(shè)置子Widget到父Widget的邊距;
- 其構(gòu)造函數(shù):
const Padding({
Key key,
@required this.padding,
Widget child,
})
- 案例代碼:
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
print("_SFHomeContentState build");
return Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text("日你啊滿滿的個單",style: TextStyle(fontSize: 20,backgroundColor: Colors.red)),
),
Padding(
padding: const EdgeInsets.only(bottom: 5),
child: Text("日你啊滿滿的個單",style: TextStyle(fontSize: 20,backgroundColor: Colors.red)),
),
Text("日你啊滿滿的個單",style: TextStyle(fontSize: 20,backgroundColor: Colors.red))
],
);
}
}
Container組件
- Container:顧名思義為容器組件,
- 其構(gòu)造函數(shù)如下:
Container({
Key key,
this.alignment,
this.padding,
this.color,
this.decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
this.clipBehavior = Clip.none,
})
- 案例代碼:
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
return Container(
// color: Colors.red,
width: 200,
height: 200,
child: Text("這是一個文本",style: TextStyle(color: Colors.white),),
alignment: Alignment(0,0),
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(
width: 5,
color: Colors.purple
),
borderRadius: BorderRadius.circular(100),
boxShadow: [
BoxShadow(color: Colors.orange,offset: Offset(10,10),spreadRadius: 5,blurRadius: 10),
BoxShadow(color: Colors.blue,offset: Offset(-10,10),spreadRadius: 5,blurRadius: 10)
]
),
);
}
}
-
padding:設(shè)置容器內(nèi)間距; -
margin:設(shè)置容器外間距; -
decoration:設(shè)置裝飾,內(nèi)部包含邊框,圓角,陰影等等; - 效果圖:

多子布局組件
- 常見的多子布局組件有:Flex,Row,Column
Flex組件
- Row組件與Column組件都是繼承自Flex組件;
- 主軸:mainAxis即主方向的軸;
- 交叉軸:crosssAxis
Row組件
Row的主軸方向為:水平方向,交叉軸為豎直方向;
-
Row在主軸方向即
水平方向上占據(jù)比較大的空間;- 若在水平方向上希望包裹內(nèi)容,那么設(shè)置
mainAxisSize = min;
- 若在水平方向上希望包裹內(nèi)容,那么設(shè)置
Row在交叉軸方向即
豎直方向上包裹內(nèi)容;-
mainAxisAlignment:主軸方向上的對齊方式-
start:主軸的開始位置依次擺放元素; -
end:主軸的結(jié)束位置依次擺放元素; -
center:主軸中心點對齊; -
spaceBetween:左右間距為0,其他元素之間間距平分; -
spaceAround:左右間距是其他元素之間間距的一半; -
spaceEvenly:所有間距平分;
-
-
crossAxisAlignment:交叉方向上的對齊方式-
start:交叉軸的開始位置依次擺放元素; -
end:交叉軸的結(jié)束位置依次擺放元素; -
center:交叉軸中心點對齊; -
textBaseline:基線對齊(必須有文本內(nèi)容才有效果); -
stretch:將所有子元素,拉伸到最大;
-
案例代碼:mainAxisSize = min
class _SFHomeContentState extends State<SFHomeContent>{
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.bug_report),
Text("bug")
],
),
);
}
}
- 案例代碼:
class RowDemo extends StatelessWidget {
const RowDemo({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
textBaseline: TextBaseline.alphabetic,
children: [
Container(width: 80,height: 60,color: Colors.red),
Container(width: 80,height: 80,color: Colors.green),
Container(width: 80,height: 100,color: Colors.blue),
Container(width: 80,height: 120,color: Colors.orange),
],
);
}
}
Flexible與Expanded
-
Flexible:空間拉伸與收縮,占滿空間; -
Expanded:空間拉伸與收縮,占滿空間,與Flexible效果等價,區(qū)別在與使用Expanded不用再設(shè)置fit: FlexFit.tight,因為Expanded在內(nèi)部已經(jīng)設(shè)置了,所以其使用更廣; - 空間剩余時,會進行拉伸占滿剩余空間;
- 空間不足時,會收縮子widget,占滿空間;
class RowDemo2 extends StatelessWidget {
const RowDemo2({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
textBaseline: TextBaseline.alphabetic,
children: [
Flexible(
child: Container(width: 80,height: 60,color: Colors.red),
fit: FlexFit.tight,
),
Expanded(
child: Container(width: 80,height: 80,color: Colors.green),
),
Container(width: 80,height: 100,color: Colors.blue),
Container(width: 80,height: 120,color: Colors.orange),
],
);
}
}
這里補充個小知識點: 如果children中出現(xiàn)2個Flexible或者Expand,那么剩余空間會根據(jù)兩個Expand的Flex比例來填充,如果沒有設(shè)置Flex,那么默認的Flex值為1
Column組件
- Column的主軸方向為:豎直方向,交叉軸為水平方向;
- 與Row組件類似,只是子組件在主軸方向即豎直方向上排列;
Stack組件
- 在開發(fā)中,我們多個組件可能需要
重疊顯示,在Android中使用Frame布局實現(xiàn),而在Flutter中我們需要使用層疊布局Stack來實現(xiàn); - 內(nèi)部子組件默認從左上角開始排布;
- Stack默認的大小是包裹內(nèi)容的尺寸大?。?/li>
- 參數(shù)alignment:表示從什么位置開始排布所有的子組件;
- 參數(shù)fit:expand表示將所有子組件拉伸到最大;
- 參數(shù)overflow:對于超出父組件區(qū)域的子組件的處理
- flip:超出部分被裁減;
- visible:超出部分依然顯示; - 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")), body: SFHomeContent());
}
}
class SFHomeContent extends StatefulWidget {
@override
State<StatefulWidget> createState() {
print("createState");
return _SFHomeContentState();
}
}
class _SFHomeContentState extends State<SFHomeContent> {
@override
Widget build(BuildContext context) {
return StackDemo1();
}
}
class StackDemo2 extends StatelessWidget {
const StackDemo2({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 200,
child: Image.asset("asset/images/180.png", fit: BoxFit.fill),
width: double.infinity),
Positioned(
child: Container(
child: Row(
children: [
Text("這是一行文本", style: TextStyle(fontSize: 17, color: Colors.white)),
IconButton(icon: Icon(Icons.favorite),color: Colors.white,onPressed: (){
})
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
color: Color.fromARGB(150, 0, 0, 0),
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 8),
),
left: 0,
right: 0,
bottom: 0,
)
],
);
}
}
class StackDemo1 extends StatelessWidget {
const StackDemo1({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
Image.asset("asset/images/180.png"),
Positioned(
child: Container(
width: 100,
height: 100,
color: Colors.green,
),
right: 0,
),
Positioned(
child: Text("推客圖標(biāo)", style: TextStyle(fontSize: 20)),
left: 0,
)
],
alignment: AlignmentDirectional.bottomStart,
overflow: Overflow.visible,
);
}
}
- StackDemo1與StackDemo2的效果圖如下:


- 滾動組件有
ListView,GridView,Sliver
ListView
- ListView創(chuàng)建的方式通常有三種,分別為
ListView(),ListView.builder(),ListView.separated()
ListView創(chuàng)建方式
- 第一種方式:
ListView() - 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")), body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
itemExtent: 100,//設(shè)置Item的高度
children: List.generate(100, (index) {
return ListTile(
leading: Icon(Icons.people),
trailing: Icon(Icons.delete),
title: Text("聯(lián)系人${index+1}"),
subtitle: Text("聯(lián)系人電話號碼:19991604555"),
);
}),
);
}
}
-
ListTile組件就是ListView的Item; -
itemExtent:設(shè)置Item的高度; - 效果圖如下:

通過
ListView()創(chuàng)建,會一次性創(chuàng)建100個Item,這樣性能比較差,其適用于Item個數(shù)確定,且數(shù)量較少的情況下才會采用;第二種方式:
ListView.builder()ListView.builder()不會一次性創(chuàng)建所有Item,而是需要展示的Item才會去創(chuàng)建,性能較好;案例代碼如下:
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 100,
itemExtent: 50,
itemBuilder: (BuildContext ctx, int index){
return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
}
);
}
}
- 第三種方式:
ListView.separated() - 案例代碼如下:
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.separated(
itemBuilder: (BuildContext ctx, int index){
return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
},
//分割線
separatorBuilder: (BuildContext ctx,int index){
return Divider(color: Colors.red,indent: 20,endIndent: 20,thickness: 5);
},
itemCount: 100
);
}
}
-
itemBuilder:創(chuàng)建Item; -
separatorBuilder:創(chuàng)建分割線;
GridView
- GridView的創(chuàng)建方式有:
GridView(),GridView.builder() - 案例代碼一:
GridView()+SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8
),
children:
List.generate(100, (index) {
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
);
}),
);
}
}
-
SliverGridDelegateWithFixedCrossAxisCount:交叉軸方向上item數(shù)量固定,其寬度根據(jù)屏幕的寬度與item的數(shù)量進行計算;-
crossAxisCount:item的個數(shù); -
childAspectRatio:item的寬高比; -
crossAxisSpacing:交叉軸方向上 item之間的間距; -
mainAxisSpacing:主軸方向上 item之間的間距;
-
案例代碼二:
GridView()+SliverGridDelegateWithMaxCrossAxisExtent
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.5
),
children: List.generate(100, (index) {
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
})
);
}
}
-
SliverGridDelegateWithMaxCrossAxisExtent:交叉軸方向上的設(shè)置item寬度,個數(shù)不固定;-
maxCrossAxisExtent:item的最大寬度;
-
案例代碼三:
GridView.builder()+SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8
),
itemBuilder: (BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
}
);
}
}
Slivers -- CustomScrollView
CustomScrollView:自定義滾動組件,需要傳入Slivers即Sliver數(shù)組,我們知道ListView與GridView都是繼承自BoxScrollView,而BoxScrollView是一個抽象類,從源碼來看ListView與GridView在察創(chuàng)建的過程中都需要執(zhí)行buildSlivers方法,其內(nèi)部調(diào)用buildChildLayout方法,這是一個抽象方法,分別由ListView與GridView來實現(xiàn),最終提供一個Sliver數(shù)組,其中ListView提供的Sliver為SliverFixedExtentList,GridView提供的Sliver為SliverGrid案例代碼:單個Sliver
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverDemo1();
}
}
class SliverDemo1 extends StatelessWidget {
const SliverDemo1({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverSafeArea(
sliver: SliverPadding(
padding: EdgeInsets.all(8),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 1.5
),
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
},
childCount: 100
),
),
),
)
],
);
}
}
自定義CustomScrollView,需傳入Slivers數(shù)組,這里傳入的Sliver為
SliverGrid;參數(shù)
gridDelegate:是提供布局信息;參數(shù)
delegate:是提供item組件,類型為SliverChildDelegate;-
SliverChildDelegate是抽象類,其作用是用來創(chuàng)建滾動組件的item,其有兩個子類分別為
SliverChildListDelegate與SliverChildBuilderDelegate-
SliverChildListDelegate:性能較差,item一次性創(chuàng)建所有; -
SliverChildBuilderDelegate:性能較好,創(chuàng)建需要展示的item;
-
-
SafeArea與SliverSafeArea的區(qū)別:- SafeArea:安全區(qū)域,讓目標(biāo)組件在安全區(qū)域內(nèi)顯示;
- SliverSafeArea:給Sliver設(shè)置安全區(qū)域,且在滾動時可以在非安全區(qū)域內(nèi)滾動,而SafeArea不可以;
SliverPadding:是Sliver自己的設(shè)置內(nèi)邊距的組件;案例代碼:多個Sliver
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent());
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
title: Text("Hello World!!",style: TextStyle(fontSize: 25),),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
childAspectRatio: 2.5
),
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return Container(
color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
);
},
childCount: 10
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext ctx,int index){
return ListTile(
leading: Icon(Icons.people),
title: Text("聯(lián)系人$index"),
);
},
childCount: 20
),
)
],
);
}
}
-
slivers數(shù)組中傳入了SliverAppBar,SliverGrid與SliverList三種類型的Sliver,效果如下:
image.png
滾動組件的監(jiān)聽
- 滾動組件的監(jiān)聽通常有兩種方式,分別為
controller與NotificationListener
controller監(jiān)聽
- 可以設(shè)置默認值offset;
- 監(jiān)聽滾動,也可以監(jiān)聽滾動的位置;
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatefulWidget {
@override
_SFHomePageState createState() => _SFHomePageState();
}
class _SFHomePageState extends State<SFHomePage> {
ScrollController controller = ScrollController(initialScrollOffset: 300);
bool isShowFloatButton = false;
@override
void initState() {
super.initState();
controller.addListener(() {
print("監(jiān)聽到滾動: ${controller.offset}");
setState(() {
isShowFloatButton = controller.offset >= 1000;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent(controller),
floatingActionButton: isShowFloatButton ? FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: (){
controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
},
) : null,
);
}
}
class SFHomeContent extends StatelessWidget {
final ScrollController controller;
SFHomeContent(this.controller);
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: controller,
itemBuilder: (BuildContext ctx, int index) {
return ListTile(
leading: Icon(Icons.people),
title: Text("聯(lián)系人$index"),
);
},
itemCount: 100,
);
}
}
- 右下角懸浮按鈕,當(dāng)前滾動偏移量>=1000時顯示;
NotificationListener監(jiān)聽
- 案例代碼:
import 'package:flutter/material.dart';
void main() => runApp(SFMyApp());
class SFMyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: SFHomePage());
}
}
class SFHomePage extends StatefulWidget {
@override
_SFHomePageState createState() => _SFHomePageState();
}
class _SFHomePageState extends State<SFHomePage> {
bool isShowFloatButton = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("基礎(chǔ)widget")),
body: SFHomeContent(),
floatingActionButton: isShowFloatButton
? FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
},
)
: null,
);
}
}
class SFHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification){
if(notification is ScrollStartNotification){
print("開始滾動");
}else if (notification is ScrollUpdateNotification){
print("正在滾動 -- 總區(qū)域:${notification.metrics.maxScrollExtent} 當(dāng)前位置: ${notification.metrics.pixels}");
}else if (notification is ScrollEndNotification){
print("結(jié)束滾動");
}
return true;
},
child: ListView.builder(
itemBuilder: (BuildContext ctx, int index) {
return ListTile(
leading: Icon(Icons.people),
title: Text("聯(lián)系人$index"),
);
},
itemCount: 100,
),
);
}
}
總結(jié)
現(xiàn)在對以上出現(xiàn)的所有Widget做一個總結(jié),主要針對繼承關(guān)系:
-
以
StatelessWidget為中心的Widget組件有如下:
image.png -
以
StatefulWidget為中心的Widget組件有如下:
image.png -
以
RenderObjectWidget為中心的Widget組件有如下:
image.png





