bdlukaa / fluent_ui

Implements Microsoft's WinUI3 in Flutter.
https://bdlukaa.github.io/fluent_ui/
BSD 3-Clause "New" or "Revised" License
2.97k stars 464 forks source link

šŸ› Flyouts with large submenus not constraining to nested navigators #1104

Closed Lootwig closed 1 month ago

Lootwig commented 3 months ago

When running the following example (*), the submenu does not respect its parent container's dimensions:

image

class Example extends StatelessWidget {
  const Example({super.key});

  @override
  Widget build(BuildContext context) {
    return FluentApp(
      home: NavigationView(
        pane: NavigationPane(
          items: [
            PaneItem(
              title: Text('hi'),
              icon: SizedBox.square(
                dimension: 28,
                child: Placeholder(),
              ),
              body: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Container(color: Colors.blue, height: 150),
                  Expanded(
                    child: Navigator(
                      pages: [
                        FluentPage(
                          child: Container(
                            color: Colors.yellow,
                            alignment: Alignment.topLeft,
                            child: BrokenMenu(),
                          ),
                        ),
                      ],
                      onDidRemovePage: (_) {},
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class BrokenMenu extends StatefulWidget {
  const BrokenMenu({
    super.key,
  });

  @override
  State<BrokenMenu> createState() => _BrokenMenuState();
}

class _BrokenMenuState extends State<BrokenMenu> {
  final _controller = FlyoutController();
  @override
  Widget build(BuildContext context) {
    return FlyoutTarget(
      controller: _controller,
      child: Button(
        child: Text('click'),
        onPressed: _openMenu,
      ),
    );
  }

  void _openMenu() {
    _controller.showFlyout(
      forceAvailableSpace: true,
      placementMode: FlyoutPlacementMode.bottomCenter,
      builder: (context) => MenuFlyout(
        items: [
          ...Iterable.generate(
            5,
            (_) => MenuFlyoutItem(text: Text('hi there'), onPressed: () {}),
          ),
          MenuFlyoutSubItem(
            text: Text('Subitems'),
            items: (_) => [
              ...Iterable.generate(
                25,
                (_) => MenuFlyoutItem(text: Text('hi there'), onPressed: () {}),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

* Note, the MouseRegion inserted in src/controls/flyouts/menu.dart does not consider the translation of the nested Navigator, causing the test for whether the cursor is on the submenu item to always fail and the submenu to close whenever the mouse moves. I used this dirty fix to get around that:

// ...

    if (keys.isNotEmpty) {
      content = MouseRegion(
        onHover: (event) {
          for (final subItem
              in keys.whereType<GlobalKey<_MenuFlyoutSubItemState>>()) {
            final state = subItem.currentState;
            if (state == null || subItem.currentContext == null) continue;
            if (!state.isShowing(menuInfo)) continue;

            final itemBox =
                subItem.currentContext!.findRenderObject() as RenderBox;
            final b= (parent?.widget.root?.context.findRenderObject() as RenderBox);
            final translation = b.getTransformTo(null).getTranslation();
            final offset = Offset(translation[0], translation[1]);
            final itemRect = (itemBox.localToGlobal(
              Offset.zero,
              ancestor: parent?.widget.root?.context.findRenderObject(),
            ) + offset) &
            itemBox.size;

            if (!itemRect.contains(event.position)) {
              state.close(menuInfo);
            }
          }
        },
        child: content,
      );
    }

// ...
Lootwig commented 1 month ago

It looks like you integrated the workaround for the unrelated mouse movement issue, but the actual issue seems to not have been addressed, at least with 4.9.2 the menu in the provided example still disappears behind the blue layer and can't be scrolled.