W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
我們之前已經(jīng)介紹過(guò)RotatedBox
,它可以旋轉(zhuǎn)子組件,但是它有兩個(gè)缺點(diǎn):一是只能將其子節(jié)點(diǎn)以90度的倍數(shù)旋轉(zhuǎn);二是當(dāng)旋轉(zhuǎn)的角度發(fā)生變化時(shí),旋轉(zhuǎn)角度更新過(guò)程沒(méi)有動(dòng)畫。
本節(jié)我們將實(shí)現(xiàn)一個(gè)TurnBox
組件,它不僅可以以任意角度來(lái)旋轉(zhuǎn)其子節(jié)點(diǎn),而且可以在角度發(fā)生變化時(shí)執(zhí)行一個(gè)動(dòng)畫以過(guò)渡到新狀態(tài),同時(shí),我們可以手動(dòng)指定動(dòng)畫速度。
TurnBox
的完整代碼如下:
import 'package:flutter/widgets.dart';
class TurnBox extends StatefulWidget {
const TurnBox({
Key key,
this.turns = .0, //旋轉(zhuǎn)的“圈”數(shù),一圈為360度,如0.25圈即90度
this.speed = 200, //過(guò)渡動(dòng)畫執(zhí)行的總時(shí)長(zhǎng)
this.child
}) :super(key: key);
final double turns;
final int speed;
final Widget child;
@override
_TurnBoxState createState() => new _TurnBoxState();
}
class _TurnBoxState extends State<TurnBox>
with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
lowerBound: -double.infinity,
upperBound: double.infinity
);
_controller.value = widget.turns;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return RotationTransition(
turns: _controller,
child: widget.child,
);
}
@override
void didUpdateWidget(TurnBox oldWidget) {
super.didUpdateWidget(oldWidget);
//旋轉(zhuǎn)角度發(fā)生變化時(shí)執(zhí)行過(guò)渡動(dòng)畫
if (oldWidget.turns != widget.turns) {
_controller.animateTo(
widget.turns,
duration: Duration(milliseconds: widget.speed??200),
curve: Curves.easeOut,
);
}
}
}
上面代碼中:
RotationTransition
和 child 來(lái)實(shí)現(xiàn)的旋轉(zhuǎn)效果。didUpdateWidget
中,我們判斷要旋轉(zhuǎn)的角度是否發(fā)生了變化,如果變了,則執(zhí)行一個(gè)過(guò)渡動(dòng)畫。
下面我們測(cè)試一下TurnBox
的功能,測(cè)試代碼如下:
import 'package:flutter/material.dart';
import '../widgets/index.dart';
class TurnBoxRoute extends StatefulWidget {
@override
_TurnBoxRouteState createState() => new _TurnBoxRouteState();
}
class _TurnBoxRouteState extends State<TurnBoxRoute> {
double _turns = .0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
TurnBox(
turns: _turns,
speed: 500,
child: Icon(Icons.refresh, size: 50,),
),
TurnBox(
turns: _turns,
speed: 1000,
child: Icon(Icons.refresh, size: 150.0,),
),
RaisedButton(
child: Text("順時(shí)針旋轉(zhuǎn)1/5圈"),
onPressed: () {
setState(() {
_turns += .2;
});
},
),
RaisedButton(
child: Text("逆時(shí)針旋轉(zhuǎn)1/5圈"),
onPressed: () {
setState(() {
_turns -= .2;
});
},
)
],
),
);
}
}
測(cè)試代碼運(yùn)行后效果如圖10-2所示:
當(dāng)我們點(diǎn)擊旋轉(zhuǎn)按鈕時(shí),兩個(gè)圖標(biāo)的旋轉(zhuǎn)都會(huì)旋轉(zhuǎn)1/5圈,但旋轉(zhuǎn)的速度是不同的,讀者可以自己運(yùn)行一下示例看看效果。
實(shí)際上本示例只組合了RotationTransition
一個(gè)組件,它是一個(gè)最簡(jiǎn)的組合類組件示例。另外,如果我們封裝的是StatefulWidget
,那么一定要注意在組件更新時(shí)是否需要同步狀態(tài)。比如我們要封裝一個(gè)富文本展示組件MyRichText
,它可以自動(dòng)處理url鏈接,定義如下:
class MyRichText extends StatefulWidget {
MyRichText({
Key key,
this.text, // 文本字符串
this.linkStyle, // url鏈接樣式
}) : super(key: key);
final String text;
final TextStyle linkStyle;
@override
_MyRichTextState createState() => _MyRichTextState();
}
接下來(lái)我們?cè)?code>_MyRichTextState中要實(shí)現(xiàn)的功能有兩個(gè):
TextSpan
緩存起來(lái);build
中返回最終的富文本樣式;
_MyRichTextState
實(shí)現(xiàn)的代碼大致如下:
class _MyRichTextState extends State<MyRichText> {
TextSpan _textSpan;
@override
Widget build(BuildContext context) {
return RichText(
text: _textSpan,
);
}
TextSpan parseText(String text) {
// 耗時(shí)操作:解析文本字符串,構(gòu)建出TextSpan。
// 省略具體實(shí)現(xiàn)。
}
@override
void initState() {
_textSpan = parseText(widget.text)
super.initState();
}
}
由于解析文本字符串,構(gòu)建出TextSpan
是一個(gè)耗時(shí)操作,為了不在每次 build 的時(shí)候都解析一次,所以我們?cè)?code>initState中對(duì)解析的結(jié)果進(jìn)行了緩存,然后再build
中直接使用解析的結(jié)果_textSpan
。這看起來(lái)很不錯(cuò),但是上面的代碼有一個(gè)嚴(yán)重的問(wèn)題,就是父組件傳入的text
發(fā)生變化時(shí)(組件樹結(jié)構(gòu)不變),那么MyRichText
顯示的內(nèi)容不會(huì)更新,原因就是initState
只會(huì)在 State 創(chuàng)建時(shí)被調(diào)用,所以在text
發(fā)生變化時(shí),parseText
沒(méi)有重新執(zhí)行,導(dǎo)致_textSpan
任然是舊的解析值。要解決這個(gè)問(wèn)題也很簡(jiǎn)單,我們只需添加一個(gè)didUpdateWidget
回調(diào),然后再里面重新調(diào)用parseText
即可:
@override
void didUpdateWidget(MyRichText oldWidget) {
if (widget.text != oldWidget.text) {
_textSpan = parseText(widget.text);
}
super.didUpdateWidget(oldWidget);
}
有些讀者可能會(huì)覺(jué)得這個(gè)點(diǎn)也很簡(jiǎn)單,是的,的確很簡(jiǎn)單,之所以要在這里反復(fù)強(qiáng)調(diào)是因?yàn)檫@個(gè)點(diǎn)在實(shí)際開發(fā)中很容易被忽略,它雖然簡(jiǎn)單,但卻很重要??傊?,當(dāng)我們?cè)?State 中會(huì)緩存某些依賴 Widget 參數(shù)的數(shù)據(jù)時(shí),一定要注意在組件更新時(shí)是否需要同步狀態(tài)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: