Skip to main content

How to Resolve Flutter HMS Core Plugin Dependency Conflicts

 Integrating Huawei Mobile Services (HMS) alongside Google Mobile Services (GMS) in a single Flutter codebase frequently results in fatal Gradle build errors. The most common symptom is the build failing with Execution failed for task :app:processDebugMainManifest.

This occurs when combining the agconnect-services plugin with Google Firebase packages. Both ecosystems inject proprietary configurations into your Android build pipeline, leading to a direct clash during the manifest merging phase.

This guide provides the technical resolution to this HMS plugin dependency error, ensuring stable cross-ecosystem builds.

Root Cause Analysis of the Manifest Conflict

To fix the error, it is necessary to understand how the Android Gradle build system processes dependencies.

When you run flutter build apk or flutter run, the Android Manifest Merger tool combines the AndroidManifest.xml from your primary application with the manifests of all integrated dependencies. Both Firebase (google-services) and HMS Core (agconnect-services) declare hidden ContentProvider and Service components to initialize their SDKs automatically at app startup.

The processDebugMainManifest task fails for three primary reasons during a Flutter HMS Core integration:

  1. Namespace Collisions: Both SDKs attempt to register competing <provider> authorities or <meta-data> tags using identical generic names.
  2. Plugin Execution Order: The agcp (AGConnect) and google-services Gradle plugins manipulate the compiled manifest. If executed out of sequence, one plugin overwrites the intermediate build artifacts of the other.
  3. Transitive Dependency Clashes: Huawei's push kit and Google's Firebase Cloud Messaging (FCM) rely on different, often incompatible versions of underlying androidx.core or play-services-basement libraries.

Step-by-Step Resolution

To resolve the agconnect-services conflict, you must explicitly define merge rules in your Android manifest and strictly control your Gradle plugin application order.

1. Enforce Manifest Merge Rules

Instruct the Android Manifest Merger on how to handle conflicting nodes by utilizing the tools namespace.

Open android/app/src/main/AndroidManifest.xml and add the xmlns:tools declaration to your root <manifest> tag. Then, apply the tools:replace directive to your <application> tag.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.yourcompany.yourapp">

    <!-- tools:replace forces the merger to use your app's definition over library defaults -->
    <application
        android:label="your_app"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        tools:replace="android:label, android:allowBackup">
        
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme"
                />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        
        <!-- Resolve FCM and HMS Push Provider Conflicts -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="high_importance_channel" 
            tools:node="merge" />
            
    </application>
</manifest>

2. Correct the Gradle Classpath Configuration

Outdated classpath dependencies will cause the build system to fail before the manifest merger even runs. Ensure you are using the latest stable plugin versions.

Update your project-level android/build.gradle:

buildscript {
    ext.kotlin_version = '1.9.22'
    repositories {
        google()
        mavenCentral()
        // Huawei Maven repository is strictly required for HMS
        maven { url 'https://developer.huawei.com/repo/' }
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:8.2.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        
        // Google Services Plugin
        classpath 'com.google.gms:google-services:4.4.1'
        
        // AGConnect Plugin (Use 1.9.1.300+)
        classpath 'com.huawei.agconnect:agcp:1.9.1.301'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://developer.huawei.com/repo/' }
    }
}

3. Sequence App-Level Plugins Properly

The execution order in your app-level build.gradle is critical. The AGConnect plugin must process the agconnect-services.json file independently of the Google Services plugin processing google-services.json.

Update your android/app/build.gradle:

// Apply core plugins first
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

// Apply third-party service plugins at the BOTTOM of the apply block
// Apply Google Services first, then AGConnect
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.huawei.agconnect'

android {
    namespace "com.yourcompany.yourapp"
    compileSdk 34

    defaultConfig {
        applicationId "com.yourcompany.yourapp"
        minSdk 23
        targetSdk 34
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    
    // Required to prevent duplicate class errors between GMS and HMS basement libs
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }
}

Deep Dive: Why This Fix Works

The combination of tools:replace and tools:node="merge" directly intercepts the internal Android Manifest Merger tool (ManifestMerger2).

When the agcp plugin attempts to inject the Huawei AppGallery authentication providers, and google-services attempts to inject Firebase providers, they occasionally compete for the android:allowBackup or android:label application attributes. By defining these at the root level and applying tools:replace, you establish your base manifest as the absolute source of truth, bypassing the conflict entirely.

Furthermore, isolating the plugin execution sequence ensures that Gradle's task graph generates the generated XML files (values.xml containing project IDs and API keys) without one plugin throwing an IllegalStateException when encountering the other's payload.

Architectural Best Practice: Gradle Product Flavors

While the above solution allows both SDKs to compile into a single binary, shipping a unified APK is an anti-pattern. Including unused GMS libraries on a Huawei AppGallery device wastes bandwidth, increases your app size, and can trigger automated rejection during the AppGallery review process if Google Play Services APIs are invoked without defensive checks.

The enterprise standard for Flutter Huawei AppGallery deployments is utilizing Android Product Flavors to completely separate the build variants.

Add this to your android/app/build.gradle:

android {
    // ... existing config ...

    flavorDimensions "store"

    productFlavors {
        google {
            dimension "store"
            // Applies GMS only to this flavor
        }
        huawei {
            dimension "store"
            // Applies HMS only to this flavor
        }
    }
}

// Conditionally apply plugins based on the active build task
gradle.taskGraph.whenReady { taskGraph ->
    def isHuaweiBuild = taskGraph.allTasks.any { it.name.toLowerCase().contains('huawei') }
    def isGoogleBuild = taskGraph.allTasks.any { it.name.toLowerCase().contains('google') }

    if (isGoogleBuild) {
        apply plugin: 'com.google.gms.google-services'
    }
    if (isHuaweiBuild) {
        apply plugin: 'com.huawei.agconnect'
    }
}

By conditionally applying the plugins based on the active flavor, you guarantee zero crossover. You can then build specifically for Huawei using flutter build apk --flavor huawei or for Google Play using flutter build apk --flavor google.

Common Pitfalls

  • Missing JSON Configurations: Ensure both google-services.json and agconnect-services.json exist in the android/app/ directory. The respective plugins will fail silently or throw cryptic missing value errors during manifest processing if these files are absent.
  • Outdated Flutter SDK: Older Flutter versions (pre-3.16) utilize an outdated declarative Gradle structure. Ensure you are on the latest stable Flutter channel to leverage modern Android Gradle Plugin (AGP) 8.x features.
  • Hardcoded API Keys: Never place HMS or Google API keys directly in the Manifest. The plugins are designed to securely map the JSON configurations to string resources at build time. Masking them manually in the manifest often circumvents the plugins, causing runtime initialization failures.