flutter PopupMenuButton菜单始终与屏幕侧面有间隙

ua4mk5z4  于 2023-05-30  发布在  Flutter
关注(0)|答案(3)|浏览(296)

我想将PopupMenuButton的菜单放在应用的侧面,但它总是与侧面留下大约10个逻辑像素的间隙:

我已经尝试给予offset属性一个负的dx值,但这似乎没有任何作用。据我所知,PopupMenuButton上没有其他属性可以改变菜单的位置。
如何将菜单准确地定位在侧面?我错过了什么明显的东西吗?
顺便说一句:PopupMenuButton位于ScaffoldAppBar中。
下面是我的PopupMenuButton代码:

PopupMenuButton(
  color: cardBackground,
  elevation: 0.0,
  shape: RoundedRectangleBorder(
    side: BorderSide(
      width: 0.5,
      color: cardText,
  )),
  padding: EdgeInsets.all(0.0),
  offset: Offset(-10.0, kToolbarHeight),
  onSelected: (value) => doPaletteAction(value),
  itemBuilder: (BuildContext context) => createPopUpItems(),
  icon: Icon(
    Icons.format_list_bulleted_rounded,
    color: appBarIconButtons,
  )
)
8ehkhllq

8ehkhllq1#

材料弹出菜单边距是硬编码的:PopupMenuButton > PopupMenuButtonState > showMenu > _PopupMenuRoute > _PopupMenuRouteLayout > _kMenuScreenPadding = 8.0
你能做的是
1.玩转:

  • 打开本地popup_menu.dart源文件(在IDE中按住Ctrl键并单击PopupMenuButton
  • 将8.0替换为0.0,进行热重新加载,观察变化
  • 记住回滚源代码以保持一致的行为

1.进行可靠的修复:

  • 复制文件到您的项目,做修复
    • 同时删除相对导入并添加import 'package:flutter/material.dart'; *
  • 然后使用你固定的“fork”代替原来的:
import 'package:myapp/popup_menu.dart' as my_popup_menu;

Scaffold(
  appBar: AppBar(
    elevation: 0,
    leading: my_popup_menu.PopupMenuButton(
      elevation: 0,
      shape: Border.all(width: 0.5),
      offset: Offset(0, kToolbarHeight),
      itemBuilder: (context) => [
        my_popup_menu.PopupMenuItem(
          child: Text('PopupMenuItem'),
        ),
      ],
    ),
  ),
)
0h4hbjxa

0h4hbjxa2#

问题出在popup_menu.dart:

const double _kMenuScreenPadding = 8.0;

填充是硬编码的,但是您可以在项目中创建一个替代CustomPopupMenuButton并更改常量值,如下面的gist
在代码中添加该类后,只需将实现切换为:

CustomPopupMenuButton(...)

弹出菜单按钮:

CustomPopupMenuButton:

yjghlzjz

yjghlzjz3#


自定义弹出菜单在我的Pixel3上看起来像这样。在另一部手机上看起来可能不同。
我不想浪费你的时间,所以你可以复制粘贴下面的代码。下面还有更长的解释
甲状腺素
创建覆盖条目并在按下菜单按钮时显示它。。您可以添加填充和其他所有内容。

import 'package:flutter/material.dart';

class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  GlobalKey _globalKey = new GlobalKey();
  OverlayEntry _overlayEntry;
  Size menuSize;
  Offset menuOffset;

  bool isMenuVisible = false;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        leading: GestureDetector(
            onTap: (){
              (isMenuVisible) ? CloseCustomPopUpMenu() : OpenCustomPopUpMenu();
            },
          child: Container(
            key: _globalKey,
            height: 20,
            width: 20,
            child: Icon(
            Icons.format_list_bulleted_rounded,
            color: Colors.black,
          ))
        ),
        title: Text("Pro Color Palette",
        style: TextStyle(color: Colors.black),),
      ),

      body: Text("This took long \u2639"),
    );
  }
  void OpenCustomPopUpMenu() {
    RenderBox renderBox = _globalKey.currentContext.findRenderObject();
    menuSize = renderBox.size*2;
    menuOffset = renderBox.localToGlobal(Offset(0,-55));
    _overlayEntry = _createOverlayEntry();
    Overlay.of(context).insert(_overlayEntry);
    isMenuVisible = !isMenuVisible;
  }

  void CloseCustomPopUpMenu() {
    _overlayEntry.remove();
    isMenuVisible = !isMenuVisible;
  }

  OverlayEntry _createOverlayEntry() {
    return OverlayEntry(builder: (context) {
      return Positioned(
        top: menuOffset.dy + menuSize.height,
        left: menuOffset.dx,
        width:menuSize.width *2,
        child: Material(
            color: Colors.transparent,
            child: CustomPopupMenu(),
        ),
      );
    },
    );
  }
}
class CustomPopupMenu extends StatefulWidget {
  @override
  _CustomPopupMenuState createState() => _CustomPopupMenuState();
}

final Container ImprovisedDivider = Container(
  height: 1,
  width:300,
  color: Colors.black,);
class _CustomPopupMenuState extends State<CustomPopupMenu> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 175,
      color: Colors.white,
      child: Column(
        children:<Widget>[
          ImprovisedDivider,
          ListTile(
            leading:Icon(
              Icons.save,
              color: Colors.black,
              size:20 ,),
            contentPadding: EdgeInsets.only(left: 1.0),
           title:Text("Save palette",),
          ),
          ImprovisedDivider,
          ListTile(
            leading:Icon(
              Icons.delete,
              color: Colors.black,
              size:20 ,),
            contentPadding: EdgeInsets.only(left: 1.0),
            title:Text("Clear palette"),
          ),
          ImprovisedDivider,
          ListTile(
            leading:Icon(
              Icons.upload_file,
              color: Colors.black,
              size:20 ,),
            contentPadding: EdgeInsets.only(left: 1.0),
            title:Text("Upload a palette"),
          ),
          ImprovisedDivider,
        ],
      ),
    );
  }
}

你需要一个覆盖来构建一个自定义弹出菜单。Overlay Docs。它允许您构建浮动在其他小部件之上的小部件。我们需要一个GlobalKey来标识持有菜单按钮的容器。我们还需要一个手势检测器来识别屏幕点击。
步骤:
1.创建菜单按钮,为全局键、覆盖、菜单大小和菜单偏移量声明变量。

class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> 
{
    //Initialize global key
      GlobalKey _globalKey = new GlobalKey();
    //Create new overlay entry
      OverlayEntry _overlayEntry;
    //Store the size of the menu
      Size menuSize;
    //Store the menu's offset
      Offset menuOffset;
    //This is used to ooen and close the menu
      bool isMenuVisible = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        leading: GestureDetector(
            onTap: (){
              (isMenuVisible) ? CloseCustomPopUpMenu() : OpenCustomPopUpMenu();
            },
          child: Container(
            key: _globalKey,//
            height: 20,
            width: 20,
            child: Icon(
            Icons.format_list_bulleted_rounded,
            color: Colors.black,
          ))
        ),
        title: Text("Pro Color Palette $isMenuVisible",
        style: TextStyle(color: Colors.black),),
      ),

      body: Text("....."),
    );
  }

1.创建一个自定义的PopupMenu部件(我使用了一堆ListTiles和临时的分隔符)。注意,我们只能改变菜单的高度。它从父节点继承宽度。

class CustomPopupMenu extends StatefulWidget {
  @override
  _CustomPopupMenuState createState() => _CustomPopupMenuState();
}

final Container ImprovisedDivider = Container(
  height: 1,
  width:300,
  color: Colors.black,);

class _CustomPopupMenuState extends State<CustomPopupMenu> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 180,//Change Menu height here
      color: Colors.white,
      child: Column(
        children:<Widget>[
          ImprovisedDivider,
          ListTile(
            leading:Icon(
              Icons.save,
              color: Colors.black,
              size:20 ,),
            contentPadding: EdgeInsets.only(left: 1.0),
           title:Text("Save palette",),
          ),
          ImprovisedDivider,
          ListTile(
            leading:Icon(
              Icons.delete,
              color: Colors.black,
              size:20 ,),
            contentPadding: EdgeInsets.only(left: 1.0),
            title:Text("Clear palette"),
          ),
          ImprovisedDivider,
          ListTile(
            leading:Icon(
              Icons.upload_file,
              color: Colors.black,
              size:20 ,),
            contentPadding: EdgeInsets.only(left: 1.0),
            title:Text("Upload a palette"),
          ),
          ImprovisedDivider,
        ],
      ),
    );
  }
}

1.在它自己的函数中创建覆盖条目。我们使用一个Positioned小部件将其放置在屏幕上的特定位置。

OverlayEntry _createOverlayEntry() {
    return OverlayEntry(builder: (context) {
      return Positioned(
        top: menuOffset.dy + menuSize.height,
        left: menuOffset.dx,
        width:menuSize.width *2,//Change menu width here
        child: Material(
            color: Colors.transparent,
            child: CustomPopupMenu(),//Call our Custom menu here
        ),
      );
    },
    );
  }

1.显示菜单的逻辑(也可以改变宽度和屏幕边缘的偏移量)。全局键在这里很有用。它允许我们获取父容器的信息,比如它的位置和大小。注意事项

void OpenCustomPopUpMenu() {
   RenderBox renderBox = _globalKey.currentContext.findRenderObject();
   menuSize = renderBox.size*2;//Change width here
   menuOffset = renderBox.localToGlobal(Offset(0,-60));//Change offset from screen edge here
   _overlayEntry = _createOverlayEntry();
   Overlay.of(context).insert(_overlayEntry);
   isMenuVisible = !isMenuVisible;
 }

1.关闭菜单的逻辑

void CloseCustomPopUpMenu() {
    _overlayEntry.remove();
    isMenuVisible = !isMenuVisible;
  }

相关问题