美文网首页flutter
Flutter-PopupMenuButton是如何实现的

Flutter-PopupMenuButton是如何实现的

作者: pj0579 | 来源:发表于2019-01-06 21:58 被阅读0次

    项目中需要自定义PopupMenuButtonPopupMenuButton只提供定制其中的Item,所以需要看源码一步步看是怎么实现的。
    一个核心方法

    void showButtonMenu() {
        // 通过findRenderObject获取PopupMenuButton的尺寸(button.size)
        final RenderBox button = context.findRenderObject();
        // 通过findRenderObject获取屏幕的尺寸(overlay.size)
        //Overlay 是个什么东西呢?
        //看下介绍 A [Stack] of entries that can be managed independently
        // 一个Stack  可以管理独立的entries。
        // 接着看Overlay.of(context)
        // 方法介绍 The state from the closest instance of this class that encloses the given context 
        // 获取包含这个context最近的OverlayState (每一个页面也是Overlay的entry)
        final RenderBox overlay = Overlay.of(context).context.findRenderObject();
        // RelativeRect也点进去看下介绍
        // An immutable 2D, axis-aligned, floating-point rectangle whose coordinates are given relative to another rectangle's edges, known as the container
        // 一个相对的2D矩形 尺寸相对于父容器
        // fromReact肯定是创建矩形,怎么创建?由一个父矩形 和 自己,
    从而计算出来了在屏幕的绝对位置。
        final RelativeRect position = RelativeRect.fromRect(
          Rect.fromPoints(
            button.localToGlobal(widget.offset, ancestor: overlay),
            button.localToGlobal(button.size.bottomRight(Offset.zero),
                ancestor: overlay),
          ),
          Offset.zero & overlay.size,
        );
        //   showMenu是核心方法
        showMenu<T>(
          context: context,
          elevation: widget.elevation,
          items: widget.itemBuilder(context),
          initialValue: widget.initialValue,
          position: position,
        ).then<void>((T newValue) {
          if (!mounted) return null;
          if (newValue == null) {
            if (widget.onCanceled != null) widget.onCanceled();
            return null;
          }
          if (widget.onSelected != null) widget.onSelected(newValue);
        });
      }
    

    showMenu源码

    Future<T> showMenu<T>({
      @required BuildContext context,
      RelativeRect position,
      @required List<PopupMenuEntry<T>> items,
      T initialValue,
      double elevation = 8.0,
      String semanticLabel,
    }) {
      assert(context != null);
      assert(items != null && items.isNotEmpty);
      assert(debugCheckHasMaterialLocalizations(context));
      String label = semanticLabel;
      switch (defaultTargetPlatform) {
        case TargetPlatform.iOS:
          label = semanticLabel;
          break;
        case TargetPlatform.android:
        case TargetPlatform.fuchsia:
          label =
              semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel;
      }
      // 返回的是Navigator.push 这边可以理解为一个透明背景的page,接下去就是走正常的布局显示。
      return Navigator.push(
          context,
          _CustomPopupMenuRoute<T>(
            position: position,
            items: items,
            initialValue: initialValue,
            elevation: elevation,
            semanticLabel: label,
            theme: Theme.of(context, shadowThemeOnly: true),
            barrierLabel:
                MaterialLocalizations.of(context).modalBarrierDismissLabel,
          ));
    }
    

    CustomPopupMenuRoute-CustomSingleChildLayout- _PopupMenu
    _PopupMenu是最后显示在屏幕上的东西,需要自定义的话,需要单独取出来修改相关布局,这样就可以自定义自己需要的Menu了,还有一种思路就是直接使用Overlay 进行添加浮动的布局,位置需要自己提前获取到。

    相关文章

      网友评论

        本文标题:Flutter-PopupMenuButton是如何实现的

        本文链接:https://www.haomeiwen.com/subject/hbtdrqtx.html