Material Designで提唱されているSpeed Dialの実装について簡単に説明します。
優秀なパッケージが既に用意されております。
speed_dial 0.0.1
お急ぎの方は是非に
1.Preliminary
ScaffoldのFAB(FloatActionButton)について
final Widget floatingActionButton;
で定義されております。
ご察しの通り、RowやColumnなどで様々なWidgetを
Scaffoldに入れいることができます。
Flutterで2つのFloatingActionButtonを表示する方法
尚、この記事はStackを利用して実装していきます。(捻りとは )
2.BlurBackground
class FloatButtonBackground extends StatelessWidget {
final Color color;
FloatButtonBackground({this.color});
@override
Widget build(BuildContext context) {
// TODO: implement buier
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0),
child: Container(
color: color,
),
);
}
}
3.FoldAnimation
class FoldAnimationWrap extends StatefulWidget {
final Duration duration;
final Curve animationCurve;
final bool isExpanded;
final Widget child;
FoldAnimationWrap({@required this.duration, this.animationCurve = Curves.ease,@required this.isExpanded, this.child});
@override
State<StatefulWidget> createState() {
return _FoldAnimationWrap();
}
}
class _FoldAnimationWrap extends State<FoldAnimationWrap> with TickerProviderStateMixin {
AnimationController _controller;
Animation<double> _expandAnimation;
@override
void initState() {
_controller = AnimationController(
duration: widget.duration,
vsync: this,
reverseDuration: widget.duration,
);
_expandAnimation = _controller.drive(CurveTween(curve: widget.animationCurve));
if(widget.isExpanded){
_controller.value = 1;
}
}
@override
void didUpdateWidget(FoldAnimationWrap oldWidget) {
super.didUpdateWidget(oldWidget);
if(oldWidget.isExpanded != widget.isExpanded) {
if (widget.isExpanded) {
_controller.forward();
}
else {
_controller.reverse();
}
}
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _expandAnimation,
child: widget.child,
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}
4.FoldFloatButtonWrap
SpeedDialを構成するクラスになります。
class FoldFloatButtonWrap extends StatelessWidget {
final bool isExpanded;
//** 常に表示されるFAB(Sampleのバーガーメニュー) **//
final Widget floatButton;
//** 折り畳まれれるFAB **//
final List<Widget> expandedWidget;
final Duration foldAnimationDuration = Duration(milliseconds: 400);
FoldFloatButtonWrap({
@required this.isExpanded,
@required this.floatButton,
@required this.expandedWidget,
});
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: <Widget>[
//** Background **//
Positioned(
top: 0,
left: 0,
right: -32, //Scaffoldによるマージン
bottom: -32,
child:FoldAnimationWrap(
isExpanded: isExpanded,
duration: foldAnimationDuration,
child: FloatButtonBackground(color: Color.fromARGB(50, 0, 0, 0)),
),
),
//** FAB **//
Positioned(
bottom: 0,
right: 0,
child: Column(
children: <Widget>[
FoldAnimationWrap(
isExpanded: isExpanded,
duration: foldAnimationDuration,
child: Column(children: expandedWidget),
),
floatButton,
],
)
),
],
);
}
}
5.Implement Widget
Speed Dialの実装個所
既存のFABをFoldFloatButtonWrapで囲んで
最後にsetStateでisExpandedで制御すれば完成です。
bool isExpanded = false;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: ...,
body: ...,
floatingActionButton:
FoldFloatButtonWrap(
isExpanded: isExpanded,
floatButton: FloatingActionButton(
child: isExpanded ? Icon(Icons.close) : Icon(Icons.menu) ,
backgroundColor: Colors.deepOrangeAccent,
onPressed: (){
setState(() {
isExpanded ^= true;
});
},
),
expandedWidget: <Widget>[
FloatingActionButton(
child: Icon(Icons.content_copy, size: 16),
backgroundColor: Colors.deepOrangeAccent,
mini: true,
onPressed: (){
setState(() {
isExpanded ^= true;
text = "Copied";
});
},
),
SizedBox(height: 4),
FloatingActionButton(
child: Icon(Icons.refresh,size: 16,),
backgroundColor: Colors.deepOrangeAccent,
mini: true,
onPressed: (){
setState(() {
isExpanded ^= true;
text = "Refrashed";
});
},
),
SizedBox(height: 4),
FloatingActionButton(
child: Icon(Icons.delete,size: 16,),
backgroundColor: Colors.deepOrangeAccent,
mini: true,
onPressed: (){
setState(() {
isExpanded ^= true;
text = "Deleted";
});
},
),
SizedBox(height: 8),
],
),
);
}
5.最後に
画面制御やアニメーションなどとっつきにくいと感じて
手間を惜しんでパッケージをついつい利用しがちになりますが、
一歩踏み込んで自分なりに解釈して実装してみるのも一興かと思います。