Giter VIP home page Giter VIP logo

Comments (5)

huycozy avatar huycozy commented on July 2, 2024

Thanks for your proposal. I think we can add this as a new example on API docs. Please feel free to create a PR for this.

from flutter.

mafreud avatar mafreud commented on July 2, 2024

Sure 😊

from flutter.

FireSourcery avatar FireSourcery commented on July 2, 2024

On the same note -

MenuAnchor is supposed to an updated PopupMenuButton
Although PopupMenu still offers an simpler way of including the menu item key/id in its callback. void Function(T value), as oppose to void Function(void).

I'd also like to consider a case of sharing the same visual menu mapped to different callbacks. For instance a right click menu whose functions are relative the object clicked.

I came up with the following solution, essentially an implementation of the flyweight pattern

// MenuSource<T> is a flyweight factory, where the menu items are shared across instances
//  either MenuItemButton callback layer indirection via context - build time
//  or instances use shallow copy - init time
class MenuSource<T> {
  MenuSource._({required this.menuItems});
  MenuSource._instance(MenuSource<T> menuSource) : menuItems = menuSource.menuItems;

  const MenuSource.items(List<MenuSourceItem> this.menuItems);
 
  MenuSource.itemBuilder({
    required Iterable<T> itemKeys,
    required Widget Function(T) itemBuilder,
    ValueSetter<T>? onPressed, 
    void Function(BuildContext context, T newValue, T oldValue)? onPressedExt,
  }) : menuItems = [
          for (final key in itemKeys)
            MenuSourceItem<T>(
              itemKey: key,
              onPressed: onPressed, 
              onPressedExt: onPressedExt,
              menuItemButton: MenuItemButton(child: itemBuilder(key)),
            ),
        ];

  final List<Widget> menuItems;

  MenuSourceInstance<T> instance() => MenuSourceInstance(this);
 
}

// alternatively shallow copy
class MenuSourceInstance<T> extends MenuSource<T> {
  MenuSourceInstance(super.menuSource) : super._instance();

  ValueNotifier<T?> notifier = ValueNotifier<T?>(null); 
}

// wrapper around MenuItemButton, to allow for a shared List<MenuItemButton> across instances
// use the same data as MenuItemButton, replacing onPressed with a callback to the notifier
// build time copy allows menuItemButton to be shared, alternatively use copyWith to create a shallow copy per instance
class MenuSourceItem<T> extends StatelessWidget {
  const MenuSourceItem({super.key, required this.menuItemButton, required this.itemKey, this.onPressed, this.onPressedExt});

  final MenuItemButton menuItemButton;
  final ValueSetter<T>? onPressed;
  final void Function(BuildContext context, T newValue, T? oldValue)? onPressedExt;
  final T itemKey;

  @override
  Widget build(BuildContext context) {
    final notifier = MenuSourceContext.of<T>(context);
    return MenuItemButton(
      onPressed: () {
        onPressed?.call(itemKey);
        onPressedExt?.call(context, itemKey, notifier.value);
        notifier.value = itemKey;
      },
      child: menuItemButton.child,
    );
  }
}

// Although MenuSource generally controls only 1 MenuListenableWidget, maps 1:1, InheritedNotifier simplifies implementation.
class MenuSourceContext<T> extends InheritedNotifier<ValueNotifier<T?>> {
  const MenuSourceContext._({super.key, required ValueNotifier<T?> super.notifier, required super.child});
  MenuSourceContext({super.key, required MenuSourceInstance<T?> source, required super.child}) : super(notifier: source.notifier);

  static ValueNotifier<T?> of<T>(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<MenuSourceContext<T>>()!.notifier!;
  }
}

class MenuSourceButton<T> extends StatelessWidget {
  const MenuSourceButton({super.key, required this.source});

  final MenuSourceInstance<T> source;

  @override
  Widget build(BuildContext context) {
    return MenuSourceContext<T>(
      source: source,
      child: MenuAnchorButton(items: source.menuItems),
    );
  }
}

// Menu 'hosts' must wrap MenuAnchor under MenuSourceContext, to allow for the notifier to be accessed by the menu items
class MenuSourceWidget<T> extends StatelessWidget {
  const MenuSourceWidget({super.key, required this.source, this.child, required this.builder});

  final MenuSourceInstance<T> source;
  final ValueWidgetBuilder<T?> builder;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    // menuItems onPressed will find the notifier from MenuSourceInstance
    return MenuSourceContext<T>(
      source: source,
      // "Dependents are notified whenever the notifier sends notifications, or whenever the identity of the notifier changes."
      // not working without ValueListenableBuilder?
      child: MenuAnchorOverlay(
        items: source.menuItems,
        child: ValueListenableBuilder<T?>(valueListenable: source.notifier, builder: builder, child: child),
      ),
    );
  }
}

// case where child depends on menu without displaying the menu
class MenuListenableBuilder<T> extends StatelessWidget {
  const MenuListenableBuilder({super.key, required this.builder, required this.source, this.child});

  final MenuSourceInstance<T> source;
  final ValueWidgetBuilder<T?> builder;
  final Widget? child;

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<T?>(valueListenable: source.notifier, builder: builder, child: child);
  }
}

class MenuSourceTheme extends ThemeExtension<MenuSourceTheme> {
  const MenuSourceTheme({this.trailingImage});

  final ImageProvider? trailingImage;

  @override
  ThemeExtension<MenuSourceTheme> copyWith() {
    throw UnimplementedError();
  }

  @override
  ThemeExtension<MenuSourceTheme> lerp(covariant ThemeExtension<MenuSourceTheme>? other, double t) {
    throw UnimplementedError();
  }
}

from flutter.

kaljitism avatar kaljitism commented on July 2, 2024

I have cloned the repo and working on it..

from flutter.

github-actions avatar github-actions commented on July 2, 2024

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

from flutter.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.