Skip to content

Repository files navigation

Hybrid Android-Flutter Application

A production-ready hybrid mobile application demonstrating seamless integration between native Android (Kotlin + Jetpack Compose) and Flutter Module with bidirectional communication.

Overview

This project showcases how to build a hybrid mobile application where:

  • Native Android screens are built with Jetpack Compose and MVVM architecture
  • Flutter screens follow Clean Architecture with BLoC pattern
  • Both layers communicate bidirectionally via Platform Channels
  • Shared FlutterEngine for optimal performance
  • Complete CI/CD pipeline with GitHub Actions and CircleCI

Features

  • Hybrid Navigation: Seamless transitions between native and Flutter screens
  • Bidirectional Communication: Native ↔ Flutter event exchange via MethodChannel
  • Appliance Management: Browse appliances in native UI, view details in Flutter
  • State Management:
    • Android: MVVM with StateFlow/SharedFlow
    • Flutter: BLoC pattern with Freezed
  • Dependency Injection:
    • Android: Dagger Hilt
    • Flutter: GetIt
  • Testing: Comprehensive unit tests for Flutter BLoC components
  • CI/CD: Automated builds and testing with GitHub Actions & CircleCI

Tech Stack

Android Native

  • Language: Kotlin
  • UI: Jetpack Compose
  • Architecture: MVVM (Model-View-ViewModel)
  • DI: Dagger Hilt
  • Networking: Retrofit + OkHttp
  • Async: Coroutines + Flow
  • Build: Gradle with Kotlin DSL

Flutter Module

  • Language: Dart
  • Architecture: Clean Architecture with BLoC
  • State Management: flutter_bloc + Freezed
  • DI: GetIt
  • Networking: Dio
  • Testing: mockito + bloc_test
  • Code Generation: build_runner, freezed

Communication Layer

  • Platform Channels: MethodChannel for bidirectional method invocation
  • Event Channel: EventChannel for streaming (setup for future use)
  • Data Format: JSON for cross-platform data serialization

Architecture

High-Level Architecture

┌──────────────────────────────────────────────────────┐
│              Android Native Layer                     │
│  ┌────────────────────────────────────────────────┐  │
│  │  DashboardActivity (Jetpack Compose)           │  │
│  │  - Appliance list                              │  │
│  │  - Image carousel                              │  │
│  │  - Support cards                               │  │
│  └────────────────────────────────────────────────┘  │
│                      │ Intent                         │
│                      ▼                                │
│  ┌────────────────────────────────────────────────┐  │
│  │  HybridFlutterActivity                         │  │
│  │  - Hosts FlutterFragment                       │  │
│  │  - Manages lifecycle                           │  │
│  └────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘
                       │
                       │ MethodChannel
                       ▼
┌──────────────────────────────────────────────────────┐
│              Flutter Module Layer                     │
│  ┌────────────────────────────────────────────────┐  │
│  │  HomePage                                      │  │
│  │  - Flutter home screen                         │  │
│  └────────────────────────────────────────────────┘  │
│                      │                                │
│                      ▼                                │
│  ┌────────────────────────────────────────────────┐  │
│  │  ApplianceDetailPage                           │  │
│  │  - Appliance details                           │  │
│  │  - Interactive controls                        │  │
│  └────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘

Communication Flow

Android Native              Platform Channel              Flutter
─────────────              ─────────────────              ───────
DashBoardViewModel
     │
     │ 1. Start HybridFlutterActivity
     ├──────────────────────────────────────────────>
     │
     │ 2. Send "navigate" event
     │    + appliance data
     ├────────────────────────> MethodChannel
     │                                  │
     │                                  │ 3. Receive event
     │                                  └────────────────> HybridBloc
     │                                                    │
     │                                                    │ 4. Navigate
     │                                                    │    to route
     │                                                    ▼
     │                                           ApplianceDetailPage
     │                                                    │
     │                          5. Send "navigateBack"   │
     │                          <────────────────────────┤
     │ 6. Receive event         │
     │<─────────────────────────┤
     │
     │ 7. Close activity
     │ finish()

Project Structure

sample_hydrid_android/
├── app/                                    # Android native app
│   ├── src/main/java/com/example/myapplication/
│   │   ├── core/
│   │   │   ├── MainApplication.kt
│   │   │   ├── service/
│   │   │   │   ├── HybridEventBus.kt      # Event communication
│   │   │   │   └── HydridNavigateService.kt
│   │   │   ├── theme/                      # Compose theme
│   │   │   └── utils/
│   │   ├── data/
│   │   │   ├── model/                      # Data models
│   │   │   ├── remote/                     # API service
│   │   │   └── repository/
│   │   ├── di/                             # Dagger Hilt modules
│   │   │   ├── FlutterEngineModule.kt
│   │   │   ├── NetworkModule.kt
│   │   │   └── ServiceModule.kt
│   │   └── ui/
│   │       ├── components/                 # Reusable Compose components
│   │       └── feature/
│   │           ├── dashboard/              # Native dashboard
│   │           └── hydrid/                 # Flutter host activity
│   └── build.gradle.kts
│
├── flutter_module/                         # Flutter module
│   ├── lib/
│   │   ├── core/
│   │   │   ├── di/
│   │   │   │   └── injection_container.dart
│   │   │   ├── routes/
│   │   │   │   ├── app_routes.dart
│   │   │   │   └── route_mapper.dart
│   │   │   ├── services/
│   │   │   │   ├── hybrid_bus_service.dart
│   │   │   │   └── navigation_service.dart
│   │   │   └── theme/
│   │   ├── data/                           # Data layer
│   │   ├── domain/                         # Domain layer
│   │   │   ├── api/
│   │   │   ├── model/
│   │   │   └── usecases/
│   │   └── ui/                             # Presentation layer
│   │       ├── home/
│   │       │   ├── bloc/
│   │       │   └── home_page.dart
│   │       └── appliance/
│   │           ├── bloc/
│   │           └── appliance_detail_page.dart
│   ├── test/                               # Unit tests
│   └── pubspec.yaml
│
├── .github/workflows/
│   └── build-apk.yml                       # GitHub Actions CI
├── .circleci/
│   └── config.yml                          # CircleCI configuration
├── HYBRID_INTEGRATION.md                   # Detailed integration docs
├── QUICK_REFERENCE.md                      # Quick reference guide
└── README.md                               # This file

Getting Started

Prerequisites

  • Android Studio: Arctic Fox or later
  • Flutter SDK: 3.36.5 or later
  • Dart SDK: Included with Flutter
  • JDK: 17 or later
  • Gradle: 8.0+

Installation

  1. Clone the repository

    git clone https://github.com/yourusername/sample_hydrid_android.git
    cd sample_hydrid_android
  2. Set up Flutter module

    cd flutter_module
    flutter pub get
    cd ..
  3. Generate Flutter code (for Freezed, Mockito, etc.)

    cd flutter_module
    dart run build_runner build --delete-conflicting-outputs
    cd ..
  4. Open in Android Studio

    • Open the root project in Android Studio
    • Wait for Gradle sync to complete

Building

Debug Build

./gradlew assembleDebug

Release Build

./gradlew assembleRelease

Flutter Module Build

cd flutter_module
flutter build aar

Testing

Run Flutter Tests

cd flutter_module
flutter test

Run Flutter Tests with Coverage

cd flutter_module
flutter test --coverage

Generate Flutter Mocks

cd flutter_module
dart run build_runner build --delete-conflicting-outputs

Run Android Unit Tests

./gradlew test

Run Android Instrumented Tests

./gradlew connectedAndroidTest

CI/CD

This project includes two CI/CD configurations:

GitHub Actions

  • Workflow: .github/workflows/build-apk.yml
  • Triggers: Push to main, develop branches
  • Steps:
    1. Checkout code
    2. Set up Java 17
    3. Set up Flutter
    4. Install Flutter dependencies
    5. Generate code (build_runner)
    6. Run Flutter tests
    7. Build Android APK
    8. Upload APK artifact

CircleCI

  • Config: .circleci/config.yml
  • Triggers: Push to any branch
  • Steps:
    1. Checkout code
    2. Restore Flutter cache
    3. Install Flutter SDK
    4. Install dependencies
    5. Generate code
    6. Run tests with coverage
    7. Build APK
    8. Store artifacts

Documentation

Detailed Documentation

  • HYBRID_INTEGRATION.md - Complete guide to the hybrid architecture
    • Communication channels
    • Android native implementation
    • Flutter module implementation
    • Navigation patterns
    • Event handling
    • Best practices
    • Troubleshooting

Quick Reference

  • QUICK_REFERENCE.md - Quick code snippets and patterns
    • Event communication examples
    • Navigation patterns
    • Dependency injection
    • Debugging tips
    • Checklists

Key Concepts

FlutterEngine Caching

The app uses a cached FlutterEngine for better performance:

// Pre-warm at startup
val engine = FlutterEngine(context).apply {
    dartExecutor.executeDartEntrypoint(
        DartExecutor.DartEntrypoint.createDefault()
    )
}
FlutterEngineCache.getInstance().put(Constants.flutterEngineName, engine)

// Reuse in activities
FlutterFragment.withCachedEngine(Constants.flutterEngineName).build()

Event Communication

Events are sent via MethodChannel with JSON data:

// Android → Flutter
hybridEventBus.sendEventToHydrid("navigate", mapOf(
    "routeName" to "/appliance/1",
    "id" to "1",
    "name" to "Refrigerator"
))

// Flutter → Android
await hybridBusService.sendEventToNative('navigateBack', {});

Navigation Pattern

  1. Native screen starts HybridFlutterActivity
  2. Activity sends navigation event with route and data
  3. Flutter receives event and navigates to route
  4. Flutter sends back event to close activity

Use Cases

Opening Appliance Details

  1. User taps appliance card in native dashboard (Compose)
  2. DashBoardViewModel starts HybridFlutterActivity
  3. Sends "navigate" event with appliance data
  4. Flutter HybridBloc receives event
  5. Navigates to ApplianceDetailPage with data
  6. User sees appliance details in Flutter UI

Returning to Native

  1. User taps back button in Flutter screen
  2. ApplianceDetailBloc sends "navigateBack" event
  3. Android HybridFlutterViewModel receives event
  4. Emits to closeActivity flow
  5. HybridFlutterActivity finishes
  6. User returns to native dashboard

Troubleshooting

Common Issues

Issue: FlutterEngine not found

Solution: Ensure FlutterEngineModule is providing the engine before
creating FlutterFragment

Issue: Events not received in Flutter

Solution:
1. Verify channel name matches: "hybrid_channel"
2. Call hybridEventBus.register() in Activity
3. Check registerListener() is called in BLoC

Issue: Activity doesn't close on back

Solution: Ensure closeActivity flow is collected in Activity:
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        hydridViewModel.closeActivity.collect { finish() }
    }
}

Performance Considerations

  • FlutterEngine caching: Reduces cold start time from ~2s to ~200ms
  • Event queuing: Prevents event loss during initialization
  • Lazy singletons: Services are created only when needed
  • StateFlow: Efficient state management with replay cache

Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Code Style

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Flutter team for the excellent Flutter Module architecture
  • Dagger Hilt for powerful dependency injection
  • BLoC pattern for predictable state management
  • Jetpack Compose for modern Android UI

Contact

For questions or feedback, please open an issue on GitHub.


Built with ❤️ using Kotlin, Flutter, and modern Android development practices

About

An example demonstrating how to integrate Flutter (following Clean Architecture) with native Android (using the MVVM pattern), including the mechanism for exchanging data between the two layers.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages