Dart - Flow

박정규·2022년 1월 26일
0

flow란?

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을 설정하여 만들 수 있다.

또 다른 예시를 통해 확인해보자, 예시에서는 flow의 활용을 보자


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,
      ),
    );
  }
}
}

실행 결과 확인하기

profile
초보 개발자

0개의 댓글