-
-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Taken haw_fab_menu widget into the app
- Loading branch information
Showing
5 changed files
with
302 additions
and
25 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
import 'dart:ui' as ui; | ||
|
||
import 'package:flutter/material.dart'; | ||
|
||
/// Wrapper that builds a FAB menu on top of [body] in a [Stack] | ||
class HawkFabMenu extends StatefulWidget { | ||
final Widget body; | ||
final List<HawkFabMenuItem> items; | ||
final double blur; | ||
final AnimatedIconData icon; | ||
final Color fabColor; | ||
final Color iconColor; | ||
HawkFabMenu({ | ||
@required this.body, | ||
@required this.items, | ||
this.blur: 5.0, | ||
this.icon, | ||
this.fabColor, | ||
this.iconColor, | ||
}) { | ||
assert(this.items.isNotEmpty); | ||
} | ||
|
||
@override | ||
_HawkFabMenuState createState() => _HawkFabMenuState(); | ||
} | ||
|
||
class _HawkFabMenuState extends State<HawkFabMenu> with TickerProviderStateMixin { | ||
/// To check if the menu is open | ||
bool _isOpen = false; | ||
|
||
/// The [Duration] for every animation | ||
Duration _duration = Duration(milliseconds: 500); | ||
|
||
/// Animation controller that animates the menu item | ||
AnimationController _iconAnimationCtrl; | ||
|
||
/// Animation that animates the menu item | ||
Animation<double> _iconAnimationTween; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
_iconAnimationCtrl = AnimationController( | ||
vsync: this, | ||
duration: _duration, | ||
); | ||
_iconAnimationTween = Tween( | ||
begin: 0.0, | ||
end: 1.0, | ||
).animate(_iconAnimationCtrl); | ||
} | ||
|
||
/// Closes the menu if open and vice versa | ||
void _toggleMenu() { | ||
setState(() { | ||
_isOpen = !_isOpen; | ||
}); | ||
if (_isOpen) { | ||
_iconAnimationCtrl.forward(); | ||
} else { | ||
_iconAnimationCtrl.reverse(); | ||
} | ||
} | ||
|
||
/// If the menu is open and the device's back button is pressed then menu gets closed instead of going back. | ||
Future<bool> _preventPopIfOpen() async { | ||
if (_isOpen) { | ||
_toggleMenu(); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return WillPopScope( | ||
onWillPop: _preventPopIfOpen, | ||
child: Stack( | ||
children: <Widget>[ | ||
widget.body, | ||
if (_isOpen) _buildBlurWidget() else Container(), | ||
if (_isOpen) _buildMenuItemList() else Container(), | ||
_buildMenuButton(context), | ||
], | ||
), | ||
); | ||
} | ||
|
||
/// Returns animated list of menu items | ||
Widget _buildMenuItemList() { | ||
return Positioned( | ||
bottom: 80, | ||
right: 15, | ||
child: ScaleTransition( | ||
scale: AnimationController( | ||
vsync: this, | ||
value: 0.7, | ||
duration: _duration, | ||
)..forward(), | ||
child: SizeTransition( | ||
axis: Axis.horizontal, | ||
sizeFactor: AnimationController( | ||
vsync: this, | ||
value: 0.5, | ||
duration: _duration, | ||
)..forward(), | ||
child: FadeTransition( | ||
opacity: AnimationController( | ||
vsync: this, | ||
value: 0.0, | ||
duration: _duration, | ||
)..forward(), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.end, | ||
children: this | ||
.widget | ||
.items | ||
.map<Widget>( | ||
(item) => _MenuItemWidget( | ||
item: item, | ||
toggleMenu: _toggleMenu, | ||
), | ||
) | ||
.toList(), | ||
), | ||
), | ||
), | ||
), | ||
); | ||
} | ||
|
||
/// Builds the blur effect when the menu is opened | ||
Widget _buildBlurWidget() { | ||
return InkWell( | ||
onTap: _toggleMenu, | ||
child: BackdropFilter( | ||
filter: ui.ImageFilter.blur( | ||
sigmaX: widget.blur, | ||
sigmaY: widget.blur, | ||
), | ||
child: Container( | ||
color: Colors.black12, | ||
), | ||
), | ||
); | ||
} | ||
|
||
/// Builds the main floating action button of the menu to the bottom right | ||
/// On clicking of which the menu toggles | ||
Widget _buildMenuButton(BuildContext context) { | ||
return Positioned( | ||
bottom: 10, | ||
right: 10, | ||
child: FloatingActionButton( | ||
backgroundColor: widget.fabColor ?? Theme.of(context).primaryColor, | ||
onPressed: _toggleMenu, | ||
child: AnimatedIcon( | ||
icon: widget.icon ?? AnimatedIcons.menu_close, | ||
progress: _iconAnimationTween, | ||
color: widget.iconColor, | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
/// Builds widget for a single menu item | ||
class _MenuItemWidget extends StatelessWidget { | ||
/// Contains details for a single menu item | ||
final HawkFabMenuItem item; | ||
|
||
/// A callback that toggles the menu | ||
final Function toggleMenu; | ||
|
||
const _MenuItemWidget({ | ||
@required this.item, | ||
@required this.toggleMenu, | ||
}); | ||
|
||
/// Closes the menu and calls the function for a particular menu item | ||
void onTap() { | ||
toggleMenu(); | ||
item.ontap(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return InkWell( | ||
onTap: onTap, | ||
child: Row( | ||
children: <Widget>[ | ||
Container( | ||
padding: const EdgeInsets.symmetric( | ||
horizontal: 10, | ||
vertical: 3, | ||
), | ||
decoration: BoxDecoration( | ||
color: item.labelBackgroundColor ?? Colors.white, | ||
borderRadius: const BorderRadius.only( | ||
topLeft: Radius.circular(10), | ||
bottomLeft: Radius.circular(10), | ||
), | ||
), | ||
child: Text( | ||
item.label, | ||
style: TextStyle(color: item.labelColor ?? Colors.black87), | ||
), | ||
), | ||
FloatingActionButton( | ||
onPressed: onTap, | ||
mini: true, | ||
backgroundColor: item.color ?? Theme.of(context).primaryColor, | ||
child: item.icon, | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} | ||
|
||
/// Model for single menu item | ||
class HawkFabMenuItem { | ||
/// Text label for for the menu item | ||
String label; | ||
|
||
/// Corresponding icon for the menu item | ||
Icon icon; | ||
|
||
/// Action that is to be performed on tapping the menu item | ||
Function ontap; | ||
|
||
/// Background color for icon | ||
Color color; | ||
|
||
/// Text color for label | ||
Color labelColor; | ||
|
||
/// Background color for label | ||
Color labelBackgroundColor; | ||
|
||
HawkFabMenuItem({ | ||
@required this.label, | ||
@required this.ontap, | ||
@required this.icon, | ||
this.color, | ||
this.labelBackgroundColor, | ||
this.labelColor, | ||
}); | ||
} |
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
Oops, something went wrong.