base_menu 0.1.1 copy "base_menu: ^0.1.1" to clipboard
base_menu: ^0.1.1 copied to clipboard

Composable widgets for building menu systems.

Base Menu #

Composable widgets for building menu systems.

Demos · Floogle Docs Standalone

Motivation #

This package is designed to fill the gap between RawMenuAnchor and the platform-specific menu implementations (MenuAnchor, MenuBar, CupertinoMenuAnchor). Many of the features contained in this package will eventually be proposed for inclusion in the Flutter SDK, but this package is intended to allow developers to start using and providing feedback on these features in the meantime.

Getting started #

Add the package to your pubspec.yaml and import it into your project:

import 'package:base_menu/base_menu.dart';

Example: #

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

  @override
  State<BaseMenuApp> createState() => _BaseMenuAppState();
}

class _BaseMenuAppState extends State<BaseMenuApp> {
  final MenuController controllerOne = MenuController();
  final MenuController controllerTwo = MenuController();
  String message = 'Nothing';

  static const panelDecoration = BoxDecoration(
    color: Color(0xFFFFFFFF),
    boxShadow: [BoxShadow(color: Color(0x28000000), blurRadius: 4, offset: Offset(0, 2))],
  );

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: .center,
      mainAxisSize: .max,
      spacing: 16,
      children: [
        const Text('BaseMenu Example', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
        Text('Pressed $message', style: const TextStyle(fontSize: 16)),
        BaseMenuBar(
          child: BaseMenuPanel(
            constraints: const BoxConstraints.tightFor(height: 30),
            children: <Widget>[
              BaseSubmenu(
                controller: controllerOne,
                requestCloseOnPointerExit: false,
                menu: DecoratedBox(
                  decoration: panelDecoration,
                  child: BaseMenuPanel(
                    constraints: const BoxConstraints.tightFor(width: 100),
                    children: <Widget>[
                      BaseMenuItem(
                        onPressed: () {
                          setState(() {
                            message = 'New';
                          });
                        },
                        child: const Label(child: Text('New')),
                      ),
                      BaseMenuItem(
                        onPressed: () {
                          setState(() {
                            message = 'Open';
                          });
                        },
                        child: const Label(child: Text('Open...')),
                      ),
                    ],
                  ),
                ),
                child: const Label(
                  color: Color(0xFFB1D2FF),
                  padding: .symmetric(horizontal: 30),
                  child: Text('File'),
                ),
              ),
              BaseSubmenu(
                controller: controllerTwo,
                requestCloseOnPointerExit: false,
                menu: DecoratedBox(
                  decoration: panelDecoration,
                  child: BaseMenuPanel(
                    constraints: const BoxConstraints.tightFor(width: 100),
                    children: <Widget>[
                      BaseMenuItem(
                        onPressed: () {
                          setState(() {
                            message = 'Undo';
                          });
                        },
                        child: const Label(child: Text('Undo')),
                      ),
                      BaseMenuItem(
                        onPressed: () {
                          setState(() {
                            message = 'Redo';
                          });
                        },
                        child: const Label(child: Text('Redo')),
                      ),
                    ],
                  ),
                ),
                child: const Label(
                  color: Color(0xFFFFDEB1),
                  padding: .symmetric(horizontal: 30),
                  child: Text('Edit'),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

class Label extends StatelessWidget {
  const Label({
    super.key,
    required this.child,
    this.color,
    this.alignment = .centerStart,
    this.padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
  });
  
  final Widget child;
  final Color? color;
  final AlignmentGeometry alignment;
  final EdgeInsetsGeometry padding;

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BaseMenuItem.isFocusedOf(context)
          ? BoxDecoration(border: Border.all(width: 2), color: color)
          : BoxDecoration(color: color),
      child: Padding(
        padding: padding,
        child: Align(alignment: alignment, child: child),
      ),
    );
  }
}

Contributing #

Contributions are welcome! Please open an issue or submit a pull request on GitHub. I'm especially interested in feedback on accessibility, API ergonomics, and edge cases in keyboard traversal and menu positioning logic.

0
likes
150
points
--
downloads

Documentation

API reference

Publisher

unverified uploader

Composable widgets for building menu systems.

Repository (GitHub)
View/report issues

Topics

#menus #ui

License

MIT (license)

Dependencies

flutter

More

Packages that depend on base_menu