Constructing a Language Studying App with Compose — Half 1 | Area Tech

not fairly Constructing a Language Studying App with Compose — Half 1 will cowl the newest and most present opinion re the world. entry slowly in view of that you just perceive skillfully and appropriately. will addition your data proficiently and reliably

That is the primary article in a brand new sequence the place I’ll share my expertise constructing a language studying app with Jetpack Compose.

I am going to present you my day by day progress, how the app has developed over time, why I made sure selections, and so on. I am going to additionally share new issues I be taught alongside the best way that could be helpful to you.

Once I began studying programming, one in all my first initiatives was a web site that I used to assessment phrases I used to be studying in different languages. Quick ahead a number of years, and now I want one thing related. This time although, I am going to construct it utilizing Jetpack Compose and attempt to publish it to the Play Retailer.

If you’re a junior engineer, this sequence will enable you by exhibiting you ways I went from nothing to having an app able to be printed on the Play Retailer. If you happen to’re a senior engineer, this sequence will train you some new issues about Jetpack Compose and will even make you rethink the best way you resolve some issues.

I am calling this app Lingua, it is Latin for “language”. It is presupposed to be a mix of Duolingo and Anki plus a number of issues that I feel can be helpful.

One in every of my targets for 2023 is to be taught Italian, and I additionally must spend a few of my time actively creating one thing as a substitute of simply passively studying new issues. I’ve learn loads about Compose, now it is time to put that data to make use of (later I spotted that I truly knew little or no about Compose).

If I come to a model that appears good and I feel is smart, I am going to publish it on the Play Retailer.

That is the primary draft I created in Figma; it’s removed from the ultimate model, nevertheless it factors me in the appropriate route.

Preliminary draft as of 09/01/2023

I am constructing this app iteratively, in order you will see early builds of the app will look fairly ugly, however that is okay, the preliminary objective is to get one thing working. Later I can come again to it and enhance the design.

I am going to break up my updates into days so you will get an thought of ​​how lengthy it took me to construct this, I am spending about 1 hour on this day by day.

To maintain monitor of my duties, I am utilizing Notion. For the design I’m utilizing Figma.

I’m utilizing Kotlin and Jetpack Compose. The opposite libraries I’ll point out as I take advantage of them.

Beneath you possibly can see my first draft of the house display. I’ll have some form of progress part that can present how a lot the consumer has discovered in a given day, in a given week, and in a given month.

I additionally wish to add some form of strike function to encourage individuals to continue learning. Knowledge could be very helpful to me, so I am going to additionally add detailed charts about issues that could be helpful to the consumer.

As a substitute of simply having the standard “Observe” that almost all apps have, I additionally wish to create a customized follow that permits you to select what you wish to follow.

And eventually, I wish to show the checklist of programs/decks the consumer is subscribed to. I am going to name these playing cards “decks” to any extent further, however the identify might change sooner or later.

Preliminary house display structure

My screens are divided into route and display. Within the instance above, you’ll create HomeRoute Y HomeScreen. The route is simply what’s added to the navigation graph, the display the precise visible content material.

Right here we have now the primary NavHost for the applying

NavHost(navController = navController, startDestination = Routes.Residence.route)  
composable(Routes.Residence) HomeRoute(navController)

and right here the HomeRoute that defines the house display.

@Composable enjoyable HomeRoute( navController: NavController, )  

i am simply utilizing NavController for now, however I am going to in all probability sum it up later. For the routes, I simply outlined a sealed class with my routes, for now this fits my wants, however I am going to in all probability change this sooner or later as effectively.

sealed class Routes(val route: String)  
object Residence : Routes("/")

Take into account that that is my first large app with Compose, so I am going to actually make errors and be taught alongside the best way.

I proceeded to create the opposite elements, however for now I solely have hard-coded values ​​for them.

One factor price mentioning is how I managed to crop the flag pictures. I am utilizing Coil, I could not add clipping on to the picture so I wrapped it with a Floor and also you outlined a rounded nook just for the underside finish nook as a result of the cardboard already clipped the highest begin nook. I additionally set the side ratio to 16/9, however I am not 100% happy with the end result.

Floor( form = RoundedCornerShape(bottomEnd = 8.dp), )  
mannequin = mannequin.imageUrl,
contentDescription = "Icon",
// TODO: add placeholder
contentScale = ContentScale.FillBounds,
modifier = Modifier
.aspectRatio(16 / 9f)

By the tip of day 1, I had created the display you possibly can see beneath, it is not purposeful but, nevertheless it’s an excellent step within the route I wish to go.

Progress on the finish of day 1

On day 2, I simply constructed the library display, it is fairly easy now. It solely lists the obtainable covers. Sooner or later I’ll add extra sorting choices to make it simpler for the consumer to search out what they’re in search of.

It additionally has a button for the consumer to create a brand new deck.

Preliminary library design

That is what I had achieved by the tip of day 2, nothing particular. only a LazyColumn and a FloatingActionButton. In truth, I additionally created the ViewModel for this display so I simply want to attach this to an information supply later and the proper knowledge can be listed.

Progress on the finish of day 2

On day 3, I began engaged on the deck constructing display. Principally, I want at the very least one deck with playing cards to be able to develop the remainder of the app, so I am going this fashion first.

As you possibly can see beneath, this display just isn’t so simple as the others.

First you possibly can identify your Deck, I am going to construct this initially.

After you can change it to public/non-public, I am going to save that for later. The whole lot can be public for now.

On the high proper you possibly can see a flag, that is cool performance, however I will not be implementing it at the moment both.

Within the center we have now an important a part of these screens, the playing cards that make up a Deck. For now there’ll solely be one kind of card and it’s only Entry <-> Departure. For instance, the English phrase Bee is Ape in Italian, so it might be one thing like Bee <-> ape. This enables me to do a number of issues:

  1. It shows the phrase “Bee” and the consumer has to kind “Ape”.
  2. It reveals the phrase “Ape” and the consumer has to kind “Bee”.
  3. If there are extra phrases, I can show one thing like “Ape” and the consumer has to decide on a phrase from an inventory like (“Apple”, “Bee, “Pie”).

That is mainly a easy model of Duolingo. I am going to add assist for sound and picture later, however for now that is simply added complexity.

Lastly we have now a button so as to add new playing cards and a save button to avoid wasting all the things.

Create Preliminary Deck Design

I bumped into some points like how do I go the deck id to this display? I am utilizing the identical display so as to add and edit decks, so the id needed to be an elective parameter. I solved this by creating a brand new object in Routes and including the createRoute Y parse strategies.

object EditDeck : Routes("/deck/id/edit")  
enjoyable createRoute(deckId: String?) = "/deck/$deckId/edit"
enjoyable parse(bundle: Bundle?): String? = bundle?.getString("id")?.takeIf it != "null"

And in my routes I added the brand new route.

val deckId = Routes.EditDeck.parse(it.arguments)
EditDeckRoute(navController = navController, deckId = deckId)

Then when my display is created I take advantage of a LaunchedEffect to load the duvet on the ViewModel.

@Composable enjoyable EditDeckRoute(
navController: NavController,
deckId: String?,
viewModel: EditDeckViewModel = hiltViewModel()
LaunchedEffect(deckId ?: "none")


the code inside LaunchedEffect will probably be executed once more if the important thing adjustments, on this case if the deck id adjustments and that’s precisely what I would like.

By the tip of day 3, I had solely constructed a small a part of this display.

Progress on the finish of day 3

On Day 4 I finished engaged on the deck constructing display and moved on to the cardboard constructing display. Decks and playing cards are the muse of this app, all the things else within the app will revolve round them.

For now I’ll solely develop the sort “Textual content” or as I referred to as it beforehand Enter <-> Departure. It would include a area for the enter and a area for the output (I want to search out higher names for these items lol).

Later I additionally wish to add an “Information” kind that works a bit in another way, however that is for later.

Create preliminary draft card

This display is sort of just like the earlier one, I’ve my EditDeckCardRoute Y EditDeckCardScreen. We even have 2 textual content inputs and a FAB to retailer the cardboard.

One distinction right here is that to create a card I want the deck id and to change the cardboard I want the cardboard id. To repair this I created 2 routes: EditCard Y AddCard.

val deckId = Routes.AddCard.parse(it.arguments)
EditCardRoute(navController, deckId = deckId)

val cardId = Routes.EditCard.parse(it.arguments)
EditCardRoute(navController, cardId = cardId)

The caller decides which path to name primarily based on what he needs to do, however on the implementation facet, I selected to make use of the identical route.

So when the ViewModel is created, calls a operate to create a brand new card or to load an current card.

enjoyable load(deckId: String?, cardId: String?)  
deckId == null && cardId == null ->
Logger.e("deckId and cardId are null")
// navigate up

deckId != null -> createNewCard(deckId)
cardId != null -> loadCard(cardId)

That is what this display seems like on the finish of day 4. Fairly easy display, however now at the very least I can create easy playing cards and add them to a given deck.

Progress on the finish of day 4

I hope the article roughly Constructing a Language Studying App with Compose — Half 1 provides acuteness to you and is helpful for including to your data

Building a Language Learning App with Compose — Part 1