FlowDelegate 의 논리에 따라 children의 크기와 위치를 효율적으로 지정하는 위젯이다. flow layout은 변환 행렬을 사용하여 children의 위치를 변경하는 데 최적화되어 있다.
모두 비슷하게 움직이는 Widget 목록의 애니메이션을 조정해야할 때 사용할 수 있다.
Flow( {Key? key, required FlowDelegate delegate, List<Widget> children = const <Widget>[], Clip clipBehavior = Clip.hardEdge} )
Flow layout안에 children을 설정하여 만드는 방식이다.
clipBehavior에서 clip에 animation을 설정하여 만들 수 있다.
import 'package:flutter/material.dart';
void main() => runApp(const FlowApp());
class FlowApp extends StatelessWidget {
const FlowApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flow Example'),
),
body: const FlowMenu(),
),
);
}
}
class FlowMenu extends StatefulWidget {
const FlowMenu({Key? key}) : super(key: key);
@override
State<FlowMenu> createState() => _FlowMenuState();
}
class _FlowMenuState extends State<FlowMenu>
with SingleTickerProviderStateMixin {
late AnimationController menuAnimation;
IconData lastTapped = Icons.notifications;
final List<IconData> menuItems = <IconData>[
Icons.home,
Icons.new_releases,
Icons.notifications,
Icons.settings,
Icons.menu,
];
void _updateMenu(IconData icon) {
if (icon != Icons.menu) {
setState(() => lastTapped = icon);
} // 메뉴 버튼이 아닐 때 아이콘의 색을 바꿔준다.
}
@override
void initState() {
super.initState();
menuAnimation = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this, // duration을 통해 애니메이션의 시간을 설정
);
}
Widget flowMenuItem(IconData icon) {
final double buttonDiameter =
MediaQuery.of(context).size.width / menuItems.length;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: RawMaterialButton(
fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,
splashColor: Colors.amber[100],
shape: const CircleBorder(),
constraints: BoxConstraints.tight(Size(buttonDiameter, buttonDiameter)),
onPressed: () {
_updateMenu(icon);
menuAnimation.status == AnimationStatus.completed
? menuAnimation.reverse()
: menuAnimation.forward();
},
child: Icon(
icon,
color: Colors.white,
size: 45.0,
),
),
);
}
@override
Widget build(BuildContext context) {
return Flow(
delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
children:
menuItems.map<Widget>((IconData icon) => flowMenuItem(icon)).toList(),
); //Flow 설정 + delegate도 설정해준다.
}
}
class FlowMenuDelegate extends FlowDelegate {
FlowMenuDelegate({required this.menuAnimation})
: super(repaint: menuAnimation);
final Animation<double> menuAnimation;
@override
bool shouldRepaint(FlowMenuDelegate oldDelegate) {
return menuAnimation != oldDelegate.menuAnimation;
}
@override
void paintChildren(FlowPaintingContext context) {
double dx = 0.0;
for (int i = 0; i < context.childCount; ++i) {
dx = context.getChildSize(i)!.width * i;
context.paintChild(
i,
transform: Matrix4.translationValues(
dx * menuAnimation.value,
0,
0,
),
);
}
}
}