La grande forme avec Jetpack Compose |  de Heba Mekawi |  février 2022
Android

La grande forme avec Jetpack Compose | de Heba Mekawi | février 2022

Le 28 février 2022 - 7 minutes de lecture

Comme toujours, toute application aura toujours besoin d’un écran de formulaire comme la connexion, l’inscription, la modification du profil, le formulaire de demande et bien d’autres…

Cet article vise à être une référence simple pour les composants de champs de formulaire communs

Voyons comment créer un formulaire avec Jetpack Compose 🚀

Nous couvrirons tous les points suivants :

  • Mot de passe avec bascule de visibilité
  • Numéro de mobile avec sélecteur de code de pays
  • Sélecteur de date
  • Sélecteur d’images de la galerie ou de l’appareil photo

Champ de texte de style

Avant de commencer, vous devez avoir une vue composable du composant de champ de texte en fonction de la conception de votre application, cela peut ressembler à ceci :

fun AppTextField(
modifier: Modifier = Modifier,
text: String,
placeholder: String,
leadingIcon: @Composable (() -> Unit)? = null,
onChange: (String) -> Unit = {},
imeAction: ImeAction = ImeAction.Next,
keyboardType: KeyboardType = KeyboardType.Text,
keyBoardActions: KeyboardActions = KeyboardActions(),
isEnabled: Boolean = true
) {
OutlinedTextField(
modifier = modifier.fillMaxWidth(),
value = text,
onValueChange = onChange,
leadingIcon = leadingIcon,
textStyle = TextStyle(fontSize = 18.sp),
keyboardOptions = KeyboardOptions(imeAction = imeAction, keyboardType = keyboardType),
keyboardActions = keyBoardActions,
enabled = isEnabled,
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Color.Black,
unfocusedBorderColor = Color.Gray,
disabledBorderColor = Color.Gray,
disabledTextColor = Color.Black
),
placeholder = {
Text(text = placeholder, style = TextStyle(fontSize = 18.sp, color = Color.LightGray))
}
)
}

Cet exemple suit également MVVM afin que le modèle de vue contienne tous les états, tous peuvent être vides ou pré-remplis avec la valeur précédente (comme modifier le cas de l’écran)

class FormViewModel : ViewModel() {var firstName by mutableStateOf("")
var lastName by mutableStateOf("")
var password by mutableStateOf("")
var mobileNumber by mutableStateOf("")
var mobileCountryCode by mutableStateOf("")
var
dateOfBirth by mutableStateOf("")
...
}

Ajouter des actions IME

Dans la vue de formulaire composable parent, nous pouvons ajouter plusieurs composants de champ de texte, tous attachés avec des actions IME du clavier

//User name text fieldColumn{    val focusManager = LocalFocusManager.current

AppTextField(
text = viewModel.firstName,
placeholder = "First Name",
onChange = {
viewModel.firstName = it
},
imeAction = ImeAction.Next,//Show next as IME button
keyboardType = KeyboardType.Text,
//Plain text keyboard
keyBoardActions = KeyboardActions(
onNext = {
focusManager.moveFocus(FocusDirection.Down)
}
)

)
...
}

De plus, si la conception a deux vues de champ de texte dans une rangée horizontale, nous pouvons utiliser FocusDirection.Left ou FocusDirection.Right

Champ Mot de passe

Simple comme un champ de texte brut mais avec PasswordVisualTransformation

AppTextField(
text = viewModel.password,
placeholder = "Password",
onChange = {
viewModel.password = it
},
imeAction = ...
visualTransformation = PasswordVisualTransformation(),
keyboardType = KeyboardType.Password,
keyBoardActions = ...
)

Ajoutons aussi le Bascule de visibilité bouton icône pour afficher / masquer le mot de passe comme celui-ci

var isPasswordVisible by remember { mutableStateOf(false) }AppTextField(
....,
leadingIcon = {
IconButton(onClick = {
isPasswordVisible = !isPasswordVisible
}) {
Icon(
imageVector = if (isPasswordVisible)
Icons.Filled.Visibility
else
Icons.Filled.VisibilityOff,
contentDescription = "Password Visibility"
)
}
},
visualTransformation = if (isPasswordVisible)
VisualTransformation.None
else
PasswordVisualTransformation(),

...
)

Veuillez noter que pour utiliser ces icônes, vous aurez besoin de la dépendance suivante

implementation "androidx.compose.material:material-icons-extended:$compose_version"

Champ Numéro de portable

Identique au champ de texte brut mais avec le type de clavier du téléphone

keyboardType = KeyboardType.Phone,

Ajoutons le Sélecteur de code de pays dans le champ de texte comme suit

Et ajoutons l’étiquette de code de pays sélectionnée au champ de texte en tant qu’étiquette principale

leadingIcon = {
viewModel.mobileCountry?.let {
CountryPickerView(
countries = viewModel.countriesList
selectedCountry = it,
onSelection = { selectedCountry ->
viewModel.mobileCountry = selectedCountry
},
)

}
},

En utilisant CountryPickerUtils.kt, le modèle de vue contiendra les états nécessaires comme d’habitude

val countriesList = getCountriesList()
var mobileCountry by mutableStateOf<Country?>(null)

Et le composable CountryPickerView peut être comme ça

@Composable
fun CountryPickerView(
selectedCountry: Country,
onSelection: (Country) -> Unit,
countries: List<Country>
) {
var showDialog by remember { mutableStateOf(false) }
Text(
modifier = Modifier
.clickable {
showDialog = true
}
.padding(start = 20.dp, end = 5.dp),
text = "${getFlagEmojiFor(selectedCountry.nameCode)} +${selectedCountry.code}"
)

if (showDialog)
CountryCodePickerDialog(countries, onSelection) {
showDialog = false
}
}

Et ceci est une implémentation très simple de CountryCodePickerDialog, améliorez absolument la conception selon vos besoins en fonction de la conception de votre application

@Composable
fun CountryCodePickerDialog(
countries: List<Country>,
onSelection: (Country) -> Unit,
dismiss: () -> Unit,
) {
Dialog(onDismissRequest = dismiss) {
Box {
LazyColumn(
Modifier
.fillMaxWidth()
.padding(horizontal = 30.dp, vertical = 40.dp)
.background(shape = RoundedCornerShape(20.dp), color = Color.White)
) {
for (country in countries) {
item {
Text(
modifier = Modifier
.clickable {
onSelection(country)
dismiss()
}
.fillMaxWidth()
.padding(10.dp),
text = "${getFlagEmojiFor(country.nameCode)} ${country.fullName}"
)
}
}
}
}
}
}

Champ de sélection de date

Il affichera une boîte de dialogue de sélection de date comme celle-ci

val context = LocalContext.current
AppTextField(
modifier = Modifier.clickable {
viewModel.showDatePickerDialog(context)
},
text = viewModel.dateOfBirth,
placeholder = "Birthdate",
onChange = {
viewModel.dateOfBirth = it
},
isEnabled = false
)

La boîte de dialogue de date affichera soit la date du jour, soit la dernière date sélectionnée dans la variable d’état dateOfBirth

private var dateFormat = "yyyy-MM-dd"fun showDatePickerDialog(context: Context) {
val calendar = getCalendar()
DatePickerDialog(
context, { _, year, month, day ->
dateOfBirth = getPickedDateAsString(year, month, day)
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
)
.show()
}
private fun getCalendar(): Calendar {
return if (dateOfBirth.isEmpty())
Calendar.getInstance()
else
getLastPickedDateCalendar()
}

private fun getLastPickedDateCalendar(): Calendar {
val dateFormat = SimpleDateFormat(dateFormat)
val calendar = Calendar.getInstance()
calendar.time = dateFormat.parse(dateOfBirth)
return calendar
}

private fun getPickedDateAsString(year: Int, month: Int, day: Int): String {
val calendar = Calendar.getInstance()
calendar.set(year, month, day)
val dateFormat = SimpleDateFormat(dateFormat)
return dateFormat.format(calendar.time)
}

Sélecteur d’images

  • Choisissez une image dans la galerie
  • Capturer l’image de l’appareil photo

Nous allons d’abord permettre à l’utilisateur de choisir l’image de la galerie comme suit

Première importation de bibliothèque de composition de bobines

implementation "io.coil-kt:coil-compose:1.4.0"

et conserver l’uri de l’image sélectionnée dans le modèle de vue

val pickedImage = mutableStateOf<Uri?>(null)

Ajouter la vue du sélecteur d’images à la vue du formulaire

ImagePickerView(
modifier = Modifier.align(Alignment.CenterHorizontally),
lastSelectedImage = viewModel.pickedImage.value,
onSelection = {
viewModel.pickedImage.value = it
}
)

Pour choisir une image dans la galerie, le composable ImagePickerView utilisera le lanceur de galerie comme celui-ci

@Composable
fun ImagePickerView(
modifier: Modifier = Modifier,
lastSelectedImage: Uri?,
onSelection: (Uri?) -> Unit
) {
val galleryLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()) {
onSelection(it)
}

Image(
modifier = modifier
.size(100.dp)
.clip(CircleShape)
.background(Color.LightGray)
.clickable {
galleryLauncher.launch("image/*")
},
painter = rememberImagePainter(lastSelectedImage),
contentDescription = "Profile Picture",
contentScale = ContentScale.Crop
)
}

Maintenant pour capturer l’image de la caméra comme suit

Commençons par gérer l’autorisation de la caméra à l’aide de la bibliothèque Accompanist et importons-la

implementation "com.google.accompanist:accompanist-permissions:0.24.1-alpha"

Et ajoutez l’autorisation de la caméra au fichier manifeste de l’application

<uses-permission android:name="android.permission.CAMERA" />

Maintenant, mettons à jour le composable ImagePickerView en

  • Ouvrir le lanceur de caméra (si l’autorisation est juste accordée ou sur clic)
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun ImagePickerView(
modifier: Modifier = Modifier,
lastSelectedImage: Uri?,
onSelection: (Uri?) -> Unit
) {
val context = LocalContext.current
val cameraPermission = rememberPermissionState(Manifest.permission.CAMERA)
var isPermissionRequested by rememberSaveable { mutableStateOf(false) }

val cameraLauncher: ManagedActivityResultLauncher<Void?, Bitmap?> =
rememberLauncherForActivityResult(ActivityResultContracts.TakePicturePreview()) {
onSelection(it?.toUri(context))
}

if (isPermissionRequested && cameraPermission.hasPermission) {
cameraLauncher.launch()
isPermissionRequested = false
}

Image(
modifier = modifier
.size(100.dp)
.clip(CircleShape)
.background(Color.LightGray)
.clickable {
if (!cameraPermission.hasPermission) {
cameraPermission.launchPermissionRequest()
isPermissionRequested = true
} else
cameraLauncher.launch()

},
painter = rememberImagePainter(lastSelectedImage),
contentDescription = "Profile Picture",
contentScale = ContentScale.Crop
)
}

Veuillez également noter que l’état d’autorisation de l’accompagnateur a devrait afficher la justification que vous pouvez utiliser pour informer l’utilisateur pourquoi vous avez besoin de cette autorisation avant de la demander, en savoir plus à ce sujet ici

C’est ça! Tu l’as fait! 💪

Voyez-vous quelque chose qui manque? commentez s’il vous plaît!

Merci d’avoir lu, rendez-vous dans le prochain article 😊

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.