1.1:Widget的狀態(tài)
Widget的本身沒有可變狀態(tài)(所有字段都必須是final)括蝠。
如果您希望將一個widget擁有可變狀態(tài)没龙,請考慮使用 StatefulWidget憎瘸,每當(dāng)它被加載為元素并合并到渲染樹中時虫碉,會創(chuàng)建State對象(通過StatefulWidget.createState)寓免。
StatefulWidget和State苞俘,用于可以在其生命周期內(nèi)多次構(gòu)建的widget盹沈。
StatelessWidget,用于在給定配置和環(huán)境的狀態(tài)的下始終以相同方式構(gòu)建的widget吃谣。
1.2: StatelessWidget 無狀態(tài)組件
該類的本身非常簡潔,由于Widget有一個createElement抽象方法乞封,
StatelessWidget類中通過StatelessElement對象完成了該抽象方法,
所以StatelessWidget只需要關(guān)注build這個抽象方法即可。
初始項目中,MyApp是繼承了StatelessWidget岗憋,它的任務(wù)在于重寫build方法肃晚。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
1.3:StatefulWidget有狀態(tài)組件
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
//State對象中有一個泛型,從源碼中來看仔戈,該泛型值接受StatefulWidget关串,
//即有狀態(tài)組件類拧廊。State作為一個抽象類,存在一個build抽象方法來返回一個Widget對象
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),//widget屬性其實就是MyHomePage
),
//略...
);
}
}
Icon源碼
Icon類中主要做了四件事:
構(gòu)造函數(shù)--> 聲明屬性字段--> 實現(xiàn)build方法晋修,返回Widget對象-->debugFillProperties
class Icon extends StatelessWidget {
const Icon(
this.icon, {
Key key,
this.size,
this.color,
this.semanticLabel,
this.textDirection,
}) : super(key: key);
final IconData icon;
final double size;
final Color color;
final String semanticLabel;
final TextDirection textDirection;
@override
Widget build(BuildContext context) {
//暫略...
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
//暫略...
}
}
可以看出吧碾,構(gòu)造函數(shù)中有一個必須的參數(shù)icon,從定義中來看是一個IconData對象
注意:構(gòu)造函數(shù)用const關(guān)鍵字修飾,字段全被修飾為final飞蚓,這就意味著字段不可再修改滤港。
這也是被稱為無狀態(tài)的原因,一旦對象構(gòu)建完成趴拧,它就樣子就無法再被改變溅漾。
CheckBox源碼簡析
class Checkbox extends StatefulWidget {
const Checkbox({
Key key,
@required this.value,
this.tristate = false,
@required this.onChanged,
this.activeColor,
this.checkColor,
this.materialTapTargetSize,
}) : assert(tristate != null),
assert(tristate || value != null),
super(key: key);
final bool value;//是否選中
final ValueChanged<bool> onChanged;//點擊回調(diào)
final Color activeColor;//激活態(tài)框顏色
final Color checkColor;//激活態(tài)對勾顏色
final bool tristate;//三態(tài)
final MaterialTapTargetSize materialTapTargetSize;
static const double width = 18.0;
@override
_CheckboxState createState() => _CheckboxState();
}
- 1.構(gòu)造函數(shù)用const修飾,每行寫一個屬性
- 2.必須的屬性用@required注解
- 3.非空的屬性用assert斷言
- 4.字段全是final類型
_CheckboxState中的build方法返回_CheckboxRenderObjectWidget對象
CheckBox具體繪制邏輯及狀態(tài)改變,在_RenderCheckbox中實現(xiàn)
---->[flutter/packages/flutter/lib/src/material/checkbox.dart:140]----
class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
//略...
}
return _CheckboxRenderObjectWidget(
//略...
);
}
}
---->[flutter/packages/flutter/lib/src/material/checkbox.dart:168]----
class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
//略...
@override
_RenderCheckbox createRenderObject(BuildContext context) => _RenderCheckbox(
//略...
);
@override
void updateRenderObject(BuildContext context, _RenderCheckbox renderObject) {
//略...
}
_RenderCheckbox繼承自RenderToggleable著榴,可以重寫paint方法
這邊簡單看一下主要的邊框和對勾的繪制方法
// 可以看出畫筆的顏色是checkColor添履,以線條的形式
void _initStrokePaint(Paint paint) {
paint
..color = checkColor
..style = PaintingStyle.stroke
..strokeWidth = _kStrokeWidth;
}
//繪制邊線
void _drawBorder(Canvas canvas, RRect outer, double t, Paint paint) {
assert(t >= 0.0 && t <= 0.5);
final double size = outer.width;
// 當(dāng)t從0.0到1.0時,逐漸填充外部矩形脑又。
final RRect inner = outer.deflate(math.min(size / 2.0, _kStrokeWidth + size * t));
canvas.drawDRRect(outer, inner, paint);
}
//繪制對勾
void _drawCheck(Canvas canvas, Offset origin, double t, Paint paint) {
assert(t >= 0.0 && t <= 1.0);
final Path path = Path();
const Offset start = Offset(_kEdgeSize * 0.15, _kEdgeSize * 0.45);//起始偏移點
const Offset mid = Offset(_kEdgeSize * 0.4, _kEdgeSize * 0.7);//中間偏移點
const Offset end = Offset(_kEdgeSize * 0.85, _kEdgeSize * 0.25);//終止偏移點
if (t < 0.5) {//t<0.5時暮胧,繪制短邊
final double strokeT = t * 2.0;
final Offset drawMid = Offset.lerp(start, mid, strokeT);
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
path.lineTo(origin.dx + drawMid.dx, origin.dy + drawMid.dy);
} else {//t>0.5時,繪制長邊
final double strokeT = (t - 0.5) * 2.0;
final Offset drawEnd = Offset.lerp(mid, end, strokeT);
path.moveTo(origin.dx + start.dx, origin.dy + start.dy);
path.lineTo(origin.dx + mid.dx, origin.dy + mid.dy);
path.lineTo(origin.dx + drawEnd.dx, origin.dy + drawEnd.dy);
}
canvas.drawPath(path, paint);
}