Android Manifest file checklist
Welcome to the fourth article in a series of tips and tricks about Compose Multiplatform. The content is based on a sample app called CMP Unit Converter. It runs on Android, iOS, and the Desktop. As its name suggests, you can convert between various units and scales. While this may provide some value, the main goal of the app and this series is to show how to use Compose Multiplatform and a couple of other multiplatform libraries while focusing on platform integration. This time, we look at the Android manifest file. The Kotlin Multiplatform wizard for sure is a great way to start your projects. The manifest file for a project I created on 2025-04-05 looks like this: The application The purpose of a project wizard is to provide only a basic setup. Therefore, the manifest contains only what is absolutely necessary. Consequently, there are no entries for permissions, further activities, services, broadcast receivers, and content providers. All these are, by the way, called App components in the App manifest overview. ☑ Add required permissions and app components The application element contains the declaration of the application. This element contains subelements that declare each of the application's components and has attributes that can affect all the components. One of its attributes, android:enableOnBackInvokedCallback (which can also be specified for an activity) is as of 2025-04-05 not set by the Kotlin Multiplatform wizard. The flag lets you opt out of predictive system animations at the activity level, which is something you should not do. Therefore: ☑ Set android:enableOnBackInvokedCallback to true An attribute you should consider adding is android:appCategory. It declares the category of your app. Categories are used to cluster multiple apps together into meaningful groups, such as when summarizing battery, network, or disk usage. Only define this value for apps that fit well into one of the specific categories. ☑ Consider adding android:appCategory Did you know there is the android:hasFragileUserData attribute? It controls whether to show the user a prompt to keep the app's data when the user uninstalls the app. The default value is false. Now, I am not advocating to set it. But I feel it should be on a checklist. ☑ Carefully consider adding android:hasFragileUserData (you probably should not add it, after all) android:theme is a reference to a style resource defining a default theme for all activities in the application. Individual activities can override the default by setting their own theme attributes. As of 2025-04-05, the Kotlin Multiplatform wizard sets it to @android:style/Theme.Material.Light.NoActionBar. If you run your Compose Multiplatform app, everything appears to work fine, including toggling between light and dark mode. However, there's a subtle issue. To understand which, let's remind ourselves what android:theme="@android:style/Theme.Material.Light.NoActionBar" does. It sets the initial theme for your application (or activity) to light mode. It makes sure there is no action bar. Having no action bar is likely what you want because a common Compose pattern is to have a Scaffold() with a top app bar. The theme setting affects the activity window and how Android draws the initial UI before your Compose code or Android Views are inflated. A part of this initial user interface is the splash screen. Putting android:theme="@android:style/Theme.Material.Light.NoActionBar" in your manifest means the splash screen background will always appear light, regardless if the system is in light or dark mode. Since modern Android apps certainly should support light and dark mode, I encourage you to change the theme. While there is no Theme.Material.Dark.NoActionBar, you could use Theme.DeviceDefault.DayNight but would also need to remove the action bar (for example in a custom theme with Theme.DeviceDefault.DayNight as its parent). This feels unnecessarily complicated. The easiest way is to include Jetpack Appcompat and use android:theme="@style/Theme.AppCompat.DayNight.NoActionBar". While this works exactly as expected, please keep in mind that any library contributes to the overall app size. Here's one final aspect: Do you need to use activities solely based on Views? Such activities may be your own or provided by a third party library like scanner or proprietary sign-in components. They deserve a proper theme treatment. That is, not being tied to light mode. ☑ Set a theme that supports light and dark mode in android:theme Activities The activity element declares an activity (an Activity subclass) that implements part of the application's visual user interface. All activities must be represented by elements in the manifest file. Any that aren't declared there aren't seen by the system and never run. One of its attributes, android:windowSoftInputMode, is as of 2025-04-05 not set by

Welcome to the fourth article in a series of tips and tricks about Compose Multiplatform. The content is based on a sample app called CMP Unit Converter. It runs on Android, iOS, and the Desktop. As its name suggests, you can convert between various units and scales. While this may provide some value, the main goal of the app and this series is to show how to use Compose Multiplatform and a couple of other multiplatform libraries while focusing on platform integration. This time, we look at the Android manifest file.
The Kotlin Multiplatform wizard for sure is a great way to start your projects. The manifest file for a project I created on 2025-04-05 looks like this:
xmlns:android="http://schemas.android.com/apk/res/android">
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme=
"@android:style/Theme.Material.Light.NoActionBar"
>
android:exported="true"
android:configChanges="orientation|screenSize|
screenLayout|keyboardHidden|
mnc|colorMode|density|fontScale|
fontWeightAdjustment|keyboard|
layoutDirection|locale|
mcc|navigation|
smallestScreenSize|touchscreen|uiMode"
android:name=".MainActivity">
android:name="android.intent.action.MAIN" />
android:name="android.intent.category.LAUNCHER" />
The application
The purpose of a project wizard is to provide only a basic setup. Therefore, the manifest contains only what is absolutely necessary. Consequently, there are no entries for permissions, further activities, services, broadcast receivers, and content providers. All these are, by the way, called App components in the App manifest overview.
☑ Add required permissions and app components
The application element contains
the declaration of the application. This element contains subelements that declare each of the application's components and has attributes that can affect all the components.
One of its attributes, android:enableOnBackInvokedCallback
(which can also be specified for an activity) is as of 2025-04-05 not set by the Kotlin Multiplatform wizard. The flag lets you opt out of predictive system animations at the activity level, which is something you should not do. Therefore:
☑ Set android:enableOnBackInvokedCallback
to true
An attribute you should consider adding is android:appCategory
. It declares the category of your app.
Categories are used to cluster multiple apps together into meaningful groups, such as when summarizing battery, network, or disk usage. Only define this value for apps that fit well into one of the specific categories.
☑ Consider adding android:appCategory
Did you know there is the android:hasFragileUserData
attribute? It controls whether
to show the user a prompt to keep the app's data when the user uninstalls the app. The default value is
false
.
Now, I am not advocating to set it. But I feel it should be on a checklist.
☑ Carefully consider adding android:hasFragileUserData
(you probably should not add it, after all)
android:theme
is a
reference to a style resource defining a default theme for all activities in the application. Individual activities can override the default by setting their own theme attributes.
As of 2025-04-05, the Kotlin Multiplatform wizard sets it to @android:style/Theme.Material.Light.NoActionBar
. If you run your Compose Multiplatform app, everything appears to work fine, including toggling between light and dark mode. However, there's a subtle issue. To understand which, let's remind ourselves what android:theme="@android:style/Theme.Material.Light.NoActionBar"
does.
- It sets the initial theme for your application (or activity) to light mode.
- It makes sure there is no action bar. Having no action bar is likely what you want because a common Compose pattern is to have a
Scaffold()
with a top app bar.
The theme setting affects the activity window and how Android draws the initial UI before your Compose code or Android View
s are inflated. A part of this initial user interface is the splash screen. Putting android:theme="@android:style/Theme.Material.Light.NoActionBar"
in your manifest means the splash screen background will always appear light, regardless if the system is in light or dark mode. Since modern Android apps certainly should support light and dark mode, I encourage you to change the theme.
While there is no Theme.Material.Dark.NoActionBar
, you could use Theme.DeviceDefault.DayNight
but would also need to remove the action bar (for example in a custom theme with Theme.DeviceDefault.DayNight
as its parent). This feels unnecessarily complicated. The easiest way is to include Jetpack Appcompat and use android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
. While this works exactly as expected, please keep in mind that any library contributes to the overall app size.
Here's one final aspect: Do you need to use activities solely based on View
s? Such activities may be your own or provided by a third party library like scanner or proprietary sign-in components. They deserve a proper theme treatment. That is, not being tied to light mode.
☑ Set a theme that supports light and dark mode in android:theme
Activities
The activity element declares
an activity (an Activity subclass) that implements part of the application's visual user interface. All activities must be represented by
elements in the manifest file. Any that aren't declared there aren't seen by the system and never run.
One of its attributes, android:windowSoftInputMode
, is as of 2025-04-05 not set by the Kotlin Multiplatform wizard. The attribute controls how
the main window of the activity interacts with the window containing the on-screen soft keyboard.
There are two things to consider:
- Should the soft keyboard be visible when the activity becomes the focus of user attention?
- Should the activity window become smaller to make room for the soft keyboard?
A couple of values control these aspects. They can be combined: stateUnspecified
, stateUnchanged
, stateHidden
, stateAlwaysHidden
, stateVisible
, stateAlwaysVisible
, adjustUnspecified
, adjustResize
, adjustPan
, adjustNothing
stateUnspecified
is the default setting for the behavior of the soft keyboard.
Whether the soft keyboard is hidden or visible isn't specified. The system chooses an appropriate state or relies on the setting in the theme.
adjustUnspecified
is the default setting for the behavior of the main window.
Whether the activity's main window resizes to make room for the soft keyboard or the contents of the window pan to make the current focus visible on-screen is unspecified. The system automatically selects one of these modes depending on whether the content of the window has any layout views that can scroll their contents. If there is such a view, the window resizes, on the assumption that scrolling can make all of the window's contents visible within a smaller area.
Both defaults sound like a good choice for many apps. Therefore, it seems safe to just omit android:windowSoftInputMode
. However, these mechanics had been designed for a View
-based world. So, what does The system automatically selects one of these modes depending on whether the content of the window has any layout views that can scroll their contents mean in a Jetpack Compose app? Please recall that setContent {}
just creates a ComposeView
, which hosts the Compose hierarchy. In the document Display content edge-to-edge in your app and handle window insets in Compose, Google suggests to add android:windowSoftInputMode="adjustResize"
to the manifest because this
setting allows your app to receive the size of the software IME as insets, which you can use to pad and lay out content appropriately when the IME appears and disappears in your app
In CMP Unit Converter, I have added this manifest attribute. I also
- apply the
imePadding()
modifier to text fields - and make sure the content is scrollable.
This results in a very nice user experience even on a smartphone in landscape mode, where space is notoriously scarce.
☑ Add android:windowSoftInputMode
The last item on my checklist is likely going to be controversial. android:configChanges lists configuration changes an activity wants to handle itself.
When a configuration change occurs at runtime, the activity shuts down and restarts by default, but declaring a configuration with this attribute prevents the activity from restarting. Instead, the activity remains running and its onConfigurationChanged() method is called.
As of 2025-04-05, the documentation lists 18 values; the Kotlin Multiplatform wizard requests to handle 17 through onConfigurationChanged()
. The only one not being requested is grammaticalGender
(which was introduced with API level 34). Here's a question: Why handling locale
but not grammaticalGender
? This omission may likely have been just a glitch, but it begs an important additional question: Why this long list?
The documentation clearly says:
Note: Use this attribute only in special cases to improve application performance and responsiveness. For more information, see Handle configuration changes.
The Handle configuration changes document is a must-read. Don't just take a glimpse, don't just browse through it. Read it. Read it again. Read it once more. Then, and only then, ask yourself which configuration changes you really want to opt out from. Below are some quotes from this document I find particularly important:
Note:
Activity
recreation due to configuration changes is only one of the cases in which the system might destroy anActivity
and recreate it later. For more information, read about the Activity lifecycle.
This means, your app needs proper lifecycle management anyway. We have established patterns for this.
Warning: Even when you disable activity recreation for a given configuration change, the change itself continues to occur. Disabling
Activity
recreation transfers the responsibility of handling that configuration change to theActivity
. If you disableActivity
recreation, your app must appropriately handle the change when it does occur.
Section React to configuration changes in Jetpack Compose starts with
Jetpack Compose lets your app more easily react to configuration changes. However, if you disable
Activity
recreation for all configuration changes where it is possible to do so, your app still must correctly handle configuration changes.
So, how should android:configChanges
look like? First, please make sure to also have read and understood paragraphs Avoid opting out as a quick fix and Don't avoid configuration changes. Now, there certainly is no definitive answer. But I firmly believe that a list as long as the one the Kotlin Multiplatform wizard creates, requires a lot of careful consideration. If in doubt, opt out from less, and let the system handle configuration changes by recreating the activity.
The Now on Android app source code is often cited as a great resource for learning how to do things. So, to conclude this, let's peek into its manifest file and seek inspiration.
That's not many, right?
☑ Carefully consider which values to set in android:configChanges
Conclusion
I would like to point out that I am neither saying nor implying that the long list the Kotlin Multiplatform wizard uses is wrong. What I definitively advocate is to carefully consider how the manifest file of your Compose Multiplatform app should look like. It's your obligation to make sure its content is correct. No wizard can provide more than a humble suggestion.
I do hope you find this checklist useful. Kindly share your thoughts in the comments.