dart 如何使用Flutter创建自定义弹出菜单

vsnjm48y  于 11个月前  发布在  Flutter
关注(0)|答案(5)|浏览(136)

我想创建一个弹出菜单时,从应用程序栏上点击按钮。.我想这样的东西出现:


的数据
有没有办法在Flutter中做到这一点?一个包或什么?

qeeaahzv

qeeaahzv1#

我试过了,但我遇到了一些问题,以这种方式显示子部件。所以,这里有两个解决方案:

class TestScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> with SingleTickerProviderStateMixin {
  AnimationController animationController;
  bool _menuShown = false;

  @override
  void initState() {
    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {    
    Animation opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(animationController);
    if (_menuShown)
      animationController.forward();
    else
      animationController.reverse();
    return Scaffold(
      appBar: AppBar(
        actions: <Widget>[IconButton(icon: Icon(Icons.menu), onPressed: (){
          setState(() {
            _menuShown = !_menuShown;
          });
        })],
      ),
      body: Stack(
        overflow: Overflow.visible,
        children: <Widget>[
          Positioned(
            child: FadeTransition(
              opacity: opacityAnimation,
              child: _ShapedWidget(),
            ),
            right: 4.0,
            top: 16.0,
          ),
        ],
      ),
    );
  }
}

class _ShapedWidget extends StatelessWidget {
  _ShapedWidget();
  final double padding = 4.0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Material(
          clipBehavior: Clip.antiAlias,
          shape:
          _ShapedWidgetBorder(borderRadius: BorderRadius.all(Radius.circular(padding)), padding: padding),
          elevation: 4.0,
          child: Container(
            padding: EdgeInsets.all(padding).copyWith(bottom: padding * 2),
            child: SizedBox(width: 150.0, height: 250.0, child: Center(child: Text('ShapedWidget'),),),
          )),
    );
  }
}

class _ShapedWidgetBorder extends RoundedRectangleBorder {
  _ShapedWidgetBorder({
    @required this.padding,
    side = BorderSide.none,
    borderRadius = BorderRadius.zero,
  }) : super(side: side, borderRadius: borderRadius);
  final double padding;

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    return Path()
      ..moveTo(rect.width - 8.0 , rect.top)
      ..lineTo(rect.width - 20.0, rect.top - 16.0)
      ..lineTo(rect.width - 32.0, rect.top)
      ..addRRect(borderRadius
          .resolve(textDirection)
          .toRRect(Rect.fromLTWH(rect.left, rect.top, rect.width, rect.height - padding)));
  }
}

字符串
在本例中,subwidget位于appbar下方

class TestScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> with SingleTickerProviderStateMixin {
  AnimationController animationController;
  bool _menuShown = false;

  @override
  void initState() {

    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {

    Animation opacityAnimation = Tween(begin: 0.0, end: 1.0).animate(animationController);
    if (_menuShown)
      animationController.forward();
    else
      animationController.reverse();
    return Scaffold(
      appBar: AppBar(
        elevation: 0.0,
        actions: <Widget>[Stack(
          overflow: Overflow.visible,
          children: <Widget>[IconButton(icon: Icon(Icons.menu), onPressed: (){
          setState(() {
            _menuShown = !_menuShown;
          });    
        }),
          Positioned(
            child: FadeTransition(
              opacity: opacityAnimation,
              child: _ShapedWidget(onlyTop: true,),
            ),
            right: 4.0,
            top: 48.0,
          ),    
          ],)],
      ),
      body: Stack(
        overflow: Overflow.visible,
        children: <Widget>[
          Positioned(
            child: FadeTransition(
              opacity: opacityAnimation,
              child: _ShapedWidget(),
            ),
            right: 4.0,
            top: -4.0,
          ),
        ],
      ),
    );
  }
}  

class _ShapedWidget extends StatelessWidget {
  _ShapedWidget({this.onlyTop = false});
  final double padding = 4.0;
  final bool onlyTop;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Material(
          clipBehavior: Clip.antiAlias,
          shape:
          _ShapedWidgetBorder(borderRadius: BorderRadius.all(Radius.circular(padding)), padding: padding),
          elevation: 4.0,
          child: Container(
            padding: EdgeInsets.all(padding).copyWith(bottom: padding * 2),
            child: onlyTop ? SizedBox(width: 150.0, height: 20.0,) :  SizedBox(width: 150.0, height: 250.0, child: Center(child: Text('ShapedWidget'),),),
          )),
    );
  }
}

class _ShapedWidgetBorder extends RoundedRectangleBorder {
  _ShapedWidgetBorder({
    @required this.padding,
    side = BorderSide.none,
    borderRadius = BorderRadius.zero,
  }) : super(side: side, borderRadius: borderRadius);
  final double padding;

  @override
  Path getOuterPath(Rect rect, {TextDirection textDirection}) {
    return Path()
      ..moveTo(rect.width - 8.0 , rect.top)
      ..lineTo(rect.width - 20.0, rect.top - 16.0)
      ..lineTo(rect.width - 32.0, rect.top)
      ..addRRect(borderRadius
          .resolve(textDirection)
          .toRRect(Rect.fromLTWH(rect.left, rect.top, rect.width, rect.height - padding)));
  }
}


在本例中,子部件的顶部位于appbar上,但appbar必须具有0.0的高程
实际上,在我看来,这两个解决方案都不完整,但它可以帮助你找到你需要的东西

mepcadol

mepcadol2#

现在回答可能为时已晚。但这可以通过使用OverlayEntry小部件来简单地实现。我们创建一个该形状的小部件,并将其传递给OverlayEntry小部件,然后使用Overlay.of(context).insert(overlayEntry)显示覆盖,并使用overlayEntry.remove方法删除它。
下面是创建Custom DropDown Menu的中间链接
希望这有帮助!

azpvetkf

azpvetkf3#

有一个名为flutter_portal的软件包,它的工作方式类似于Overlay/OverlayEntry,但以声明的方式工作。您可以使用它来实现自定义工具提示,上下文菜单或对话框。

kkbh8khc

kkbh8khc4#

CustomPopupMenu(
                  pressType: PressType.singleClick,
                  controller: menu,
                  arrowColor: AppColor.white,
                  menuBuilder: () => ClipRect(
                      clipBehavior: Clip.hardEdge,
                      child: Container(
                        height: MediaQuery.of(context).size.height *
                            ComponentSize.container1height,
                        width: MediaQuery.of(context).size.width *
                            ComponentSize.conatiner1width,
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(
                              ComponentSize.borderradius),
                          color: AppColor.white,
                        ),
                        child: ListView.builder(
                          itemCount: Details.length,
                          itemBuilder: (context, index) {
                            return Column(
                              children: [
                                InkWell(
                                  onTap: () {
                                   
                                   do somthing

                                  },
                                  child: Column(
                                    children: [
                                      Container(
                                        padding: EdgeInsets.only(
                                            left:
                                                ComponentSize.paddingleft),
                                        alignment: Alignment.centerLeft,
                                        child: Text(
                                          Details[index],
                                          style: const TextStyle(
                                              color: Colors.black,
                                              fontFamily: 'Taml_001'),
                                          textAlign: TextAlign.start,
                                        ),
                                      ),
                                      Container(
                                        alignment: Alignment.centerLeft,
                                        padding: EdgeInsets.only(
                                            left:
                                                ComponentSize.paddingleft),
                                        child: Text(Details[index],
                                            style: TextStyle(
                                                color: AppColor.black
                                                    .withOpacity(
                                                        ComponentSize
                                                            .opacity1),
                                                fontSize: ComponentSize
                                                    .containerfontsize)),
                                      )
                                    ],
                                  ),
                                ),
                                const Divider(),
                              ],
                            );
                          },
                        ),
                      )),
                  child: Container(
                    color: AppColor.white,
                    padding: EdgeInsets.only(
                        top: ComponentSize.paddingbottom,
                        bottom: ComponentSize.paddingtop,
                        left: ComponentSize.padding1left),
                    width: ComponentSize.container2width,
                    height: ComponentSize.container2height,
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        SizedBox(
                          child: Column(
                            mainAxisAlignment:
                                MainAxisAlignment.spaceBetween,
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              SizedBox(
                                width: ComponentSize.textcontainerwidth,
                                height: ComponentSize.textcontainerheight,
                                child: SingleChildScrollView(
                                  scrollDirection: Axis.horizontal,
                                  child: Text(
                                    Tamil,
                                    style: const TextStyle(
                                        color: Colors.black,
                                        fontFamily: 'Taml_001'),
                                  ),
                                ),
                              ),
                              SizedBox(
                                width: ComponentSize.textcontainerwidth,
                                height: ComponentSize.textcontainerheight,
                                child: SingleChildScrollView(
                                  scrollDirection: Axis.horizontal,
                                  child: Text(
                                    English,
                                    style: const TextStyle(
                                        color: Colors.black),
                                  ),
                                ),
                              )
                            ],
                          ),
                        ),
                        SizedBox(
                          child: Icon(
                            Icons.expand_more,
                            size: ComponentSize.iconarrowsize,
                            color: Colors.black,
                          ),
                        )
                      ],
                    ),
                  ),
                ),

字符串

yzxexxkh

yzxexxkh5#

This is custom Tooltipwidget. You can apply this widget to shape attribute. For example PopupmenuButton(shape: TooltipShapeWidget());

  class TooltipShapeWidget extends ShapeBorder {
  const TooltipShapeWidget();

  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.addRRect(
  
_borderRadius.resolve(textDirection).toRRect(rect).deflate(_side.width),
    );

    return path;
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    final path = Path();
    final 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,
      );
}

字符串

相关问题