Skip to content

Nested Popover menus trigger overlay problem behavior #823

@joshireman

Description

@joshireman

Describe the bug

Having a nested menu, have very inconsistent results sometimes it works properly, sometimes it silently does nothing, sometimes I get an assertion:

DartError: Assertion failed: file:///Users/***/flutter/flutter/packages/flutter/lib/src/widgets/overlay.dart:1679:14
overlay.dart:1679
_zOrderIndex != null
is not true

To Reproduce

// MenuElement(id: '1', parentId: null, text: 'Parent 1'),
// MenuElement(id: '2', parentId: null, text: 'Parent 2'),
// MenuElement(id: '3', parentId: '1',, text: 'Child 1'),
// MenuElement(id: '4', parentId: '1',, text: 'Child 2'),
// MenuElement(id: '5', parentId: '2', text: 'Child 3'),
// MenuElement(id: '6', parentId: '2', text: 'Child 4'),

import 'package:flutter/material.dart';
import 'package:forui/forui.dart';

class MenuElement {
  String id;
  String? parentId;
  String text;

  MenuElement({required this.id, required this.parentId, required this.text});
}

/// MenuButton
class MenuButton extends StatelessWidget {
  /// The available elements
  final List<MenuElement> elements;

  /// The callback when a element is selected
  final ValueSetter<MenuElement>? onSelected;

  /// Constructor
  const MenuButton({super.key, required this.elements, this.onSelected});

  List<FTileMixin> _buildMenuTiles(List<MenuElement> parentElements) {
    return parentElements.map<FTileMixin>((element) {
      final children = elements
          .where((child) => child.parentId == element.id)
          .toList();

      if (children.isEmpty) {
        // Leaf node
        return FTile(
          onPress: () => onSelected?.call(element),
          title: Text(element.text),
        );
      } else {
        // Parent node
        return _SubmenuTile(
          element: element,
          children: children,
          onSelected: onSelected,
        );
      }
    }).toList();
  }

  @override
  Widget build(BuildContext context) {
    final parentElements = elements
        .where((element) => element.parentId == null)
        .toList();

    return FPopoverMenu.tiles(
      control: const .managed(),
      menu: [FTileGroup(children: _buildMenuTiles(parentElements))],
      builder: (context, controller, child) => FButton(
        style: FButtonStyle.outline(),
        onPress: controller.toggle,
        mainAxisSize: MainAxisSize.min,
        suffix: const Icon(FIcons.chevronDown),
        child: const Text('Select...'),
      ),
    );
  }
}

/// Custom tile that creates a submenu when pressed
class _SubmenuTile extends StatelessWidget implements FTileMixin {
  /// The element this tile represents
  final MenuElement element;

  /// The children of this element (always leaf nodes)
  final List<MenuElement> children;

  /// The callback when a element is selected
  final ValueSetter<MenuElement>? onSelected;

  /// Constructor
  const _SubmenuTile({
    required this.element,
    required this.children,
    this.onSelected,
  });

  @override
  Widget build(BuildContext context) {
    return FPopoverMenu.tiles(
      menuAnchor: Alignment.topLeft,
      childAnchor: Alignment.topRight,
      menu: [
        FTileGroup(
          children: children
              .map(
                (child) => FTile(
                  onPress: () => onSelected?.call(child),
                  title: Text(child.text),
                ),
              )
              .toList(),
        ),
      ],
      builder: (context, controller, child) => FTile(
        onPress: controller.toggle,
        title: Text(element.text),
        suffix: const Icon(FIcons.chevronRight),
      ),
    );
  }
}

Expected behavior

Consistent behavior

Screenshots
n/a

Device (please complete the following information):

  • Flutter version: Flutter (Channel stable, 3.38.1, on macOS 26.2 25C56 darwin-arm64, locale en-US)
  • Forui version: 0.17.0 (same behavior on 16)
  • Device: web
  • OS: OSX
  • Browser Chrome
  • Version

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions