@@ -156,7 +156,6 @@ internal fun AddNewUserRoute(
156156 )
157157}
158158
159- @OptIn(ExperimentalMaterial3Api ::class )
160159@Composable
161160private fun AddNewUserContent (
162161 viewState : ViewState ,
@@ -166,17 +165,8 @@ private fun AddNewUserContent(
166165 onSubmit : () -> Unit ,
167166 modifier : Modifier = Modifier ,
168167) {
169- val emailError =
170- if (viewState.emailChanged && UserValidationError .INVALID_EMAIL_ADDRESS in viewState.errors) " Invalid email"
171- else null
172-
173- val firstNameError =
174- if (viewState.firstNameChanged && UserValidationError .TOO_SHORT_FIRST_NAME in viewState.errors) " Too short first name"
175- else null
176-
177- val lastNameError =
178- if (viewState.lastNameChanged && UserValidationError .TOO_SHORT_LAST_NAME in viewState.errors) " Too short last name"
179- else null
168+ val resources = LocalContext .current.resources
169+ val errors = viewState.errors
180170
181171 Box (
182172 modifier = modifier
@@ -191,125 +181,200 @@ private fun AddNewUserContent(
191181 ) {
192182 Spacer (modifier = Modifier .height(16 .dp))
193183
194- TextField (
195- modifier = Modifier .fillMaxWidth(),
196- value = viewState.email ? : " " ,
197- onValueChange = onEmailChanged,
198- label = { Text (text = " Email" ) },
199- leadingIcon = {
200- Icon (
201- imageVector = Icons .Filled .Email ,
202- contentDescription = " Email"
203- )
204- },
205- maxLines = 1 ,
206- singleLine = true ,
207- keyboardOptions = KeyboardOptions (
208- keyboardType = KeyboardType .Email ,
209- imeAction = ImeAction .Next
210- ),
211- isError = emailError != = null ,
212- supportingText = {
213- emailError?.let {
214- Text (text = it)
184+ EmailTextField (
185+ email = viewState.email ? : " " ,
186+ onEmailChanged = onEmailChanged,
187+ emailError = remember(viewState.emailChanged, errors, resources) {
188+ if (viewState.emailChanged && UserValidationError .INVALID_EMAIL_ADDRESS in errors) {
189+ resources.getString(R .string.invalid_email)
190+ } else {
191+ null
215192 }
216193 }
217194 )
218195
219196 Spacer (modifier = Modifier .height(16 .dp))
220197
221- TextField (
222- modifier = Modifier .fillMaxWidth(),
223- value = viewState.firstName ? : " " ,
224- onValueChange = onFirstNameChanged,
225- label = { Text (text = " First name" ) },
226- leadingIcon = {
227- Icon (
228- imageVector = Icons .Filled .Person ,
229- contentDescription = " First name"
230- )
231- },
232- maxLines = 1 ,
233- singleLine = true ,
234- keyboardOptions = KeyboardOptions (
235- keyboardType = KeyboardType .Text ,
236- imeAction = ImeAction .Next
237- ),
238- isError = firstNameError != = null ,
239- supportingText = {
240- firstNameError?.let {
241- Text (text = it)
198+ FirstNameTextField (
199+ firstName = viewState.firstName ? : " " ,
200+ onFirstNameChanged = onFirstNameChanged,
201+ firstNameError = remember(viewState.firstNameChanged, errors, resources) {
202+ if (viewState.firstNameChanged && UserValidationError .TOO_SHORT_FIRST_NAME in errors) {
203+ resources.getString(R .string.too_short_first_name)
204+ } else {
205+ null
242206 }
243207 }
244208 )
245209
246210 Spacer (modifier = Modifier .height(16 .dp))
247211
248- TextField (
249- modifier = Modifier .fillMaxWidth(),
250- value = viewState.lastName ? : " " ,
251- onValueChange = onLastNameChanged,
252- label = { Text (text = " Last name" ) },
253- leadingIcon = {
254- Icon (
255- imageVector = Icons .Filled .Person ,
256- contentDescription = " Last name"
257- )
258- },
259- maxLines = 1 ,
260- singleLine = true ,
261- keyboardOptions = KeyboardOptions (
262- keyboardType = KeyboardType .Text ,
263- imeAction = ImeAction .Done
264- ),
265- isError = lastNameError != = null ,
266- supportingText = {
267- lastNameError?.let {
268- Text (text = it)
212+ LastNameTextField (
213+ lastName = viewState.lastName ? : " " ,
214+ onLastNameChanged = onLastNameChanged,
215+ lastNameError = remember(viewState.lastNameChanged, errors, resources) {
216+ if (viewState.lastNameChanged && UserValidationError .TOO_SHORT_LAST_NAME in errors) {
217+ resources.getString(R .string.too_short_last_name)
218+ } else {
219+ null
269220 }
270221 }
271222 )
272223
273224 Spacer (modifier = Modifier .height(24 .dp))
274225
275- Crossfade (
226+ AddButton (
227+ isLoading = viewState.isLoading,
228+ onSubmit = onSubmit,
229+ )
230+
231+ Spacer (modifier = Modifier .height(16 .dp))
232+ }
233+ }
234+ }
235+
236+ @Composable
237+ private fun AddButton (
238+ isLoading : Boolean ,
239+ onSubmit : () -> Unit ,
240+ modifier : Modifier = Modifier ,
241+ ) {
242+ Crossfade (
243+ modifier = modifier
244+ .fillMaxWidth()
245+ .heightIn(min = 64 .dp),
246+ targetState = isLoading,
247+ animationSpec = tween(durationMillis = 200 ),
248+ label = " LoadingIndicator/ElevatedButton" ,
249+ ) { state ->
250+ if (state) {
251+ LoadingIndicator (
252+ modifier = Modifier
253+ .fillMaxWidth(),
254+ )
255+ } else {
256+ ElevatedButton (
276257 modifier = Modifier
277258 .fillMaxWidth()
278- .heightIn(min = 64 .dp),
279- targetState = viewState.isLoading,
280- animationSpec = tween(durationMillis = 200 ),
281- label = " LoadingIndicator/ElevatedButton" ,
282- ) { isLoading ->
283- if (isLoading) {
284- LoadingIndicator (
285- modifier = Modifier
286- .fillMaxWidth(),
287- )
288- } else {
289- ElevatedButton (
290- modifier = Modifier
291- .fillMaxWidth()
292- .wrapContentSize(Alignment .Center ),
293- colors = ButtonDefaults .elevatedButtonColors(
294- containerColor = MaterialTheme .colorScheme.primary,
295- contentColor = MaterialTheme .colorScheme.onPrimary
296- ),
297- onClick = onSubmit,
298- contentPadding = PaddingValues (
299- horizontal = 32 .dp,
300- vertical = 16 .dp,
301- ),
302- ) {
303- Text (text = " Add" )
304- }
305- }
259+ .wrapContentSize(Alignment .Center ),
260+ colors = ButtonDefaults .elevatedButtonColors(
261+ containerColor = MaterialTheme .colorScheme.primary,
262+ contentColor = MaterialTheme .colorScheme.onPrimary
263+ ),
264+ onClick = onSubmit,
265+ contentPadding = PaddingValues (
266+ horizontal = 32 .dp,
267+ vertical = 16 .dp,
268+ ),
269+ ) {
270+ Text (text = " Add" )
306271 }
307-
308- Spacer (modifier = Modifier .height(16 .dp))
309272 }
310273 }
311274}
312275
276+ @Composable
277+ @OptIn(ExperimentalMaterial3Api ::class )
278+ private fun LastNameTextField (
279+ lastName : String ,
280+ onLastNameChanged : (String ) -> Unit ,
281+ lastNameError : String? ,
282+ modifier : Modifier = Modifier ,
283+ ) {
284+ TextField (
285+ modifier = modifier.fillMaxWidth(),
286+ value = lastName,
287+ onValueChange = onLastNameChanged,
288+ label = { Text (text = " Last name" ) },
289+ leadingIcon = {
290+ Icon (
291+ imageVector = Icons .Filled .Person ,
292+ contentDescription = " Last name"
293+ )
294+ },
295+ maxLines = 1 ,
296+ singleLine = true ,
297+ keyboardOptions = KeyboardOptions (
298+ keyboardType = KeyboardType .Text ,
299+ imeAction = ImeAction .Done
300+ ),
301+ isError = lastNameError != = null ,
302+ supportingText = {
303+ lastNameError?.let {
304+ Text (text = it)
305+ }
306+ }
307+ )
308+ }
309+
310+ @Composable
311+ @OptIn(ExperimentalMaterial3Api ::class )
312+ private fun FirstNameTextField (
313+ firstName : String ,
314+ onFirstNameChanged : (String ) -> Unit ,
315+ firstNameError : String? ,
316+ modifier : Modifier = Modifier ,
317+ ) {
318+ TextField (
319+ modifier = modifier.fillMaxWidth(),
320+ value = firstName,
321+ onValueChange = onFirstNameChanged,
322+ label = { Text (text = " First name" ) },
323+ leadingIcon = {
324+ Icon (
325+ imageVector = Icons .Filled .Person ,
326+ contentDescription = " First name"
327+ )
328+ },
329+ maxLines = 1 ,
330+ singleLine = true ,
331+ keyboardOptions = KeyboardOptions (
332+ keyboardType = KeyboardType .Text ,
333+ imeAction = ImeAction .Next
334+ ),
335+ isError = firstNameError != = null ,
336+ supportingText = {
337+ firstNameError?.let {
338+ Text (text = it)
339+ }
340+ }
341+ )
342+ }
343+
344+ @Composable
345+ @OptIn(ExperimentalMaterial3Api ::class )
346+ private fun EmailTextField (
347+ email : String ,
348+ onEmailChanged : (String ) -> Unit ,
349+ emailError : String? ,
350+ modifier : Modifier = Modifier ,
351+ ) {
352+ TextField (
353+ modifier = modifier.fillMaxWidth(),
354+ value = email,
355+ onValueChange = onEmailChanged,
356+ label = { Text (text = " Email" ) },
357+ leadingIcon = {
358+ Icon (
359+ imageVector = Icons .Filled .Email ,
360+ contentDescription = " Email"
361+ )
362+ },
363+ maxLines = 1 ,
364+ singleLine = true ,
365+ keyboardOptions = KeyboardOptions (
366+ keyboardType = KeyboardType .Email ,
367+ imeAction = ImeAction .Next
368+ ),
369+ isError = emailError != = null ,
370+ supportingText = {
371+ emailError?.let {
372+ Text (text = it)
373+ }
374+ }
375+ )
376+ }
377+
313378@Preview(
314379 showBackground = true ,
315380 showSystemUi = true ,
0 commit comments