NeuroAgent

Fix FlutterRoot Error in settings.gradle.kts

Resolve 'Unresolved reference: flutterRoot' error in Gradle Kotlin DSL by properly defining variables within pluginManagement block scope.

Question

How to resolve “Unresolved reference: flutterRoot” error in settings.gradle.kts when adding local maven repository for pluginManagement in Flutter?

I’m encountering a compilation error in my settings.gradle.kts file when trying to add a local Flutter plugin repository inside the pluginManagement block. The error message is “Unresolved reference: flutterRoot”, which suggests that the flutterRoot variable I’m defining at the top of the file isn’t in scope within the pluginManagement block.

Here’s my current settings.gradle.kts file:

kotlin
import java.util.Properties
import java.io.File

fun loadLocalProperties(rootDir: File): Properties {
    val props = Properties()
    val localPropsFile = File(rootDir, "local.properties")
    if (localPropsFile.isFile) {
        localPropsFile.inputStream().use { props.load(it) }
    }
    return props
}

val localProperties = loadLocalProperties(rootProject.projectDir)
val flutterRoot = localProperties.getProperty("flutter.sdk")
    ?: error("flutter.sdk not set in local.properties")

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        // Error occurs on the next line
        maven(url = File(flutterRoot, "packages/flutter_tools/gradle/flutter_plugin_loader_repo").toURI())
    }
}

plugins {
    id("com.android.settings") version "8.3.0"
    id("dev.flutter.flutter-plugin-loader") version "1.0.0"
}

include(":app", ":modelComponent")
project(":modelComponent").projectDir = file("modelComponent")

What’s the correct way to define flutterRoot so it’s accessible inside the pluginManagement block in Gradle Kotlin DSL?

NeuroAgent

The “Unresolved reference: flutterRoot” error occurs because variables defined at the top level of settings.gradle.kts aren’t accessible within the pluginManagement block due to scope limitations in Gradle Kotlin DSL. To resolve this, you need to move the variable definition inside the pluginManagement block where it’s needed.

Here’s the corrected version of your settings.gradle.kts:

kotlin
import java.util.Properties
import java.io.File

pluginManagement {
    // Define flutterRoot inside this block where it's needed
    val flutterRoot = run {
        val properties = java.util.Properties()
        val localPropsFile = File(rootProject.projectDir, "local.properties")
        if (localPropsFile.isFile) {
            localPropsFile.inputStream().use { properties.load(it) }
        }
        val flutterSdkPath = properties.getProperty("flutter.sdk")
            ?: error("flutter.sdk not set in local.properties")
        flutterSdkPath
    }
    
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        // Now flutterRoot is accessible here
        maven(url = File(flutterRoot, "packages/flutter_tools/gradle/flutter_plugin_loader_repo").toURI())
    }
}

plugins {
    id("com.android.settings") version "8.3.0"
    id("dev.flutter.flutter-plugin-loader") version "1.0.0"
}

include(":app", ":modelComponent")
project(":modelComponent").projectDir = file("modelComponent")

Contents


Understanding the Scope Issue

The core problem lies in how Gradle Kotlin DSL handles variable scope in settings files. As mentioned in the Gradle Kotlin DSL documentation, variables defined at the top level of settings.gradle.kts aren’t automatically accessible within nested blocks like pluginManagement.

This is a fundamental limitation of the Kotlin DSL in Gradle settings files. The Gradle Forums discussion confirms that “variables defined in the settings.gradle.kts cannot be referenced in the plugins block” - and the same limitation applies to pluginManagement.


Correct Variable Definition in pluginManagement

The solution is to define the flutterRoot variable directly within the pluginManagement block where it’s needed. This approach follows the standard pattern used in Flutter’s official documentation:

kotlin
pluginManagement {
    val flutterRoot = run {
        val properties = java.util.Properties()
        val localPropsFile = File(rootProject.projectDir, "local.properties")
        if (localPropsFile.isFile) {
            localPropsFile.inputStream().use { properties.load(it) }
        }
        val flutterSdkPath = properties.getProperty("flutter.sdk")
            ?: error("flutter.sdk not set in local.properties")
        flutterSdkPath
    }
    
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven(url = File(flutterRoot, "packages/flutter_tools/gradle/flutter_plugin_loader_repo").toURI())
    }
}

This approach ensures the variable is in the correct scope and accessible within the repositories block.


Alternative Solutions

1. Using settingsEvaluated Hook

For more complex scenarios, you can use the settingsEvaluated hook:

kotlin
settingsEvaluated { settings ->
    val flutterRoot = run {
        val properties = java.util.Properties()
        val localPropsFile = File(rootProject.projectDir, "local.properties")
        if (localPropsFile.isFile) {
            localPropsFile.inputStream().use { properties.load(it) }
        }
        val flutterSdkPath = properties.getProperty("flutter.sdk")
            ?: error("flutter.sdk not set in local.properties")
        flutterSdkPath
    }
    
    settings.pluginManagement.repositories.apply {
        maven(url = File(flutterRoot, "packages/flutter_tools/gradle/flutter_plugin_loader_repo").toURI())
    }
}

2. Predefined Flutter SDK Path

If you need the flutterRoot variable in multiple places, define it once in pluginManagement and then reference it:

kotlin
pluginManagement {
    val flutterRoot = run {
        val properties = java.util.Properties()
        val localPropsFile = File(rootProject.projectDir, "local.properties")
        if (localPropsFile.isFile) {
            localPropsFile.inputStream().use { properties.load(it) }
        }
        val flutterSdkPath = properties.getProperty("flutter.sdk")
            ?: error("flutter.sdk not set in local.properties")
        flutterSdkPath
    }
    
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven(url = File(flutterRoot, "packages/flutter_tools/gradle/flutter_plugin_loader_repo").toURI())
    }
}

// Now you can use flutterRoot in other parts of your settings file
// It will be available in the settings script scope

Best Practices for Flutter Gradle Configuration

1. Follow Official Flutter Patterns

The Flutter documentation shows the recommended approach for handling Flutter SDK paths:

kotlin
pluginManagement {
    def flutterSdkPath = {
        def properties = new Properties()
        file("local.properties").withInputStream { properties.load(it) }
        def flutterSdkPath = properties.getProperty("flutter.sdk")
        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
        return flutterSdkPath
    }()
    
    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
    
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

2. Error Handling

Always include proper error handling when reading properties:

kotlin
val flutterRoot = run {
    val properties = java.util.Properties()
    val localPropsFile = File(rootProject.projectDir, "local.properties")
    if (localPropsFile.isFile) {
        localPropsFile.inputStream().use { properties.load(it) }
    }
    val flutterSdkPath = properties.getProperty("flutter.sdk")
        ?: error("flutter.sdk not set in local.properties")
    flutterSdkPath
}

3. Repository Configuration Order

Configure repositories in the correct order - local repositories first, then remote ones:

kotlin
repositories {
    mavenLocal()  // If you have local plugins
    maven(url = File(flutterRoot, "packages/flutter_tools/gradle/flutter_plugin_loader_repo").toURI())
    google()
    mavenCentral()
    gradlePluginPortal()
}

Troubleshooting Common Issues

1. File Not Found Errors

If you encounter file-related errors, ensure the local.properties file exists and has the correct path:

bash
flutter config --android-sdk <your-sdk-path>

2. Repository Resolution Issues

If plugins can’t be resolved, check your repository configuration:

kotlin
settingsEvaluated { settings ->
    settings.pluginManagement.resolutionStrategy {
        eachPlugin {
            // Force specific versions if needed
            when (requested.id.id) {
                "com.android.application" -> useVersion("8.3.0")
            }
        }
    }
}

3. Kotlin DSL Syntax Errors

Ensure you’re using the correct Kotlin DSL syntax:

kotlin
// Correct
maven(url = File(flutterRoot, "path/to/repo").toURI())

// Incorrect
maven { url = "$flutterRoot/path/to/repo" }

The key takeaway is that in Gradle Kotlin DSL settings files, variables must be defined in the same scope where they’re used. For pluginManagement blocks, this means defining variables directly within that block rather than at the top level of the file.


Sources

  1. Gradle Kotlin DSL Primer - Official Documentation
  2. Flutter Android Gradle Plugin Documentation
  3. Gradle Forums: Kotlin DSL Variable Scope Discussion
  4. Stack Overflow: Unresolved Reference in Settings Gradle KTS
  5. Flutter Plugins Configuration Documentation

Conclusion

The “Unresolved reference: flutterRoot” error is a common issue when working with Gradle Kotlin DSL in Flutter projects. The solution involves understanding variable scope limitations and defining variables in the appropriate scope. By moving the flutterRoot variable definition inside the pluginManagement block, you ensure it’s accessible where needed. Always follow Flutter’s official patterns and include proper error handling for robust configuration. Remember that in settings.gradle.kts, variable scope is strictly limited to where they’re defined, so plan your configuration accordingly.