Search code examples
flutterflutter-bottomnavigation

Is there a way I can create a bottom navigation menu like what is shown n the attachment using flutter


I've spent a lot of time googling on how I can achieve this but failed, maybe I'm googling it wrong, I tried using path, but failed.

I just want all items to change colors only when pressed, and the center docked will retain its shape and everything.

Please anyone who can shade some lights on this will be appreciated. Thank you

enter image description here

Here what I've tried to put together with google help. I just want to get exactly like what is shown on the attachment but my knowledge is limited at the moment.

 return Scaffold(
          backgroundColor: Color(0xFFFFFFFF),
          bottomNavigationBar: Material(
            color: Colors.transparent,
            elevation: 100,
            child: ClipPath(
              child: SizedBox(
                child: Container(
                    decoration: BoxDecoration(
                      color: Colors.white,
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: <Widget>[
                        IconButton(
                            icon: Icon(
                              Icons.home,
                              color: Colors.deepPurple,
                              size: 30,
                            ),
                            onPressed: () {
                              setState() {}
                            }),
                        IconButton(
                            icon: Icon(
                              Icons.branding_watermark,
                              size: 30,
                              color: Colors.black54,
                            ),
                            onPressed: () {}),
                        SizedBox(
                          width: 50,
                        ),
                        IconButton(
                            icon: Icon(
                              Icons.cloud_download,
                              size: 30,
                              color: Colors.black54,
                            ),
                            onPressed: () {}),
                        IconButton(
                            icon: Icon(
                              Icons.face,
                              size: 30,
                              color: Colors.black54,
                            ),
                            onPressed: () {}),
                      ],
                    )),
                height: 100,
                width: double.infinity,
              ),
              clipper: CurveDraw(),
            ),
          ),
          floatingActionButton: MaterialButton(
            color: Colors.deepPurple,
            padding: EdgeInsets.all(20),
            onPressed: () {},
            shape: CircleBorder(),
            child: Icon(
              Icons.add,
              size: 35,
              color: Colors.white,
            ),
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        );

Curve

getClip(Size size) {
    double sw = size.width;
    double sh = size.height;
    double gapConst = 50;
    Path path = Path();
    path.moveTo(0, sh);
    path.lineTo(0, sh / 2);
    path.quadraticBezierTo(0, 0, sh / 2, 0); //1st curve
    path.lineTo(sw / 2 - sw / 5, 0);
    path.cubicTo(sw / 2 - sw / 8, 0, sw / 2 - sw / 8, sh / 2, sw / 2, sh / 2);
    path.cubicTo(
        sw / 2 + sw / 8, sh / 2, sw / 2 + sw / 8, 0, sw / 2 + sw / 5, 0);

    path.lineTo(sw - sh / 2, 0);

    path.quadraticBezierTo(sw, 0, size.width, sh / 2);
    path.lineTo(sw, sh);
    path.close();

    // rotate the path around the x-axis (flip it upside down)
    path.transform(Matrix4.rotationX(pi));

    return path;
  }

Solution

  • I have implemented this bottom navigation bar without any 3rd party dependency, you can check this out:

    Also don't forget to add vector_math dependecy in your pubspec and this import line at the top of the widget:

    import 'package:vector_math/vector_math.dart' show radians;

    class DemoWidget extends StatefulWidget {
      const DemoWidget({super.key});
    
      @override
      State<DemoWidget> createState() => _DemoWidgetState();
    }
    
    class _DemoWidgetState extends State<DemoWidget> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: const Color.fromARGB(255, 82, 49, 49),
          bottomNavigationBar: Container(
              height: 100,
              width: double.infinity,
              decoration: const BoxDecoration(
                color: Colors.white,
              ),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  IconButton(
                      icon: const Icon(
                        Icons.home,
                        color: Colors.deepPurple,
                        size: 30,
                      ),
                      onPressed: () {
                        setState() {}
                      }),
                  IconButton(
                      icon: const Icon(
                        Icons.branding_watermark,
                        size: 30,
                        color: Colors.black54,
                      ),
                      onPressed: () {}),
                  const SizedBox(
                    width: 50,
                  ),
                  IconButton(
                      icon: const Icon(
                        Icons.cloud_download,
                        size: 30,
                        color: Colors.black54,
                      ),
                      onPressed: () {}),
                  IconButton(
                      icon: const Icon(
                        Icons.face,
                        size: 30,
                        color: Colors.black54,
                      ),
                      onPressed: () {}),
                ],
              )),
          floatingActionButton: CustomPaint(
            painter: HalfPainter(Colors.white),
            child: Padding(
              padding: const EdgeInsets.only(left: 18, right: 18, top: 30),
              child: MaterialButton(
                color: Colors.deepPurple,
                padding: const EdgeInsets.all(20),
                onPressed: () {},
                shape: const CircleBorder(),
                child: const Icon(
                  Icons.add,
                  size: 35,
                  color: Colors.white,
                ),
              ),
            ),
          ),
          floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        );
      }
    }
    
    class HalfPainter extends CustomPainter {
      HalfPainter(Color paintColor) {
        this.arcPaint = Paint()..color = paintColor;
      }
    
      late Paint arcPaint;
    
      @override
      void paint(Canvas canvas, Size size) {
        final Rect beforeRect = Rect.fromLTWH(0, (size.height / 2) - 10, 10, 10);
        final Rect largeRect = Rect.fromLTWH(10, 16, size.width - 20, 73);
        final Rect afterRect =
            Rect.fromLTWH(size.width - 10, (size.height / 2) - 10, 10, 10);
    
        final path = Path();
        path.arcTo(beforeRect, radians(0), radians(90), false);
        path.lineTo(20, size.height / 2);
        path.arcTo(largeRect, radians(0), -radians(180), false);
        path.moveTo(size.width - 10, size.height / 2);
        path.lineTo(size.width - 10, (size.height / 2) - 20);
        path.arcTo(afterRect, radians(180), radians(-100), false);
        path.close();
    
        canvas.drawPath(path, arcPaint);
      }
    
      @override
      bool shouldRepaint(CustomPainter oldDelegate) {
        return true;
      }
    }