草庐IT

dart - Flutter - 长按弹出菜单

coder 2023-05-09 原文

我正在制作一个图片库,我需要用户能够长按图片以显示一个弹出菜单,让他可以删除图片。

我的代码,到目前为止:

  return GestureDetector(
    onLongPress: () {
      showMenu(
        items: <PopupMenuEntry>[
          PopupMenuItem(
            value: this._index,
            child: Row(
              children: <Widget>[
                Icon(Icons.delete),
                Text("Delete"),
              ],
            ),
          )
        ],
        context: context,
      );
    },
    child: Image.memory(
      this._asset.thumbData.buffer.asUint8List(),
      fit: BoxFit.cover,
      gaplessPlayback: true,
    ),
  );

产生:

但是,当调用 longPress 函数时,我不知道如何完全删除图像的小部件。该怎么做?

最佳答案

OP 和 First Answerer 使用 PopupMenuButton 绕过了原始问题,这在他们的情况下运行良好。但我认为如何定位自己的菜单以及如何在不使用 PopupMenuButton 的情况下接收用户响应的更一般的问题值得回答,因为有时我们希望在一个自定义小部件,我们希望它出现在一些手势上,而不是简单的点击(例如,OP 的初衷是长按)。

我开始制作一个简单的应用程序来演示以下内容:

  1. 使用 GestureDetector 捕捉长按
  2. 使用函数showMenu()显示弹出菜单,并将其定位在手指触摸的附近
  3. 如何接收用户的选择
  4. (Bonus) 如何制作一个代表多个值的PopupMenuEntry(常用的PopupMenuItem只能代表一个值)

结果是,当你长按一个大的黄色区域时,会出现一个弹出菜单,您可以在其中选择 +1-1,大number 会相应地增加或减少:

整个代码体跳到最后。评论被洒在那里解释我在做什么。这里有几点需要注意:

  1. showMenu()position 参数需要一些努力才能理解。它是一个 RelativeRect ,它表示较小的矩形如何定位在较大的矩形内。在我们的例子中,较大的矩形是整个屏幕,较小的矩形是触摸区域。 Flutter 根据这些规则定位弹出菜单(用简单的英语):

    • 如果较小的矩形向较大矩形的一半倾斜,弹出菜单将与较小矩形的左边缘对齐

    • 如果较小的矩形向较大矩形的一半倾斜,弹出菜单将与较小矩形的右边缘对齐

    • 如果较小的矩形在中间,则哪个边获胜取决于语言的文本方向。如果使用英语和其他从左到右的语言,左边缘获胜,否则右边缘获胜。

引用 PopupMenuButton's official implementation 来了解它如何使用 showMenu() 来显示菜单总是很有用的。

  1. showMenu() 返回一个 Future。使用 Future.then() 注册回调以处理用户选择。另一种选择是使用 await

  2. 请记住,PopupMenuEntryStatefulWidget 的(子类)。您可以在其中布局任意数量的子小部件。这就是您在 PopupMenuEntry 中表示多个值的方式。如果你想让它代表两个值,只要让它包含两个按钮,不管你想布局它们。

  3. 要关闭弹出菜单,请使用 Navigator.pop() 。 Flutter 将弹出菜单视为较小的“页面”。当我们显示一个弹出菜单时,我们实际上是在将一个“页面”推送到导航器的堆栈中。要关闭一个弹出菜单,我们将它从堆栈中弹出,从而完成前面提到的Future

这里是完整的代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Popup Menu Usage',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Popup Menu Usage'),
    );
  }
}

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  var _count = 0;
  var _tapPosition;

  void _showCustomMenu() {
    final RenderBox overlay = Overlay.of(context).context.findRenderObject();

    showMenu(
      context: context,
      items: <PopupMenuEntry<int>>[PlusMinusEntry()],
      position: RelativeRect.fromRect(
          _tapPosition & const Size(40, 40), // smaller rect, the touch area
          Offset.zero & overlay.size   // Bigger rect, the entire screen
      )
    )
    // This is how you handle user selection
    .then<void>((int delta) {
      // delta would be null if user taps on outside the popup menu
      // (causing it to close without making selection)
      if (delta == null) return;

      setState(() {
        _count = _count + delta;
      });
    });

    // Another option:
    //
    // final delta = await showMenu(...);
    //
    // Then process `delta` however you want.
    // Remember to make the surrounding function `async`, that is:
    //
    // void _showCustomMenu() async { ... }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GestureDetector(
              // This does not give the tap position ...
              onLongPress: _showCustomMenu,

              // Have to remember it on tap-down.
              onTapDown: _storePosition,

              child: Container(
                color: Colors.amberAccent,
                padding: const EdgeInsets.all(100.0),
                child: Text(
                  '$_count',
                  style: const TextStyle(
                      fontSize: 100, fontWeight: FontWeight.bold),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class PlusMinusEntry extends PopupMenuEntry<int> {
  @override
  double height = 100;
  // height doesn't matter, as long as we are not giving
  // initialValue to showMenu().

  @override
  bool represents(int n) => n == 1 || n == -1;

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

class PlusMinusEntryState extends State<PlusMinusEntry> {
  void _plus1() {
    // This is how you close the popup menu and return user selection.
    Navigator.pop<int>(context, 1);
  }

  void _minus1() {
    Navigator.pop<int>(context, -1);
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        Expanded(child: FlatButton(onPressed: _plus1, child: Text('+1'))),
        Expanded(child: FlatButton(onPressed: _minus1, child: Text('-1'))),
      ],
    );
  }
}

关于dart - Flutter - 长按弹出菜单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54300081/

有关dart - Flutter - 长按弹出菜单的更多相关文章

  1. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  2. ruby - Ruby 中的选项菜单 - 2

    我正在尝试在Ruby中创建一个菜单,以便根据用户输入的内容,取决于调用的类。然后在这种情况下它将返回到“Main”或类“Options”。我希望有人能帮助我。这是我的代码。modulePhysicsG=21C=20000Pi=3.14D=100endclassOptionsputs"Pleaseselect1forAccelerationand2forEnergy."option=gets()ifoption==1thenputs"AccelCalc"#ThisisthebitthatneedstodirecttheusertotheclassAccelCalcelseputs"Ene

  3. ruby - 下拉菜单在应该被选中的时候没有被选中……为什么? - 2

    我正在尝试解决我们测试中的一个错误,我认为它应该有效。我很确定这是selectize或capybara中的错误,但我不明白为什么。我已经进入了capybara的源代码,一切似乎都在正常工作。我真的不确定如何前进。为了测试这个错误,我已经尽可能地把这个错误剥离成一个小的testapplication.请参阅下面的设置bugs/show.html.erbOneTwoThreeFourOneTwoThreeFourbug_spec.rbfeature'bug'doit"specsetup",js:truedovisitbug_pathfind('div.selectize-inputinpu

  4. Flutter 环境变量配置和flutter doctor中的错误解决 - 2

    一、环境变量右键点击我的电脑-属性:然后找到环境变量 1.Android的SDK不在C盘的话需要额外配这个到用户环境变量:ANDROID_HOMED:\AndroidSDK2.然后在系统变量:Path中添加一条这样的值        D:\Flutter\flutter\bin             这个值写flutter包解压的实际地址即可 3.在系统变量中添加两个镜像变量:        变量名:FLUTTER_STORAGE_BASE_URL      变量值:https://storage.flutter-io.cn        变量名:PUB_HOSTED_URL      变量

  5. ruby - 处理在 keyup 事件上发生的 javascript 弹出窗口 - 2

    我在HTML页面上有一个文本字段,用于检查您是否输入了1到365之间的值。如果用户输入了无效值,如非数字字符或不在范围内的值,它显示一个弹出窗口。我在watirwiki上看到有一个select_no_wait方法,用于在您从列表中选择无效值时关闭弹出窗口。处理键盘事件时出现的弹出窗口的好方法是什么?我是否需要按照select_no_wait方法的实现方式进行操作,或者我们是否可以启动一个不同的进程来消除调用set方法时可能出现的弹出窗口。带有Javascript验证函数的HTML文件示例如下:varnum=0functionvalidate(e){varcharPressed=Stri

  6. ruby-on-rails - 在 Rails 中,如何使用字符串数组实现 HTML 选择菜单? - 2

    我有一个FinancialDocument#document_type模型属性。我想让用户从由字符串数组填充的HTML选择菜单中选择文档类型...doctypes=['Invoice','Packingslip','Other']对于每个选项,显示的标签和返回的值都是相同的。我查看了select和collection_select助手,但它们似乎适合选择子模型,而不仅仅是一个String值。我找不到如何让它们达到我的目的。这是我正在尝试的方法(我使用的是Haml,而不是Erb)...form_for(@financial_document)do|f|-doctypes=['Invoic

  7. ruby - 切换到 cucumber , capybara 中的弹出窗口 - 2

    在RSpec中我可以使用这样的代码切换到弹出窗口,link,我怎么能在cucumber步骤中做这样的事情?login_window=page.driver.find_window('PPA_identity_window')main_window=page.driver.find_window('')#Weusethistoexecutethenextinstructionsinthepopupwindowpage.within_window(login_window)do#Normallyfillintheformandloginfill_in'email',:with=>""fil

  8. ruby - OS X 中作为守护进程运行的脚本的菜单栏图标? - 2

    我有一个ruby​​脚本(https://github.com/daemonza/MacBak)作为守护进程在我的macbook上运行并监视一堆目录的文件更改和rsync发生的任何更改。我想知道我能否让它在顶部的菜单栏中创建一个图标?只是为了让我知道它确实在运行,而不必使用ps检查它。也许以后如果需要的话,我可能希望能够从那里控制脚本,简单的带有停止和状态条目的下拉菜单等。从ObjectC看来我可以调用NSStatusItem来获取图标,但我真的只是想通过我的Ruby脚本轻松地完成它。也许我可以做一些applescript调用? 最佳答案

  9. ruby-on-rails - Ruby on Rails : allow the user to enter a new information, 或单击下拉菜单选择现有信息 - 2

    这里是新的ROR程序员。我正在尝试构建一个Web应用程序,该应用程序允许用户填写表单,他们在其中输入公司信息,然后通过单击提交,将输入添加到数据库中。目前,如果用户要创建一个新条目,他们会看到几个字段,例如“公司名称”。有一个空白框供他们输入新公司,旁边有一个下拉菜单,用户可以使用该菜单查看数据库中的现有公司。"SelectaCompany")%>我正在寻找一种允许用户输入新公司的方法,或者单击下拉菜单并选择现有公司。目前:如果未在文本框中输入任何内容且未从下拉列表中选择任何选项,则将其保存为空白。如果输入了一些内容,但没有选择任何选项,它将被保存为空白。但是,如果输入内容并从下拉列表

  10. ruby - 为什么我将鼠标悬停在 Ruby 文件的任何单词上时会收到弹出消息? - 2

    如果我将鼠标悬停在Ruby文件的任何单词上,我会收到一条工具提示消息。该弹出消息的屏幕截图位于popupmessage.cat~/.gvimrc返回:function!SyntaxBalloon()letsynID=synID(v:beval_lnum,v:beval_col,0)letgroupID=synIDtrans(synID)letname=synIDattr(synID,"name")letgroup=synIDattr(groupID,"name")returnname."\n".groupendfunctionsetballoonexpr=SyntaxBalloon()

随机推荐