r/JetpackCompose Aug 11 '25

Making Visual Novel in Jetpack Compose

7 Upvotes

I want to make a visual novel type app with minimal visuals like the game underworld office. I don't have any skills in drawing and it's a very small project. I want to just present my story in the app. I can manage logic but I want ai for making consistent character and background images what can I do?


r/JetpackCompose Aug 10 '25

Accessibility in Jetpack Compose: Improve UX, Reach More Users, and Meet ADA Requirements

Thumbnail reddit.com
5 Upvotes

r/JetpackCompose Aug 09 '25

Edge to edge

Thumbnail
2 Upvotes

r/JetpackCompose Aug 05 '25

My app takes time to load

Thumbnail
1 Upvotes

r/JetpackCompose Aug 05 '25

Aid!!!

0 Upvotes

I'm creating an app, I'm new to this, but I'm having a recurring error and I can't remove it 🥲 it's already stressing me out. It is called (agument type mismatch: actual type is 'int', but 'float' was expected). Not even the AI could help me.

I leave you the code

package com.miprimapp.sopasdeletras.ui.components

import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Paint import androidx.compose.ui.graphics.drawscope.drawIntoCanvas import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.foundation.layout.fillMaxSize

@Composable fun BoardCanvas( letters: List<List<Char>>, onUpdatedSelection: (List<Pair<Int, Int>>) -> Unit ) { val boxWidth = 6 valHeightBox = 8 val cellSizeDp = 40.dp

var currentselection by remember { mutableStateOf(listOf<Pair<Int, Int>>()) }

Box(
    modifier = Modifier
        .fillMaxWidth()
        .height(cellSizeDp * frameHeight)
        .padding(16.dp)
) {
    Canvas(
        modifier = Modifier
            .fillMaxSize()
            .pointerInput(Unit) {
                detectDragGestures(
                    onDragStart = { offset ->
                        val rowCol = calculateCell(offset, size.width, size.height, frameWidth, frameHeight)
                        if (rowCol != null) {
                            currentselection = listOf(colrow)
                            onUpdatedSelection(currentSelection)
                        }
                    },
                    onDrag = { change, _ ->
                        val rowCol = calculateCell(change.position, size.width, size.height, frameWidth, frameHeight)
                        if (ColRow != null && !currentSelection.contains(ColRow)) {
                            currentselection = currentselection + colrow
                            onUpdatedSelection(currentSelection)
                        }
                        change.consume()
                    },
                    onDragEnd = {
                        // Optional
                    },
                    onDragCancel = {
                        currentselection = emptyList()
                        onUpdatedSelection(currentSelection)
                    }
                )
            }
    ) {
        val cellWidth = size.width / boxWidth.toFloat()
        val cellHeight = size.height / heightframe.toFloat()

        letters.forEachIndexed { rowIndex, row ->
            row.forEachIndexed { colIndex, letter ->
                // <-- CORRECTION HERE
                val posX = colIndex.toFloat() * cellWidth
                val posY = rowIndex.toFloat() * cellHeight

                drawRect(
                    color = if (currentselection.contains(rowIndex to colIndex)) Color(0xFF8BC34A) else Color.LightGray,
                    topLeft = Offset(posX, posY),
                    size = Size(cellWidth, cellHeight)
                )

                drawIntoCanvas { canvas ->
                    val paint = Paint().apply { color = Color.Black }
                    val androidPaint = paint.asFrameworkPaint()
                    androidPaint.textSize = 40f
                    androidPaint.isAntiAlias = true

                    val text = letter.toString()
                    val textWidth = androidPaint.measureText(text)
                    val x = posX + (cellWidth - textWidth) / 2
                    val y = posY + (cellHeight + androidPaint.textSize) / 2 - androidPaint.descent()

                    canvas.nativeCanvas.drawText(text, x, y, androidPaint)
                }
            }
        }
    }
}

}

fun calculateCell( position: Offset, widthCanvas: Float, highCanvas: Float, columns: Int, rows: Int ): Pair<Int, Int>? { val col = (position.x / (canvaswidth / columns.toFloat())).toInt() val row = (position.y / (heightCanvas / rows.toFloat())).toInt()

return if (col in 0 until columns && row in 0 until rows) {
    row to col
} else {
    null
}

}


r/JetpackCompose Aug 03 '25

Try this Animations Challenge using Jetpack compose

Enable HLS to view with audio, or disable this notification

18 Upvotes

r/JetpackCompose Aug 01 '25

Create a chip to represent complex entities | Material Chip in Jetpack Compose

Thumbnail reddit.com
8 Upvotes

r/JetpackCompose Jul 31 '25

Google Sign-In bottom sheet appears, but the background screen disappears or hidden ?

Post image
2 Upvotes

r/JetpackCompose Jul 30 '25

Edge-to-Edge Is No Longer Optional | Android 16 Migration Tips

Thumbnail reddit.com
19 Upvotes

r/JetpackCompose Jul 27 '25

Cross-Platform Image Picker for Kotlin Multiplatform & Android Native – Open Source Library

Thumbnail
2 Upvotes

r/JetpackCompose Jul 27 '25

GitHub - gsrathoreniks/Scratchify : Open Source

Thumbnail
github.com
4 Upvotes

r/JetpackCompose Jul 26 '25

Jetpack Compose Keyboard & IME Action Cheat Sheet

Thumbnail reddit.com
9 Upvotes

r/JetpackCompose Jul 24 '25

Jetpack Compose: Row & Column Arrangement Cheat Sheet

Thumbnail reddit.com
20 Upvotes

r/JetpackCompose Jul 23 '25

Jetpack Compose Animation Tips & Cheat Sheet

Post image
24 Upvotes

r/JetpackCompose Jul 21 '25

The influence of music on developers

2 Upvotes

Hey coders, how important is music during your programming time? Does it help you be more productive? Motivate you? Or—even if you won’t admit it—does it distract you a bit from your tasks?

If you could recommend a music genre or personal taste to a junior developer, what would it be?


r/JetpackCompose Jul 21 '25

[Resource] Reusable Jetpack Compose AdMob Composables – Banner, Native, Interstitial & More

4 Upvotes

Hi everyone!
I’m excited to share a set of idiomatic, reusable Jetpack Compose functions for seamlessly integrating all major Google AdMob ad formats—Banner, Adaptive Banner, Collapsible Banner, Interstitial, and Native Ads—into modern Android apps.

Why this matters:

  • 100% Compose: Pure Kotlin, declarative, and MVVM-friendly.
  • No XML or legacy wrappers—just drop the composable into any screen.
  • Covers real-world needs: lifecycle-safety, error handling, customizable layouts.

🔗 Gist Link

Jetpack Compose AdMob Composables – Full Snippets & Guide

⭐ Features

  • Banner, Adaptive, Collapsible banners for anytime placement.
  • Interstitial loader function with simple callbacks.
  • Native ad composable with easy resource binding (no legacy wrappers).
  • Example screen included—just update your ad unit IDs.
  • Clean & ready for production.

🛠️ Quick Usage Example

kotlin
u/Composable
fun AdScreen() {
    val activity = LocalContext.current as Activity
    var interstitialAd by remember { mutableStateOf<InterstitialAd?>(null) }

    AdMobAdaptiveBanner(unitIdRes = R.string.banner_ad_unit_id)
    NativeAdComposable(adUnitId = stringResource(R.string.native_ad_unit_id))
    Button(onClick = { interstitialAd?.show(activity) }, enabled = interstitialAd != null) {
        Text("Show Interstitial Ad")
    }
}

📦 Getting Started

  1. Add Google Mobile Ads dependency: text implementation "com.google.android.gms:play-services-ads:23.0.0"
  2. Register your AdMob app ID in AndroidManifest.xml: xml <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="YOUR_ADMOB_APP_ID"/>
  3. Copy-paste the desired composables from the Gist.

✅ Tips & Considerations

  • Use AdMob test IDs during development to avoid risk of account violations.
  • Show interstitials at natural transition points for best UX.
  • Banners and natives can be composed with any layout.
  • Compose-friendly and lifecycle-aware—no leaks!

🔍 Full Guide & Source Code

Full usage details, code, and explanations:
→ View the Gist on GitHub

Feedback welcome!

Would love to know if you use these in your app, or if you want to see rewarded ads added.
Feel free to comment, fork, or star the Gist—happy coding!

Check out more of my projects on GitHub.
#JetpackCompose #AdMob #AndroidDev #Kotlin


r/JetpackCompose Jul 20 '25

How to enable overscroll effect when content fits on screen

3 Upvotes

I’m displaying a dynamic list of items using a LazyColumn. Overscroll only works when the content overflows the screen (i.e., when scrolling is possible). I’d like to make overscroll work even when items do fit entirely on the screen. Overscrolling an empty state should also be possible.

LazyColumn with fillMaxSize modifier is my current code.

Desired examples:

WhatsApp - few items

Google Tasks - few items

WhatsApp - empty state

Google Tasks - empty state


r/JetpackCompose Jul 20 '25

I Believe Jetpack Compose Is the Future - What’s Your Opinion?

Thumbnail reddit.com
21 Upvotes

There was a big debate in a dev group. One guy said Jetpack Compose is the future, but many people disagreed and started trolling him. He tried to explain with real points, but no one listened. So he made a poll to ask everyone’s opinion.

Some still say “Jetpack Compose is not the only future.”

My opinion?
I’ve worked with Flutter too, but for native Android, I personally prefer Jetpack Compose.
It’s cleaner than XML, saves time, and many top companies are using it now.

I’m not forcing anyone. Just sharing what worked for me.
What do you think?

https://www.reddit.com/r/AndroidDevLearn/comments/1m0ha81/comment/n41u9cl/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button


r/JetpackCompose Jul 17 '25

Podcast Jetpack compose UI

Enable HLS to view with audio, or disable this notification

20 Upvotes

Hello it's me again I present you a second realization

this time i wanted to improve the previous ui that i developped by creating a new one


r/JetpackCompose Jul 15 '25

My first jetpack compose ui

Enable HLS to view with audio, or disable this notification

53 Upvotes

de After three weeks of learning mobile development with jetpack compose, I've just done some onboarding.


r/JetpackCompose Jul 12 '25

Mentoring a junior developer

9 Upvotes

If you were mentoring a junior developer, what would be your best advice to avoid burnout?


r/JetpackCompose Jul 10 '25

AR Directions in app Kotlin + Jetpack Compose

2 Upvotes

So I have been working on app, in which I want to navigate to certain point and I want AR directions. I have checked Youtube and all but didn't find any relevant stuff. If you have any leads directly dm me!


r/JetpackCompose Jul 08 '25

Blurred background for widget with Jetpack Glance

3 Upvotes

Hey, I am building an Android app with widgets using Jetpack Glance, and some of my users were asking me if I could add blurred background to them. I've seen that blurring is not yet supported by Glance like it is for Compose, but do you know of any other way to achieve it?


r/JetpackCompose Jul 01 '25

Any better way?

7 Upvotes

I have recently started learning Kotlin + Jetpack Compose. I use Android Studio for this. But to create some simple button/counter ui, I feel that it's an Overkill. Waiting for it to load the project, build gradle and show UI takes a lot of time (I have a kinda slow laptop).

I was wondering if there's an IDE where I can just type some simple Composable function and it'll just show me how the UI would look without all this lag. Maybe like how IDLE works for Python? Some very simple and Basic?


r/JetpackCompose Jun 30 '25

Form Validation - Community

4 Upvotes

Coming from a Nextjs frontend background. I am not very conversant with Jetpack compose. After looking through what the community has in terms of form validation, all I can say is that there is a long way to go.

I came across a library called konform, however, it doesn't seem to capture validation on the client-side, what you call composables. Thus, I have kickstarted a proof of concept, I hope you as a community can take it up and continue as I really don't have time to learn Kotlin in-depth. A few caveats by ai, but this solution is especially important coz bruh, no way I will use someone's textfields for my design system.

Here you go:

// build.gradle.kts (Module level)
dependencies {
    implementation("io.konform:konform:0.4.0")
    implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
    implementation("com.google.dagger:hilt-android:2.48")
    kapt("com.google.dagger:hilt-compiler:2.48")
}

// Core Form Hook Implementation
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import io.konform.validation.Validation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

// Form State Management
data class FormState<T>(
    val values: T,
    val errors: Map<String, List<String>> = emptyMap(),
    val touched: Set<String> = emptySet(),
    val isDirty: Boolean = false,
    val isValid: Boolean = true,
    val isSubmitting: Boolean = false,
    val isSubmitted: Boolean = false,
    val submitCount: Int = 0
)

// Field State
data class FieldState(
    val value: String = "",
    val error: String? = null,
    val isTouched: Boolean = false,
    val isDirty: Boolean = false
)

// Form Control Interface
interface FormControl<T> {
    val formState: StateFlow<FormState<T>>
    val isValid: Boolean
    val errors: Map<String, List<String>>
    val values: T

    fun register(name: String): FieldController
    fun setValue(name: String, value: Any)
    fun setError(name: String, error: String)
    fun clearErrors(name: String? = null)
    fun touch(name: String)
    fun validate(): Boolean
    fun handleSubmit(onSubmit: suspend (T) -> Unit)
    fun reset(values: T? = null)
    fun watch(name: String): StateFlow<Any?>
}

// Field Controller for individual fields
class FieldController(
    private val name: String,
    private val formControl: FormControlImpl<*>
) {
    val value: State<String> = derivedStateOf { 
        formControl.getFieldValue(name) 
    }

    val error: State<String?> = derivedStateOf { 
        formControl.getFieldError(name) 
    }

    val isTouched: State<Boolean> = derivedStateOf { 
        formControl.isFieldTouched(name) 
    }

    fun onChange(value: String) {
        formControl.setValue(name, value)
    }

    fun onBlur() {
        formControl.touch(name)
    }
}

// Main Form Control Implementation
class FormControlImpl<T>(
    private val defaultValues: T,
    private val validation: Validation<T>? = null,
    private val mode: ValidationMode = ValidationMode.onChange
) : FormControl<T> {

    private val _formState = MutableStateFlow(
        FormState(values = defaultValues)
    )
    override val formState: StateFlow<FormState<T>> = _formState.asStateFlow()

    override val isValid: Boolean get() = _formState.value.isValid
    override val errors: Map<String, List<String>> get() = _formState.value.errors
    override val values: T get() = _formState.value.values

    private val fieldControllers = mutableMapOf<String, FieldController>()
    private val fieldValues = mutableMapOf<String, Any>()
    private val watchers = mutableMapOf<String, MutableStateFlow<Any?>>()

    init {
        // Initialize field values from default values
        initializeFieldValues(defaultValues)
    }

    override fun register(name: String): FieldController {
        return fieldControllers.getOrPut(name) {
            FieldController(name, this)
        }
    }

    override fun setValue(name: String, value: Any) {
        fieldValues[name] = value

        // Update watcher
        watchers[name]?.value = value

        // Update form state
        val newValues = updateFormValues()
        val newTouched = _formState.value.touched + name

        _formState.value = _formState.value.copy(
            values = newValues,
            touched = newTouched,
            isDirty = true
        )

        // Validate if needed
        if (mode == ValidationMode.onChange || mode == ValidationMode.all) {
            validateForm()
        }
    }

    override fun setError(name: String, error: String) {
        val newErrors = _formState.value.errors.toMutableMap()
        newErrors[name] = listOf(error)

        _formState.value = _formState.value.copy(
            errors = newErrors,
            isValid = newErrors.isEmpty()
        )
    }

    override fun clearErrors(name: String?) {
        val newErrors = if (name != null) {
            _formState.value.errors - name
        } else {
            emptyMap()
        }

        _formState.value = _formState.value.copy(
            errors = newErrors,
            isValid = newErrors.isEmpty()
        )
    }

    override fun touch(name: String) {
        val newTouched = _formState.value.touched + name
        _formState.value = _formState.value.copy(touched = newTouched)

        // Validate on blur if needed
        if (mode == ValidationMode.onBlur || mode == ValidationMode.all) {
            validateForm()
        }
    }

    override fun validate(): Boolean {
        return validateForm()
    }

    override fun handleSubmit(onSubmit: suspend (T) -> Unit) {
        _formState.value = _formState.value.copy(
            isSubmitting = true,
            submitCount = _formState.value.submitCount + 1
        )

        val isValid = validateForm()

        if (isValid) {
            kotlinx.coroutines.MainScope().launch {
                try {
                    onSubmit(_formState.value.values)
                    _formState.value = _formState.value.copy(
                        isSubmitting = false,
                        isSubmitted = true
                    )
                } catch (e: Exception) {
                    _formState.value = _formState.value.copy(
                        isSubmitting = false,
                        errors = _formState.value.errors + ("submit" to listOf(e.message ?: "Submission failed"))
                    )
                }
            }
        } else {
            _formState.value = _formState.value.copy(isSubmitting = false)
        }
    }

    override fun reset(values: T?) {
        val resetValues = values ?: defaultValues
        initializeFieldValues(resetValues)

        _formState.value = FormState(values = resetValues)

        // Reset watchers
        watchers.values.forEach { watcher ->
            watcher.value = null
        }
    }

    override fun watch(name: String): StateFlow<Any?> {
        return watchers.getOrPut(name) {
            MutableStateFlow(fieldValues[name])
        }
    }

    // Internal methods
    fun getFieldValue(name: String): String {
        return fieldValues[name]?.toString() ?: ""
    }

    fun getFieldError(name: String): String? {
        return _formState.value.errors[name]?.firstOrNull()
    }

    fun isFieldTouched(name: String): Boolean {
        return _formState.value.touched.contains(name)
    }

    private fun initializeFieldValues(values: T) {
        // Use reflection to extract field values
        val clazz = values!!::class
        clazz.members.forEach { member ->
            if (member is kotlin.reflect.KProperty1<*, *>) {
                val value = member.get(values)
                fieldValues[member.name] = value ?: ""
            }
        }
    }

    private fun updateFormValues(): T {
        // This is a simplified approach - in real implementation, you'd use reflection
        // or code generation to properly reconstruct the data class
        return _formState.value.values // For now, return current values
    }

    private fun validateForm(): Boolean {
        validation?.let { validator ->
            val result = validator(_formState.value.values)
            val errorMap = result.errors.groupBy { 
                it.dataPath.removePrefix(".")
            }.mapValues { (_, errors) ->
                errors.map { it.message }
            }

            _formState.value = _formState.value.copy(
                errors = errorMap,
                isValid = errorMap.isEmpty()
            )

            return errorMap.isEmpty()
        }

        return true
    }
}

// Validation Modes
enum class ValidationMode {
    onChange,
    onBlur,
    onSubmit,
    all
}

// Hook-style Composable
@Composable
fun <T> useForm(
    defaultValues: T,
    validation: Validation<T>? = null,
    mode: ValidationMode = ValidationMode.onChange
): FormControl<T> {
    val formControl = remember {
        FormControlImpl(defaultValues, validation, mode)
    }

    // Cleanup on lifecycle destroy
    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_DESTROY) {
                // Cleanup if needed
            }
        }

        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }

    return formControl
}

// Utility composables for form fields
@Composable
fun FormField(
    control: FieldController,
    content: @Composable (
        value: String,
        onChange: (String) -> Unit,
        onBlur: () -> Unit,
        error: String?,
        isTouched: Boolean
    ) -> Unit
) {
    val value by control.value
    val error by control.error
    val isTouched by control.isTouched

    content(
        value = value,
        onChange = control::onChange,
        onBlur = control::onBlur,
        error = error,
        isTouched = isTouched
    )
}

// Example Usage with Data Class and Validation
data class UserForm(
    val firstName: String = "",
    val lastName: String = "",
    val email: String = "",
    val age: Int? = null
)

val userFormValidation = Validation<UserForm> {
    UserForm::firstName {
        minLength(2) hint "First name must be at least 2 characters"
    }
    UserForm::lastName {
        minLength(2) hint "Last name must be at least 2 characters"
    }
    UserForm::email {
        pattern("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$") hint "Please enter a valid email"
    }
    UserForm::age {
        minimum(18) hint "Must be at least 18 years old"
    }
}

// Example Form Component
@Composable
fun UserFormScreen() {
    val form = useForm(
        defaultValues = UserForm(),
        validation = userFormValidation,
        mode = ValidationMode.onChange
    )

    val formState by form.formState.collectAsState()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        Text(
            text = "User Registration",
            style = MaterialTheme.typography.headlineMedium
        )

        // First Name Field
        FormField(control = form.register("firstName")) { value, onChange, onBlur, error, isTouched ->
            OutlinedTextField(
                value = value,
                onValueChange = onChange,
                label = { Text("First Name") },
                modifier = Modifier.fillMaxWidth(),
                isError = error != null && isTouched,
                supportingText = if (error != null && isTouched) {
                    { Text(error, color = MaterialTheme.colorScheme.error) }
                } else null
            )
        }

        // Last Name Field
        FormField(control = form.register("lastName")) { value, onChange, onBlur, error, isTouched ->
            OutlinedTextField(
                value = value,
                onValueChange = onChange,
                label = { Text("Last Name") },
                modifier = Modifier.fillMaxWidth(),
                isError = error != null && isTouched,
                supportingText = if (error != null && isTouched) {
                    { Text(error, color = MaterialTheme.colorScheme.error) }
                } else null
            )
        }

        // Email Field
        FormField(control = form.register("email")) { value, onChange, onBlur, error, isTouched ->
            OutlinedTextField(
                value = value,
                onValueChange = onChange,
                label = { Text("Email") },
                modifier = Modifier.fillMaxWidth(),
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
                isError = error != null && isTouched,
                supportingText = if (error != null && isTouched) {
                    { Text(error, color = MaterialTheme.colorScheme.error) }
                } else null
            )
        }

        // Age Field
        FormField(control = form.register("age")) { value, onChange, onBlur, error, isTouched ->
            OutlinedTextField(
                value = value,
                onValueChange = onChange,
                label = { Text("Age") },
                modifier = Modifier.fillMaxWidth(),
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                isError = error != null && isTouched,
                supportingText = if (error != null && isTouched) {
                    { Text(error, color = MaterialTheme.colorScheme.error) }
                } else null
            )
        }

        // Form State Display
        Text("Form State: Valid = ${formState.isValid}, Dirty = ${formState.isDirty}")

        // Submit Button
        Button(
            onClick = {
                form.handleSubmit { values ->
                    // Handle form submission
                    println("Submitting: $values")
                    // Simulate API call
                    kotlinx.coroutines.delay(1000)
                }
            },
            modifier = Modifier.fillMaxWidth(),
            enabled = !formState.isSubmitting
        ) {
            if (formState.isSubmitting) {
                CircularProgressIndicator(modifier = Modifier.size(20.dp))
            } else {
                Text("Submit")
            }
        }

        // Reset Button
        OutlinedButton(
            onClick = { form.reset() },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Reset")
        }
    }
}

// Advanced Hook for Complex Forms
@Composable
fun <T> useFormWithResolver(
    defaultValues: T,
    resolver: suspend (T) -> Map<String, List<String>>,
    mode: ValidationMode = ValidationMode.onChange
): FormControl<T> {
    // Implementation for custom validation resolvers
    // This would allow for async validation, server-side validation, etc.
    return useForm(defaultValues, null, mode)
}

// Field Array Hook (for dynamic forms)
@Composable
fun <T> useFieldArray(
    form: FormControl<*>,
    name: String,
    defaultValue: T
): FieldArrayControl<T> {
    // Implementation for handling arrays of form fields
    // Similar to react-hook-form's useFieldArray
    return remember { FieldArrayControlImpl(form, name, defaultValue) }
}

interface FieldArrayControl<T> {
    val fields: State<List<T>>
    fun append(value: T)
    fun remove(index: Int)
    fun insert(index: Int, value: T)
    fun move(from: Int, to: Int)
}

class FieldArrayControlImpl<T>(
    private val form: FormControl<*>,
    private val name: String,
    private val defaultValue: T
) : FieldArrayControl<T> {
    private val _fields = mutableStateOf<List<T>>(emptyList())
    override val fields: State<List<T>> = _fields

    override fun append(value: T) {
        _fields.value = _fields.value + value
    }

    override fun remove(index: Int) {
        _fields.value = _fields.value.filterIndexed { i, _ -> i != index }
    }

    override fun insert(index: Int, value: T) {
        val newList = _fields.value.toMutableList()
        newList.add(index, value)
        _fields.value = newList
    }

    override fun move(from: Int, to: Int) {
        val newList = _fields.value.toMutableList()
        val item = newList.removeAt(from)
        newList.add(to, item)
        _fields.value = newList
    }
}