1.動(dòng)畫(huà)簡(jiǎn)介
Animation、Curve、Controller、Tween這四個(gè)角色,它們一起配合來(lái)完成一個(gè)完整動(dòng)畫(huà)
- Animation
Animation是抽象類,和UI渲染沒(méi)有關(guān)系,功能是保存動(dòng)畫(huà)的插值和狀態(tài);比較常用的是Animation
addListener:幀監(jiān)聽(tīng)器中最常見(jiàn)的行為是改變狀態(tài)后調(diào)用setState()來(lái)觸發(fā)UI重建
addStatusListener:動(dòng)畫(huà)開(kāi)始、結(jié)束、正向或反向(見(jiàn)AnimationStatus定義)時(shí)會(huì)調(diào)用狀態(tài)改變的監(jiān)聽(tīng)器。 - Curve
動(dòng)畫(huà)過(guò)程可以是勻速的、勻加速的或者先加速后減速等。Flutter中通過(guò)Curve(曲線)來(lái)描述動(dòng)畫(huà)過(guò)程,我們把勻速動(dòng)畫(huà)稱為線性的(Curves.linear),而非勻速動(dòng)畫(huà)稱為非線性的。
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.easeIn);
Curves曲線 動(dòng)畫(huà)過(guò)程
linear 勻速的
decelerate 勻減速
ease 開(kāi)始加速,后面減速
easeIn 開(kāi)始慢,后面快
easeOut 開(kāi)始快,后面慢
easeInOut 開(kāi)始慢,然后加速,最后再減速
也可以自定義一個(gè)正弦曲線:
class ShakeCurve extends Curve {
@override
double transform(double t) {
return math.sin(t * math.PI * 2);
}
}
- AnimationController
AnimationController用于控制動(dòng)畫(huà),它包含動(dòng)畫(huà)的啟動(dòng)forward()、停止stop() 、反向播放 reverse()等
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 2000),
lowerBound: 10.0,
upperBound: 20.0,
vsync: this
);
- Tween
默認(rèn)AnimationController對(duì)象值的范圍是[0.0,1.0],但可以使用Tween來(lái)改變范圍
例如,像下面示例,Tween生成[-200.0,0.0]的值
final Tween doubleTween = Tween<double>(begin: -200.0, end: 0.0);
完整示例:
以下示例構(gòu)建了一個(gè)控制器、一條曲線和一個(gè) Tween:
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut);
Animation<int> alpha = IntTween(begin: 0, end: 255).animate(curve);
線性插值lerp函數(shù):
//a 為起始顏色,b為終止顏色,t為當(dāng)前動(dòng)畫(huà)的進(jìn)度[0,1]
Color.lerp(a, b, t);
2.動(dòng)畫(huà)實(shí)現(xiàn)和監(jiān)聽(tīng)
AnimatedBuilder可以封裝常見(jiàn)的過(guò)渡效果來(lái)復(fù)用動(dòng)畫(huà)
class GrowTransition extends StatelessWidget {
const GrowTransition({Key? key,
required this.animation,
this.child,
}) : super(key: key);
final Widget? child;
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: animation,
builder: (BuildContext context, child) {
return SizedBox(
height: animation.value,
width: animation.value,
child: child,
);
},
child: child,
),
);
}
}
...
Widget build(BuildContext context) {
return GrowTransition(
child: Image.asset("images/avatar.png"),
animation: animation,
);
}
Flutter中正是通過(guò)這種方式封裝了很多動(dòng)畫(huà),如:FadeTransition、ScaleTransition、SizeTransition等,很多時(shí)候都可以復(fù)用這些預(yù)置的過(guò)渡類
Animation的addStatusListener()方法來(lái)添加動(dòng)畫(huà)狀態(tài)改變監(jiān)聽(tīng)器。Flutter中,有四種動(dòng)畫(huà)狀態(tài),在AnimationStatus枚舉類中定義
dismissed 動(dòng)畫(huà)在起始點(diǎn)停止
forward 動(dòng)畫(huà)正在正向執(zhí)行
reverse 動(dòng)畫(huà)正在反向執(zhí)行
completed 動(dòng)畫(huà)在終點(diǎn)停止
3. 自定義路由切換動(dòng)畫(huà)
無(wú)論是MaterialPageRoute、CupertinoPageRoute,還是PageRouteBuilder,它們都繼承自PageRoute
MaterialPageRoute組件,它可以使用和平臺(tái)風(fēng)格一致的路由切換動(dòng)畫(huà),如在iOS上會(huì)左右滑動(dòng)切換,而在Android上會(huì)上下滑動(dòng)切換
CupertinoPageRoute是Cupertino組件庫(kù)提供的iOS風(fēng)格的路由切換組件,它實(shí)現(xiàn)的就是左右滑動(dòng)切換。
自定義切換動(dòng)畫(huà)優(yōu)先考慮使用PageRouteBuilder
Navigator.push(
context,
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 500), //動(dòng)畫(huà)時(shí)間為500毫秒
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
return FadeTransition(
//使用漸隱漸入過(guò)渡,
opacity: animation,
child: PageB(), //路由B
);
},
),
);
但是有些時(shí)候PageRouteBuilder是不能滿足需求的,例如在應(yīng)用過(guò)渡動(dòng)畫(huà)時(shí)我們需要讀取當(dāng)前路由的一些屬性,這時(shí)就只能通過(guò)繼承PageRoute的方式了
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
//當(dāng)前路由被激活,是打開(kāi)新路由
if(isActive) {
return FadeTransition(
opacity: animation,
child: builder(context),
);
}else{
//是返回,則不應(yīng)用過(guò)渡動(dòng)畫(huà)
return Padding(padding: EdgeInsets.zero);
}
}
4. Hero動(dòng)畫(huà)
在Flutter中將圖片從一個(gè)路由“飛”到另一個(gè)路由稱為hero動(dòng)畫(huà)
例如A路由有一個(gè)圓形用戶頭像,點(diǎn)擊后跳到B路由,可以查看大圖
class HeroAnimationRouteA extends StatelessWidget {
const HeroAnimationRouteA({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.topCenter,
child: Column(
children: <Widget>[
InkWell(
child: Hero(
tag: "avatar", //唯一標(biāo)記,前后兩個(gè)路由頁(yè)Hero的tag必須相同
child: ClipOval(
child: Image.asset(
"imgs/avatar.png",
width: 50.0,
),
),
),
onTap: () {
//打開(kāi)B路由
Navigator.push(context, PageRouteBuilder(
pageBuilder: (
BuildContext context,
animation,
secondaryAnimation,
) {
return FadeTransition(
opacity: animation,
child: Scaffold(
appBar: AppBar(
title: const Text("原圖"),
),
body: const HeroAnimationRouteB(),
),
);
},
));
},
),
const Padding(
padding: EdgeInsets.only(top: 8.0),
child: Text("點(diǎn)擊頭像"),
)
],
),
);
}
}
class HeroAnimationRouteB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Hero(
tag: "avatar", //唯一標(biāo)記,前后兩個(gè)路由頁(yè)Hero的tag必須相同
child: Image.asset("imgs/avatar.png"),
),
);
}
}
實(shí)現(xiàn) Hero 動(dòng)畫(huà)只需要用Hero組件將要共享的 widget 包裝起來(lái),并提供一個(gè)相同的 tag 即可
5.交織動(dòng)畫(huà)
比如:有一個(gè)柱狀圖,需要在高度增長(zhǎng)的同時(shí)改變顏色,等到增長(zhǎng)到最大高度后,我們需要在X軸上平移一段距離??梢园l(fā)現(xiàn)上述場(chǎng)景在不同階段包含了多種動(dòng)畫(huà),要實(shí)現(xiàn)這種效果,使用交織動(dòng)畫(huà)(Stagger Animation)會(huì)非常簡(jiǎn)單
實(shí)現(xiàn)步驟:
1.要?jiǎng)?chuàng)建交織動(dòng)畫(huà),需要使用多個(gè)動(dòng)畫(huà)對(duì)象(Animation)。
2.一個(gè)AnimationController控制所有的動(dòng)畫(huà)對(duì)象。
3.給每一個(gè)動(dòng)畫(huà)對(duì)象指定時(shí)間間隔(Interval)
class StaggerAnimation extends StatelessWidget {
StaggerAnimation({
Key? key,
required this.controller,
}) : super(key: key) {
//高度動(dòng)畫(huà)
height = Tween<double>(
begin: .0,
end: 300.0,
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.0, 0.6, //間隔,前60%的動(dòng)畫(huà)時(shí)間
curve: Curves.ease,
),
),
);
color = ColorTween(
begin: Colors.green,
end: Colors.red,
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.0, 0.6, //間隔,前60%的動(dòng)畫(huà)時(shí)間
curve: Curves.ease,
),
),
);
padding = Tween<EdgeInsets>(
begin: const EdgeInsets.only(left: .0),
end: const EdgeInsets.only(left: 100.0),
).animate(
CurvedAnimation(
parent: controller,
curve: const Interval(
0.6, 1.0, //間隔,后40%的動(dòng)畫(huà)時(shí)間
curve: Curves.ease,
),
),
);
}
late final Animation<double> controller;
late final Animation<double> height;
late final Animation<EdgeInsets> padding;
late final Animation<Color?> color;
Widget _buildAnimation(BuildContext context, child) {
return Container(
alignment: Alignment.bottomCenter,
padding: padding.value,
child: Container(
color: color.value,
width: 50.0,
height: height.value,
),
);
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
builder: _buildAnimation,
animation: controller,
);
}
}
StaggerAnimation中定義了三個(gè)動(dòng)畫(huà),分別是對(duì)Container的height、color、padding屬性設(shè)置的動(dòng)畫(huà),然后通過(guò)Interval來(lái)為每個(gè)動(dòng)畫(huà)指定在整個(gè)動(dòng)畫(huà)過(guò)程中的起始點(diǎn)和終點(diǎn)
使用:
class StaggerRoute extends StatefulWidget {
@override
_StaggerRouteState createState() => _StaggerRouteState();
}
class _StaggerRouteState extends State<StaggerRoute>
with TickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
}
_playAnimation() async {
try {
//先正向執(zhí)行動(dòng)畫(huà)
await _controller.forward().orCancel;
//再反向執(zhí)行動(dòng)畫(huà)
await _controller.reverse().orCancel;
} on TickerCanceled {
//捕獲異常??赡馨l(fā)生在組件銷毀時(shí),計(jì)時(shí)器會(huì)被取消。
}
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => _playAnimation(),
child: Text("start animation"),
),
Container(
width: 300.0,
height: 300.0,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.1),
border: Border.all(
color: Colors.black.withOpacity(0.5),
),
),
//調(diào)用我們定義的交錯(cuò)動(dòng)畫(huà)Widget
child: StaggerAnimation(controller: _controller),
),
],
),
);
}
}
6.動(dòng)畫(huà)切換
AnimatedSwitcher組件,它定義了一種通用的UI切換抽象
const AnimatedSwitcher({
Key? key,
this.child,
required this.duration, // 新child顯示動(dòng)畫(huà)時(shí)長(zhǎng)
this.reverseDuration,// 舊child隱藏的動(dòng)畫(huà)時(shí)長(zhǎng)
this.switchInCurve = Curves.linear, // 新child顯示的動(dòng)畫(huà)曲線
this.switchOutCurve = Curves.linear,// 舊child隱藏的動(dòng)畫(huà)曲線
this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, // 動(dòng)畫(huà)構(gòu)建器
this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder, //布局構(gòu)建器
})
當(dāng)AnimatedSwitcher的 child 發(fā)生變化時(shí)(類型或 Key 不同),舊 child 會(huì)執(zhí)行隱藏動(dòng)畫(huà),新 child 會(huì)執(zhí)行執(zhí)行顯示動(dòng)畫(huà)。究竟執(zhí)行何種動(dòng)畫(huà)效果則由transitionBuilder參數(shù)決定,該參數(shù)接受一個(gè)AnimatedSwitcherTransitionBuilder類型的 builder
typedef AnimatedSwitcherTransitionBuilder =
Widget Function(Widget child, Animation<double> animation);
defaultTransitionBuilder :默認(rèn)AnimatedSwitcher會(huì)對(duì)新舊child執(zhí)行“漸隱”和“漸顯”動(dòng)畫(huà)
現(xiàn)一個(gè)計(jì)數(shù)器,然后在每一次自增的過(guò)程中,舊數(shù)字執(zhí)行縮小動(dòng)畫(huà)隱藏,新數(shù)字執(zhí)行放大動(dòng)畫(huà)顯示
import 'package:flutter/material.dart';
class AnimatedSwitcherCounterRoute extends StatefulWidget {
const AnimatedSwitcherCounterRoute({Key key}) : super(key: key);
@override
_AnimatedSwitcherCounterRouteState createState() => _AnimatedSwitcherCounterRouteState();
}
class _AnimatedSwitcherCounterRouteState extends State<AnimatedSwitcherCounterRoute> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
//執(zhí)行縮放動(dòng)畫(huà)
return ScaleTransition(child: child, scale: animation);
},
child: Text(
'$_count',
//顯示指定key,不同的key會(huì)被認(rèn)為是不同的Text,這樣才能執(zhí)行動(dòng)畫(huà)
key: ValueKey<int>(_count),
style: Theme.of(context).textTheme.headline4,
),
),
ElevatedButton(
child: const Text('+1',),
onPressed: () {
setState(() {
_count += 1;
});
},
),
],
),
);
}
}
Flutter SDK中還提供了一個(gè)AnimatedCrossFade組件,它也可以切換兩個(gè)子元素,切換過(guò)程執(zhí)行漸隱漸顯的動(dòng)畫(huà),和AnimatedSwitcher不同的是AnimatedCrossFade是針對(duì)兩個(gè)子元素,而AnimatedSwitcher是在一個(gè)子元素的新舊值之間切換
示例:實(shí)現(xiàn)各種“滑動(dòng)出入動(dòng)畫(huà)”便非常容易,只需給direction傳遞不同的方向值即可
class SlideTransitionX extends AnimatedWidget {
SlideTransitionX({
Key? key,
required Animation<double> position,
this.transformHitTests = true,
this.direction = AxisDirection.down,
required this.child,
}) : super(key: key, listenable: position) {
switch (direction) {
case AxisDirection.up:
_tween = Tween(begin: const Offset(0, 1), end: const Offset(0, 0));
break;
case AxisDirection.right:
_tween = Tween(begin: const Offset(-1, 0), end: const Offset(0, 0));
break;
case AxisDirection.down:
_tween = Tween(begin: const Offset(0, -1), end: const Offset(0, 0));
break;
case AxisDirection.left:
_tween = Tween(begin: const Offset(1, 0), end: const Offset(0, 0));
break;
}
}
final bool transformHitTests;
final Widget child;
final AxisDirection direction;
late final Tween<Offset> _tween;
@override
Widget build(BuildContext context) {
final position = listenable as Animation<double>;
Offset offset = _tween.evaluate(position);
if (position.status == AnimationStatus.reverse) {
switch (direction) {
case AxisDirection.up:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.right:
offset = Offset(-offset.dx, offset.dy);
break;
case AxisDirection.down:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.left:
offset = Offset(-offset.dx, offset.dy);
break;
}
}
return FractionalTranslation(
translation: offset,
transformHitTests: transformHitTests,
child: child,
);
}
}
AnimatedSwitcher(
duration: Duration(milliseconds: 200),
transitionBuilder: (Widget child, Animation<double> animation) {
var tween=Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0))
return SlideTransitionX(
child: child,
direction: AxisDirection.down, //上入下出
position: animation,
);
},
...//省略其余代碼
)
7.Flutter預(yù)置的動(dòng)畫(huà)過(guò)渡組件
AnimatedPadding 在padding發(fā)生變化時(shí)會(huì)執(zhí)行過(guò)渡動(dòng)畫(huà)到新?tīng)顟B(tài)
AnimatedPositioned 配合Stack一起使用,當(dāng)定位狀態(tài)發(fā)生變化時(shí)會(huì)執(zhí)行過(guò)渡動(dòng)畫(huà)到新的狀態(tài)。
AnimatedOpacity 在透明度opacity發(fā)生變化時(shí)執(zhí)行過(guò)渡動(dòng)畫(huà)到新?tīng)顟B(tài)
AnimatedAlign 當(dāng)alignment發(fā)生變化時(shí)會(huì)執(zhí)行過(guò)渡動(dòng)畫(huà)到新的狀態(tài)。
AnimatedContainer 當(dāng)Container屬性發(fā)生變化時(shí)會(huì)執(zhí)行過(guò)渡動(dòng)畫(huà)到新的狀態(tài)。
AnimatedDefaultTextStyle 當(dāng)字體樣式發(fā)生變化時(shí),子組件中繼承了該樣式的文本組件會(huì)動(dòng)態(tài)過(guò)渡到新樣式。
import 'package:flutter/material.dart';
class AnimatedWidgetsTest extends StatefulWidget {
const AnimatedWidgetsTest({Key? key}) : super(key: key);
@override
_AnimatedWidgetsTestState createState() => _AnimatedWidgetsTestState();
}
class _AnimatedWidgetsTestState extends State<AnimatedWidgetsTest> {
double _padding = 10;
var _align = Alignment.topRight;
double _height = 100;
double _left = 0;
Color _color = Colors.red;
TextStyle _style = const TextStyle(color: Colors.black);
Color _decorationColor = Colors.blue;
double _opacity = 1;
@override
Widget build(BuildContext context) {
var duration = const Duration(milliseconds: 400);
return SingleChildScrollView(
child: Column(
children: <Widget>[
ElevatedButton(
onPressed: () {
setState(() {
_padding = 20;
});
},
child: AnimatedPadding(
duration: duration,
padding: EdgeInsets.all(_padding),
child: const Text("AnimatedPadding"),
),
),
SizedBox(
height: 50,
child: Stack(
children: <Widget>[
AnimatedPositioned(
duration: duration,
left: _left,
child: ElevatedButton(
onPressed: () {
setState(() {
_left = 100;
});
},
child: const Text("AnimatedPositioned"),
),
)
],
),
),
Container(
height: 100,
color: Colors.grey,
child: AnimatedAlign(
duration: duration,
alignment: _align,
child: ElevatedButton(
onPressed: () {
setState(() {
_align = Alignment.center;
});
},
child: const Text("AnimatedAlign"),
),
),
),
AnimatedContainer(
duration: duration,
height: _height,
color: _color,
child: TextButton(
onPressed: () {
setState(() {
_height = 150;
_color = Colors.blue;
});
},
child: const Text(
"AnimatedContainer",
style: TextStyle(color: Colors.white),
),
),
),
AnimatedDefaultTextStyle(
child: GestureDetector(
child: const Text("hello world"),
onTap: () {
setState(() {
_style = const TextStyle(
color: Colors.blue,
decorationStyle: TextDecorationStyle.solid,
decorationColor: Colors.blue,
);
});
},
),
style: _style,
duration: duration,
),
AnimatedOpacity(
opacity: _opacity,
duration: duration,
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue)),
onPressed: () {
setState(() {
_opacity = 0.2;
});
},
child: const Text(
"AnimatedOpacity",
style: TextStyle(color: Colors.white),
),
),
),
AnimatedDecoratedBox1(
duration: Duration(
milliseconds: _decorationColor == Colors.red ? 400 : 2000),
decoration: BoxDecoration(color: _decorationColor),
child: Builder(builder: (context) {
return TextButton(
onPressed: () {
setState(() {
_decorationColor = _decorationColor == Colors.blue
? Colors.red
: Colors.blue;
});
},
child: const Text(
"AnimatedDecoratedBox toggle",
style: TextStyle(color: Colors.white),
),
);
}),
)
].map((e) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: e,
);
}).toList(),
),
);
}
}
自定義組件
1.簡(jiǎn)介
“組合”是自定義組件最簡(jiǎn)單的方法,在任何需要自定義組件的場(chǎng)景下,都應(yīng)該優(yōu)先考慮是否能夠通過(guò)組合來(lái)實(shí)現(xiàn)。
而通過(guò)CustomPaint和RenderObject自繪的方式本質(zhì)上是一樣的,都需要開(kāi)發(fā)者調(diào)用Canvas API手動(dòng)去繪制UI
2.組合組件
- 自定義漸變背景按鈕
DecoratedBox可以支持背景色漸變和圓角,InkWell在手指按下有漣漪效果,所以我們可以通過(guò)組合DecoratedBox和InkWell來(lái)實(shí)現(xiàn)GradientButton
import 'package:flutter/material.dart';
class GradientButton extends StatelessWidget {
const GradientButton({Key? key,
this.colors,
this.width,
this.height,
this.onPressed,
this.borderRadius,
required this.child,
}) : super(key: key);
// 漸變色數(shù)組
final List<Color>? colors;
// 按鈕寬高
final double? width;
final double? height;
final BorderRadius? borderRadius;
//點(diǎn)擊回調(diào)
final GestureTapCallback? onPressed;
final Widget child;
@override
Widget build(BuildContext context) {
ThemeData theme = Theme.of(context);
//確保colors數(shù)組不空
List<Color> _colors =
colors ?? [theme.primaryColor, theme.primaryColorDark];
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(colors: _colors),
borderRadius: borderRadius,
//border: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
child: Material(
type: MaterialType.transparency,
child: InkWell(
splashColor: _colors.last,
highlightColor: Colors.transparent,
borderRadius: borderRadius,
onTap: onPressed,
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(height: height, width: width),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: DefaultTextStyle(
style: const TextStyle(fontWeight: FontWeight.bold),
child: child,
),
),
),
),
),
),
);
}
}
GradientButton是由DecoratedBox、Padding、Center、InkWell等組件組合而成,
flukit組件庫(kù)已收錄GradientButton
使用:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-685950.html
children: <Widget>[
GradientButton(
colors: const [Colors.orange, Colors.red],
height: 50.0,
child: const Text("Submit"),
onPressed: onTap,
),
3.CustomPaint 和 RenderObject
painter: 背景畫(huà)筆,會(huì)顯示在子節(jié)點(diǎn)后面;
foregroundPainter: 前景畫(huà)筆,會(huì)顯示在子節(jié)點(diǎn)前面
size:當(dāng)child為null時(shí),代表默認(rèn)繪制區(qū)域大小,如果有child則忽略此參數(shù),畫(huà)布尺寸則為child尺寸。如果有child但是想指定畫(huà)布為特定大小,可以使用SizeBox包裹CustomPaint實(shí)現(xiàn)。
isComplex:是否復(fù)雜的繪制,如果是,F(xiàn)lutter會(huì)應(yīng)用一些緩存策略來(lái)減少重復(fù)渲染的開(kāi)銷。
willChange:和isComplex配合使用,當(dāng)啟用緩存時(shí),該屬性代表在下一幀中繪制是否會(huì)改變
CustomPaint({
Key key,
this.painter,
this.foregroundPainter,
this.size = Size.zero,
this.isComplex = false,
this.willChange = false,
Widget child, //子節(jié)點(diǎn),可以為空
})
//自定義
class MyPainter extends CustomPainter
class CustomCheckbox extends LeafRenderObjectWidget
Canvas常用:
drawLine 畫(huà)線
drawPoint 畫(huà)點(diǎn)
drawPath 畫(huà)路徑
drawImage 畫(huà)圖像
drawRect 畫(huà)矩形
drawCircle 畫(huà)圓
drawOval 畫(huà)橢圓
drawArc 畫(huà)圓弧文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-685950.html
到了這里,關(guān)于Flutter(九)Flutter動(dòng)畫(huà)和自定義組件的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!