├── LICENSE ├── CHANGELOG.md ├── gif ├── menu_1.gif └── menu_2.gif ├── .gitignore ├── lib ├── floating_menu_item.dart ├── floating_menu_callback.dart └── flutter_expandable_menu.dart ├── .metadata ├── flutter_expandable_menu.iml ├── pubspec.yaml ├── pubspec.lock └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | TODO: Add your license here. 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.0.1] - TODO: Add release date. 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /gif/menu_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UttamPanchasara/FlutterExpandableMenu/HEAD/gif/menu_1.gif -------------------------------------------------------------------------------- /gif/menu_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UttamPanchasara/FlutterExpandableMenu/HEAD/gif/menu_2.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | ios/.generated/ 9 | ios/Flutter/Generated.xcconfig 10 | ios/Runner/GeneratedPluginRegistrant.* 11 | -------------------------------------------------------------------------------- /lib/floating_menu_item.dart: -------------------------------------------------------------------------------- 1 | class FloatingMenuItem { 2 | final icon; 3 | final id; 4 | final backgroundColor; 5 | 6 | FloatingMenuItem({this.id, this.icon, this.backgroundColor}); 7 | } 8 | -------------------------------------------------------------------------------- /lib/floating_menu_callback.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_expandable_menu/floating_menu_item.dart'; 2 | 3 | class FloatingMenuCallback { 4 | void onMenuClick(FloatingMenuItem floatingMenuItem) {} 5 | } 6 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 8661d8aecd626f7f57ccbcb735553edc05a2e713 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /flutter_expandable_menu.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_expandable_menu 2 | description: A new Flutter package. 3 | version: 0.0.1 4 | author: Uttam Panchasara 5 | homepage: https://github.com/UttamPanchasara/FlutterExpandableMenu 6 | 7 | environment: 8 | sdk: ">=2.1.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://www.dartlang.org/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | 24 | # To add assets to your package, add an assets section, like this: 25 | # assets: 26 | # - images/a_dot_burr.jpeg 27 | # - images/a_dot_ham.jpeg 28 | # 29 | # For details regarding assets in packages, see 30 | # https://flutter.io/assets-and-images/#from-packages 31 | # 32 | # An image asset can refer to one or more resolution-specific "variants", see 33 | # https://flutter.io/assets-and-images/#resolution-aware. 34 | 35 | # To add custom fonts to your package, add a fonts section here, 36 | # in this "flutter" section. Each entry in this list should have a 37 | # "family" key with the font family name, and a "fonts" key with a 38 | # list giving the asset and other descriptors for the font. For 39 | # example: 40 | # fonts: 41 | # - family: Schyler 42 | # fonts: 43 | # - asset: fonts/Schyler-Regular.ttf 44 | # - asset: fonts/Schyler-Italic.ttf 45 | # style: italic 46 | # - family: Trajan Pro 47 | # fonts: 48 | # - asset: fonts/TrajanPro.ttf 49 | # - asset: fonts/TrajanPro_Bold.ttf 50 | # weight: 700 51 | # 52 | # For details regarding fonts in packages, see 53 | # https://flutter.io/custom-fonts/#from-packages 54 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.0.8" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | flutter: 33 | dependency: "direct main" 34 | description: flutter 35 | source: sdk 36 | version: "0.0.0" 37 | flutter_test: 38 | dependency: "direct dev" 39 | description: flutter 40 | source: sdk 41 | version: "0.0.0" 42 | matcher: 43 | dependency: transitive 44 | description: 45 | name: matcher 46 | url: "https://pub.dartlang.org" 47 | source: hosted 48 | version: "0.12.3+1" 49 | meta: 50 | dependency: transitive 51 | description: 52 | name: meta 53 | url: "https://pub.dartlang.org" 54 | source: hosted 55 | version: "1.1.6" 56 | path: 57 | dependency: transitive 58 | description: 59 | name: path 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "1.6.2" 63 | pedantic: 64 | dependency: transitive 65 | description: 66 | name: pedantic 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "1.4.0" 70 | quiver: 71 | dependency: transitive 72 | description: 73 | name: quiver 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "2.0.1" 77 | sky_engine: 78 | dependency: transitive 79 | description: flutter 80 | source: sdk 81 | version: "0.0.99" 82 | source_span: 83 | dependency: transitive 84 | description: 85 | name: source_span 86 | url: "https://pub.dartlang.org" 87 | source: hosted 88 | version: "1.5.4" 89 | stack_trace: 90 | dependency: transitive 91 | description: 92 | name: stack_trace 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.9.3" 96 | stream_channel: 97 | dependency: transitive 98 | description: 99 | name: stream_channel 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.6.8" 103 | string_scanner: 104 | dependency: transitive 105 | description: 106 | name: string_scanner 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.0.4" 110 | term_glyph: 111 | dependency: transitive 112 | description: 113 | name: term_glyph 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.1.0" 117 | test_api: 118 | dependency: transitive 119 | description: 120 | name: test_api 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "0.2.2" 124 | typed_data: 125 | dependency: transitive 126 | description: 127 | name: typed_data 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.1.6" 131 | vector_math: 132 | dependency: transitive 133 | description: 134 | name: vector_math 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "2.0.8" 138 | sdks: 139 | dart: ">=2.1.0 <3.0.0" 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_expandable_menu 2 | [![Download](https://img.shields.io/badge/flutter_expandable_menu-0.0.1-blue.svg)](https://pub.dartlang.org/packages/flutter_expandable_menu) 3 | 4 | A new Flutter package which helps developers in creating Expandable Menu. 5 | 6 | ## Getting Started 7 | | With Dark Background | Transparent Background | 8 | |-----------------------------------------|------------------------------------------| 9 | | Screenshot | Screenshot | 10 | 11 | ## Usage 12 | 13 | [Example](https://github.com/UttamPanchasara/FlutterExamples) 14 | 15 | To use this package : 16 | 17 | * add the dependency to your [pubspec.yaml](https://github.com/UttamPanchasara/FlutterExpandableMenu/blob/master/pubspec.yaml) file. 18 | 19 | ```yaml 20 | dependencies: 21 | flutter: 22 | sdk: flutter 23 | flutter_expandable_menu: ^0.0.1 24 | ``` 25 | 26 | ## Quick Start: 27 | In order to use Expandable Menu Widget in your Application you will have to **Put this widget inside the Stack Widget** as below. 28 | 29 | ```dart 30 | 31 | Stack( 32 | children: [ 33 | Center( 34 | child: Text( 35 | centerText, 36 | style: TextStyle(color: Colors.black), 37 | ), 38 | ), 39 | FlutterExpandableMenu( 40 | menuList: floatMenuList, 41 | callback: this, 42 | backgroundColor: Colors.transparent, 43 | menuBackgroundColor: Colors.black, 44 | menuIcon: AnimatedIcons.menu_close, 45 | menuAlignment: Alignment.center, 46 | outerTransitionDuration: Duration(milliseconds: 300), 47 | menusTransitionDuration: Duration(milliseconds: 500), 48 | menusTransitionDelay: Duration(milliseconds: 200), 49 | ), 50 | ], 51 | ) 52 | 53 | ``` 54 | 55 | ## Example - How to use: 56 | 57 | ```dart 58 | 59 | class _RoundMenuState extends State 60 | with TickerProviderStateMixin 61 | implements FloatingMenuCallback { 62 | String centerText = "Home"; 63 | 64 | final List floatMenuList = [ 65 | FloatingMenuItem( 66 | id: 1, icon: Icons.favorite, backgroundColor: Colors.deepOrangeAccent), 67 | FloatingMenuItem(id: 2, icon: Icons.map, backgroundColor: Colors.brown), 68 | FloatingMenuItem(id: 3, icon: Icons.email, backgroundColor: Colors.indigo), 69 | FloatingMenuItem(id: 4, icon: Icons.event, backgroundColor: Colors.pink), 70 | FloatingMenuItem(id: 5, icon: Icons.print, backgroundColor: Colors.green), 71 | FloatingMenuItem( 72 | id: 6, icon: Icons.home, backgroundColor: Colors.deepPurple), 73 | FloatingMenuItem( 74 | id: 7, icon: Icons.location_on, backgroundColor: Colors.blueAccent), 75 | ]; 76 | 77 | @override 78 | Widget build(BuildContext context) { 79 | return Scaffold( 80 | appBar: AppBar( 81 | title: Text('Round Menu'), 82 | leading: new IconButton( 83 | icon: new Icon(Icons.arrow_back, color: Colors.black), 84 | onPressed: () => Navigator.of(context).pop(), 85 | ), 86 | ), 87 | body: Stack( 88 | children: [ 89 | Center( 90 | child: Text( 91 | centerText, 92 | style: TextStyle(color: Colors.black), 93 | ), 94 | ), 95 | FlutterExpandableMenu( 96 | menuList: floatMenuList, 97 | callback: this, 98 | backgroundColor: Colors.transparent, 99 | menuBackgroundColor: Colors.black, 100 | menuIcon: AnimatedIcons.menu_close, 101 | menuAlignment: Alignment.center, 102 | outerTransitionDuration: Duration(milliseconds: 300), 103 | menusTransitionDuration: Duration(milliseconds: 500), 104 | menusTransitionDelay: Duration(milliseconds: 200), 105 | ), 106 | ], 107 | ), 108 | ); 109 | } 110 | 111 | @override 112 | void onMenuClick(FloatingMenuItem floatingMenuItem) { 113 | if (floatingMenuItem != null) { 114 | print("onMenuClicked : " + floatingMenuItem.id.toString()); 115 | switch (floatingMenuItem.id) { 116 | case 1: 117 | { 118 | centerText = "Favorite"; 119 | } 120 | break; 121 | case 2: 122 | { 123 | centerText = "Map"; 124 | } 125 | break; 126 | case 3: 127 | { 128 | centerText = "Email"; 129 | } 130 | break; 131 | case 4: 132 | { 133 | centerText = "Event"; 134 | } 135 | break; 136 | case 5: 137 | { 138 | centerText = "Print"; 139 | } 140 | break; 141 | case 6: 142 | { 143 | centerText = "Home"; 144 | } 145 | break; 146 | case 7: 147 | { 148 | centerText = "Location"; 149 | } 150 | break; 151 | } 152 | 153 | setState(() {}); 154 | } 155 | } 156 | } 157 | 158 | ``` 159 | 160 | ## Questions? 161 | 162 | **Ping-Me on :** [![Twitter](https://img.shields.io/badge/Twitter-%40UTM__Panchasara-blue.svg)](https://twitter.com/UTM_Panchasara) 163 | [![Facebook](https://img.shields.io/badge/Facebook-Uttam%20Panchasara-blue.svg)](https://www.facebook.com/UttamPanchasara94) 164 | 165 | 166 | 167 | profile for Uttam Panchasara at Stack Overflow, Q&A for professional and enthusiast programmers 168 | 169 | 170 | 171 | ## Donate 172 | > If you found this package helpful or you learned something from the source code and want to thank me, consider buying me a cup of :coffee: 173 | - Paypal **https://paypal.me/UttamPanchasara** 174 | 175 | 176 | ## License 177 | 178 | ``` 179 | Copyright 2019 Uttam Panchasara 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | ``` 193 | 194 | -------------------------------------------------------------------------------- /lib/flutter_expandable_menu.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_expandable_menu/floating_menu_callback.dart'; 3 | import 'package:flutter_expandable_menu/floating_menu_item.dart'; 4 | 5 | class FlutterExpandableMenu extends StatefulWidget { 6 | final List menuList; 7 | final AnimatedIconData menuIcon; 8 | final Color backgroundColor; 9 | final Color menuBackgroundColor; 10 | final Duration outerTransitionDuration; 11 | final Duration menusTransitionDuration; 12 | final Duration menusTransitionDelay; 13 | final FloatingMenuCallback callback; 14 | final Alignment menuAlignment; 15 | 16 | const FlutterExpandableMenu( 17 | {Key key, 18 | @required this.menuList, 19 | this.menuIcon, 20 | this.backgroundColor, 21 | this.menuBackgroundColor, 22 | this.outerTransitionDuration, 23 | this.menusTransitionDuration, 24 | this.menusTransitionDelay, 25 | this.menuAlignment, 26 | @required this.callback}) 27 | : super(key: key); 28 | 29 | @override 30 | _FlutterExpandableMenuState createState() => _FlutterExpandableMenuState(); 31 | } 32 | 33 | class _FlutterExpandableMenuState extends State 34 | with TickerProviderStateMixin { 35 | bool isMenuClicked = false; 36 | var maxItemCount = 7; 37 | 38 | FloatingMenuItem selectedMenuItem; 39 | 40 | var myHeight = 0.0; 41 | var myWidth = 0.0; 42 | 43 | AnimationController controller; 44 | 45 | Animation offsetTop; 46 | Animation offsetTopRight; 47 | Animation offsetBottomRight; 48 | Animation offsetBottom; 49 | Animation offsetBottomLeft; 50 | Animation offsetTopLeft; 51 | Animation _animateIcon; 52 | Animation _animateMenu; 53 | 54 | @override 55 | void initState() { 56 | super.initState(); 57 | if (widget.menuList.length == maxItemCount) { 58 | controller = AnimationController( 59 | vsync: this, 60 | duration: widget.menusTransitionDuration != null 61 | ? widget.menusTransitionDuration 62 | : Duration(milliseconds: 150)); 63 | 64 | _animateIcon = Tween(begin: 0.0, end: 1.0).animate(controller); 65 | _animateMenu = CurvedAnimation(parent: controller, curve: Curves.easeIn); 66 | 67 | initOffsetValues(); 68 | } 69 | } 70 | 71 | void initOffsetValues() { 72 | var curve = Curves.linear; 73 | var top = FractionalOffset( 74 | FractionalOffset.topCenter.x, FractionalOffset.topCenter.y) 75 | .alongSize(Size.fromRadius(0.15)); 76 | 77 | offsetTop = Tween(begin: Offset.zero, end: Offset(top.dx, top.dy)) 78 | .animate(CurvedAnimation(parent: controller, curve: curve)); 79 | 80 | var topRight = FractionalOffset( 81 | FractionalOffset.topRight.x, FractionalOffset.topRight.y) 82 | .alongSize(Size.fromRadius(0.14)); 83 | 84 | offsetTopRight = Tween( 85 | begin: Offset.zero, 86 | end: Offset(topRight.dx + 0.05, topRight.dy + 0.15)) 87 | .animate(CurvedAnimation( 88 | parent: controller, curve: Interval(0.16, 1.0, curve: curve))); 89 | 90 | var bottomRight = FractionalOffset( 91 | FractionalOffset.bottomRight.x, FractionalOffset.bottomRight.y) 92 | .alongSize(Size.fromRadius(0.14)); 93 | 94 | offsetBottomRight = Tween( 95 | begin: Offset.zero, 96 | end: Offset(bottomRight.dx + 0.05, bottomRight.dy - 0.15)) 97 | .animate(CurvedAnimation( 98 | parent: controller, curve: Interval(0.32, 1.0, curve: curve))); 99 | 100 | var bottom = FractionalOffset( 101 | FractionalOffset.bottomCenter.x, FractionalOffset.bottomCenter.y) 102 | .alongSize(Size.fromRadius(0.15)); 103 | 104 | offsetBottom = 105 | Tween(begin: Offset.zero, end: Offset(bottom.dx, bottom.dy)) 106 | .animate(CurvedAnimation( 107 | parent: controller, curve: Interval(0.48, 1.0, curve: curve))); 108 | 109 | var bottomLeft = FractionalOffset( 110 | FractionalOffset.bottomLeft.x, FractionalOffset.bottomLeft.y) 111 | .alongSize(Size.fromRadius(0.14)); 112 | 113 | offsetBottomLeft = Tween( 114 | begin: Offset.zero, 115 | end: Offset(bottomLeft.dx - 0.05, bottomLeft.dy - 0.15)) 116 | .animate(CurvedAnimation( 117 | parent: controller, curve: Interval(0.54, 1.0, curve: curve))); 118 | 119 | var topLeft = 120 | FractionalOffset(FractionalOffset.topLeft.x, FractionalOffset.topLeft.y) 121 | .alongSize(Size.fromRadius(0.14)); 122 | 123 | offsetTopLeft = Tween( 124 | begin: Offset.zero, 125 | end: Offset(topLeft.dx - 0.05, topLeft.dy + 0.15)) 126 | .animate(CurvedAnimation( 127 | parent: controller, curve: Interval(0.70, 1.0, curve: curve))); 128 | } 129 | 130 | @override 131 | Widget build(BuildContext context) { 132 | if (widget.menuList.length == maxItemCount) { 133 | return Stack( 134 | children: [ 135 | _showMenu(), 136 | Align( 137 | alignment: Alignment.bottomRight, 138 | child: Container( 139 | margin: EdgeInsets.all(16), 140 | child: FloatingActionButton( 141 | onPressed: () { 142 | _changeMenuVisibility(); 143 | }, 144 | backgroundColor: widget.menuBackgroundColor != null 145 | ? widget.menuBackgroundColor 146 | : Theme.of(context).accentColor, 147 | child: AnimatedIcon( 148 | icon: widget.menuIcon != null 149 | ? widget.menuIcon 150 | : AnimatedIcons.menu_close, 151 | progress: _animateIcon), 152 | ), 153 | ), 154 | ), 155 | ], 156 | ); 157 | } else { 158 | throw ("Menu List must have seven(7) items."); 159 | } 160 | } 161 | 162 | void _changeMenuVisibility() { 163 | isMenuClicked = !isMenuClicked; 164 | if (!isMenuClicked) { 165 | controller.addStatusListener((state) { 166 | if (state == AnimationStatus.dismissed) { 167 | setState(() {}); 168 | if (selectedMenuItem != null) { 169 | widget.callback.onMenuClick(selectedMenuItem); 170 | } 171 | } 172 | }); 173 | controller.reverse(); 174 | } else { 175 | setState(() {}); 176 | } 177 | } 178 | 179 | void _animate() { 180 | Duration delay = widget.menusTransitionDelay != null 181 | ? widget.menusTransitionDelay 182 | : Duration(milliseconds: 400); 183 | 184 | Future.delayed(delay, () { 185 | controller.forward(); 186 | }); 187 | } 188 | 189 | Widget _showMenu() { 190 | if (isMenuClicked) { 191 | myWidth = myHeight = MediaQuery.of(context).size.width; 192 | _animate(); 193 | } else { 194 | myWidth = myHeight = 0.0; 195 | } 196 | return Align( 197 | alignment: widget.menuAlignment != null 198 | ? widget.menuAlignment 199 | : Alignment.center, 200 | child: AnimatedContainer( 201 | duration: widget.outerTransitionDuration != null 202 | ? widget.outerTransitionDuration 203 | : Duration(milliseconds: 300), 204 | decoration: ShapeDecoration( 205 | shape: RoundedRectangleBorder( 206 | borderRadius: BorderRadius.circular(0), 207 | ), 208 | color: widget.backgroundColor != null 209 | ? widget.backgroundColor 210 | : Colors.black, 211 | ), 212 | width: myWidth == 0 ? 0 : MediaQuery.of(context).size.width, 213 | height: myHeight == 0 ? 0 : MediaQuery.of(context).size.height, 214 | child: Align( 215 | alignment: Alignment.center, 216 | child: AnimatedContainer( 217 | width: myWidth, 218 | height: myHeight, 219 | margin: EdgeInsets.all(30), 220 | duration: Duration(milliseconds: 500), 221 | decoration: ShapeDecoration( 222 | shape: CircleBorder(), 223 | color: widget.backgroundColor != null 224 | ? widget.backgroundColor 225 | : Colors.black, 226 | ), 227 | child: isMenuClicked ? _menuItems() : Container(), 228 | ), 229 | ), 230 | ), 231 | ); 232 | } 233 | 234 | Widget _menuItem(int index) { 235 | selectedMenuItem = null; 236 | 237 | var menuItem = widget.menuList[index]; 238 | if (menuItem == null) { 239 | return Container(); 240 | } 241 | Animation offset; 242 | switch (index) { 243 | case 0: 244 | { 245 | offset = offsetTop; 246 | } 247 | break; 248 | case 1: 249 | { 250 | offset = offsetTopRight; 251 | } 252 | break; 253 | case 2: 254 | { 255 | offset = offsetBottomRight; 256 | } 257 | break; 258 | case 3: 259 | { 260 | offset = offsetBottom; 261 | } 262 | break; 263 | case 4: 264 | { 265 | offset = offsetBottomLeft; 266 | } 267 | break; 268 | case 5: 269 | { 270 | offset = offsetTopLeft; 271 | } 272 | break; 273 | } 274 | 275 | if (index != (maxItemCount - 1)) { 276 | return SlideTransition( 277 | position: offset, 278 | child: Center( 279 | child: FloatingActionButton( 280 | onPressed: () { 281 | selectedMenuItem = menuItem; 282 | _changeMenuVisibility(); 283 | }, 284 | backgroundColor: menuItem.backgroundColor != null 285 | ? menuItem.backgroundColor 286 | : Theme.of(context).accentColor, 287 | child: Icon(menuItem.icon), 288 | ), 289 | ), 290 | ); 291 | } else { 292 | return Center( 293 | child: FloatingActionButton( 294 | onPressed: () { 295 | selectedMenuItem = menuItem; 296 | _changeMenuVisibility(); 297 | }, 298 | backgroundColor: menuItem.backgroundColor != null 299 | ? menuItem.backgroundColor 300 | : Theme.of(context).accentColor, 301 | child: Icon(menuItem.icon), 302 | ), 303 | ); 304 | } 305 | } 306 | 307 | Widget _menuItems() { 308 | return Stack( 309 | children: [ 310 | Stack( 311 | children: [ 312 | FadeTransition(opacity: _animateMenu, child: _menuItem(0)), 313 | FadeTransition(opacity: _animateMenu, child: _menuItem(1)), 314 | FadeTransition(opacity: _animateMenu, child: _menuItem(2)), 315 | FadeTransition(opacity: _animateMenu, child: _menuItem(3)), 316 | FadeTransition(opacity: _animateMenu, child: _menuItem(4)), 317 | FadeTransition(opacity: _animateMenu, child: _menuItem(5)), 318 | FadeTransition(opacity: _animateMenu, child: _menuItem(6)), 319 | ], 320 | ), 321 | ], 322 | ); 323 | } 324 | } 325 | --------------------------------------------------------------------------------