fluttercustomizationshapestrianglepopupmenubutton

Custom shape of PopupMenuButton in flutter


I wan to change the shape of my PopupMenuButton in flutter, want to add a triangle on top as shown in below picture, I have spent a lot of time on google but no achievement please help me out I am new to flutter so I do not know how to change this default container, now it simply has white rounded container, white arrow/triangle is not being added on top of it. please help out, thanks in advance

 popUpMenu= PopupMenuButton<String>(
     key: _menuKey,
     offset: Offset(50,100),
     padding: EdgeInsets.all(0.0),
     onSelected: (value) {
       if (value == "Tax & Additional Charges") {
         endDrawerController.drawerKey.value =
             EndDrawerKeys.TaxAndAdditionalChargesEndDrawer;
         endDrawerController.scaffoldKey.currentState.openEndDrawer();
         print("Entering in tax");
       } else if (value == "Hold this Invoice") {
         endDrawerController.drawerKey.value =
             EndDrawerKeys.HoldInvoiceEndDrawer;
         endDrawerController.scaffoldKey.currentState.openEndDrawer();
       }
     },
     shape: RoundedRectangleBorder(
         borderRadius: BorderRadius.all(Radius.circular(10.h))),
     itemBuilder: (context) => [
       PopupMenuItem(
         value: "Tax & Additional Charges",
         child: popUpMenuSingleItem(
             icon: AppAssets.DeliveryIcon,
             text: "Tax & Additional Charges",
             topMargin: 15.h),
       ),
       PopupMenuItem(
         value: "Hold this Invoice",
         child: popUpMenuSingleItem(
             icon: AppAssets.DeliveryIcon,
             text: "Hold this Invoice",
             topMargin: 25.h),
       ),
     ],
    );

This is how I want my PopupMenuButton to be appear

enter image description here


Solution

  • You can create a shape for your custom PopupMenuButton.

    Sample...

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          home: HomePage(),
        );
      }
    }
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            actions: <Widget>[
              PopupMenuButton(
                offset: Offset(0, 50),
                shape: const TooltipShape(),
                itemBuilder: (_) => <PopupMenuEntry>[
                  PopupMenuItem(
                      child: ListTile(
                    leading: const Icon(Icons.calculate_outlined),
                    title: const Text('Tax & Additional Charges'),
                  )),
                  PopupMenuItem(
                      child: ListTile(
                    leading: const Icon(Icons.av_timer_outlined),
                    title: const Text('Hold This Invoice'),
                  )),
                ],
              ),
            ],
          ),
        );
      }
    }
    
    /// I'm using [RoundedRectangleBorder] as my reference...
    class TooltipShape extends ShapeBorder {
      const TooltipShape();
    
      final BorderSide _side = BorderSide.none;
      final BorderRadiusGeometry _borderRadius = BorderRadius.zero;
    
      @override
      EdgeInsetsGeometry get dimensions => EdgeInsets.all(_side.width);
    
      @override
      Path getInnerPath(
        Rect rect, {
        TextDirection? textDirection,
      }) {
        final Path path = Path();
    
        path.addRRect(
          _borderRadius.resolve(textDirection).toRRect(rect).deflate(_side.width),
        );
    
        return path;
      }
    
      @override
      Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
        final Path path = Path();
        final RRect rrect = _borderRadius.resolve(textDirection).toRRect(rect);
    
        path.moveTo(0, 10);
        path.quadraticBezierTo(0, 0, 10, 0);
        path.lineTo(rrect.width - 30, 0);
        path.lineTo(rrect.width - 20, -10);
        path.lineTo(rrect.width - 10, 0);
        path.quadraticBezierTo(rrect.width, 0, rrect.width, 10);
        path.lineTo(rrect.width, rrect.height - 10);
        path.quadraticBezierTo(
            rrect.width, rrect.height, rrect.width - 10, rrect.height);
        path.lineTo(10, rrect.height);
        path.quadraticBezierTo(0, rrect.height, 0, rrect.height - 10);
    
        return path;
      }
    
      @override
      void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
    
      @override
      ShapeBorder scale(double t) => RoundedRectangleBorder(
            side: _side.scale(t),
            borderRadius: _borderRadius * t,
          );
    }