Можно ли динамически создать всплывающее меню (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),
// ),
),
);
}
}