Fiddling with the login and signup screens

This commit is contained in:
Z. Charles Dziura 2024-08-17 21:48:49 -04:00
parent ecea0e12d6
commit 45d632f786
7 changed files with 180 additions and 35 deletions

View file

@ -64,6 +64,8 @@ dependencies {
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.navigation.compose) implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.lifecycle.runtime.compose)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)

View file

@ -1,28 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:tools="http://schemas.android.com/tools">
<application <application
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.DebtPirate" android:theme="@style/Theme.DebtPirate">
tools:targetApi="31"> <activity
<activity android:name=".MainActivity"
android:name=".MainActivity" android:exported="true"
android:exported="true" android:theme="@style/Theme.DebtPirate"
android:label="@string/title_activity_main" android:windowSoftInputMode="adjustResize">
android:theme="@style/Theme.DebtPirate"> <intent-filter>
<intent-filter> <action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
</application> </application>
</manifest> </manifest>

View file

@ -4,12 +4,14 @@ import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import software.makeshift.debtpirate.ui.theme.DebtPirateTheme import software.makeshift.debtpirate.ui.theme.DebtPirateTheme
import software.makeshift.debtpirate.usecases.login.LoginScreen import software.makeshift.debtpirate.usecases.login.LoginScreen
import software.makeshift.debtpirate.usecases.login.LoginScreenViewModel
@Serializable @Serializable
object LoginRoute object LoginRoute
@ -24,7 +26,11 @@ class MainActivity : ComponentActivity() {
DebtPirateTheme { DebtPirateTheme {
NavHost(navController = navController, startDestination = LoginRoute) { NavHost(navController = navController, startDestination = LoginRoute) {
composable<LoginRoute> { composable<LoginRoute> {
LoginScreen() val viewModel: LoginScreenViewModel by viewModels()
LoginScreen(
viewModel.loginState,
viewModel::updateLoginUsername
)
} }
} }
} }

View file

@ -1,23 +1,123 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package software.makeshift.debtpirate.usecases.login package software.makeshift.debtpirate.usecases.login
import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.StateFlow
import software.makeshift.debtpirate.R
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable @Composable
fun LoginScreen() { fun LoginScreen(
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> loginState: StateFlow<LoginState>,
Greeting(modifier = Modifier.padding(innerPadding)) updateLoginUsername: (String) -> Unit
) {
Scaffold(modifier = Modifier.fillMaxSize()) {
Column {
Banner(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
)
LoginAndSignUpForms(
loginState,
modifier = Modifier
.weight(3f)
)
}
} }
} }
@Composable @Composable
private fun Greeting(modifier: Modifier = Modifier) { private fun Banner(modifier: Modifier = Modifier) {
Text( Box(modifier = modifier.background(Color.Green)) {
text = "Hello from Login!", Text(
modifier = modifier text = "Hello from Login! I'm in a box!",
) modifier = Modifier.align(Alignment.Center)
)
}
} }
@Composable
private fun LoginAndSignUpForms(
loginTabState: StateFlow<LoginState>,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
val pagerState = rememberPagerState(initialPage = 0) { 2 }
SecondaryTabRow(
selectedTabIndex = pagerState.currentPage,
modifier = Modifier.height(48.dp)
) {
val tabTextStyle = MaterialTheme.typography.titleLarge
Tab(
selected = pagerState.currentPage == 0,
onClick = { pagerState.requestScrollToPage(0) },
modifier = Modifier.fillMaxHeight()
) {
Text(
text = stringResource(id = R.string.loginscreen_logintab_logintitle),
style = tabTextStyle,
modifier = Modifier.fillMaxHeight()
)
}
Tab(
selected = pagerState.currentPage == 1,
onClick = { pagerState.requestScrollToPage(1) },
modifier = Modifier.fillMaxHeight()
) {
Text(
text = stringResource(id = R.string.loginscreen_logintab_signuptitle),
style = tabTextStyle,
modifier = Modifier.fillMaxHeight()
)
}
}
HorizontalPager(
state = pagerState,
userScrollEnabled = false,
modifier = Modifier.fillMaxSize()
) { page ->
when (page) {
0 -> {
Text("Page 1!")
}
1 -> {
Text("Page 2!")
}
}
}
}
}
//@Composable
//private fun LoginForm(state: StateFlow<LoginState>, onUsernameUpdate: (String) -> Unit, onPasswordUpdate: (String) -> Unit) {
// Column {
// OutlinedTextField(value = , onValueChange = )
// }
//}

View file

@ -0,0 +1,32 @@
package software.makeshift.debtpirate.usecases.login
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
data class LoginState(val username: String? = null, val password: String? = null)
data class SignUpState(
val username: String? = null,
val password: String? = null,
val passwordConfirmed: String? = null,
val email: String? = null
)
class LoginScreenViewModel : ViewModel() {
private val _loginState = MutableStateFlow(LoginState())
val loginState = _loginState.asStateFlow()
val loginUsername: Flow<String?>
get() = loginState.map { loginState -> loginState.username }
fun updateLoginUsername(username: String) {
_loginState.update { state ->
val (_, password) = state
LoginState(username, password)
}
}
}

View file

@ -1,4 +1,7 @@
<resources> <resources>
<string name="app_name">Debt Pirate</string> <string name="app_name">Debt Pirate</string>
<string name="title_activity_main">MainActivity</string>
<!-- Login Screen -->
<string name="loginscreen_logintab_logintitle">Login</string>
<string name="loginscreen_logintab_signuptitle">Sign Up</string>
</resources> </resources>

View file

@ -9,6 +9,8 @@ appcompat = "1.7.0"
material = "1.12.0" material = "1.12.0"
material3 = "1.3.0-beta05" material3 = "1.3.0-beta05"
kotlinxSerializationJson = "1.7.1" kotlinxSerializationJson = "1.7.1"
lifecycleRuntimeCompose = "2.8.4"
lifecycleViewmodelCompose = "2.8.4"
lifecycleRuntimeKtx = "2.8.4" lifecycleRuntimeKtx = "2.8.4"
activityCompose = "1.9.1" activityCompose = "1.9.1"
composeBom = "2024.06.00" composeBom = "2024.06.00"
@ -23,6 +25,8 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } kotlinx-serialization = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }