I'm building an app that has a Bottom Navigation Bar and I want to provide to this bar the ability to overlay the main page when I change its height in order to prevent the contents in the main page to be reduced to fit the available area. How can I do that?
I think that explaining it by images could be better, so there we go:
This is how the main page looks like together with the Bottom Navigation Bar:
Normal App:
When I click in the Yellow Circle Button (middle button), the BottomAppBar
has to increase its height, but when I do this, the main content reduces its size to fit the available space.
Weird App:
I want to prevent it. How can I do it?
This is my code:
class HomePage extends StatefulWidget {
HomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _appBarHeight = 60.0;
void _openBottomAppBar() {
setState(() {
if (_appBarHeight > 60) {
_appBarHeight = 60.0;
} else {
_appBarHeight = 300.0;
}
});
}
@override
Widget build(BuildContext context) {
final Shader textGradient = LinearGradient(
colors: <Color>[Theme.of(context).primaryColorDark, Theme.of(context).primaryColorLight],
).createShader(Rect.fromLTWH(0.0, 0.0, 200.0, 70.0));
return Scaffold(
body: Container(
child: Column(
children: <Widget>[
Expanded(
flex: 3,
child: ClipShadowPath(
clipper: ApplicationBar(),
shadow: Shadow(blurRadius: 10),
child: LayoutBuilder(builder: (context, constraints) {
return Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: AssetImage(
'images/tiny-squares.png',
),
repeat: ImageRepeat.repeat,
),
),
),
Container(
decoration: BoxDecoration(
backgroundBlendMode: BlendMode.multiply,
gradient: LinearGradient(
begin: const Alignment(-1.0, 0.0),
end: const Alignment(0.6, 0.0),
colors: <Color>[Theme.of(context).primaryColorDark, Theme.of(context).primaryColorLight],
),
),
),
Positioned(
right: constraints.biggest.width * 0.1,
top: constraints.biggest.height * 0.35,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: Material(
elevation: 4.0,
clipBehavior: Clip.antiAliasWithSaveLayer,
type: MaterialType.circle,
color: Colors.transparent,
)),
Padding(
padding: EdgeInsets.only(top: 8),
),
],
))
],
);
})),
),
Expanded(
flex: 4,
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.only(top: 16, left: 64),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 8),
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
Padding(
padding: EdgeInsets.only(top: 8),
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
),
)
],
),
),
],
),
))
],
)),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
onPressed: _openBottomAppBar,
tooltip: 'Increment',
child: Text('A'),
elevation: 10.0,
),
bottomNavigationBar: NotchedBottomBar(
centerItemText: '',
height: _appBarHeight,
color: Colors.grey,
selectedColor: Theme.of(context).accentColor,
notchedShape: FollowerNotchedShape(inverted: true),
items: [
NotchedBottomBarItem(iconData: Icons.home, text: 'This'),
NotchedBottomBarItem(iconData: Icons.payment, text: 'is'),
NotchedBottomBarItem(iconData: Icons.input, text: 'Bottom'),
NotchedBottomBarItem(iconData: Icons.settings_applications, text: 'Bar'),
],
),
);
}
}
class ApplicationBar extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final path = Path();
path.lineTo(size.width, 0);
path.lineTo(size.width, 0.84 * size.height);
path.lineTo(0.72 * size.width, 0.983 * size.height);
final firstEndPoint = new Offset(0.7 * size.width, (0.986 + 0.0065) * size.height);
final controlPoint = new Offset(0.675 * size.width, 0.989 * size.height);
path.quadraticBezierTo(firstEndPoint.dx, firstEndPoint.dy, controlPoint.dx, controlPoint.dy);
path.lineTo(0, 0.87 * size.height);
path.close();
return path;
}
@override
bool shouldReclip(YEXApplicationBar oldClipper) {
return oldClipper != this;
}
}
Code for the Bottom Bar
import 'package:flutter/material.dart';
import 'dart:math' as math;
class FollowerNotchedShape extends CircularNotchedRectangle {
int _inverterMultiplier;
@override
Path getOuterPath(Rect host, Rect guest) {
if (!host.overlaps(guest)) return Path()..addRect(host);
final double notchRadius = guest.width / 2.0;
const double s1 = 15.0;
const double s2 = 1.0;
final double r = notchRadius;
final double a = -1.0 * r - s2;
final double b = host.top - guest.center.dy;
final double n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r));
final double p2xA = ((a * r * r) - n2) / (a * a + b * b);
final double p2xB = ((a * r * r) + n2) / (a * a + b * b);
final double p2yA = math.sqrt(r * r - p2xA * p2xA);
final double p2yB = math.sqrt(r * r - p2xB * p2xB);
final List<Offset> p = List<Offset>(6);
// p0, p1, and p2 are the control points for segment A.
p[0] = Offset(a - s1, b);
p[1] = Offset(a, b);
final double cmp = b < 0 ? -1.0 : 1.0;
p[2] =
cmp * p2yA > cmp * p2yB ? Offset(p2xA, _inverterMultiplier * p2yA) : Offset(p2xB, _inverterMultiplier * p2yB);
// p3, p4, and p5 are the control points for segment B, which is a mirror
// of segment A around the y axis.
p[3] = Offset(-1.0 * p[2].dx, p[2].dy);
p[4] = Offset(-1.0 * p[1].dx, p[1].dy);
p[5] = Offset(-1.0 * p[0].dx, p[0].dy);
// translate all points back to the absolute coordinate system.
for (int i = 0; i < p.length; i += 1) p[i] += guest.center;
final Path path = Path()
..moveTo(host.left, -host.top)
..lineTo(p[0].dx, -p[0].dy)
..quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy);
if (guest.height == guest.width) {
// circle
path.arcToPoint(
p[3],
radius: Radius.circular(notchRadius),
clockwise: _inverterMultiplier == 1 ? false : true,
);
} else {
// stadium
path
..arcToPoint(
(_inverterMultiplier == 1 ? guest.bottomLeft : guest.topLeft) + Offset(guest.height / 2, 0), // here
radius: Radius.circular(guest.height / 2),
clockwise: _inverterMultiplier == 1 ? false : true,
)
..lineTo(guest.right - guest.height / 2, (_inverterMultiplier == 1 ? guest.bottom : guest.top)) // here
..arcToPoint(
p[3],
radius: Radius.circular(guest.height / 2),
clockwise: _inverterMultiplier == 1 ? false : true,
);
}
path
..quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy)
..lineTo(host.right, host.top)
..lineTo(host.right, host.bottom)
..lineTo(host.left, host.bottom)
..close();
return path;
}
FollowerNotchedShape({inverted: false}) {
if (inverted) {
_inverterMultiplier = -1;
} else
_inverterMultiplier = 1;
}
}
class NotchedBottomBarItem {
NotchedBottomBarItem({this.iconData, this.text});
IconData iconData;
String text;
}
class NotchedBottomBar extends StatefulWidget {
NotchedBottomBar({
this.items,
this.centerItemText,
this.height: 60.0,
this.iconSize: 24.0,
this.backgroundColor,
this.color,
this.selectedColor,
this.notchedShape,
this.onTabSelected,
}) {
assert(this.items.length == 2 || this.items.length == 4);
}
final List<NotchedBottomBarItem> items;
final String centerItemText;
double height;
final double iconSize;
final Color backgroundColor;
final Color color;
final Color selectedColor;
final NotchedShape notchedShape;
final ValueChanged<int> onTabSelected;
@override
State<StatefulWidget> createState() => NotchedBottomBarState();
}
class NotchedBottomBarState extends State<NotchedBottomBar> {
int _selectedIndex = 0;
_updateIndex(int index) {
widget.onTabSelected(index);
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
List<Widget> items = List.generate(widget.items.length, (int index) {
return _buildTabItem(
item: widget.items[index],
index: index,
onPressed: _updateIndex,
);
});
items.insert(items.length >> 1, _buildMiddleTabItem());
return BottomAppBar(
shape: widget.notchedShape,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: items,
),
color: widget.backgroundColor,
);
}
Widget _buildMiddleTabItem() {
return Expanded(
child: SizedBox(
height: widget.height,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: widget.iconSize),
Text(
widget.centerItemText ?? '',
style: TextStyle(color: widget.color),
),
],
),
),
);
}
Widget _buildTabItem({
NotchedBottomBarItem item,
int index,
ValueChanged<int> onPressed,
}) {
Color color = _selectedIndex == index ? widget.selectedColor : widget.color;
return Expanded(
child: SizedBox(
height: widget.height,
child: Material(
type: MaterialType.transparency,
color: Colors.transparent,
child: InkWell(
onTap: () {
onPressed(index);
},
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 8),
),
Icon(item.iconData, color: color, size: widget.iconSize),
Padding(
padding: EdgeInsets.only(bottom: 4),
),
Text(
item.text,
style: TextStyle(color: color),
)
],
),
),
),
),
);
}
}
Any help?
use "extendBody: true" inside "Scaffold" class
Scaffold(
extendBody: true,
body: Container()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With