Create your Password Generator in Jetpack Compose | Part 1 | Interface

WhiteBatCodes
6 min readMay 25, 2023

Password generators are commonly used to generate strong passwords for your accounts. This application is for you to make your own custom Password generator app and even store those passwords in a secure location for later usage.

This is going to be part 1 of this project. In this part, we’ll create the UI for the password generator, as you can see in the screenshot above.

Let’s start with a new empty Activity as a starter activity. After you create your Activity. Make sure it’s set to the starter activity in your manifest by moving the intent-filter block from MainActivity to your class, in this project I called it PasswordGeneratorActivity. The result should look something like this :

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PasswordVault"
tools:targetApi="31">
<activity
android:name=".PasswordGeneratorActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.PasswordVault" />
</application>

Before we dig into the UI, we’ll create some Strings that we can translate later on. Go to /res/values/strings.xml and add the following strings:

<string name="generate_a_password">Generate a Password</string>
<string name="generate">Generate</string>
<string name="copy">Copy</string>
<string name="upper_case">Upper Case</string>
<string name="lower_case">Lower Case</string>
<string name="numeric">Numeric</string>
<string name="click_on_generate">Click on Generate</string>
<string name="password_size">Password Size</string>

In your PasswordGeneratorActivity, add the content in your onCreate() as follows :

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
PasswordGen()
}
}

Now let’s start defining the password generator interface!

First, we’ll create a Column that will host the generated password and a butty copy, to allow the user to copy the value :

@Composable
fun PasswordGen() {
var generatedPassword by remember { mutableStateOf("") }

Surface {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 10.dp),
verticalArrangement = Arrangement.Center
) { // parent column
Text(
stringResource(id = R.string.generate_a_password),
modifier = Modifier.align(CenterHorizontally),
style = MaterialTheme.typography.headlineSmall
) // title of the page
Spacer(modifier = Modifier.height(10.dp))

Column(
verticalArrangement = Arrangement.Center
) { // generated password column
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = generatedPassword.ifEmpty { stringResource(id = R.string.click_on_generate) },
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(5.dp))
Button(
onClick = { /*TODO*/ },
enabled = generatedPassword.isNotEmpty(),
shape = RoundedCornerShape(5.dp)
) {
Text(stringResource(id = R.string.copy))
}
}
}
}
}
}

This should display a title, a textfield and a copy button. Note that in the code, the textfield will display the string value “click on generate” if the generated password string is empty. You can see the password if you change the initial state of the generated password by adding any non-empty string.

Next, the tricky part! We’ll have to create 3 labeled checkboxes, 1 Editable checkbox, a row for the password size and at last, the Generate button.

For the Labeled checkboxes, we’ll have to define the components separately to make it simpler and reusable :

@Composable
fun LabeledCheckbox(label: String, onCheckChange: () -> Unit, isChecked: Boolean) {
Row(
modifier = Modifier
.clickable(
onClick = onCheckChange
)
.fillMaxWidth()
.padding(vertical = 5.dp)
) {
Checkbox(checked = isChecked, onCheckedChange = null)
Text(text = label)
}
}

The row here is what will be clickable, as the checkbox doesn’t have a default label for it. So here, once we click on the row, the checkbox will either be checked or unchecked. We pass this method as a paramter to the function, and we also pass the label to display and the intial state of the checkbox.

For the Editable checkbox, we can base it on the previous checkbox and add a TextField instead of a simple Text. This will allow the user to edit the data. The method should look like this :

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditableCheckBox(
value: String,
onCheckChange: () -> Unit,
onValueChange: (String) -> Unit,
isChecked: Boolean
) {
Row(
modifier = Modifier
.clickable(
onClick = onCheckChange
)
.fillMaxWidth()
.padding(vertical = 5.dp),
verticalAlignment = CenterVertically
) {
Checkbox(checked = isChecked, onCheckedChange = null)
TextField(value = value, onValueChange = onValueChange)
}
}

Note that the checkbox logic is the same, but we added here a method for the value of the TextField to listen to a change in the field.

Let’s add these checkboxes to the PasswordGen method and see how it looks like:

@Composable
fun PasswordGen() {
var generatedPassword by remember { mutableStateOf("") }
var customPasswordSetting by remember { mutableStateOf("@{}") }

var isUpper by remember { mutableStateOf(true) }
var isLower by remember { mutableStateOf(true) }
var isNumeric by remember { mutableStateOf(false) }
var isCustom by remember { mutableStateOf(false) }

Surface {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 10.dp),
verticalArrangement = Arrangement.Center
) {
Text(
stringResource(id = R.string.generate_a_password),
modifier = Modifier.align(CenterHorizontally),
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(10.dp))

Column(
verticalArrangement = Arrangement.Center
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = generatedPassword.ifEmpty { stringResource(id = R.string.click_on_generate) },
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(5.dp))
Button(
onClick = { /*TODO*/ },
enabled = generatedPassword.isNotEmpty(),
shape = RoundedCornerShape(5.dp)
) {
Text(stringResource(id = R.string.copy))
}
}
}

Spacer(modifier = Modifier.height(10.dp))

LabeledCheckbox(
label = stringResource(id = R.string.upper_case),
onCheckChange = {
isUpper = !isUpper
},
isChecked = isUpper
)
LabeledCheckbox(
label = stringResource(id = R.string.lower_case),
onCheckChange = {
isLower = !isLower
},
isChecked = isLower
)
LabeledCheckbox(
label = stringResource(id = R.string.numeric),
onCheckChange = {
isNumeric = !isNumeric
},
isChecked = isNumeric
)
EditableCheckBox(
value = customPasswordSetting,
onCheckChange = {
if (customPasswordSetting.isNotEmpty())
isCustom = !isCustom
},
isChecked = isCustom,
onValueChange = {
if (it.isEmpty()) {
isCustom = false
}
customPasswordSetting = it
}
)

}
}
}

With this like so, you’ll be able to view the 4 fields and also see that the first 2 fields are checked (the default state) and also we have added a variable called customPasswordSetting. This variable is the default value of the custom password text field. That field will serve us as to improve the strength of the generated password. This will give the user more flexibility when generating the password.

Let’s now jump to the password size. We’ll have to use a text field too, but we’ll customize it to allow the users to input numbers only :

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PasswordSize(passwordSize: String, onValueChange: (String) -> Unit) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = CenterVertically
) {
Text(
text = stringResource(id = R.string.password_size),
modifier = Modifier.weight(1f)
)
TextField(
value = passwordSize,
onValueChange = onValueChange,
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
placeholder = { Text("Max 200") },
modifier = Modifier.weight(1f)
)
}
}

In this method, the keyboard is set to a numeric keyboard and the placeholder is set to display maximum 200.

Let’s add this method and our Button to the PasswordGen() method. With everything in place, it should look like this:

@Composable
fun PasswordGen() {
var generatedPassword by remember { mutableStateOf("") }
var passwordSize by remember { mutableStateOf("8") }
var customPasswordSetting by remember { mutableStateOf("?!@,-_&#(){}[]") }

var isUpper by remember { mutableStateOf(true) }
var isLower by remember { mutableStateOf(true) }
var isNumeric by remember { mutableStateOf(false) }
var isCustom by remember { mutableStateOf(false) }

Surface {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 10.dp),
verticalArrangement = Arrangement.Center
) {
Text(
stringResource(id = R.string.generate_a_password),
modifier = Modifier.align(CenterHorizontally),
style = MaterialTheme.typography.headlineSmall
)
Spacer(modifier = Modifier.height(10.dp))

Column(
verticalArrangement = Arrangement.Center
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = generatedPassword.ifEmpty { stringResource(id = R.string.click_on_generate) },
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(5.dp))
Button(
onClick = { /*TODO*/ },
enabled = generatedPassword.isNotEmpty(),
shape = RoundedCornerShape(5.dp)
) {
Text(stringResource(id = R.string.copy))
}
}
}

Spacer(modifier = Modifier.height(10.dp))

LabeledCheckbox(
label = stringResource(id = R.string.upper_case),
onCheckChange = {
isUpper = !isUpper
},
isChecked = isUpper
)
LabeledCheckbox(
label = stringResource(id = R.string.lower_case),
onCheckChange = {
isLower = !isLower
},
isChecked = isLower
)
LabeledCheckbox(
label = stringResource(id = R.string.numeric),
onCheckChange = {
isNumeric = !isNumeric
},
isChecked = isNumeric
)
EditableCheckBox(
value = customPasswordSetting,
onCheckChange = {
if (customPasswordSetting.isNotEmpty())
isCustom = !isCustom
},
isChecked = isCustom,
onValueChange = {
if (it.isEmpty()) {
isCustom = false
}
customPasswordSetting = it
}
)

PasswordSize(
passwordSize = passwordSize,
onValueChange = {
if ((it.isNotEmpty() && it.toInt() < 200) || it.isEmpty()) {
passwordSize = it
}
}
)
Spacer(modifier = Modifier.height(10.dp))
Button(
shape = RoundedCornerShape(5.dp),
onClick = { /*TODO*/ },
modifier = Modifier.fillMaxWidth(),
enabled = (isUpper || isLower || isCustom || isNumeric) && passwordSize.isNotEmpty() && passwordSize.toInt() > 0
) {
Text(text = stringResource(id = R.string.generate))
}

}
}
}

Here, the generate button will be hidden unless one of the checkboxes is checked, the password is not empty and the password is greater than 0.

Notice that, in the custom password, the value change changes the value of the check if the field is empty. If the user didn’t enter any value, the checkbox should be unchecked.

You can add a preview to see how everything looks like or run your application to see the result.

The GitHub code can be found here : GitHub repo

Here’s a YouTube video if you want to follow the same steps more visually :

You can watch part 2 here : Create your Password Generator in Jetpack Compose | PwdGen

--

--

WhiteBatCodes

A software engineer👨‍💻 with a passion for IT 📱💻 and cyber security 🔐.