Vision Wheel Menu

A beautiful, customizable Vision OS-style radial menu for Flutter that appears on long press. This package provides a sleek, glass-morphic wheel menu that allows users to select options by dragging and releasing.
Installation
Add this to your package's pubspec.yaml file:
dependencies:
vision_wheel_menu: ^0.1.0
Then run:
flutter pub get
Usage
Basic Implementation
import 'package:flutter/material.dart';
import 'package:vision_wheel_menu/vision_wheel_menu.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Define your menu options
final options = [
MenuOption(icon: Icons.home, label: 'Home'),
MenuOption(icon: Icons.edit, label: 'Edit'),
MenuOption(icon: Icons.share, label: 'Share'),
MenuOption(icon: Icons.delete, label: 'Delete'),
];
return Stack(
children: [
// Your existing UI here
// Add the wheel menu widget
VisionWheelMenuWidget(
options: options,
onOptionSelected: (option) {
print('Selected: ${option.label}');
// Handle option selection here
},
),
],
);
}
}
How It Works
- User long presses anywhere on the screen
- The wheel menu appears at the touch position
- User drags finger to the desired option (it highlights as they hover)
- User releases finger to select the option
- The selected option is passed to your callback function
Components
MenuOption
This is the data model for each option in the wheel menu.
MenuOption(
icon: Icons.home, // Flutter IconData
label: 'Home', // Text label
)
Parameters
| Parameter | Type | Description |
|---|---|---|
| icon | IconData | The icon to display for this option |
| label | String | The text label to display below the icon |
VisionWheelMenuWidget
The main widget that provides the complete wheel menu functionality.
VisionWheelMenuWidget(
options: options,
onOptionSelected: handleOptionSelected,
centerText: 'MENU',
wheelSize: 220,
animationDuration: Duration(milliseconds: 200),
)
Parameters
| Parameter | Type | Description | Default |
|---|---|---|---|
| options | List<MenuOption> | The options to display in the wheel menu | Required |
| onOptionSelected | Function(MenuOption) | Callback when an option is selected | Required |
| centerText | String | Text to display in the center of the wheel | 'MENU' |
| wheelSize | double | Size of the wheel menu in pixels | 220.0 |
| animationDuration | Duration | Duration of the appearance/disappearance animation | 200ms |
WheelMenu
For advanced customization, you can use the WheelMenu widget directly:
WheelMenu(
options: options,
hoveredIndex: 2, // Optional: pre-highlight an option
onOptionSelected: handleOptionSelected,
centerText: 'MENU',
size: 220,
)
Parameters
| Parameter | Type | Description | Default |
|---|---|---|---|
| options | List<MenuOption> | The options to display in the wheel menu | Required |
| onOptionSelected | Function(MenuOption) | Callback when an option is selected | Required |
| hoveredIndex | int? | Index of the option to highlight | null |
| centerText | String | Text to display in the center of the wheel | 'MENU' |
| size | double | Size of the wheel menu in pixels | 220.0 |
Advanced Usage
Custom Number of Options
The menu automatically adapts to any number of options:
final List<MenuOption> _options = [
MenuOption(icon: Icons.home, label: 'Home'),
MenuOption(icon: Icons.edit, label: 'Edit'),
MenuOption(icon: Icons.share, label: 'Share'),
// Add as many as you need - the wheel adapts!
];
Custom Styling (Coming Soon)
In future releases, we'll add more customization options including:
- Custom colors
- Option size
- Font styles
- Background opacity
Complete Example
For a complete example, see the example folder.
import 'package:flutter/material.dart';
import 'package:vision_wheel_menu/vision_wheel_menu.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Vision Wheel Menu Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String _selectedOption = '';
final List<MenuOption> _options = [
MenuOption(icon: Icons.home, label: 'Home'),
MenuOption(icon: Icons.edit, label: 'Edit'),
MenuOption(icon: Icons.share, label: 'Share'),
MenuOption(icon: Icons.delete, label: 'Delete'),
MenuOption(icon: Icons.file_copy, label: 'New'),
MenuOption(icon: Icons.settings, label: 'Settings'),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Background
Container(
decoration: BoxDecoration(
gradient: RadialGradient(
center: Alignment(0.3, 0.3),
radius: 0.8,
colors: [
Color(0xFF1E1E2A),
Color(0xFF0A0A0A),
],
),
),
child: Center(
child: Text(
'Selected: $_selectedOption',
style: TextStyle(color: Colors.white70),
),
),
),
// Wheel menu
VisionWheelMenuWidget(
options: _options,
onOptionSelected: (option) {
setState(() {
_selectedOption = option.label;
});
print('Selected: ${option.label}');
},
),
],
),
);
}
}
Requirements
- Flutter 2.10.0 or higher
- Dart 2.16.0 or higher
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Credits
Inspired by Apple's Vision OS interface design.