📱 Global Context Menu
A Flutter plugin that enables adding custom actions to Android's text selection toolbar using the ACTION_PROCESS_TEXT
intent.
✨ Features
- 🔠 Add custom actions to Android's text selection toolbar
- 📝 Process selected text from any app
- 🔄 Return modified text to replace the selection
- 🔒 Support for both read-only and editable text selections
With return action | Read only |
📋 Requirements
- 📱 Android 6.0 (API level 23) or higher
- 💙 Flutter 2.5.0 or higher
📦 Installation
Add the plugin to your pubspec.yaml
:
dependencies:
global_context_menu: any
Run:
flutter pub get
🛠️ Setup
1️⃣ Create a Flutter Activity to handle the text processing
First, create a Flutter activity in your app that will be launched when the user selects your text processing action.
Example activity implementation in Kotlin:
package com.example.yourapp
import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
class YourProcessTextActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// The GlobalContextMenu plugin handles the rest in your Flutter code
}
}
2️⃣ Register the activity in your AndroidManifest.xml
Add the following to your app's android/app/src/main/AndroidManifest.xml
file inside the <application>
tag:
<activity
android:name=".YourProcessTextActivity"
android:label="Your Action Name"> // Add your action name
<intent-filter>
<action android:name="android.intent.action.PROCESS_TEXT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
⚠️ Important notes:
- The
android:label
attribute will be shown as the action name in the text selection toolbar, so keep it short and descriptive.- You can add multiple activities with different labels for different text processing actions.
- As of Android 6.0.1, avoid adding
android:exported="false"
as it can cause issues.
💻 Usage
Check if the device supports the feature
bool isSupported = await GlobalContextMenu.isSupported();
if (!isSupported) {
// Show fallback UI or message
}
Get the selected text
try {
final Map<String, dynamic> textData = await GlobalContextMenu.getProcessedText();
final String selectedText = textData['text'] as String? ?? '';
final bool isReadOnly = textData['isReadOnly'] as bool? ?? false;
// Process the text as needed
print('Selected text: $selectedText');
print('Is read-only: $isReadOnly');
} catch (e) {
print('Error getting text: $e');
}
Return modified text
// Only works if text is not read-only
try {
String modifiedText = "Modified: $originalText";
await GlobalContextMenu.setProcessedText(modifiedText);
await GlobalContextMenu.finishActivity();
} catch (e) {
print('Error setting text: $e');
}
Close the activity
// Close without modifying text
GlobalContextMenu.finishActivity();
📝 Complete Example
Here's a simple example of a text processor that converts selected text to uppercase:
import 'package:flutter/material.dart';
import 'package:global_context_menu/global_context_menu.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _selectedText = 'No text selected yet';
bool _isReadOnly = false;
bool _isSupported = false;
@override
void initState() {
super.initState();
checkSupport();
getSelectedText();
}
Future<void> checkSupport() async {
final isSupported = await GlobalContextMenu.isSupported();
setState(() {
_isSupported = isSupported;
});
}
Future<void> getSelectedText() async {
try {
final textData = await GlobalContextMenu.getProcessedText();
setState(() {
_selectedText = textData['text'] as String? ?? 'No text selected';
_isReadOnly = textData['isReadOnly'] as bool? ?? false;
});
} catch (e) {
print('Error: $e');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Text Processor'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (!_isSupported)
Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
padding: EdgeInsets.all(8.0),
color: Colors.amber[100],
child: Text(
'This device does not support text processing. '
'Android 6.0+ required.',
style: TextStyle(color: Colors.red[900]),
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text('Selected text: $_selectedText'),
),
if (!_isReadOnly)
ElevatedButton(
onPressed: () async {
final upperText = _selectedText.toUpperCase();
try {
await GlobalContextMenu.setProcessedText(upperText);
await GlobalContextMenu.finishActivity();
} catch (e) {
print("Error: $e");
}
},
child: Text('Convert to UPPERCASE'),
),
ElevatedButton(
onPressed: () {
GlobalContextMenu.finishActivity();
},
child: Text('Cancel'),
),
],
),
),
),
);
}
}
🔍 How It Works
- 📲 The plugin registers activities with the
ACTION_PROCESS_TEXT
intent filter in AndroidManifest.xml - 👆 When a user selects text in any app and chooses your action, your activity is launched
- 📊 The plugin retrieves the selected text and provides it to your Flutter app
- 🔄 Your app processes the text and optionally returns modified text
- 🏁 The activity finishes and returns to the calling app
⚠️ Limitations
- 📱 Only works on Android 6.0 (API level 23) or higher
- 🌐 Your action will appear in every text selection context in all apps
- 🔍 You cannot filter when your action appears based on text content
- 🚫 Services cannot be directly triggered by ACTION_PROCESS_TEXT
🔮 Use Cases
- 📚 Dictionary/definition lookup
- 🌐 Translation services
- 🔍 Search in your app
- 📊 Analysis or statistics
- 🔤 Text formatting/transformation
📱 Example Apps Using Similar Functionality
- 📖 Wikipedia (Search Wikipedia)
- 🌐 Google Translate (Translate)
- 🔍 Google Search (Web Search)
🚀 Planned Features
The following features are planned for future releases:
- 🎨 Rich Text Support - Handle formatted text (HTML, markdown) with styling preserved
- 🔄 Batch Processing - Process multiple text selections at once
- 🌐 Enhanced Translation - Built-in translation capabilities with multiple language support
- 📊 Text Analytics - Word count, readability scores, and other text metrics
- ⚙️ Advanced Configuration - More options to control when and how your action appears
- 🔒 Enhanced Security - Additional security features for sensitive text processing
- 📦 Pre-built Processors - Common text processors ready to use out of the box
- 📄 Multi-platform Support - Exploring options for similar functionality on other platforms
🐛 Troubleshooting
Common Issues
- Action doesn't appear: Make sure you're running Android 6.0+ and have correctly configured your AndroidManifest.xml
- SecurityException: Avoid using
android:exported="false"
in your activity declaration - Activity not launching: Ensure your activity class name matches exactly what's in the manifest
👨💻 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📧 Contact
If you have any questions or feedback, please open an issue on the GitHub repository.
Made with ❤️ by Lokesh Jangid