Jetpack Compose

Jetpack compose date input text

Jetpack compose date input text

Hi Everyone in this article i am sharing about the Jetpack compose date input text.

In this course you’ll create stunning animations using Jetpack Compose and learn about the API that allows you to create these animations in a snap.

Jetpack Compose is an innovative and super-cool toolkit that allows you to build an native Android UI in a declarative manner. The code that is written in a declarative manner specifies how the UI will appear based on the current state instead of altering the UI every time state changes. This is why Jetpack Compose lets you develop applications that use a more modular design by creating less code, and arranging it into smaller and reuseable components that are simpler to maintain. What is Jetpack Compose’s approach to Animations?

Jetpack compose date input text Example

Create a new Project with jetpack compose activity. and Build your project. after the follow below the steps.

Dependancies

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
    implementation 'androidx.activity:activity-compose:1.4.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
}

 

MainActivity.kt  Source Code Jetpack compose date input text.

import android.os.Build
import android.os.Bundle

import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.*
import androidx.compose.ui.text.input.*
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.jetpack.speechtotext.ui.theme.SpeechToTextTheme
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*

class MainActivity : ComponentActivity() {
    private var talk by mutableStateOf("Click to button Speech text ")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SpeechToTextTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Scaffold(
                        topBar = {
                            TopAppBar(
                                title = {
                                    Text(
                                        text = "Jetpack Compose Date Input ",
                                        modifier = Modifier.fillMaxWidth(),
                                        textAlign = TextAlign.Left
                                    )
                                }
                            )
                        }
                    ) {
                        DateInput()
                    }
                }
            }
        }
    }

 

Create a Function DateInput.

@Composable
fun DateInput() {
    Column(
        modifier = Modifier
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        var date by remember {
            mutableStateOf(
                TextFieldValue(
                    text = "dd-MM-yyyy"
                )
            )
        }

        DateTextField(
            value = date,
            onValueChange = {
                date = it
            }
        )

    }
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun DateTextField(
    value: TextFieldValue,
    modifier: Modifier = Modifier,
    onValueChange: (TextFieldValue) -> Unit
) {

    var isBackspacePressed by remember { mutableStateOf(false) }

    OutlinedTextField(
        value = value,
        visualTransformation = DateFormatVisualTransformation(LocalTextStyle.current),
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number,
            imeAction = ImeAction.Done
        ),
        onValueChange = {
            // ex: "01-1M-yyyy" -> "011"
            val date = it.text.takeDigitString()

            if (date.length < 9) {

                // if date length is 3 or 5, move cursor to index + 1
                // ex: (| = cursor) "01-|1M-yyyy" to "01-1|M-yyyy"
                val selection = if (!isBackspacePressed) {
                    when (date.length) {
                        3, 5 -> it.selection.start + 1
                        else -> it.selection.start
                    }
                } else it.selection.start

                onValueChange(
                    it.copy(
                        text = TextFieldDateFormatter.format(it),
                        selection = TextRange(selection)
                    )
                )
            }
        },
        modifier = modifier
            .onKeyEvent { event ->
                if (event.key == Key.Backspace) {
                    isBackspacePressed = true
                    return@onKeyEvent true
                } else {
                    isBackspacePressed = false
                }

                false
            }
    )
}

class DateFormatVisualTransformation(
    private val textStyle: TextStyle
): VisualTransformation {

    override fun filter(text: AnnotatedString): TransformedText {
        return TransformedText(
            text = buildAnnotatedString {
                for (word in text) {
                    withStyle(
                        textStyle.copy(
                            color = if (word.isDigit() || word == '-') textStyle.color
                            else Color.Gray
                        ).toSpanStyle()
                    ) {
                        append(word)
                    }
                }
            },
            offsetMapping = OffsetMapping.Identity
        )
    }
}

object TextFieldDateFormatter {

    private const val ddMMyyyy = "ddMMyyyy"

    /**
     * format "ddMMyyyy" to "dd-MMM-yyyy"
     * @return formatted string, ex: "11-01-2007" or "11-0M-YYYY"
     */
    fun format(
        fieldValue: TextFieldValue,
        minYear: Int = 2000,
        maxYear: Int = 3000
    ): String {
        val builder = StringBuilder()

        val s = fieldValue.text.replace(
            oldValue = listOf(" ", ".", ",", "-", "d", "M", "y"),
            newValue = ""
        )

        if (s.length != 8) {
            for (i in 0 until 8) {
                builder.append(
                    try {
                        s[i]
                    } catch (e: Exception) {
                        ddMMyyyy[i]
                    }
                )
            }
        } else builder.append(s)

        repeat(3) {
            when (it) {
                0 -> {
                    val day = try {
                        "${builder[0]}${builder[1]}".toInt()
                    } catch (e: Exception) {
                        null
                    }

                    if (day != null) {
                        val dayMax = day
                            .coerceIn(
                                minimumValue = 1,
                                maximumValue = 31,
                            )
                            .toString()

                        builder.replace(
                            0,
                            2,
                            if (dayMax.length == 1) "0$dayMax" else dayMax
                        )
                    }
                }
                1 -> {
                    val month = try {
                        "${builder[2]}${builder[3]}".toInt()
                    } catch (e: Exception) {
                        null
                    }

                    if (month != null) {
                        val monthMax = month
                            .coerceIn(
                                minimumValue = 1,
                                maximumValue = 12,
                            )
                            .toString()

                        builder.replace(
                            2,
                            4,
                            if (monthMax.length == 1) "0$monthMax" else monthMax
                        )
                    }
                }
                2 -> {
                    val year = try {
                        "${builder[4]}${builder[5]}${builder[6]}${builder[7]}".toInt()
                    } catch (e: Exception) {
                        null
                    }

                    if (year != null) {
                        val yearMaxMin = year.coerceIn(
                            minimumValue = minYear,
                            maximumValue = maxYear
                        ).toString()

                        builder.replace(4, 6, yearMaxMin.substring(0, 2))
                        builder.replace(6, 8, yearMaxMin.substring(2, 4))
                    }
                }
            }
        }

        return builder.toString()
            .addStringBefore("-", 2)  // dd-MMyyyy
            .addStringBefore("-", 5)   // dd-MM-yyyy
    }

    /**
     * format time in milli second to formatted date
     * @return formatted string, ex: "11-01-2007"
     */
//    fun format(timeInMillis: Long): String {
//        return SimpleDateFormat("dd-MM-yyyy", deviceLocale).format(timeInMillis)
//    }

    /**
     * check if formattedDate is valid
     *
     * "01-01-2000" -> valid
     * "01-0M-YYYY" -> not valid
     * @param formattedDate "01-01-2000"
     */
    fun isValid(formattedDate: String): Boolean {
        return formattedDate.takeDigitString().length == 8
    }

    /**
     * convert formatted string date to time in millis
     * @param formattedDate "01-01-2000"
     * @return time in millis
     */
    @RequiresApi(Build.VERSION_CODES.O)
    fun parse(formattedDate: String): Long {
        val date = "${formattedDate[6]}${formattedDate[7]}${formattedDate[8]}${formattedDate[9]}-"
            .plus("${formattedDate[3]}${formattedDate[4]}-")
            .plus("${formattedDate[0]}${formattedDate[1]}")
            .plus("T00:00")

        return LocalDateTime.parse(date)
            .atZone(ZoneId.of(TimeZone.getDefault().id, ZoneId.SHORT_IDS))
            .toInstant()
            .toEpochMilli()
    }

}

/**
 * take a string that is a number
 *
 * ex:
 * val s = "123a456b"
 * s.takeDigitString() => "123456"
 * @author kafri8889
 */
fun String.takeDigitString(): String {
    var builder = ""

    forEach { if (it.isDigit()) builder += it }

    return builder
}

/**
 * add a new string before the given index
 * @author kafri8889
 */
fun String.addStringBefore(s: String, index: Int): String {
    val result = StringBuilder()
    forEachIndexed { i, c ->
        if (i == index) {
            result.append(s)
            result.append(c)
        } else result.append(c)
    }

    return result.toString()
}

/**
 * Returns a new string with all occurrences of oldValue replaced with newValue.
 * @author kafri8889
 */
fun String.replace(
    oldValue: List<String>,
    newValue: String,
    ignoreCase: Boolean = false
): String {
    var result = this

    oldValue.forEach { s ->
        if (this.contains(s)) {
            result = result.replace(s, newValue, ignoreCase)
        }
    }

    return result
}

Now run your project and see the output. to enter the date month and year in input filed jetpack compose. Jetpack compose date input text.

 

Read More Tutorial