Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make the Bottom Navigation Bar overlay the main page in Flutter?

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?

like image 278
Athus Vieira Avatar asked Jan 30 '19 22:01

Athus Vieira


1 Answers

use "extendBody: true" inside "Scaffold" class

Scaffold(
            extendBody: true,
            body: Container()
like image 113
Fabio Hideki Avatar answered Oct 05 '22 07:10

Fabio Hideki