Как динамически создавать и показывать всплывающее меню во флаттере? - PullRequest
0 голосов
/ 08 июня 2018

Можно ли динамически создать всплывающее меню (PopupMenuButton), нажав кнопку действия и отобразить это меню в середине экрана?Например, как изменить стандартное приложение флаттера для реализации этого сценария:

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

Мне удалось несколько продвинуться в решении проблемы, но все же есть вопросы.Вот текст main.dart.При нажатии на холст функция _showPopupMenu3 (context) вызывается из _handleTapDown (...).Меню действительно появляется, я могу поймать вариант, но после выбора меню не закрывается.Для закрытия меню необходимо нажать кнопку НАЗАД или нажать на холст.Это, вероятно, соответствует ситуации ОТМЕНА.Итак, вопросы: 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),
//        ),
      ),
    );
  }
}

1 Ответ

0 голосов
/ 15 июня 2018

Закрыть меню довольно просто: нужно добавить строку: Navigator.pop (context);

      onTap: ()
      {
        setState(()
        {
          print("onTap [${popupRoute}] ");
          selectedPopupRoute = popupRoute;
          Navigator.pop(context);
        });
      }

Остается вопрос с координатами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...