工具的價值在於使用它的人,而非工具自身。會拿筆的人很多,但繪畫宗師寥寥無幾。--張風捷特烈
龍少(疑惑):Align這麼簡單,有什麼好講的。
捷特: 你會拿筆嗎?
龍少:你是在懷疑我的智商?我拿給你看:
捷特: 既然會拿筆,給我畫一張。
龍少: ...,我懷疑你在水文,而且我證據確鑿。
捷特: 這都被你發現了,水一頁可真不容易。
1.Align的原始碼
捷特: 你平時怎麼用Align?
龍少: 還能怎麼用,用鍵盤敲唄。Align中有一個alignment屬性,通過Alignment列舉,可以指定一個元件在容器中的相對位置,雖然挺好用,但也沒什麼要點,你確定能水一篇?
捷特: 你確定是列舉。
龍少: 一共就9種型別,不是列舉是什麼? (進入Alignment原始碼)
class AlignTest extends StatelessWidget {
AlignTest({Key key}) :super(key: key);
@override
Widget build(BuildContext context) {
var childBox = Container(//孩子元件
width: 40,
height: 40,
color: Colors.cyanAccent,
);
var childLayout = Align(//佈局元件Align
alignment: Alignment.center,
child: childBox,
);
return Container(//父親元件
width: 110,
height: 68,
color: Colors.grey,
child: childLayout,
);
}
}
複製程式碼
龍少: 嗯?居然是一個類,而且那些疑似列舉的都是靜態常量。
捷特: so,Alignment的能力並非僅是九種對齊方式,它是一種排布。
龍少: so... what?
class Alignment extends AlignmentGeometry {
...
static const Alignment topLeft = Alignment(-1.0, -1.0);
static const Alignment topCenter = Alignment(0.0, -1.0);
static const Alignment topRight = Alignment(1.0, -1.0);
static const Alignment centerLeft = Alignment(-1.0, 0.0);
static const Alignment center = Alignment(0.0, 0.0);
static const Alignment centerRight = Alignment(1.0, 0.0);
static const Alignment bottomLeft = Alignment(-1.0, 1.0);
static const Alignment bottomCenter = Alignment(0.0, 1.0);
static const Alignment bottomRight = Alignment(1.0, 1.0);
複製程式碼
捷特: 這樣就能很容易操縱元素的位置,現在有個需求,給你一批元件,讓他們安裝指定的函式曲線排布,你該怎麼辦?
龍少: 把刀拿來!我看看是哪個不走心的提的需求...
捷特: 淡定淡定,先看如何完成sin定位元件位置。這裡用一個Slider演示一下資料變化時的效果
捷特: 先準備一個小球元件,可指定大小和顏色:
class Ball extends StatelessWidget {
Ball({Key key, this.radius=15, this.color=Colors.blue,}) :super(key: key);
final double radius; //半徑
final Color color; //顏色
@override
Widget build(BuildContext context) {
return Container(
width: radius*2,
height: radius*2,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
),
);
}
}
複製程式碼
龍少: 嗯?有點意思,確實挺巧妙,為什麼有種想打你一頓的衝動
class SinLayout extends StatefulWidget {
SinLayout({Key key,}) : super(key: key);
@override
_SinLayoutState createState() => _SinLayoutState();
}
class _SinLayoutState extends State<SinLayout> {
var _x=0.0;//Alignment座標系上的x座標
@override
Widget build(BuildContext context) {
var item= Container(
width: 300,
height: 200,
color: Colors.black.withAlpha(10),
child: Align(
child: Ball(color: Colors.orangeAccent,),
alignment: Alignment(_x,f(_x*pi)), //<--- 根據_x聯動y,確定位置
),
);
var slider=Slider(
max: 180,
min: -180,
divisions:360,
label: "${_x.toStringAsFixed(2)}π",
value: _x*180,
onChanged: (v) {
setState(() {_x=v/180;});//拖動時更新橫座標
});
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[slider, item],
);
}
double f(x) {//對映函式 -- 可隨意指定
double y = sin(x);
return y;
}
}
複製程式碼
現在封裝一個sin影象的位置擺放元件
class SinPlace extends StatelessWidget {
SinPlace({Key key, this.child,this.t=0}) : super(key: key);
final Widget child;
final double t;
@override
Widget build(BuildContext context) {
return Container(
child:Align(
child: child,
alignment: Alignment(t,g(t*pi)), //<--- 根據——x聯動y,確定位置
));
}
double g(t) {//對映函式 -- 可隨意指定
double y = 0.5*sin(t);
return y;
}
}
複製程式碼
通過Stack進行擺放
class SinLayout extends StatelessWidget {
SinLayout({Key key, this.items}) :super(key: key);
final Map<double,Widget> items;
@override
Widget build(BuildContext context) => Stack(
children: items.keys.toList().map((key)=>
SinPlace(t: key,child: items[key],)).toList() ,
);
}
複製程式碼
使用items傳入即可
var items=<double,Widget>{
-0.2:CircleAvatar(backgroundImage:AssetImage("images/caver.jpeg") ,),
-0.4:CircleAvatar(backgroundImage:AssetImage("images/honor.png") ,),
-0.6:CircleAvatar(backgroundImage:AssetImage("images/caver.jpeg") ,),
-0.8:CircleAvatar(backgroundImage:AssetImage("images/leaf.png") ,),
-1:CircleAvatar(backgroundImage:AssetImage("images/caver.jpeg") ,),
0:CircleAvatar(backgroundImage:AssetImage("images/icon_head.png") ,),
0.2:CircleAvatar(backgroundImage:AssetImage("images/caver.jpeg") ,),
0.4:CircleAvatar(backgroundImage:AssetImage("images/wy_200x300.jpg") ,),
0.6:CircleAvatar(backgroundImage:AssetImage("images/caver.jpeg") ,),
0.8:CircleAvatar(backgroundImage:AssetImage("images/icon_head.png") ,),
1:CircleAvatar(backgroundImage:AssetImage("images/caver.jpeg") ,),
};
複製程式碼
也許你看出來了,其實就是一個函式而已,所以可以它還能更強大,完全可以封成個自定義函式影象排列。根據引數方程的形式來建構函式,通過f和g
typedef FunNum1=Function(double t );
class MathPlace extends StatelessWidget {
MathPlace({Key key, this.child,this.t=0,this.f,this.g}) : super(key: key);
final Widget child;
final FunNum1 f;
final FunNum1 g;
final double t;
@override
Widget build(BuildContext context) {
var result= Container(
child:Align(
child: child,
alignment: Alignment(f(t),g(t)), //<--- 根據——x聯動y,確定位置
));
return result;
}
}
class MathLayout extends StatelessWidget {
MathLayout({Key key, this.items,this.f,this.g}) :super(key: key);
final FunNum1 f;
final FunNum1 g;
final Map<double,Widget> items;
@override
Widget build(BuildContext context) => Stack(
children: items.keys.toList().map((key)=>MathPlace(t: key,child: items[key],f: f,g: g,)).toList() ,
);
}
複製程式碼
圓形排布,小意思
Container(
width: 350,
height: 350,
child: MathLayout(
items: items,
f: (t) => cos(t * pi),
g: (t) => sin(t * pi),
))
複製程式碼
橢圓排布,好說
Container(
width: 350,
height: 350,
child: MathLayout(
items: items,
f: (t) => 0.8*sin(t * pi),
g: (t) => 0.6*cos(t * pi),
)),
,
複製程式碼
龍少: 好吧,我要去小學重新學數學。(手動笑哭)
本文到此接近尾聲了,如果想快速嚐鮮Flutter,《Flutter七日》會是你的必備佳品;如果想細細探究它,那就跟隨我的腳步,完成一次Flutter之旅。
另外本人有一個Flutter微信交流群,歡迎小夥伴加入,共同探討Flutter的問題,本人微訊號:zdl1994328
,期待與你的交流與切磋。
滿紙荒唐言,一把辛酸淚。都言作者痴,誰解其中味。