Table of Contents
Understanding Packages in Kotlin
In Kotlin, a package is a way to group related code into a named namespace. When you start building Android apps, your classes, functions, and other declarations will almost always belong to a package. Packages help you organize your project and avoid name conflicts between classes that have the same simple name.
In every Kotlin file, you can optionally declare a package at the very top using the package keyword. For example:
package com.example.myapp.utils
class StringFormatter {
fun formatName(name: String): String {
return name.trim().uppercase()
}
}
Here, com.example.myapp.utils is the package name, and StringFormatter is a class that belongs to that package. The full name of the class is its package plus its class name, in this case com.example.myapp.utils.StringFormatter. This full name is known as the fully qualified name.
If you do not specify any package at the top of a file, Kotlin places the declarations into the default unnamed package. In Android development, you will almost always work with explicit packages, because Android Studio generates them automatically when you create a project or a new Kotlin file.
In Android projects, always use a proper package declaration. Avoid putting code in the default unnamed package.
Package Naming in Android Projects
Android projects traditionally use a reversed domain name style for packages. For example, if your organization domain is example.com, a common root package is com.example. Android Studio adds more segments, usually the application id, such as com.example.myfirstapp.
The package name also relates closely to the application id defined in your Gradle build, which uniquely identifies your app on the Google Play Store and on devices. While the exact rules of application ids are handled elsewhere, it is important to recognize that package names in your Kotlin files often follow the same structure.
A typical Android project has multiple related packages. For instance you might use:
package com.example.myfirstapp
package com.example.myfirstapp.ui
package com.example.myfirstapp.data
package com.example.myfirstapp.utilsEven though these share a common prefix, each is a separate package. Kotlin does not automatically give access between them; you still use imports to reference declarations that live in a different package.
File Organization and Packages
In Android Studio, the physical folder structure of your Kotlin files usually matches the package structure. If a file has package com.example.myfirstapp.ui, you will typically find it under app/src/main/java/com/example/myfirstapp/ui.
The package declaration in the file is what Kotlin uses, not the folder path. However, you should keep them aligned. This avoids confusion and works smoothly with Android Studio tools, code navigation, and refactoring.
You can place multiple classes or functions in the same file under a single package declaration. For example:
package com.example.myfirstapp.data
class User(val name: String)
class UserRepository {
fun getUser(): User = User("Alice")
}
Both User and UserRepository belong to com.example.myfirstapp.data.
Basic Imports in Kotlin
Imports allow you to use declarations from other packages without writing their full names each time. At the top of a file, after the package line and before any declarations, you can write one or more import statements.
For example, if you want to use StringFormatter from com.example.myapp.utils in another file, you can write:
package com.example.myapp.ui
import com.example.myapp.utils.StringFormatter
class MainScreen {
private val formatter = StringFormatter()
fun show(name: String): String {
return formatter.formatName(name)
}
}Without the import, you would need to write the fully qualified name each time:
val formatter = com.example.myapp.utils.StringFormatter()Imports are compile time constructs. They do not affect the generated APK size and they do not change the runtime behavior of your app. They only tell the compiler where to find the symbol you are using.
Using Wildcard Imports
If you need several declarations from the same package, Kotlin allows a wildcard import with *. For example:
package com.example.myapp.ui
import com.example.myapp.utils.*
// Now you can use all public declarations from com.example.myapp.utils
This imports all accessible classes, functions, properties, and other declarations from com.example.myapp.utils. While wildcard imports can reduce typing, they can also make it harder to see exactly which types are used, especially in large files. Android Studio can manage imports for you and often prefers explicit imports.
In Android development, common external libraries such as Retrofit or Room are also imported through package names, for instance:
import retrofit2.Retrofit
import androidx.room.EntityThese work the same way as imports from your own packages, as long as the library is added as a dependency in your Gradle configuration.
Importing from Kotlin Standard Library and Android Framework
Many types and functions you use come from the Kotlin standard library or from the Android framework packages. Some are imported automatically, others you import explicitly.
Certain core Kotlin packages are always imported by default. For example, you can use String, Int, or functions like println without any import, because they are part of the default imports. Similarly in Android, some core classes are imported automatically, but most Android classes are not.
When you write an Activity, it is common to import classes like:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
These imports let you refer to Bundle and AppCompatActivity directly. If you did not import them, you would have to write android.os.Bundle or androidx.appcompat.app.AppCompatActivity every time.
Android Studio can automatically add imports when you type a class name and press an auto complete shortcut. It can also optimize imports by removing unused ones.
Name Conflicts and Aliased Imports
Sometimes two different packages provide declarations with the same simple name. For example, you could have:
com.example.myapp.data.User
com.example.otherlib.User
If you import both directly, the compiler cannot know which User you mean. Kotlin allows you to resolve this by using import aliases. An alias changes how you refer to a class inside the file, without changing the original class name.
You can write:
import com.example.myapp.data.User
import com.example.otherlib.User as ExternalUser
class Example {
fun printUsers(u1: User, u2: ExternalUser) {
println(u1)
println(u2)
}
}
In this file, User refers to com.example.myapp.data.User and ExternalUser refers to com.example.otherlib.User. The alias only applies inside this file.
Aliases are especially helpful in Android apps where you might use classes with generic names across different libraries. They keep your code readable and avoid confusion.
Static-like Imports: Top-Level Declarations and Companion Objects
Kotlin allows top level functions and properties, which can be declared directly in a file inside a package, not inside a class. For example:
package com.example.myapp.utils
fun formatTitle(title: String): String {
return title.trim().replaceFirstChar { it.uppercase() }
}
If you want to use formatTitle in another file, you import it as usual:
import com.example.myapp.utils.formatTitle
val result = formatTitle(" my screen ")You can also use wildcard imports for functions:
import com.example.myapp.utils.*This is similar to static imports in some other languages. You call the function by its simple name, and the compiler finds it through the import.
Companion objects in Kotlin can also be imported at the member level. Suppose you have:
package com.example.myapp.config
class AppConfig {
companion object {
const val BASE_URL = "https://example.com"
}
}You can import the constant directly:
import com.example.myapp.config.AppConfig.BASE_URL
val url = BASE_URLThis style is common for constants or helper functions used throughout an Android app.
Imports and Visibility
Imports only make names available in the current file. They do not override visibility rules. A declaration must still be visible, typically marked as public or at least accessible from the current package.
If a class or function is marked as internal or private, imports from other packages cannot access it. The import statement alone is not enough. It is simply a shortcut for the name, not a permission mechanism.
In Android, when you structure your code into multiple packages for UI, data, and domain layers, you may choose visibility modifiers to control which parts of your code can be used from which packages. The import system then works together with these rules to keep your architecture clean.
How Packages and Imports Shape Android Code
As your Android project grows, packages provide a high level structure, and imports connect those pieces at the file level. UI components import data classes or repositories, background workers import APIs or schedulers, and so on.
Organizing your code into meaningful packages, such as ui, data, domain, network, or utils, makes it easier to find files and reason about dependencies. Proper imports then keep each file focused, using only what it needs from other parts of the app.
When you work in Android Studio, you rarely write all imports manually. The IDE helps you add and manage them. However, understanding how packages and imports work is essential when you read code, resolve name conflicts, or reorganize your project.
Over time, you will see common Android and library package names often, like androidx.activity, androidx.lifecycle, com.google.android.material, or kotlinx.coroutines. They follow the same Kotlin rules for packages and imports that you use in your own code.