context_menu_android 1.2.0 copy "context_menu_android: ^1.2.0" to clipboard
context_menu_android: ^1.2.0 copied to clipboard

iOS-style context menu widget for Flutter with blur, smooth animations, and nested sub-menus; optimized for Android.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:context_menu_android/context_menu_android.dart';
import 'package:context_menu_android/core/theme/colors.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Context Menu Example',
      theme: ThemeData(
        useMaterial3: true,
        brightness: Brightness.light,
        colorSchemeSeed: ColorsManager.primary,
        scaffoldBackgroundColor: const Color(
          0xFFF2F2F7,
        ), // iOS default grey background
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        brightness: Brightness.dark,
        colorSchemeSeed: ColorsManager.primary,
        scaffoldBackgroundColor: Colors.black,
      ),
      themeMode: ThemeMode.system,
      home: const ContextMenuDemo(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final bool isDark = Theme.of(context).brightness == Brightness.dark;

    return Scaffold(
      appBar: AppBar(
        title: const Text(
          '🍏 iOS Style Context Menu',
          style: TextStyle(fontWeight: FontWeight.w600),
        ),
        centerTitle: true,
        backgroundColor: Colors.transparent,
        elevation: 0,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 32),
        physics: const BouncingScrollPhysics(),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            const Text(
              'Interactive Showcase',
              style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 8),
            Text(
              'Long-press the items below to see the context menus in action.',
              style: TextStyle(
                fontSize: 16,
                color: isDark ? Colors.grey[400] : Colors.grey[600],
              ),
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 48),

            /// 🚀 Example 1: Standard Button with Subtitles
            _SectionTitle('Standard iOS Action Menu'),
            ContextMenuWrapper(
              actions: [
                ContextMenuItem(
                  icon: Icons.share_rounded,
                  label: 'Share',
                  subtitle: 'Send to friends or social media',
                  onTap: () => debugPrint('Share tapped'),
                ),
                ContextMenuItem(
                  icon: Icons.copy_rounded,
                  label: 'Copy Link',
                  subtitle: 'Copy to clipboard',
                  onTap: () => debugPrint('Copy tapped'),
                ),
                ContextMenuItem(
                  icon: Icons.archive_outlined,
                  label: 'Archive Item',
                  enabled: false,
                  onTap: () {},
                ),
                ContextMenuItem(
                  icon: Icons.delete_rounded,
                  label: 'Delete',
                  subtitle: 'This action cannot be undone',
                  isDestructive: true,
                  onTap: () => debugPrint('Delete tapped'),
                ),
              ],
              child: Container(
                height: 110,
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    colors: [Color(0xFF007AFF), Color(0xFF0056B3)],
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                  ),
                  borderRadius: BorderRadius.circular(24),
                  boxShadow: [
                    BoxShadow(
                      color: const Color(0xFF007AFF).withValues(alpha: 0.3),
                      blurRadius: 20,
                      offset: const Offset(0, 10),
                    ),
                  ],
                ),
                child: const Center(
                  child: Text(
                    'Long Press Me',
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 18,
                      letterSpacing: 0.5,
                    ),
                  ),
                ),
              ),
            ),

            const SizedBox(height: 48),

            /// 🚀 Example 2: Deeply Nested Sub-menus
            _SectionTitle('Deeply Nested Sub-menus'),
            ContextMenuWrapper(
              actions: [
                ContextMenuItem(
                  icon: Icons.play_arrow_rounded,
                  label: 'Play Next',
                  onTap: () {},
                ),
                ContextMenuItem(
                  icon: Icons.more_horiz_rounded,
                  label: 'More Options',
                  subMenu: [
                    ContextMenuItem(
                      icon: Icons.info_outline_rounded,
                      label: 'View Details',
                      onTap: () {},
                    ),
                    ContextMenuItem(
                      icon: Icons.settings_rounded,
                      label: 'Advanced Settings',
                      subMenu: [
                        ContextMenuItem(
                          icon: Icons.equalizer_rounded,
                          label: 'Audio Equalizer',
                          onTap: () {},
                        ),
                        ContextMenuItem(
                          icon: Icons.spatial_audio_off_rounded,
                          label: 'Spatial Audio',
                          onTap: () {},
                        ),
                      ],
                    ),
                    ContextMenuItem(
                      icon: Icons.playlist_add_rounded,
                      label: 'Add to Playlist',
                      onTap: () {},
                    ),
                  ],
                ),
                ContextMenuItem(
                  icon: Icons.remove_circle_outline_rounded,
                  label: 'Remove from Library',
                  isDestructive: true,
                  onTap: () {},
                ),
              ],
              child: Card(
                elevation: 0,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(16),
                ),
                color: isDark ? const Color(0xFF1C1C1E) : Colors.white,
                child: ListTile(
                  contentPadding: const EdgeInsets.symmetric(
                    horizontal: 16,
                    vertical: 8,
                  ),
                  leading: Container(
                    padding: const EdgeInsets.all(10),
                    decoration: BoxDecoration(
                      color: const Color(0xFF007AFF).withValues(alpha: 0.1),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child: const Icon(
                      Icons.music_note_rounded,
                      color: Color(0xFF007AFF),
                    ),
                  ),
                  title: const Text(
                    'Inception Soundtrack',
                    style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16),
                  ),
                  subtitle: const Text('Hans Zimmer'),
                  trailing: const Icon(Icons.more_horiz),
                ),
              ),
            ),

            const SizedBox(height: 48),

            /// 🚀 Example 3: Perfect Circular Clipping
            _SectionTitle('Perfect Circular Clipping'),
            Text(
              'Showcasing the precision GlobalKey dimensions and unforced border radius on a perfectly circular widget.',
              style: TextStyle(
                fontSize: 14,
                color: isDark ? Colors.grey[500] : Colors.grey[600],
              ),
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 16),
            Center(
              child: ContextMenuWrapper(
                childBorderRadius: BorderRadius.circular(
                  100,
                ), // Forces exact circular clip if needed
                actions: [
                  ContextMenuItem(
                    icon: Icons.person_add_rounded,
                    label: 'Add Friend',
                    onTap: () {},
                  ),
                  ContextMenuItem(
                    icon: Icons.message_rounded,
                    label: 'Send Message',
                    onTap: () {},
                  ),
                  ContextMenuItem(
                    icon: Icons.block_rounded,
                    label: 'Block User',
                    isDestructive: true,
                    onTap: () {},
                  ),
                ],
                child: Container(
                  width: 120,
                  height: 120,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black.withValues(alpha: 0.1),
                        blurRadius: 10,
                        offset: const Offset(0, 5),
                      ),
                    ],
                  ),
                  child: ClipOval(
                    child: Image.network(
                      'https://picsum.photos/200',
                      fit: BoxFit.cover,
                      loadingBuilder: (context, child, loadingProgress) {
                        if (loadingProgress == null) return child;
                        return const Center(child: CircularProgressIndicator());
                      },
                      errorBuilder: (context, error, stackTrace) =>
                          const Icon(Icons.person, size: 50),
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _SectionTitle extends StatelessWidget {
  final String title;
  const _SectionTitle(this.title);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Text(
        title,
        style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        textAlign: TextAlign.center,
      ),
    );
  }
}
10
likes
160
points
160
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

iOS-style context menu widget for Flutter with blur, smooth animations, and nested sub-menus; optimized for Android.

Repository (GitHub)
View/report issues

Topics

#context-menu #ios #android #ui #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on context_menu_android