This project demonstrates how to integrate a Flutter module within an Ionic application, allowing bidirectional communication between both frameworks.
├── android/ # Android native code
│ └── app/
│ └── src/
│ └── main/
│ └── java/
│ └── io/
│ └── ionic/
│ └── starter/
│ ├── MainActivity.java # Main integration point
├── src/
│ ├── app/
│ │ └── home/
│ │ └── home.page.ts # Ionic UI for Flutter interaction
├── capacitor-bridge/ # Capacitor bridge plugin
│ ├── src/
│ │ ├── index.ts # Plugin interface
│ │ └── definitions.ts # Type definitions
│ └── android/
│ └── src/
│ └── main/
│ └── java/
│ └── io/
│ └── ionic/
│ └── starter/
│ └── CustomBridgePlugInPlugin.java # Native implementation
└── flutter_module/ # Flutter module (separate project)
The integration uses a hybrid approach with the following components:
- Native Bridge: Custom Capacitor plugin (
CustomBridgePlugInPlugin
) handles communication between Ionic and native Android code - Flutter Engine: Embedded Flutter engine in the Android app
- Broadcast Receivers: Handle communication between Ionic and Flutter
- Method Channels: Enable bidirectional communication between native and Flutter code
-
Ionic → Flutter:
- Ionic app triggers Flutter view through native bridge
- Message is passed via Intent to Flutter activity
- Flutter receives message through MethodChannel
-
Flutter → Ionic:
- Flutter sends data through MethodChannel
- Native code broadcasts message via Intent
- Ionic receives message through BroadcastReceiver
- Node.js and npm
- Android Studio
- Flutter SDK
- Ionic CLI
- Capacitor
-
Create Ionic Project:
ionic start flutterPOCApp blank --type=angular cd flutterPOCApp
-
Add Capacitor:
npm install @capacitor/core @capacitor/android npx cap init
-
Create Flutter Module:
flutter create --template module flutter_module
-
Build Flutter Module:
cd flutter_module flutter build aar
-
Add Flutter Dependencies: Add the following to
android/app/build.gradle
:dependencies { implementation project(':flutter') }
-
Configure MainActivity:
- Extend
BridgeActivity
- Initialize Flutter engine
- Set up method channels
- Register broadcast receivers
- Extend
-
Launch Flutter from Ionic:
// In Ionic component import { MyCustomBridge } from 'capacitor-bridge'; launchFlutter() { MyCustomBridge.showFlutterView({ message: 'Hello from Ionic!' }); }
-
Receive Messages in Flutter:
// In Flutter static const platform = MethodChannel('com.example.channel'); Future<void> _receiveMessage() async { try { final String result = await platform.invokeMethod('getData'); print('Received from Ionic: $result'); } catch (e) { print('Error: $e'); } }
- Manages Flutter engine lifecycle
- Handles communication between Ionic and Flutter
- Registers broadcast receivers for message passing
- Custom Capacitor plugin for native communication
- Handles message passing between Ionic and native code
- Located in
capacitor-bridge/src/index.ts
- Contains Flutter UI and business logic
- Communicates with native code through method channels
-
Flutter Engine Not Initializing:
- Check Flutter SDK path
- Verify Flutter module build
- Ensure proper dependency configuration
-
Communication Issues:
- Verify method channel names match
- Check broadcast receiver registration
- Ensure proper message format
-
Build Errors:
- Clean and rebuild project
- Verify Gradle configuration
- Check Flutter module integration
-
Message Format:
- Use consistent message structure
- Implement error handling
- Validate data types
-
Performance:
- Initialize Flutter engine once
- Cache Flutter engine instance
- Clean up resources properly
-
Security:
- Validate incoming messages
- Use secure communication channels
- Implement proper error handling
Feel free to submit issues and enhancement requests.
This project is licensed under the MIT License.
// In Ionic component
import { MyCustomBridge } from 'capacitor-bridge';
// Send message to native
async function sendToNative() {
try {
await MyCustomBridge.sendMessage({
message: 'Hello from Ionic to Native!'
});
} catch (error) {
console.error('Error sending message:', error);
}
}
// In Ionic component
import { MyCustomBridge } from 'capacitor-bridge';
// Listen for native messages
MyCustomBridge.addListener('onNativeMessage', (data: { message: string }) => {
console.log('Received from Native:', data.message);
// Handle the message
});
// In MainActivity.java
private CustomBridgePlugInPlugin customPluginRef;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get plugin reference
PluginHandle pluginHandle = getBridge().getPlugin("CustomBridgePlugInPlugin");
if (pluginHandle != null && pluginHandle.getInstance() instanceof CustomBridgePlugInPlugin) {
customPluginRef = (CustomBridgePlugInPlugin) pluginHandle.getInstance();
}
// Send message to Ionic
customPluginRef.sendMessageToJS("Hello from Native!");
}
The plugin supports the following message types:
- Text Messages: Simple string communication
- JSON Data: Structured data exchange
- Events: Custom event notifications
// Error handling example
try {
await MyCustomBridge.sendMessage({
message: 'Test message'
});
} catch (error) {
if (error.code === 'PLUGIN_ERROR') {
console.error('Plugin error:', error.message);
} else {
console.error('Unknown error:', error);
}
}