| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /// Flutter code sample for [Flow]. |
| |
| import 'package:flutter/material.dart'; |
| |
| void main() => runApp(const FlowApp()); |
| |
| class FlowApp extends StatelessWidget { |
| const FlowApp({super.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({super.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, |
| ); |
| } |
| |
| 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(), |
| ); |
| } |
| } |
| |
| 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, |
| ), |
| ); |
| } |
| } |
| } |