Flutter Web ShowMenu 不随屏幕大小调整而移动

Ale*_*ser 8 flutter flutter-web

棘手的问题,希望有人有好的想法。

showMenu(...)在 flutter 中调用我的文本按钮,效果很好。但是,有时在调整屏幕大小期间,菜单会卡在屏幕上的某个位置(远离其预期位置)。有时它会遵循其锚定位置。非常奇怪,我也注意到下拉菜单的这种行为。

这是我的示例代码。我想要么始终随屏幕移动菜单,或者在最坏的情况下,在屏幕调整大小事件上隐藏菜单。任何关于如何做的想法都会很棒!

class ZHomeMenuBar extends StatefulWidget {
  const ZHomeMenuBar({Key? key}) : super(key: key);

  @override
  State<ZHomeMenuBar> createState() => _ZHomeMenuBarState();
}

class _ZHomeMenuBarState extends State<ZHomeMenuBar> {
  final GlobalKey _mesKey = GlobalKey();
  final GlobalKey _accKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    final zuser = Provider.of<ZUser?>(context);
    return Container(
      height: 66,
      decoration: BoxDecoration(color: context.backgroundColor),
      padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: Row(
        children: [
          Text(zuser == null ? "" : zuser.displayName ?? ""),
          const Spacer(),
          ZTextButton(text: "Portfolio", onPressed: () => {}),
          context.sh,
          ZTextButton(
              key: _mesKey,
              text: "Messages",
              onPressed: () {
                _showMessage(context);
              }),
          context.sh,
          ZTextButton(key: _accKey, text: "Account", onPressed: () {}),
          context.sh,
        ],
      ),
    );
  }

  _showMessage(context) {
    final RenderBox renderBox =
        _mesKey.currentContext?.findRenderObject() as RenderBox;
    final Size size = renderBox.size;
    final Offset offset = renderBox.localToGlobal(Offset.zero);

    showMenu(
        context: context,
        position: RelativeRect.fromLTRB(offset.dx, offset.dy + size.height,
            offset.dx + size.width, offset.dy + size.height),
        items: [
          PopupMenuItem<String>(child: const Text('menu option 1'), value: '1'),
          PopupMenuItem<String>(child: const Text('menu option 2'), value: '2'),
          PopupMenuItem<String>(child: const Text('menu option 3'), value: '3'),
        ]);
  }
}
Run Code Online (Sandbox Code Playgroud)

Vic*_*ele 4

为了在调整屏幕大小时隐藏菜单,可以使用dart:htmlwindow对象向浏览器的调整大小添加事件侦听器并弹出菜单的上下文。

这是您的小部件的更新代码ZHomeMenuBar

class ZHomeMenuBar extends StatefulWidget {
  ...
}

class _ZHomeMenuBarState extends State<ZHomeMenuBar> {
  ...

  Timer? timer;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    if (kIsWeb) {

      final int debounceDuration = 100;

      html.window.addEventListener("resize", (_) {
        final modalRoute = ModalRoute.of(context);

        if (modalRoute == null) {
          return;
        }

        if (!modalRoute.isCurrent) {
          Navigator.pop(context);

          if (timer != null && timer!.isActive) {
            timer!.cancel();
          }

          timer = Timer(Duration(milliseconds: 100), () {
            _showMessage(context);
          });
        }
      });

    }

  }

  @override
  void dispose() {
    super.dispose();
    timer?.cancel();
  }

  @override
  Widget build(BuildContext context) {
    ...
  }

  _showMessage(context) {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

那么该方法中的代码的didChangeDependencies作用是:

  1. 窗口监听调整大小事件。
  2. ModalRoute获得上下文的。
  3. ModalRoute如果为 null,则该方法返回
  4. 检查其isCurrent属性。ModalRoute如果为 false,则意味着该路径ModalRoute不是导航器上最顶层的路径,并且屏幕上有一个对话框。
  5. 因此,如果isCurrent为 false,我们会弹出上下文(这将删除对话框),并使用 设置一个短计时器,debounceDuration然后显示对话框。这将使用新重新计算的尺寸并在正确的位置显示对话框。
  6. 如果计时器处于活动状态,而另一个计时器正在运行,我们将取消前一个计时器并将新计时器分配给变量timer

debounceDuration您可以根据需要更新。