import 'dart:math'; import 'package:flutter/material.dart'; import 'package:vector_math/vector_math.dart' show radians; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: SizedBox.expand( child: RadialMenu(), ), ), ); } } class RadialMenu extends StatefulWidget { @override _RadialMenuState createState() => _RadialMenuState(); } class _RadialMenuState extends State<RadialMenu> with SingleTickerProviderStateMixin { AnimationController controller; @override void initState() { super.initState(); controller = AnimationController( duration: Duration(milliseconds: 900), vsync: this, ); } @override Widget build(BuildContext context) { return RadialAnimetion( controller: controller, ); } } class RadialAnimetion extends StatelessWidget { RadialAnimetion({Key key, this.controller}) : scale = Tween<double>( begin: 1.0, end: 0.0, ).animate( CurvedAnimation( parent: controller, curve: Curves.fastOutSlowIn, ), ), translation = Tween<double>( begin: 0.0, end: 100.0, ).animate( CurvedAnimation( parent: controller, curve: Curves.linear, ), ), rotation = Tween<double>( begin: 0.0, end: 360.0, ).animate( CurvedAnimation( parent: controller, curve: Interval(0.0, 0.8), // 到80%結束 ), ), super(key: key); final AnimationController controller; final Animation<double> scale; final Animation<double> translation; final Animation<double> rotation; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: controller, builder: (context, builder) { return Transform.rotate( angle: radians(rotation.value), child: Stack( alignment: Alignment.center, children: <Widget>[ _buildButton(0, color: Colors.orange, icon: Icons.cloud_upload), _buildButton(45, color: Colors.yellow, icon: Icons.comment), _buildButton(90, color: Colors.pink, icon: Icons.color_lens), _buildButton(135, color: Colors.green, icon: Icons.date_range), _buildButton(180, color: Colors.blueGrey, icon: Icons.details), _buildButton(225, color: Colors.purple, icon: Icons.do_not_disturb_on), _buildButton(270, color: Colors.lime, icon: Icons.drafts), _buildButton(315, color: Colors.indigo, icon: Icons.error_outline), Transform.scale( scale: scale.value - 1, child: FloatingActionButton( child: Icon(Icons.close), onPressed: _close, backgroundColor: Colors.red, ), ), Transform.scale( scale: scale.value, child: FloatingActionButton( child: Icon(Icons.menu), onPressed: _open, backgroundColor: Colors.blue, ), ), ], ), ); }, ); } _buildButton(double angle, {Color color, IconData icon}) { // 將[度]轉換爲弧度。 final double rad = radians(angle); return Transform( transform: Matrix4.identity() ..translate( translation.value * cos(rad), translation.value * sin(rad), ), child: FloatingActionButton( backgroundColor: color, child: Icon(icon), onPressed: () {}, ), ); } _open() { controller.forward(); } _close() { controller.reverse(); } }