pie_menu 2.0.0 copy "pie_menu: ^2.0.0" to clipboard
pie_menu: ^2.0.0 copied to clipboard

A Flutter package that provides a highly customizable circular/radial context menu

example/lib/main.dart

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:pie_menu/pie_menu.dart';
import 'package:url_launcher/url_launcher_string.dart';

extension ContextExtension on BuildContext {
  void showSnackBar(String message) {
    ScaffoldMessenger.of(this).removeCurrentSnackBar();
    ScaffoldMessenger.of(this).showSnackBar(
      SnackBar(
        content: Text(message),
        duration: const Duration(seconds: 2),
      ),
    );
  }
}

void launchUrlExternally(String url) {
  launchUrlString(url, mode: LaunchMode.externalApplication);
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
  SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
    statusBarColor: Colors.transparent,
    systemNavigationBarColor: Colors.transparent,
  ));
  runApp(const SandboxApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Pie Menu',
      home: const HomePage(),
      theme: ThemeData(
        fontFamily: 'Poppins',
        textTheme: const TextTheme().apply(fontFamily: 'Poppins'),
        snackBarTheme: const SnackBarThemeData(
          contentTextStyle: TextStyle(
            fontFamily: 'Poppins',
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _navigationIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'Flutter Pie Menu 🥧',
          style: TextStyle(fontWeight: FontWeight.w600),
        ),
      ),
      body: IndexedStack(
        index: _navigationIndex,
        children: const [
          StylingPage(),
          ListViewPage(),
          AboutPage(),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _navigationIndex,
        onTap: (index) => setState(() => _navigationIndex = index),
        items: const [
          BottomNavigationBarItem(
            icon: FaIcon(FontAwesomeIcons.palette),
            label: 'Styling',
          ),
          BottomNavigationBarItem(
            icon: FaIcon(FontAwesomeIcons.list),
            label: 'ListView',
          ),
          BottomNavigationBarItem(
            icon: FaIcon(FontAwesomeIcons.circleInfo),
            label: 'About',
          ),
        ],
      ),
    );
  }
}

/// Different styles
class StylingPage extends StatelessWidget {
  const StylingPage({super.key});

  static const double spacing = 20;

  @override
  Widget build(BuildContext context) {
    return PieCanvas(
      theme: const PieTheme(
        delayDuration: Duration.zero,
        tooltipTextStyle: TextStyle(
          fontSize: 32,
          fontWeight: FontWeight.w600,
        ),
      ),
      child: Builder(builder: (context) {
        return SafeArea(
          child: Padding(
            padding: const EdgeInsets.all(spacing),
            child: Row(
              children: [
                Expanded(
                  child: Column(
                    children: [
                      Expanded(
                        child: PieMenu(
                          actions: [
                            PieAction(
                              tooltip: const Text('Play'),
                              onSelect: () => context.showSnackBar('Play'),

                              /// Optical correction
                              child: const Padding(
                                padding: EdgeInsets.only(left: 4),
                                child: FaIcon(FontAwesomeIcons.play),
                              ),
                            ),
                            PieAction(
                              tooltip: const Text('Like'),
                              onSelect: () => context.showSnackBar('Like'),
                              child:
                                  const FaIcon(FontAwesomeIcons.solidThumbsUp),
                            ),
                            PieAction(
                              tooltip: const Text('Share'),
                              onSelect: () => context.showSnackBar('Share'),
                              child: const FaIcon(FontAwesomeIcons.share),
                            ),
                          ],
                          child: _buildCard(
                            color: Colors.orangeAccent,
                            iconData: FontAwesomeIcons.solidSun,
                          ),
                        ),
                      ),
                      const SizedBox(height: spacing),
                      Expanded(
                        child: PieMenu(
                          theme: PieTheme.of(context).copyWith(
                            buttonTheme: const PieButtonTheme(
                              backgroundColor: Colors.deepOrange,
                              iconColor: Colors.white,
                            ),
                            buttonThemeHovered: const PieButtonTheme(
                              backgroundColor: Colors.orangeAccent,
                              iconColor: Colors.black,
                            ),
                            brightness: Brightness.dark,
                          ),
                          actions: [
                            PieAction.builder(
                              tooltip: const Text('how'),
                              onSelect: () => context.showSnackBar('1'),
                              builder: (hovered) {
                                return _buildTextButton('1', hovered);
                              },
                            ),
                            PieAction.builder(
                              tooltip: const Text('cool'),
                              onSelect: () => context.showSnackBar('2'),
                              builder: (hovered) {
                                return _buildTextButton('2', hovered);
                              },
                            ),
                            PieAction.builder(
                              tooltip: const Text('is'),
                              onSelect: () => context.showSnackBar('3'),
                              builder: (hovered) {
                                return _buildTextButton('3', hovered);
                              },
                            ),
                            PieAction.builder(
                              tooltip: const Text('this?!'),
                              onSelect: () =>
                                  context.showSnackBar('Pretty cool :)'),
                              builder: (hovered) {
                                return _buildTextButton('4', hovered);
                              },
                            ),
                          ],
                          child: _buildCard(
                            color: Colors.deepPurple,
                            iconData: FontAwesomeIcons.solidMoon,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
                const SizedBox(width: spacing),
                Expanded(
                  child: Column(
                    children: [
                      Expanded(
                        child: PieMenu(
                          theme: PieTheme.of(context).copyWith(
                            tooltipTextStyle: const TextStyle(
                              color: Colors.white,
                            ),
                            overlayColor: Colors.teal.withOpacity(0.7),
                            pointerSize: 40,
                            pointerDecoration: BoxDecoration(
                              shape: BoxShape.circle,
                              color: Colors.red.withOpacity(0.5),
                            ),
                            buttonTheme: const PieButtonTheme(
                              backgroundColor: Colors.black,
                              iconColor: Colors.white,
                            ),
                            buttonThemeHovered: const PieButtonTheme(
                              backgroundColor: Colors.white,
                              iconColor: Colors.black,
                            ),
                            buttonSize: 84,
                            leftClickShowsMenu: false,
                            rightClickShowsMenu: true,
                          ),
                          onPressedWithDevice: (kind) {
                            if (kind == PointerDeviceKind.mouse) {
                              context.showSnackBar(
                                'Right click to show the menu',
                              );
                            }
                          },
                          actions: [
                            PieAction(
                              tooltip: const Text('Available on pub.dev'),
                              onSelect: () {
                                launchUrlExternally(
                                    'https://pub.dev/packages/pie_menu');
                              },
                              child: const FaIcon(FontAwesomeIcons.boxOpen),
                            ),
                            PieAction(
                              tooltip: const Text('Highly customizable'),
                              onSelect: () {
                                launchUrlExternally(
                                    'https://pub.dev/packages/pie_menu');
                              },

                              /// Custom background color
                              buttonTheme: PieButtonTheme(
                                backgroundColor: Colors.black.withOpacity(0.7),
                                iconColor: Colors.white,
                              ),
                              child: const FaIcon(FontAwesomeIcons.palette),
                            ),
                            PieAction(
                              tooltip:
                                  const Text('Now with right click support!'),
                              buttonTheme: PieButtonTheme(
                                backgroundColor: Colors.black.withOpacity(0.5),
                                iconColor: Colors.white,
                              ),
                              onSelect: () {
                                launchUrlExternally(
                                    'https://pub.dev/packages/pie_menu');
                              },
                              child:
                                  const FaIcon(FontAwesomeIcons.computerMouse),
                            ),
                          ],
                          child: _buildCard(
                            color: Colors.teal,
                            iconData: FontAwesomeIcons.solidHeart,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }),
    );
  }

  Widget _buildCard({
    Color? color,
    required IconData iconData,
  }) {
    return Container(
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(16),
      ),
      child: Center(
        child: FaIcon(
          iconData,
          color: Colors.white,
          size: 64,
        ),
      ),
    );
  }

  Widget _buildTextButton(String text, bool hovered) {
    return Text(
      text,
      style: TextStyle(
        color: hovered ? Colors.black : Colors.white,
        fontSize: 20,
        fontWeight: FontWeight.w600,
      ),
    );
  }
}

/// List view example
class ListViewPage extends StatefulWidget {
  const ListViewPage({super.key});

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  bool _menuActive = false;

  static const double spacing = 20;

  @override
  Widget build(BuildContext context) {
    return PieCanvas(
      onMenuToggle: (active) {
        setState(() => _menuActive = active);
      },
      theme: const PieTheme(
        rightClickShowsMenu: true,
        tooltipTextStyle: TextStyle(
          fontSize: 32,
          fontWeight: FontWeight.w600,
        ),
      ),
      child: ListView.separated(
        padding: EdgeInsets.only(
          top: spacing,
          bottom: spacing,
          left: MediaQuery.of(context).padding.left + spacing,
          right: MediaQuery.of(context).padding.right + spacing,
        ),
        physics: _menuActive
            ? const NeverScrollableScrollPhysics()
            : const BouncingScrollPhysics(),
        itemCount: 16,
        separatorBuilder: (context, index) => const SizedBox(height: spacing),
        itemBuilder: (context, index) {
          return SizedBox(
            height: 200,
            child: PieMenu(
              onPressed: () {
                context.showSnackBar(
                  '#$index — Long press or right click to show the menu',
                );
              },
              actions: [
                PieAction(
                  tooltip: const Text('Like'),
                  onSelect: () => context.showSnackBar('Like #$index'),
                  child: const FaIcon(FontAwesomeIcons.solidHeart),
                ),
                PieAction(
                  tooltip: const Text('Comment'),
                  onSelect: () => context.showSnackBar('Comment #$index'),
                  child: const FaIcon(FontAwesomeIcons.solidComment),
                ),
                PieAction(
                  tooltip: const Text('Save'),
                  onSelect: () => context.showSnackBar('Save #$index'),
                  child: const FaIcon(FontAwesomeIcons.solidBookmark),
                ),
                PieAction(
                  tooltip: const Text('Share'),
                  onSelect: () => context.showSnackBar('Share #$index'),
                  child: const FaIcon(FontAwesomeIcons.share),
                ),
              ],
              child: DecoratedBox(
                decoration: BoxDecoration(
                  color: Colors.orangeAccent,
                  borderRadius: BorderRadius.circular(16),
                ),
                child: Center(
                  child: Text(
                    '#$index',
                    style: const TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.w600,
                      fontSize: 64,
                    ),
                  ),
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

/// About the developer
class AboutPage extends StatelessWidget {
  const AboutPage({super.key});

  @override
  Widget build(BuildContext context) {
    return PieCanvas(
      theme: PieTheme(
        delayDuration: Duration.zero,
        tooltipTextStyle: const TextStyle(
          fontSize: 32,
          fontWeight: FontWeight.w600,
        ),
        tooltipUseFittedBox: true,
        buttonTheme: const PieButtonTheme(
          backgroundColor: Colors.black,
          iconColor: Colors.white,
        ),
        buttonThemeHovered: PieButtonTheme(
          backgroundColor: Colors.lime[200],
          iconColor: Colors.black,
        ),
        overlayColor: Colors.blue[200]?.withOpacity(0.7),
        rightClickShowsMenu: true,
      ),
      child: Center(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              const Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  FlutterLogo(size: 100),
                  SizedBox(width: 16),
                  Text(
                    '🥧',
                    style: TextStyle(
                      fontSize: 100,
                      height: 0.8,
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 32),
              Center(
                child: PieMenu(
                  actions: [
                    PieAction(
                      tooltip: const Text('github/rasitayaz'),
                      onSelect: () {
                        launchUrlExternally('https://github.com/rasitayaz');
                      },
                      child: const FaIcon(FontAwesomeIcons.github),
                    ),
                    PieAction(
                      tooltip: const Text('linkedin/rasitayaz'),
                      onSelect: () {
                        launchUrlExternally(
                          'https://linkedin.com/in/rasitayaz/',
                        );
                      },
                      child: const FaIcon(FontAwesomeIcons.linkedinIn),
                    ),
                    PieAction(
                      tooltip: const Text('mrasitayaz@gmail.com'),
                      onSelect: () {
                        launchUrlExternally('mailto:mrasitayaz@gmail.com');
                      },
                      child: const FaIcon(FontAwesomeIcons.solidEnvelope),
                    ),
                    PieAction(
                      tooltip: const Text('buy me a coffee'),
                      onSelect: () {
                        launchUrlExternally(
                          'https://buymeacoffee.com/rasitayaz',
                        );
                      },
                      child: const FaIcon(FontAwesomeIcons.mugSaucer, size: 20),
                    ),
                  ],
                  child: FittedBox(
                    child: Container(
                      padding: const EdgeInsets.all(32),
                      decoration: BoxDecoration(
                        color: Colors.blue,
                        borderRadius: BorderRadius.circular(16),
                      ),
                      child: const Column(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Text(
                            'created by',
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 36,
                            ),
                          ),
                          Text(
                            'Raşit Ayaz',
                            style: TextStyle(
                              color: Colors.white,
                              fontWeight: FontWeight.w800,
                              fontSize: 40,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
159
likes
0
pub points
89%
popularity

Publisher

unverified uploader

A Flutter package that provides a highly customizable circular/radial context menu

Repository (GitHub)
View/report issues

Funding

Consider supporting this project:

buymeacoffee.com

License

unknown (LICENSE)

Dependencies

flutter, vector_math

More

Packages that depend on pie_menu