代码之家  ›  专栏  ›  技术社区  ›  avi12

长按时弹出菜单

  •  0
  • avi12  · 技术社区  · 5 年前

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

    到目前为止,我的代码:

      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函数时完全删除图像的小部件。怎么做?

    2 回复  |  直到 5 年前
        1
  •  0
  •   avi12    5 年前

    如果要使用GridView或ListView在屏幕上布局图像,可以使用手势检测器包装每个项目,然后应将图像保留在列表中的某个位置,然后只需从列表中删除图像并调用SetState()。

    像下面这样。(这段代码可能不会编译,但它应该给你一个想法)

        ListView.builder(
            itemCount: imageList.length,
            itemBuilder: (BuildContext context, int index) {
              return GestureDetector(
                    onLongPress: () {
                      showMenu(
                        onSelected: () => setState(() => imageList.remove(index))}
                        items: <PopupMenuEntry>[
                          PopupMenuItem(
                            value: this._index,
                            child: Row(
                              children: <Widget>[
                                Icon(Icons.delete),
                                Text("Delete"),
                              ],
                            ),
                          )
                        ],
                        context: context,
                      );
                    },
                    child: imageList[index],
                );
              }
           )
    

    编辑:您也可以使用弹出菜单,如下所示

    Container(
      margin: EdgeInsets.symmetric(vertical: 10),
      height: 100,
      width: 100,
      child: PopupMenuButton(
        child: FlutterLogo(),
        itemBuilder: (context) {
          return <PopupMenuItem>[new PopupMenuItem(child: Text('Delete'))];
        },
      ),
    ),
    
        2
  •  0
  •   Nick Lee    5 年前

    OP和第一个回答者使用 PopupMenuButton 在他们的案件中效果很好。但我认为更普遍的问题是如何定位自己的菜单以及如何接收用户的响应 不使用 弹出按钮 值得回答,因为有时我们需要一个自定义小部件上的弹出菜单,我们希望它出现在一些手势上,而不是简单的点击(例如,操作的初衷是长按)。

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

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

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

    Popup Menu Usage App

    跳到整个代码体的结尾。我的评论被撒在那里解释我在做什么。以下是需要注意的几点:

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

      • 如果小直肠向 左边 大矩形的一半,弹出菜单将与 较小矩形的左边缘

      • 如果小直肠向 正确的 大矩形的一半,弹出菜单将与 小矩形的右边缘

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

    参考总是有用的 PopupMenuButton 's official implementation 看看它是怎么用的 显示菜单() 显示菜单。

    1. 显示菜单() 返回A Future . 使用 Future.then() 注册回调以处理用户选择。另一个选择是使用 await .

    2. 记住 PopupMenuEntry 是(的子类) StatefulWidget . 您可以在其中布局任意数量的子部件。这是如何在 弹出菜单 . 如果您想让它代表两个值,只需让它包含两个按钮,不管您想如何布局它们。

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

    以下是完整代码:

    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 & 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'))),
          ],
        );
      }
    }