Как показать меню при нажатии / пальце / мыши / позиции курсора во флаттере - PullRequest
0 голосов
/ 20 января 2019

У меня есть этот кусок кода, который я получил от Стиль буфера обмена в флаттере

showMenu(
        context: context,
        // TODO: Position dynamically based on cursor or textfield
        position: RelativeRect.fromLTRB(0.0, 600.0, 300.0, 0.0),
        items: [
          PopupMenuItem(
            child: Row(
              children: <Widget>[
                // TODO: Dynamic items / handle click
                PopupMenuItem(
                  child: Text(
                    "Paste",
                    style: Theme.of(context)
                        .textTheme
                        .body2
                        .copyWith(color: Colors.red),
                  ),
                ),
                PopupMenuItem(
                  child: Text("Select All"),
                ),
              ],
            ),
          ),
        ],
      );

Этот код прекрасно работает, за исключением того, что созданное всплывающее окно находится в фиксированной позиции, как бы я сделал так, чтобы оно всплывало в позиции мыши / нажатия / пальца / курсора или где-то рядом с этим, вроде как когда Вы хотите скопировать и вставить на свой телефон. (Всплывающее диалоговое окно не будет использоваться для копирования и вставки)

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Мне удалось решить подобную проблему с помощью этого ответа: https://stackoverflow.com/a/54714628/559525

По сути, я добавил GestureDetector () вокруг каждого ListTile, а затем вы используете onTapDown для хранения вашего местоположения печати и onLongPress для вызова вашей функции showMenu. Вот критические функции, которые я добавил:

  _showPopupMenu() async {
    final RenderBox overlay = Overlay.of(context).context.findRenderObject();

    await showMenu(
      context: context,
      position: RelativeRect.fromRect(
          _tapPosition & Size(40, 40), // smaller rect, the touch area
          Offset.zero & overlay.size // Bigger rect, the entire screen
          ),
      items: [
        PopupMenuItem(
          child: Text("Show Usage"),
        ),
        PopupMenuItem(
          child: Text("Delete"),
        ),
      ],
      elevation: 8.0,
    );
  }

  void _storePosition(TapDownDetails details) {
    _tapPosition = details.globalPosition;
  }
}

А вот и полный код (вам нужно настроить несколько вещей, таких как изображение и заполнить список устройств):

import 'package:flutter/material.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'dart:core';

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

  final String title;

  @override
  _RecentsPageState createState() => _RecentsPageState();
}

class _RecentsPageState extends State<RecentsPage> {

  List<String> _recents;

  var _tapPosition;

  @override
  void initState() {
    super.initState();
    getRecents().then((value) {
      setState(() {
        _recents = value;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFFFFFFF),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Container(height: 25),
              Stack(
                children: <Widget>[
                  Container(
                    padding: EdgeInsets.only(left: 40),
                    child: Center(
                      child: AutoSizeText(
                        "Recents",
                        maxLines: 1,
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 32),
                      ),
                    ),
                  ),
                  Container(
                    padding: EdgeInsets.only(left: 30, top: 0),
                    child: GestureDetector(
                        onTap: () => Navigator.of(context).pop(),
                        child: Transform.scale(
                          scale: 2.0,
                          child: Icon(
                            Icons.chevron_left,
                          ),
                        )),
                  ),
                ],
              ),
              Container(
                height: 15,
              ),
              Container(
                height: 2,
                color: Colors.blue,
              ),
              Container(
                height: 10,
              ),
              Flexible(
                child: ListView(
                  padding: EdgeInsets.all(15.0),
                  children: ListTile.divideTiles(
                    context: context,
                    tiles: _getRecentTiles(),
                  ).toList(),
                ),
              ),
              Container(height: 15),
            ],
          ),
        ),
      ),
    );
  }

  List<Widget> _getRecentTiles() {
    List<Widget> devices = List<Widget>();
    String _dev;
    String _owner = "John Doe";

    if (_recents != null) {
      for (_dev in _recents.reversed) {
        if (_dev != null) {
          _dev = _dev.toUpperCase().trim();

            String serial = "12341234";

            devices.add(GestureDetector(
                onTapDown: _storePosition,
                onLongPress: () {
                  print("long press of $serial");
                  _showPopupMenu();
                },
                child: ListTile(
                  contentPadding: EdgeInsets.symmetric(vertical: 20),
                  leading: Transform.scale(
                      scale: 0.8,
                      child: Image(
                        image: _myImage,
                      )),
                  title: AutoSizeText(
                    "$_owner",
                    maxLines: 1,
                    style: TextStyle(fontSize: 22),
                  ),
                  subtitle: Text("Serial #: $serial"),
                  trailing: Icon(Icons.keyboard_arrow_right),
                )));
        }
      }
    } else {
      devices.add(ListTile(
        contentPadding: EdgeInsets.symmetric(vertical: 20),
        title: AutoSizeText(
          "No Recent Devices",
          maxLines: 1,
          style: TextStyle(fontSize: 20),
        ),
        subtitle:
            Text("Click the button to add a device"),
        onTap: () {
          print('add device');
        },
      ));
    }
    return devices;
  }

  _showPopupMenu() async {
    final RenderBox overlay = Overlay.of(context).context.findRenderObject();

    await showMenu(
      context: context,
      position: RelativeRect.fromRect(
          _tapPosition & Size(40, 40), // smaller rect, the touch area
          Offset.zero & overlay.size // Bigger rect, the entire screen
          ),
      items: [
        PopupMenuItem(
          child: Text("Show Usage"),
        ),
        PopupMenuItem(
          child: Text("Delete"),
        ),
      ],
      elevation: 8.0,
    );
  }

  void _storePosition(TapDownDetails details) {
    _tapPosition = details.globalPosition;
  }
}
0 голосов
/ 20 мая 2019

Вот многоразовый виджет, который делает то, что вам нужно.Просто оберните ваш Text или другой Widget этим CopyableWidget и введите onGetCopyTextRequested.При длительном нажатии на виджет отобразится меню «Копирование», скопированное текстовое содержимое возвращается в буфер обмена, а при завершении отобразится Snackbar.

/// The text to copy to the clipboard should be returned or null if nothing can be copied
typedef GetCopyTextCallback = String Function();

class CopyableWidget extends StatefulWidget {

    final Widget child;
    final GetCopyTextCallback onGetCopyTextRequested;

    const CopyableWidget({
        Key key,
        @required this.child,
        @required this.onGetCopyTextRequested,
    }) : super(key: key);

    @override
    _CopyableWidgetState createState() => _CopyableWidgetState();
}

class _CopyableWidgetState extends State<CopyableWidget> {

    Offset _longPressStartPos;

    @override
    Widget build(BuildContext context) {
        return InkWell(
            highlightColor: Colors.transparent,
            onTapDown: _onTapDown,
            onLongPress: () => _onLongPress(context),
            child: widget.child
        );
    }

    void _onTapDown(TapDownDetails details) {
        setState(() {
            _longPressStartPos = details?.globalPosition;
        });
    }

    void _onLongPress(BuildContext context) async {
        if (_longPressStartPos == null)
            return;

        var isCopyPressed = await showCopyMenu(
            context: context,
            pressedPosition: _longPressStartPos
        );
        if (isCopyPressed == true && widget.onGetCopyTextRequested != null) {
            var copyText = widget.onGetCopyTextRequested();
            if (copyText != null) {
                await Clipboard.setData(ClipboardData(text: copyText));

                _showSuccessSnackbar(
                    context: context,
                    text: "Copied to the clipboard"
                );
            }
        }
    }

    void _showSuccessSnackbar({
        @required BuildContext context,
        @required String text
    }) {
        var scaffold = Scaffold.of(context, nullOk: true);
        if (scaffold != null) {
            scaffold.showSnackBar(
                SnackBar(
                    content: Row(
                        children: <Widget>[
                            Icon(
                                Icons.check_circle_outline,
                                size: 24,
                            ),
                            SizedBox(width: 8),
                            Expanded(
                                child: Text(text)
                            )
                        ],
                    )
                )
            );
        }
    }
}

Future<bool> showCopyMenu({
    BuildContext context,
    Offset pressedPosition
}) {
    var x = pressedPosition.dx;
    var y = pressedPosition.dy;

    return showMenu<bool>(
        context: context,
        position: RelativeRect.fromLTRB(x, y, x + 1, y + 1),
        items: [
            PopupMenuItem<bool>(value: true, child: Text("Copy")),
        ]
    );
}
...