Flutter Gradle KTS: “Unresolved reference: flutterRoot” in settings.gradle.kts when adding local maven repository for pluginManagement
I’m encountering a Gradle configuration issue in my Flutter project when trying to set up Deferred Components. After resolving the “v1 embedding” error by recreating the project, I’m now facing a compilation error in my settings.gradle.kts file.
Problem Description
The error occurs when I try to add the local Flutter plugin repository inside the pluginManagement block:
Unresolved reference: flutterRoot
Current Configuration
Here’s my complete settings.gradle.kts file:
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()
// The error happens 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")
Question
What is the correct way to define the flutterRoot variable so it’s accessible inside the pluginManagement block in Gradle Kotlin DSL? I’m clearly missing something fundamental about variable scope in this context.
Any guidance on resolving this “Unresolved reference: flutterRoot” error would be greatly appreciated.
The “Unresolved reference: flutterRoot” error occurs because variables defined at the top level of settings.gradle.kts are not accessible within the pluginManagement block due to Gradle’s scope rules. The solution is to either move the variable definition inside the pluginManagement block or use Gradle’s properties system to make it available throughout the file.
Contents
- Understanding the Scope Issue
- Solution 1: Moving Variables Inside pluginManagement
- Solution 2: Using Gradle Properties
- Solution 3: Using providers
- Complete Working Configuration
- Best Practices for Flutter Gradle Setup
- Troubleshooting Additional Issues
Understanding the Scope Issue
In Gradle Kotlin DSL, the pluginManagement block has its own evaluation context and scope. Variables defined at the top level of settings.gradle.kts are not automatically available within this block, which is why you’re encountering the “Unresolved reference: flutterRoot” error.
As explained in Gradle Forums discussions, this is a common issue when migrating between Gradle versions, particularly from 5.x to 6.x and later, where the scoping rules became more strict.
The problem occurs because Gradle evaluates the pluginManagement block early in the configuration phase, before it has access to variables defined in the main script scope.
Solution 1: Moving Variables Inside pluginManagement
The most straightforward solution is to move the variable definitions directly inside the pluginManagement block where they’re needed:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// Load local properties and flutter root inside the block
val localProps = Properties()
File(rootDir, "local.properties").takeIf { it.isFile }?.inputStream()?.use {
localProps.load(it)
}
val flutterRoot = localProps.getProperty("flutter.sdk")
?: error("flutter.sdk not set in local.properties")
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")
This approach works because the variables are now defined within the same scope where they’re used.
Solution 2: Using Gradle Properties
For better organization and reusability, you can use Gradle’s properties system to make variables available throughout the entire settings file:
// Define properties at the top level
val flutterRoot: String by settings
val localProperties: Properties by settings
// Initialize properties
settingsEvaluated {
val props = Properties()
File(rootDir, "local.properties").takeIf { it.isFile }?.inputStream()?.use {
props.load(it)
}
localProperties["flutter.sdk"] = props.getProperty("flutter.sdk")
?: error("flutter.sdk not set in local.properties")
flutterRoot = localProperties["flutter.sdk"] as String
}
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// Now flutterRoot is accessible
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")
This approach provides better organization and makes the variables available throughout the entire settings file.
Solution 3: Using providers
For a more modern approach compatible with recent Gradle versions, you can use providers:
import org.gradle.api.provider.Provider
val flutterRootProvider: Provider<String> = providers.gradleProperty("flutter.sdk")
.orElse(providers.provider {
error("flutter.sdk not set in local.properties")
})
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// Use the provider
maven(url = File(flutterRootProvider.get(), "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")
This approach is more robust and handles the property resolution in a lazy manner.
Complete Working Configuration
Here’s a complete working configuration that addresses the scope issue:
import java.util.Properties
import java.io.File
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
// Safe property loading inside pluginManagement
val flutterSdkPath = {
val properties = Properties()
val localPropsFile = File(rootDir, "local.properties")
if (localPropsFile.isFile) {
localPropsFile.inputStream().use { properties.load(it) }
}
properties.getProperty("flutter.sdk") ?: error("flutter.sdk not set in local.properties")
}()
maven(url = File(flutterSdkPath, "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")
This configuration:
- Defines the Flutter SDK path directly within the
pluginManagementblock - Uses a safe property loading approach with error handling
- Maintains all the necessary functionality for Deferred Components
Best Practices for Flutter Gradle Setup
Based on the research findings, here are some best practices:
-
Use the Plugin DSL syntax: As noted in the Flutter documentation, migrate from imperative
apply plugin:to the modernplugins { id ... }syntax. -
Ensure Gradle version compatibility: The “Unresolved reference” errors are often caused by using an outdated Gradle version. Make sure you’re using Gradle 6.8 or later for
dependencyResolutionManagementsupport. -
Organize your settings.gradle.kts: Keep related configurations together and use appropriate scoping to avoid reference errors.
-
Handle local properties safely: Always check if the local.properties file exists before trying to read it.
-
Use proper error handling: Provide meaningful error messages when required properties are missing.
Troubleshooting Additional Issues
If you encounter similar issues, consider these additional troubleshooting steps:
-
Check Gradle wrapper version: Ensure your
gradle-wrapper.propertiesuses a compatible version. -
Update Android Gradle Plugin: Make sure you’re using a compatible version of the Android Gradle Plugin.
-
Clean and rebuild: Sometimes simply cleaning the project and rebuilding can resolve configuration issues.
-
Check for deprecated syntax: Remove any deprecated
apply plugin:statements and use the modern plugin DSL. -
Verify plugin versions: Ensure all plugin versions are compatible with each other and with your Flutter version.
The key takeaway is that Gradle Kotlin DSL has specific scoping rules, and variables need to be defined in the appropriate scope where they’re used. By moving the Flutter SDK path resolution inside the pluginManagement block, you can resolve the “Unresolved reference: flutterRoot” error and successfully configure your Flutter project with Deferred Components.
Sources
- Flutter Gradle Plugin Migration Guide
- Gradle Forums - Unresolved References in settings.gradle.kts
- Stack Overflow - Unresolved reference: dependencyResolutionManagement
- Gradle Issue #34145 - pluginManagement Unresolved reference
- Stack Overflow - Gradle 6 settings.gradle.kts properties problem
Conclusion
- The “Unresolved reference: flutterRoot” error occurs due to Gradle’s scoping rules in Kotlin DSL
- Move variable definitions inside the
pluginManagementblock where they’re needed - Use Gradle properties or providers for better organization and reusability
- Ensure your Gradle version is compatible (6.8+ for modern features)
- Follow Flutter’s recommended plugin DSL syntax instead of deprecated imperative apply
- Always handle local properties safely with proper error checking
By implementing these solutions, you can successfully configure your Flutter project with Deferred Components and avoid Gradle configuration issues.