classFlowMenuextendsStatefulWidget{constFlowMenu({super.key});@overrideState<FlowMenu>createState()=>_FlowMenuState();}class_FlowMenuStateextendsState<FlowMenu>withSingleTickerProviderStateMixin{lateAnimationControllermenuAnimation;//애니메이션을 제어하기 위한 컨트롤러IconDatalastTapped=Icons.notifications;//마지막으로 선택한 아이콘finalList<IconData>menuItems=<IconData>[Icons.home,Icons.new_releases,Icons.notifications,Icons.settings,Icons.menu,];//아이콘 목록//메뉴 변경void_updateMenu(IconDataicon){if(icon!=Icons.menu){setState(()=>lastTapped=icon);}}@overridevoidinitState(){super.initState();menuAnimation=AnimationController(duration:constDuration(milliseconds:250),//250ms동안 애니메이션 동작vsync:this,);}//메뉴 위젯WidgetflowMenuItem(IconDataicon){finaldoublebuttonDiameter=MediaQuery.of(context).size.width/menuItems.length;//버튼 크기returnPadding(padding:constEdgeInsets.symmetric(vertical:8.0),child:RawMaterialButton(fillColor:lastTapped==icon?Colors.amber[700]:Colors.blue,splashColor:Colors.amber[100],shape:constCircleBorder(),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,),),);}@overrideWidgetbuild(BuildContextcontext){returnFlow(delegate:FlowMenuDelegate(menuAnimation:menuAnimation),//대리자 지정children:menuItems.map<Widget>((IconDataicon)=>flowMenuItem(icon)).toList(),);}}classFlowMenuDelegateextendsFlowDelegate{FlowMenuDelegate({requiredthis.menuAnimation}):super(repaint:menuAnimation);finalAnimation<double>menuAnimation;@overrideboolshouldRepaint(FlowMenuDelegateoldDelegate){returnmenuAnimation!=oldDelegate.menuAnimation;}@overridevoidpaintChildren(FlowPaintingContextcontext){doubledx=0.0;for(inti=0;i<context.childCount;++i){dx=context.getChildSize(i)!.width*i;context.paintChild(i,transform:Matrix4.translationValues(dx*menuAnimation.value,0,0,),);}}}