Reputation: 925
I am trying to build an app that includes a bottom navigation bar for navigating between screens.
In the middle of the bottom navigation bar, I want to add a button which expands the bottom navigation bar with a semicircle, and revels more buttons.
I've read the documentation of the bottom navigation bar, and searched a lot in pub.dev if there is something similar I can use, but I couldn't find any.
Does anyone know if it's achievable, and if so, how?
Thank you very much
Upvotes: 0
Views: 1693
Reputation: 140
I think you need to learn about Flutter Animated Radial Menu and how to implement it in your code, you can go with this article and try to implement in your way.
Upvotes: 0
Reputation: 3326
You can check this simple implementation with showDialog
and CustomPainter
. Basically it involved displaying a showDialog
with bottom padding equals the height of BottomNavigationBar
, then arrange the items within a Stack
. The half circle is drawn using CustomPainter
.
Full example app:
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test App'),
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.blue,
showUnselectedLabels: true,
selectedFontSize: 14,
unselectedFontSize: 14,
type: BottomNavigationBarType.fixed,
onTap: (index) {
if (index == 2) {
final diameter = 200.0;
final iconSize = 40;
showDialog(
context: context,
barrierDismissible: true,
barrierColor: Colors.grey.withOpacity(0.1),
builder: (context) => Material(
color: Colors.transparent,
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
Container(
width: diameter + iconSize,
height: diameter / 1.5,
alignment: Alignment.bottomCenter,
margin:
EdgeInsets.only(bottom: kBottomNavigationBarHeight),
child: Stack(
children: [
Container(
alignment: Alignment.bottomCenter,
child: MyArc(diameter: diameter)),
Positioned(
left: 0,
bottom: 10,
child: _buildButton(),
),
Positioned(
left: diameter / 4,
top: 10,
child: _buildButton(),
),
Positioned(
right: diameter / 4,
top: 10,
child: _buildButton(),
),
Positioned(
right: 0,
bottom: 10,
child: _buildButton(),
)
],
),
),
],
),
),
);
}
},
items: List<BottomNavigationBarItem>.generate(
5,
(index) =>
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
),
),
);
}
_buildButton() {
return Container(
constraints: BoxConstraints.tightFor(width: 40, height: 60),
child: Column(
children: [
Text(
'Title',
style: TextStyle(fontSize: 12),
),
SizedBox(height: 3),
CircleAvatar(
backgroundColor: Colors.white,
child: Icon(Icons.home),
),
],
),
);
}
}
class MyArc extends StatelessWidget {
final double diameter;
const MyArc({Key key, this.diameter = 200}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: MyPainter(),
size: Size(diameter, diameter),
);
}
}
// This is the Painter class
class MyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..shader = RadialGradient(
colors: [
Colors.blue,
Colors.purpleAccent.withOpacity(0.4),
],
).createShader(Rect.fromCircle(
center: Offset(size.width / 2, size.height),
radius: 200,
));
canvas.drawArc(
Rect.fromCenter(
center: Offset(size.width / 2, size.height),
height: size.height * 1.5,
width: size.width,
),
math.pi,
math.pi,
false,
paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
Upvotes: 2