Jetpack Compose

Workshop: Jetpack Compose #

In this workshop, we will introduce the Jetpack Compose framework used for building Android apps. The demo we will be following is a minimal online Notes app. You can clone this code, follow the steps to setup Firebase, then you should be able to run the app yourself as well.

This document will explain several key concepts and steps:

  1. How to create UI with Jetpack Compose, following the MVVM architecture;
  2. How to connect with Firebase, which provides various of backend services including authentication and database.

Jetpack Compose #

UI (Composable) #

In Jetpack Compose, each UI element is called a composable, and creating UI is essentially a top-down process of writing composables that contains smaller composables.

For example, this is a excerpt from the NoteScreen showing how composable works:

@Composable
fun NoteScreen(.., viewModel: NoteViewModel) {
    ..
    Column(modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight()) {
        TopAppBar(
            title = {
                Text(text = note.value.getTitle(),)
            },
            actions = {
                IconButton(onClick = { viewModel.saveNote(popUpScreen) }) {
                    Icon(Icons.Filled.Done, "Save note")
                }
                IconButton(onClick = { viewModel.deleteNote(popUpScreen) }) {
                    Icon(Icons.Filled.Delete, "Delete note")
                }
            }
        )
        Spacer()
        Column(..) {
            TextField(
                value = note.value.text,
                onValueChange = { viewModel.updateNote(it) },
                ..
            )
        }
    }
}

Which essentially creates a screen like this:

NoteScreen

MVVM #

The MVVM (Model-View-ViewModel) architecture is an architectural style used in the presentation+domain layers of modern apps with UI. A detailed introduction to MVVM is available in a separate document: MVVM (MVC, MVP).

A typical way of organizing code, in this demo, is:

screens/                          # View and ViewModel
  account_center/                 # .. a pair of View and ViewModel for a screen
    AccountCenterScreen.kt
    AccountCenterViewModel.kt
  authentication/
    sign_in/
      SignInScreen.kt
      SignInViewModel.kt
    sign_up/
      SignUpScreen.kt
      SignUpViewModel.kt
    CredentialsExt.kt             # .. utility composables/functions
    ValidationsExt.kt             #    shared by multiple screens
  note/
    NoteScreen.kt
    NoteViewModel.kt
  notes_list/
    NotesListScreen.kt
    NotesListViewModel.kt
  splash/
    SplashScreen.kt
    SplashViewModel.kt
model/                           # Model
  Note.kt                        # .. data classes
  User.kt
  service/                       # .. interface
    AccountService.kt
    StorageService.kt
    impl/                        # .. implementation
      AccountServiceImpl.kt
      StorageServiceImpl.kt

Authentication with Firebase #

We use Firebase Authentication to handle user sign-in, which also supports many third-party OAuth providers such as Google Sign-In.

Logic Flow #

Below is the sequence diagram for the regular sign-in with email and password flow:

Email Auth Sequence

For comparison, here is the sequence diagram for the Google Sign-In flow:

Auth Sequence

Setup Firebase #

Official Documentation: Authenticate with Google on Android

  • Create a project in the Firebase Console.

  • Go to Settings > General > Your apps, add your Android app; follow the steps to update build configurations.

Firebase Add Android App

  • Go to Authentication (if first time, click “Get Started” to enable this service), enable “Email/Password”, “Anonymous”, and “Google” providers.

Firebase Authentication Providers

  • For Signin with Google to work, your app needs a signing certificate SHA-1. You can get a debug SHA-1 now by running ./gradlew signingReport (see screenshot below), but later when you release the app on Play Store, switch to Play App Signing.

Firebase Get Debug SHA-1

  • Go back to Firebase Console, Settings > General > Your apps, “Add fingerprint” to add the signing certificate; then don’t forget to download the updated google-services.json file and place it in the app/ directory of your project.

Firebase Set SHA-1

  • Go to Firestore (if first time, click “Get Started” to enable this service), add a collection called “notes”.

Firebase Firestore Add Collection

  • Switch to the “Rules” tab, and insert the following rules (before the default denying all rule match /{document=**} { allow read, write: if false; }):
    match /notes/{note} {
      // Users can only read, update, and delete their own notes
      allow read, update, delete: if request.auth.uid == resource.data.userId;

      // Users can only create notes for themselves
      allow create: if request.auth.uid == request.resource.data.userId;
    }

Firebase Firestore Rules

Misc Technical Notes #

Build Configuration #

A couple of new dependencies and plugins need to be added (Jetpack Compose, Firebase, Hilt) compared to the project template created by Android Studio. Check the app-level build.gradle.kts for details, where I’ve tried to group the added dependencies/plugins by framework. Note that Firebase’s dependencies versions are managed via BoM.

Resources #

The text used in buttons/labels are added in strings.xml. They can be used in code like R.string.XXX. There are also two image resources added: google_g.xml and auth_image.png.

Manifest Configuration #

In the manifest file, you need to add android:name=".NotesHiltApp" into the application field for Hilt to work.

Further Reading #

Other technologies used in the demo: