Instantly share code, notes, and snippets.
Forked from Simple-Complexity/speed_dial_floating_action_button.dart
Created
December 25, 2017 15:56
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save jmolins/98ee91e1567e93cc4aff7b986ebc4124 to your computer and use it in GitHub Desktop.
A speed dial fab implementation for Flutter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'dart:math'; | |
import 'package:hone/common/defaults.dart'; | |
enum ExpandDirection { | |
UP, | |
DOWN, | |
} | |
enum LabelDirection { | |
LEFT, | |
RIGHT, | |
} | |
class SpeedDialFabItem { | |
VoidCallback onPressed; | |
IconData icon; | |
String label; | |
SpeedDialFabItem({this.onPressed, this.icon, this.label}); | |
} | |
class SpeedDialFloatingActionButton extends StatefulWidget { | |
final ExpandDirection expandDirection; | |
final LabelDirection labelDirection; | |
final List<SpeedDialFabItem> speedDialFabItems; | |
final IconData closedIcon; | |
SpeedDialFloatingActionButton({ | |
this.expandDirection : ExpandDirection.UP, | |
this.labelDirection: LabelDirection.LEFT, | |
this.speedDialFabItems : const [], | |
this.closedIcon : Icons.add, | |
}); | |
@override | |
State<StatefulWidget> createState() => new _SpeedDialFloatingActionButtonState(); | |
} | |
class _SpeedDialFloatingActionButtonState extends State<SpeedDialFloatingActionButton> with TickerProviderStateMixin { | |
static const FAB_SIZE_DIFFERENCE = DEFAULT_FAB_SIZE - MINI_FAB_SIZE; | |
AnimationController _controller; | |
static const List<IconData> icons = const [ Icons.sms, Icons.mail, Icons.phone ]; | |
@override | |
void initState() { | |
super.initState(); | |
_controller = new AnimationController( | |
vsync: this, | |
duration: const Duration(milliseconds: 300), | |
); | |
} | |
@override | |
Widget build(BuildContext context) { | |
Color backgroundColor = Theme.of(context).cardColor; | |
Color foregroundColor = Theme.of(context).accentColor; | |
bool isExpandUp = widget.expandDirection == ExpandDirection.UP; | |
List<SpeedDialFabItem> fabItems = (isExpandUp) ? | |
widget.speedDialFabItems.reversed.toList() : | |
widget.speedDialFabItems; | |
Widget expandFab = new Container( | |
margin: new EdgeInsets.only( | |
top: isExpandUp ? 8.0 : 0.0, | |
bottom: isExpandUp ? 0.0 : 8.0, | |
), | |
child: new FloatingActionButton( | |
child: new AnimatedBuilder( | |
animation: _controller, | |
builder: (BuildContext context, Widget child) { | |
return new Transform( | |
transform: new Matrix4.rotationZ(_controller.value * 0.5 * PI), | |
alignment: FractionalOffset.center, | |
child: new Icon(_controller.isDismissed ? widget.closedIcon : Icons.close), | |
); | |
}, | |
), | |
onPressed: () { | |
if (_controller.isDismissed) { | |
_controller.forward(); | |
} else { | |
_controller.reverse(); | |
} | |
}, | |
), | |
); | |
List<Widget> widgetList = new List.generate(fabItems.length, (int index) { | |
var fabItem = fabItems[index]; | |
Widget child = new Container( | |
height: 60.0, | |
alignment: FractionalOffset.centerRight, | |
margin: new EdgeInsets.only(right: FAB_SIZE_DIFFERENCE/2), | |
child: new ScaleTransition( | |
scale: new CurvedAnimation( | |
parent: _controller, | |
curve: new Interval( | |
isExpandUp ? 0.0 : 0.0 + index / fabItems.length / 2.0, | |
isExpandUp ? 1.0 - index / fabItems.length / 2.0 : 1.0, | |
curve: Curves.easeOut, | |
), | |
), | |
child: new Stack( | |
overflow: Overflow.visible, | |
children: <Widget>[ | |
(fabItem.label != null && fabItem.label.isNotEmpty) ? new Positioned( | |
child: new Container( | |
color: Colors.black, | |
padding: const EdgeInsets.all(4.0), | |
child: new Text(fabItem.label, | |
style: new TextStyle( | |
color: Colors.white, | |
), | |
), | |
), | |
right: 50.0, | |
top: 9.0, | |
) : new Container(), | |
new FloatingActionButton( | |
backgroundColor: backgroundColor, | |
mini: true, | |
child: new Icon(fabItem.icon, color: foregroundColor), | |
onPressed: fabItem.onPressed, | |
heroTag: 'fab_${fabItem.icon.toString()}', | |
), | |
], | |
), | |
), | |
); | |
return child; | |
}).toList(); | |
if(isExpandUp){ | |
// Add to the end of the widget list | |
widgetList.add(expandFab); | |
}else{ | |
// insert into the beginning of the widget list | |
widgetList.insert(0, expandFab); | |
} | |
return new Column( | |
mainAxisSize: MainAxisSize.min, | |
crossAxisAlignment: CrossAxisAlignment.end, | |
children: widgetList, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment