Time Limited Offer

50% off Flutter in Production

Spring Sale ends May 8th

View Offer

Kotlin DSL in Flutter 3.29: How to Update Your Android Gradle Files

The recent Flutter 3.29 release introduced many new updates to Impeller, Cupertino widgets, DevTools and more. But one big change flew under the radar: new Flutter projects now use the Kotlin DSL for Gradle files by default.

This has some implications for projects that rely on custom Gradle configurations, such as flavors, code signing, and more.

This article breaks down what changed, how it affects you, and how to avoid common pitfalls.

What's changed?

New projects generated with Flutter 3.29 now have .kts files instead of .gradle:

  • android/build.gradleandroid/build.gradle.kts
  • android/settings.gradleandroid/settings.gradle.kts
  • android/app/build.gradleandroid/app/build.gradle.kts
Android project folder generated with Flutter 3.27 (left) and Flutter 3.29 (right)
Android project folder generated with Flutter 3.27 (left) and Flutter 3.29 (right)

Before you panic: both Groovy (old) and Kotlin (new) files are fully supported.

If your project is using Groovy .gradle files, you don't need to migrate. Everything will still work with Flutter 3.29.

Only brand-new projects start with .kts files.

That said, if you are working with .kts, there are a few things you’ll want to know. 👇

The New Kotlin DSL Syntax

Let’s play spot-the-difference between these two snippets:

// Example 1 - is this Groovy or 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() } } plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "8.1.0" apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false } include ":app"
// Example 2 - is this Groovy or Kotlin? pluginManagement { val flutterSdkPath = run { val properties = java.util.Properties() file("local.properties").inputStream().use { properties.load(it) } val flutterSdkPath = properties.getProperty("flutter.sdk") require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } flutterSdkPath } includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") repositories { google() mavenCentral() gradlePluginPortal() } } plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.7.0" apply false id("org.jetbrains.kotlin.android") version "1.8.22" apply false } include(":app")

Both snippets do the same thing, but there are key differences:

  • Groovy uses def for variables; Kotlin uses val.
  • Groovy lets you skip parentheses in method calls; Kotlin requires them.
  • Groovy assertions use assert; Kotlin uses require.

There are plenty more differences, all neatly documented here:

But what does this actually mean for your project?

Common Gradle Setup Tasks

Here are the typical things you’ll need to adjust:

  1. Set Android NDK, minSdk, targetSdk, Java version
  2. Configure code signing
  3. Add the com.google.gms.google-services plugin for apps using Firebase
  4. Set up multiple flavors

Old tutorials and Stack Overflow answers? Mostly outdated now. Here's how to handle it.

1. Setting the Android NDK, minSdk, targetSdk, Java Version

Same settings as before—just with the correct Kotlin syntax (assignment operator, double quotes for string literals):

... android { namespace = "com.codewithandrea.flutter_ship_app" compileSdk = flutter.compileSdkVersion ndkVersion = "27.0.12077973" // Updated compileOptions { sourceCompatibility = JavaVersion.VERSION_17 // Updated targetCompatibility = JavaVersion.VERSION_17 // Updated } kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() // Updated } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.codewithandrea.flutter_ship_app" minSdk = 21 // Updated targetSdk = 34 // Updated versionCode = flutter.versionCode versionName = flutter.versionName } ... } ...

Pro Tip: updating all these values manually is a pain, and I do it so often that I created a script to automate the process. Here's how it works:

Download it here and add it to your project:

2. Configuring Code Signing

The official Flutter docs about building and releasing an Android app are already updated for the new Kotlin DSL.

Key steps:

Here's a sample code showing the required changes to the android/app/build.gradle.kts file:

import java.io.FileInputStream import java.util.Properties plugins { ... } val keystoreProperties = Properties() val keystorePropertiesFile = rootProject.file("key.properties") if (keystorePropertiesFile.exists()) { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) } android { // ... signingConfigs { create("release") { keyAlias = keystoreProperties["keyAlias"] as String keyPassword = keystoreProperties["keyPassword"] as String storeFile = keystoreProperties["storeFile"]?.let { file(it) } storePassword = keystoreProperties["storePassword"] as String } } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, // so `flutter run --release` works. signingConfig = signingConfigs.getByName("debug") signingConfig = signingConfigs.getByName("release") } } ... }

3. Adding the com.google.gms.google-services plugin (Firebase)

This is handled automatically when you add Firebase to your app with the FlutterFire CLI.

Resulting settings.gradle.kts:

plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.7.0" apply false // START: FlutterFire Configuration id("com.google.gms.google-services") version("4.3.15") apply false // END: FlutterFire Configuration id("org.jetbrains.kotlin.android") version "1.8.22" apply false }

The same plugin will also be listed in android/app/build.gradle.kts:

plugins { id("com.android.application") // START: FlutterFire Configuration id("com.google.gms.google-services") // END: FlutterFire Configuration id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") }

4. Adding Support for Multiple Flavors

This one is a bit tricky as it requires defining separate product flavors for your app.

The Flutter Flavorizr plugin already handles this for you when you run these processors:

dart run flutter_flavorizr -p android:buildGradle,android:flavorizrGradle,android:androidManifest

If you're doing it manually, open android/app/build.gradle.kts and add this line at the very bottom:

apply { from("flavors.gradle.kts") }

Then, create a android/app/flavors.gradle.kts file with these contents:

import com.android.build.gradle.AppExtension val android = project.extensions.getByType(AppExtension::class.java) android.apply { flavorDimensions("flavor-type") productFlavors { create("dev") { dimension = "flavor-type" applicationId = "com.codewithandrea.flutter_ship_app.dev" resValue(type = "string", name = "app_name", value = "Flutter Ship Dev") } create("stg") { dimension = "flavor-type" applicationId = "com.codewithandrea.flutter_ship_app.stg" resValue(type = "string", name = "app_name", value = "Flutter Ship Stg") } create("prod") { dimension = "flavor-type" applicationId = "com.codewithandrea.flutter_ship_app" resValue(type = "string", name = "app_name", value = "Flutter Ship") } } }

Notes:

  • Make sure the flavor names match your flutter run --flavor [name] command.
  • Update the applicationId and app name for each flavor.
  • In AndroidManifest.xml, set the app label dynamically in the android:label attribute:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application android:label="@string/app_name" <-- updated android:name="${applicationName}" android:icon="@mipmap/ic_launcher"> ... </application> ... </manifest>

There's much more to adding flavors than what I've shown here. For a full guide, check out my Flutter in Production course.

What If You Need to Recreate Your Android Project?

Old Flutter apps sometimes break on new Android tooling. If you’re stuck, it’s often easier to nuke and recreate the Android folder:

# Commit to git before making any changes git add . && git commit -m "Working copy" # Delete android folder rm -rf android # Create it again with the Flutter CLI flutter create . --platforms android --org com.yourorgname # See what's changed git diff android # Reapply previous settings, following steps above # Run again flutter run # All good? Commit to git git add . && git commit -m "Updated Android project"

When you recreate the Android project from scratch, any old settings you had applied will also be lost. Always run git diff android to see what's changed, and selectively restore any custom settings as needed.

Conclusion

Starting with Flutter 3.29, new projects use the Kotlin DSL for Gradle.

Honestly? I see little real benefit over Groovy. Yet, our tech overlords have decided this is the way, and we have to keep up and learn a new syntax. 🤷‍♂️

My advice:

  • If you're not forced to migrate, stick with Groovy.
  • If you start fresh or need Kotlin DSL, this guide has you covered.

Need more help shipping your app? Keep reading.

Flutter in Production

Shipping and maintaining apps in production takes more than just writing code:

  • Preparing for release: splash screens, flavors, environments, error reporting, analytics, force update, privacy, T&Cs.
  • App Submissions: metadata, screenshots, compliance, testing vs distribution tracks, dealing with rejections.
  • Release automation: CI workflows, environment variables, custom build steps, code signing, uploading to the stores.
  • Post-release: monitoring crashes, fixing bugs, OTA updates, feature flags & A/B testing.

My latest course will help you ship faster and with fewer headaches. Learn more here. 👇

Want More?

Invest in yourself with my high-quality Flutter courses.

Flutter Foundations Course

Flutter Foundations Course

Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.

Flutter & Firebase Masterclass

Flutter & Firebase Masterclass

Learn about Firebase Auth, Cloud Firestore, Cloud Functions, Stripe payments, and much more by building a full-stack eCommerce app with Flutter & Firebase.

The Complete Dart Developer Guide

The Complete Dart Developer Guide

Learn Dart Programming in depth. Includes: basic to advanced topics, exercises, and projects. Last updated to Dart 2.15.

Flutter Animations Masterclass

Flutter Animations Masterclass

Master Flutter animations and build a completely custom habit tracking application.