可見如下範例:
import 'package:flutter/material.dart';
//buttonSize是子物件的size
const double buttonSize =80;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
//body裡放置的是自己定義的LinerFlowWidget方法
body:LinerFlowWidget(), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class LinerFlowWidget extends StatefulWidget {
LinerFlowWidget({Key? key}) : super(key: key);
@override
State<LinerFlowWidget> createState() => _LinerFlowWidgetState();
}
class _LinerFlowWidgetState extends State<LinerFlowWidget> with SingleTickerProviderStateMixin{
//建立一個動畫控制器,加上late延遲給予初始值
late AnimationController controller;
@override
void initState() {
//於initState於程式第一次執行時,將動畫控制器初始化,並設定每0.3秒執行一次
controller=AnimationController(
//設定執行週期,每0.3秒執行一次
duration: Duration(milliseconds: 300),
//這裡的this要配上面加入with SingleTickerProviderStateMixin,這裡我忘了為什麼要這樣配合了,只記得這樣寫,有空再研究
vsync: this);
// TODO: implement initState
super.initState();
}
@override
void dispose() {
//設定回收機制,不使用時回收記憶體
controller.dispose();
// TODO: implement dispose
super.dispose();
}
@override
Widget build(BuildContext context) {
//使用Flow布局,Flow裡面一定要放的兩個參數,一為delegate,delegate告知程式繪製的方法
//另一個為childred在布局裡的子元件,在這裡有mail call 及notifications三個icons
return Flow(
//FlowMenuDelegate是自己寫的繪製方法,參數的controller動畫的控制器,這裡寫可以在每個ticker定時的監聽動畫的變化,然後持續的執行
delegate: FlowMenuDelegate(controller:controller),
//這裡透過list.map的方法,將原本list中的四個部件,逐一放進buildItem執行一次,然後回傳FloatingActionButton類型的widget
//並將回傳的四個FloatingActionButton類型widget,存進<IconData>這個陣列裡面。
children: <IconData>[
Icons.menu,
Icons.call,
Icons.mail,
Icons.notifications,
].map<Widget>(buildItem).toList(),
);
}
//這裡的參數icon指的是上面menu mail.call 和nofifications四個icon
Widget buildItem(IconData icon) {
return SizedBox(
width:buttonSize,
height: buttonSize,
child: FloatingActionButton(
elevation: 0,
splashColor: Colors.black,
child: Icon(icon,color: Colors.white,size: 45,),
onPressed: (){
//點選之後,當動畫狀態已經完成的時候,執行反轉動畫reverse(),假如沒有動畫不是完成,就執行一次動畫forward()
if(controller.status==AnimationStatus.completed){
controller.reverse();
}else{
controller.forward();
}
},),
);
}
}
//這裡繼承了FlowDelegate,FlowDelegate提供了兩個方法
// 一是paintChildren,
//二是shouldRepaint,決定如果到相同的子物件是否重複繪製
class FlowMenuDelegate extends FlowDelegate{
//可以透過下面paintChildren參數中的content,取得子物件的下列的數值,並透過這些取得的數值,
//context.size 父物件的大小
//context.size 子物件的數量
//context.getChildSize(int i) 取得個別子物件的大小
//paintChild(int i, { Matrix4 transform, double opacity = 1.0 }); 決定繪製子類別的方法
// 上面的參數,i是指要繪製第幾個子物件,transform是Matrix4的繪製方法,opacity 是透明度
final Animation<double> controller;
//建立FlowMenuDelegate的建構式,要求一定要給予controller這個命名參數
const FlowMenuDelegate({required this.controller}):super(repaint: controller);
@override
void paintChildren(FlowPaintingContext context) {
//size為父件的大小,
final size=context.size;
//xStart為第一個按鈕的起始位置x軸,由父物件的寬度-子物件的長度。
final xStart=size.width-buttonSize;
//yStart為第一個按鈕的起始位置y軸,由父物件的長度-子物件的長度。
final yStart=size.height-buttonSize;
//這裡用-1是因為menu是第一個,先執行的先放,這樣最後一個執行是menu,它就會在最上面。
for(int i=context.childCount-1;i>=0;i--){
//margin為子物件和另一個子物件的間距
final margin=8;
//取得個別子物件的寬度,這裡就上面設定的80
final childSize=context.getChildSize(i)!.width;
//dx為子物件的長度加上間距,乘上i,可以控制每個子物件的位置
final dx=(childSize+margin)*i;
//子物件的x軸的位置=剛才設定的起點位置開始倒推,第1個是起始的位置,第二個是起始的位置-(80+8)*1,第三個是起始的位置-(80+8)*2....依此類推
//這裡的controller.value是指動畫持續不斷傳過來的數值,起點為0,終點為1,每次傳過來就會執行一次的setState,一直更新子元件的位置,因此會產生一個動畫的效果
final x=xStart-dx*controller.value;
//y軸保持不變,以這個程式就是全部的長度-80的位置
final y=yStart;
//在x軸為x,y軸為y,及z軸為0的地方將圖呈現出來
context.paintChild(i,transform:Matrix4.translationValues(x, y, 0),);
}
// TODO: implement paintChildren
}
//這裡設置回傳true,不論何時都重複繪製
@override
bool shouldRepaint(covariant FlowDelegate oldDelegate) {
return true;
}
}
沒有留言:
張貼留言