popup - 如何在flutter中动态创建和显示弹出菜单?

标签 popup contextmenu flutter

是否可以通过按下操作按钮来动态创建弹出菜单(PopupMenuButton)并在屏幕中间显示此菜单?比如如何修改标准的flutter应用来实现这个场景:

  void _showPopupMenu()
  {
  // Create and show popup menu 
     ...
  }

我设法在解决问题方面取得了一些进展,但仍然存在问题。这是 main.dart 的文本。通过单击 Canvas ,从 _handleTapDown (...) 调用 _showPopupMenu3(上下文)函数。菜单确实出现了,我可以捕捉选项,但是选择菜单后没有关闭。要关闭菜单需要按 BACK 按钮或单击 Canvas 。这可能对应于 CANCEL 情况。所以问题是:
1)选择项目后如何关闭菜单(可能只是菜单属性的一些参数)?
2)应该传递给位置参数的坐标的目的和意义不是很清楚。如何提高点击坐标旁边的菜单?

资料来源:
import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or press Run > Flutter Hot Reload in IntelliJ). Notice that the
        // counter didn't reset back to zero; the application is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: new TouchTestPage(title: 'Flutter Demo Home Page'),
    );
  }
}

class TouchTestPage extends StatefulWidget {
  TouchTestPage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _TouchTestPageState createState() => new _TouchTestPageState();
}

class _TouchTestPageState extends State<TouchTestPage> {

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return new Scaffold(
      appBar: new AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: new Text(widget.title),
      ),
      body: new Container(
        decoration: new BoxDecoration(
          color: Colors.white70,
          gradient: new LinearGradient(
              colors: <Color>[Colors.lightBlue, Colors.white30]),
          border: new Border.all(
            color: Colors.blueGrey,
            width: 1.0,
          ),
        ),
        child: new Center(child: new TouchControl()),
      ),
    );
  }
}

class TouchControl extends StatefulWidget {
  final double xPos;
  final double yPos;
  final ValueChanged<Offset> onChanged;

  const TouchControl({
    Key key,
    this.onChanged,
    this.xPos: 0.0,
    this.yPos: 0.0,
  })
      : super(key: key);

  @override
  TouchControlState createState() => new TouchControlState();
}

class TouchControlState extends State<TouchControl> {
  double xPos = 0.0;
  double yPos = 0.0;

  double xStart = 0.0;
  double yStart = 0.0;

  double _scale     = 1.0;
  double _prevScale = null;

  void reset()
  {
    xPos  = 0.0;
    yPos  = 0.0;
  }

  final List<String> popupRoutes = <String>[
    "Properties", "Delete", "Leave"
  ];
  String selectedPopupRoute = "Properties";

  void _showPopupMenu3(BuildContext context)
  {
    showMenu<String>(
      context: context,
      initialValue: selectedPopupRoute,
      position: new RelativeRect.fromLTRB(40.0, 60.0, 100.0, 100.0),
      items: popupRoutes.map((String popupRoute) {
        return new PopupMenuItem<String>(
          child: new
          ListTile(
              leading: const Icon(Icons.visibility),
              title: new Text(popupRoute),
              onTap: ()
              {
                setState(()
                {
                  print("onTap [${popupRoute}] ");
                  selectedPopupRoute = popupRoute;
                });
              }
          ),
          value: popupRoute,
        );
      }).toList(),
    );
  }

  void onChanged(Offset offset)
  {
    final RenderBox referenceBox = context.findRenderObject();
    Offset position = referenceBox.globalToLocal(offset);
    if (widget.onChanged != null)
    {
      //    print('---- onChanged.CHANGE ----');
      widget.onChanged(position);
    }
    else
    {
      //    print('---- onChanged.NO CHANGE ----');
    }

    xPos = position.dx;
    yPos = position.dy;

  }

  @override
  bool hitTestSelf(Offset position) => true;

  void _handlePanStart(DragStartDetails details) {
    print('start');
    //  _scene.clear();


    final RenderBox referenceBox = context.findRenderObject();
    Offset position = referenceBox.globalToLocal(details.globalPosition);

    onChanged(details.globalPosition);
    xStart = xPos;
    yStart = yPos;
  }

  void _handlePanEnd(DragEndDetails details) {

    print("_handlePanEnd");
    print('end');

  }

  void _handleTapDown(TapDownDetails details) {

    print('--- _handleTapDown ---');
    final RenderBox referenceBox = context.findRenderObject();
    Offset position = referenceBox.globalToLocal(details.globalPosition);
    onChanged(new Offset(0.0, 0.0));

     _showPopupMenu3(context); //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    print('+++ _handleTapDown [${position.dx},${position.dy}] +++');
  }

  void _handleTapUp(TapUpDetails details) {
    //  _scene.clear();

    print('--- _handleTapUp   ---');
    final RenderBox referenceBox = context.findRenderObject();
    Offset position = referenceBox.globalToLocal(details.globalPosition);
    onChanged(new Offset(0.0, 0.0));

    //_showPopupMenu(context);
    print('+++ _handleTapUp   [${position.dx},${position.dy}] +++');
  }

  void _handleDoubleTap() {
    print('_handleDoubleTap');
  }

  void _handleLongPress() {
    print('_handleLongPress');
  }

  void _handlePanUpdate(DragUpdateDetails details) {

    //  logger.clear("_handlePanUpdate");
    final RenderBox referenceBox = context.findRenderObject();
    Offset position = referenceBox.globalToLocal(details.globalPosition);
    onChanged(details.globalPosition);
  }

  @override
  Widget build(BuildContext context) {
    return new ConstrainedBox(
      constraints: new BoxConstraints.expand(),
      child: new GestureDetector(
        behavior: HitTestBehavior.opaque,
        onPanStart:     _handlePanStart,
        onPanEnd:       _handlePanEnd,
        onPanUpdate:    _handlePanUpdate,
        onTapDown:      _handleTapDown,
        onTapUp:        _handleTapUp,
        onDoubleTap:    _handleDoubleTap,
        onLongPress:    _handleLongPress,
//        onScaleStart:   _handleScaleStart,
//        onScaleUpdate:  _handleScaleUpdate,
//        onScaleEnd:     _handleScaleEnd,
//        child: new CustomPaint(
//          size: new Size(xPos, yPos),
//          painter: new ScenePainter(editor.getScene()),
//          foregroundPainter: new TouchControlPainter(/*_scene*//*editor.getScene(),*/ xPos, yPos),
//        ),
      ),
    );
  }
}

最佳答案

对的,这是可能的

    void _showPopupMenu() async {
      await showMenu(
        context: context,
        position: RelativeRect.fromLTRB(100, 100, 100, 100),
        items: [
          PopupMenuItem(
            value: 1
            child: Text("View"),
          ),
          PopupMenuItem(
             value: 2
            child: Text("Edit"),
          ),
          PopupMenuItem(
            value: 3
            child: Text("Delete"),
          ),
        ],
        elevation: 8.0,
      ).then((value){

// NOTE: even you didnt select item this method will be called with null of value so you should call your call back with checking if value is not null 


      if(value!=null)
       print(value);

       });
    }

有时您会想要显示 _showPopupMenu 您按下按钮的位置
为此使用 GestureDetector
GestureDetector(
  onTapDown: (TapDownDetails details) {
    _showPopupMenu(details.globalPosition);
  },
  child: Container(child: Text("Press Me")),
);
然后 _showPopupMenu 会像
_showPopupMenu(Offset offset) async {
    double left = offset.dx;
    double top = offset.dy;
    await showMenu(
    context: context,
    position: RelativeRect.fromLTRB(left, top, 0, 0),
    items: [
      ...,
    elevation: 8.0,
  );
}

关于popup - 如何在flutter中动态创建和显示弹出菜单?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50758121/

相关文章:

flutter - 使用 BottomNavigationBar 根据页面更改 AppBar 标题

flutter Hooks Widget useState() with Object

firebase - flutter/Dart 错误 : The argument type 'Future<File>' can't be assigned to the parameter type 'File'

javascript - 如何延迟javascript中元素的onclick响应

javascript - 带有弹出窗口的 Chrome 扩展 Onclick()

google-chrome-extension - 如何从 chrome 扩展程序的上下文菜单中打开默认弹出窗口

javascript - IE中的ContextMenu获取当前打开的浏览器url

python - 在 Blender 中创建错误对话框

android - 在显示对话时播放声音

wpf - 使用 MVVM 模式单击项目时如何在 WPF 上下文菜单上获取 PlacementTarget