简体   繁体   中英

How to achieve an expandable bottom navigation bar in Flutter

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

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;
}

Result:
在此处输入图像描述

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM