Giter VIP home page Giter VIP logo

composehooks's Introduction

ComposeHooks

English | 简体中文

License Version maven-central latest releast stars Average time to resolve an issue Percentage of issues still open

About

The idea for the project comes from alibaba/hooks, which is a very easy-to-use collection of React Hooks.

It encapsulates most common operations as custom hooks, and useRequest is the top priority. It is designed to be very lightweight, highly configurable, and easy to use.

Therefore, based on this design idea, Hooks that can be used in the Compose project were created using similar API names.

The hooks that have been implemented so far are as follows:

Note: All use functions also have the signature of remember. If you prefer Compose’s naming method, just use rememberXxx!

hook name effect
useRequest Manage network requests and implement: manual and automatic triggering; life cycle callbacks; refresh; mutate changes; cancel requests; polling; Ready; dependency refresh; debounce, throttle; error retry;
useAsync A hook that encapsulates rememberCoroutineScope to make it easier to use coroutines
useBoolean Hook to manage boolean state.
useBackToFrontEffect & useFrontToBackEffect Execute effect when app goes to the background or come back to the foreground
useBatteryInfo A hook that can get the battery level and if is charging.
useBuildInfo A hook that can get the brand, model, and version of android.
useClipboard Easy to use Clipboard
useContext just like react
useCreation useCreation is the replacement for useRef.
useDebounce A hook that deal with the debounced value.
useDebounceFn A hook that deal with the debounced function.
useDisableScreenshot A hook used to handle the prohibition of screenshots on privacy pages.
useEffect just like react
useEvent Implement lightweight cross-component communication using the subscribe-publish pattern
useFlashlight A Hook for convenient use of flashlight.
useForm A Hook that can easier control headless component Form
useGetState A Hooks that handle state using destructuring declaration syntax.
useInterval A hook that handles the setInterval timer function.
useKeyboard A Hook that controls the display and hiding of the soft keyboard.
useLatest A Hook that returns the latest value, effectively avoiding the closure problem.
useMount A hook that executes a function after the component is mounted.
useNow A hook that return now date, default format:yyyy-MM-dd HH:mm:ss
useNetwork A hook for obtaining network connection status and type.
usePersistent A lightweight persistence hook, you need to implement the persistence method yourself (memory persistence is used by default)
usePrevious A Hook to return the previous state.
useReducer just like react
useRef just like react
useScreenInfo A hook that obtains information about the screen width, height, horizontal and vertical orientation.
useSelector/useDispatch easier to management global state,just like use redux-react
useState just like react
useThrottle A hook that deal with the throttled value.
useThrottleFn A hook that deal with the throttled function.
useToggle A hook that toggle states.
useTimeout A hook that handles the setTimeout timer function.
useTimestamp A hook that return now timestamp as a reactive state.
useUndo A Hook for handling undo and redo.
useUnmount A hook that executes the function right before the component is unmounted.
useUpdate A hook that returns a function which can be used to force the component to re-render.
useUpdateEffect A hook alike useEffect but skips running the effect for the first time.
useVibrate A hook that make using vibration feedback easy

Add to dependencies

implementation 'xyz.junerver.compose:hooks:<latest_release>'
implementation("xyz.junerver.compose:hooks:<latest_release>")

Quick Setup

  1. Use useState to quickly create controlled components

    val (name, setName) = useState("")
    OutlinedTextField(
        value = name,
        onValueChange = setName,
        label = { Text("Input Name") }
    )
  2. Use useEffect to perform component LaunchedEffects

  3. Use useRef to create object references that are not affected by component recompose

    val countRef = useRef(0)
    Button(onClick = {
        countRef.current += 1
        println(countRef)
    }) {
        Text(text = "Ref= ${countRef.current}")
    }
  4. Use useRequest to easily manage network query state

    val (data, loading, error, run) = useRequest(
        requestFn = WebService::login.asRequestFn(), //Encapsulate the corresponding extension functions yourself,to make retrofit friendly
        optionsOf {
            manual = true
        }
    )
    if (loading) {
        Text(text = "loading ....")
    }
    if (data != null) {
        Text(text = "resp: $data")
    }
    if (error != null) {
        Text(text = "error: $error")
    }
    Button(onClick = { run(arrayOf(requestBody)) }) {
        Text(text = "Login")
    }

    useRequest organizes code through a plug-in pattern, the core code is extremely simple, and can be easily extended for more advanced features. Current features include:

    • Automatic/manual request
    • Polling
    • Debounce
    • Throttle
    • Error retry
    • Loading delay
    • SWR(stale-while-revalidate)
    • Caching

Live Templates

Copy hooks in the Live Templates directory File, paste into C:\Users\<user-name>\AppData\Roaming\Google\AndroidStudio2023.2\templates\

You can easily create code snippets of useState and useRequest through us and ur.

Open Inlay Hints for Kotlin Type

For hooks like useRequest, its return value can deconstruct many objects and functions. It is necessary to enable InlayHint:

Editor - Inlay Hints - Types - Kotlin

ProGuard

If you are using ProGuard you might need to add the following option:

-keep class xyz.junerver.composehooks.** { *; }
-keepclassmembers class xyz.junerver.composehooks.** { *; }
-dontwarn xyz.junerver.composehooks.**

Documentation

Todo:

  • KMP friendly
  • Unit Test
  • CI
  • Complete documentation

参考/Thanks

  1. alibaba/hooks
  2. pavi2410/useCompose

License

Apache License 2.0

composehooks's People

Contributors

junerver avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

composehooks's Issues

useReducer当遇到mutableStateListOf时候怎么办?

useReducer默认用mutableStateOf实现,我现在想用mutableStateListOf。

需求来自:https://zh-hans.react.dev/learn/scaling-up-with-reducer-and-context

package com.example.myapplication.task1

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.MyApplicationTheme

class TaskDemo : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    TaskApp()
                }
            }
        }
    }
}

@Composable
fun TaskList(tasks: List<Task>, onChangeTask: (Task) -> Unit, onDeleteTask: (Int) -> Unit) {
    tasks.forEach { task ->
        key(task.id) {
            TaskItem(task = task, onChange = onChangeTask, onDelete = onDeleteTask)
        }
    }
}

@Composable
fun TaskItem(task: Task, onChange: (Task) -> Unit, onDelete: (Int) -> Unit) {
    var isEditing by remember { mutableStateOf(false) }
    var text by remember { mutableStateOf(task.text) }

    Row(verticalAlignment = Alignment.CenterVertically) {
        Checkbox(
                checked = task.done,
                onCheckedChange = { checked ->
                    onChange(task.copy(done = checked))
                }
        )

        if (isEditing) {
            TextField(
                    modifier = Modifier.width(100.dp),
                    value = text,
                    onValueChange = { newText ->
                        text = newText
                        onChange(task.copy(text = newText))
                    }
            )
            Button(onClick = { isEditing = false }) {
                Text("Save")
            }
        } else {
            Text(text = task.text)
            Button(onClick = { isEditing = true }) {
                Text("Edit")
            }
        }

        Button(onClick = { onDelete(task.id) }) {
            Text("Delete")
        }
    }
}

data class Task(val id: Int, var text: String, var done: Boolean)

@Composable
fun AddTask(onAddTask: (String) -> Unit) {
    var text by remember { mutableStateOf("") }

    Row {
        TextField(
                modifier = Modifier.width(100.dp),
                value = text,
                onValueChange = { newText ->
                    text = newText
                },
                placeholder = { Text("Add task") }
        )
        Button(onClick = {
            onAddTask(text)
            text = ""
        }) {
            Text("Add")
        }
    }
}

@Composable
fun TaskApp() {
    val tasks = remember { mutableStateListOf(
            Task(id = 0, text = "Task 1", done = false),
            Task(id = 1, text = "Task 2", done = false),
            Task(id = 2, text = "Task 3", done = false)
    ) }

    fun handleAddTask(text: String) {
        tasks.add(Task(nextId++, text, false))
    }

    fun handleChangeTask(task: Task) {
        tasks.replaceAll { if (it.id == task.id) task else it }
    }

    fun handleDeleteTask(taskId: Int) {
        tasks.removeIf { it.id == taskId }
    }

    Column {
        Text(text = "Day off in Kyoto", style = MaterialTheme.typography.titleLarge)
        AddTask(onAddTask = ::handleAddTask)
        TaskList(tasks = tasks, onChangeTask = ::handleChangeTask, onDeleteTask = ::handleDeleteTask)
    }
}

var nextId = 3

目前我遇到的问题是,用useReducer

val (tasks, dispatch) = useReducer<List<Task>, TaskAction>(
            { prevState, action ->
               when (action) {
                   is TaskAction.Added -> prevState + Task(nextId++, action.text, false)
                   is TaskAction.Changed -> prevState.map { if (it.id == action.task.id) action.task else it }
                   is TaskAction.Deleted -> prevState.filter { it.id != action.taskId }
               }
            },
            initialTasks
    )


    fun handleAddTask(text: String) {
        dispatch(TaskAction.Added(text))
    }

    fun handleChangeTask(task: Task) {
        dispatch(TaskAction.Changed(task))
    }

    fun handleDeleteTask(taskId: Int) {
        dispatch(TaskAction.Deleted(taskId))
    }

val initialTasks = listOf(
        Task(id = 0, text = "Philosopher’s Path", done = true),
        Task(id = 1, text = "Visit the temple", done = false),
        Task(id = 2, text = "Drink matcha", done = false)
)

的时候不管我添加多少个task,列表里面只显示4个。。。

使用 Reducer 和 Context 拓展你的应用

详细步骤请看:https://zh-hans.react.dev/learn/scaling-up-with-reducer-and-context

TaskDemo.kt

package com.example.myapplication

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.myapplication.ui.theme.MyApplicationTheme

class TaskDemo : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    TaskApp()
                }
            }
        }
    }
}

@Composable
fun TaskApp() {
    TasksProvider {
        Column {
            Text(text = "Day off in Kyoto", style = MaterialTheme.typography.titleLarge)
            AddTask()
            TaskList()
        }
    }
}

@Composable
fun AddTask() {
    var text by remember { mutableStateOf("") }
    val dispatch = useTasksDispatch()
    Row {
        TextField(
                modifier = Modifier.width(100.dp),
                value = text,
                onValueChange = { newText ->
                    text = newText
                },
                placeholder = { Text("Add task") }
        )
        Button(onClick = {
            dispatch(TaskAction.Added(text))
            text = ""
        }) {
            Text("Add")
        }
    }
}

@Composable
fun TaskList() {
    val tasks = useTasks()
    tasks.forEach { task ->
        key(task.id) {
            TaskItem(task = task)
        }
    }
}

@Composable
fun TaskItem(task: Task) {
    var isEditing by remember { mutableStateOf(false) }
    var text by remember { mutableStateOf(task.text) }

    val dispatch = useTasksDispatch()

    Row(verticalAlignment = Alignment.CenterVertically) {
        Checkbox(
                checked = task.done,
                onCheckedChange = { checked ->
                    dispatch(TaskAction.Changed(task.copy(done = checked)))
                }
        )

        if (isEditing) {
            TextField(
                    modifier = Modifier.width(100.dp),
                    value = text,
                    onValueChange = { newText ->
                        text = newText
                        dispatch(TaskAction.Changed(task.copy(text = newText)))
                    }
            )
            Button(onClick = { isEditing = false }) {
                Text("Save")
            }
        } else {
            Text(text = task.text)
            Button(onClick = { isEditing = true }) {
                Text("Edit")
            }
        }

        Button(onClick = {
            dispatch(TaskAction.Deleted(task.id))
        }) {
            Text("Delete")
        }
    }
}

data class Task(val id: Int, val text: String, val done: Boolean)

sealed interface TaskAction {
    data class Added(val text: String) : TaskAction
    data class Changed(val task: Task) : TaskAction
    data class Deleted(val taskId: Int) : TaskAction
}


TasksContext.kt

package com.example.myapplication

import androidx.compose.runtime.Composable
import xyz.junerver.compose.hooks.Reducer
import xyz.junerver.compose.hooks.createContext
import xyz.junerver.compose.hooks.useContext
import xyz.junerver.compose.hooks.useReducer

val TasksContext = createContext(emptyList<Task>())
val TasksDispatchContext = createContext<(TaskAction) -> Unit> {}

@Composable
fun TasksProvider(content: @Composable () -> Unit) {
    val (tasks, dispatch) = useReducer(listReducer, initialTasks)
    TasksContext.Provider(value = tasks) {
        TasksDispatchContext.Provider(value = dispatch) {
            content()
        }
    }
}

val listReducer: Reducer<List<Task>, TaskAction> =
        { prevState, action ->
            when (action) {
                is TaskAction.Added -> {
                    prevState + Task(nextId++, action.text, false)
                }
                is TaskAction.Changed -> {
                    prevState.map { if (it.id == action.task.id) action.task else it }
                }
                is TaskAction.Deleted -> {
                    prevState.filter { it.id != action.taskId }
                }
            }
        }

var nextId = 3
val initialTasks = listOf(
        Task(id = 0, text = "Philosopher’s Path", done = true),
        Task(id = 1, text = "Visit the temple", done = false),
        Task(id = 2, text = "Drink matcha", done = false)
)

@Composable
fun useTasks(): List<Task> {
    return useContext(TasksContext)
}

@Composable
fun useTasksDispatch(): (TaskAction) -> Unit {
    return useContext(TasksDispatchContext)
}

初学compose感谢作者提供丰富的脚手架!

请问作者目前这样结合使用 reducer 和 context还有什么问题或优化的地方吗?

当alias没有添加的时候,报错日志无法看出是没有添加对应的alias,建议优化错误日志。提示:对应的alias未添加。

Discussed in #9

Originally posted by danatechgithub4 May 9, 2024

java.lang.NullPointerException
                                                                                                    	at com.example.myapplication.UseReduxTestKt.UseReduxFetch(UseReduxTest.kt:591)
                                                                                                    	at com.example.myapplication.ComposableSingletons$UseReduxTestKt$lambda-1$1.invoke(UseReduxTest.kt:60)
                                                                                                    	at com.example.myapplication.ComposableSingletons$UseReduxTestKt$lambda-1$1.invoke(UseReduxTest.kt:56)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                    	at xyz.junerver.compose.hooks.useredux.ReduxProviderKt$ReduxProvider$2.invoke(ReduxProvider.kt:47)
                                                                                                    	at xyz.junerver.compose.hooks.useredux.ReduxProviderKt$ReduxProvider$2.invoke(ReduxProvider.kt:46)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                    	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:248)
                                                                                                    	at xyz.junerver.compose.hooks.UseContextKt$createContext$1.Provider(useContext.kt:34)
                                                                                                    	at xyz.junerver.compose.hooks.useredux.ReduxProviderKt.ReduxProvider(ReduxProvider.kt:46)
                                                                                                    	at com.example.myapplication.UseReduxTestKt.UseReduxTest(UseReduxTest.kt:56)
                                                                                                    	at com.example.myapplication.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:50)
                                                                                                    	at com.example.myapplication.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:34)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                                                                                                    	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)

错误使用 !! 非空断言,导致在map[alias] 为空时不能正确触发抛出 registerErr

升级2.0.0版本后`useEvent`相关hook抛出转型异常

复现过程:

运行项目,进入useEvent ,点击 refresh 按钮后 crash,异常如下:

FATAL EXCEPTION: main
Process: xyz.junerver.composehooks, PID: 17180
java.lang.ClassCastException: java.lang.Object[] cannot be cast to kotlin.Unit
at xyz.junerver.composehooks.example.UseEventExampleKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)
at xyz.junerver.compose.hooks.TypesKt.invoke(types.kt:38)
at xyz.junerver.composehooks.example.UseEventExampleKt$Container$$inlined$useEventPublish$1.invoke(useEvent.kt:108)
at xyz.junerver.composehooks.example.UseEventExampleKt$Container$$inlined$useEventPublish$1.invoke(useEvent.kt:104)
at xyz.junerver.composehooks.example.UseEventExampleKt.Container$lambda$4$lambda$2$lambda$1(UseEventExample.kt:36)
at xyz.junerver.composehooks.example.UseEventExampleKt.$r8$lambda$A5YqchR-ul7zIwnmkLsrw1RSolc(Unknown Source:0)
at xyz.junerver.composehooks.example.UseEventExampleKt$$ExternalSyntheticLambda4.invoke(D8$$SyntheticClass:0)
at xyz.junerver.composehooks.ui.component.TButtonKt.TButton$lambda$0(TButton.kt:29)
at xyz.junerver.composehooks.ui.component.TButtonKt.$r8$lambda$gECPPYeg3VdXcnjdqpNWnBWU_hI(Unknown Source:0)
at xyz.junerver.composehooks.ui.component.TButtonKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)
at androidx.compose.foundation.ClickablePointerInputNode$pointerInput$3.invoke-k-4lQ0M(Clickable.kt:987)
at androidx.compose.foundation.ClickablePointerInputNode$pointerInput$3.invoke(Clickable.kt:981)
at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:179)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:168)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497)
at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368)
at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:665)
at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.dispatchPointerEvent(SuspendingPointerInputFilter.kt:544)
at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:566)
at androidx.compose.foundation.AbstractClickablePointerInputNode.onPointerEvent-H0pRuoY(Clickable.kt:947)
at androidx.compose.foundation.AbstractClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:795)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:317)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:303)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:303)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:303)
at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:185)
at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:104)
at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:113)
at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1576)
at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1527)
at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:1466)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3249)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2938)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3249)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2938)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3249)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2938)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3249)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2938)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:765)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:2013)
at android.app.Activity.dispatchTouchEvent(Activity.java:4180)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:697)
at android.view.View.dispatchPointerEvent(View.java:13967)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:6489)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:6284)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5673)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5726)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5692)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5850)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5700)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5907)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5673)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5726)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5692)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5700)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5673)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:8857)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:8777)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:8730)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:9117)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:239)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:363)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:8673)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@db9cee5, androidx.compose.runtime.BroadcastFrameClock@f450dba, StandaloneCoroutine{Cancelling}@271b46b, AndroidUiDispatcher@5249bc8]

该问题在 kotlin 2.0 之前不发生,推测为升级2.0导致

useInterval的ready使用教程?

Discussed in #15

Originally posted by danatechgithub4 May 16, 2024
感觉哪里不对劲。。。useInterval被我用的稀烂。。。

package com.example.myapplication

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import xyz.junerver.compose.hooks.optionsOf
import xyz.junerver.compose.hooks.useBoolean
import xyz.junerver.compose.hooks.useEffect
import xyz.junerver.compose.hooks.useInterval
import xyz.junerver.compose.hooks.useLatestState
import xyz.junerver.compose.hooks.useState
import kotlin.time.Duration.Companion.seconds

class MainActivity8 : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Surface(
                    modifier = Modifier
                            .fillMaxSize()
                            .padding(16.dp)) {
                Column {
                    val (text, setText) = remember { mutableStateOf("") }
                    CustomTextField(
                            text = text,
                            onTextChanged = { setText(it) },
                            onSendClicked = { /* Handle send button click */ }
                    )
                }
            }
        }
    }
}

@Composable
fun CustomTextField(
    text: String,
    onTextChanged: (String) -> Unit,
    onSendClicked: () -> Unit,
    placeholderText: String = "请输入手机号",
    buttonText: String = "获取验证码"
) {
    BasicTextField(
            value = text,
            onValueChange = { onTextChanged(it) },
            keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Number,
                    imeAction = ImeAction.Send
            ),
            keyboardActions = KeyboardActions(
                    onSend = { onSendClicked() }
            ),
            textStyle = TextStyle(
                    fontSize = 12.sp,
                    color = Color(0xFF222222)
            ),
            decorationBox = { innerTextField ->
                MyDecorationBox(
                        innerTextField = innerTextField,
                        text = text,
                        placeholderText = placeholderText,
                        buttonText = buttonText,
                        onSendClicked = onSendClicked
                )
            }
    )
}

@Composable
fun MyDecorationBox(
    innerTextField: @Composable () -> Unit,
    countDownTimer: Int = 10,
    text: String,
    placeholderText: String,
    buttonText: String,
    onSendClicked: () -> Unit
) {
    val (state, toggle, _, setTrue, setFalse) = useBoolean(false)
    val (countdown, setCountdown) = useState(countDownTimer)
    val currentCount by useLatestState(value = countdown)
    val (startCountdown, cancel, isCounting) = useInterval(
            optionsOf {
                initialDelay = 1.seconds
                period = 1.seconds
                ready = state
            }
    ) {
        setCountdown(currentCount - 1)
    }
    useEffect(currentCount) {
        if (currentCount == 0) {
            setCountdown(countDownTimer)
            setFalse()
            cancel()
        }
    }
    Row(
            modifier = Modifier
                    .width(235.dp)
                    .height(40.dp)
                    .background(color = Color(0xFFF7F7F7), shape = RoundedCornerShape(size = 4.dp)),
            verticalAlignment = Alignment.CenterVertically
    ) {
        Box(
                modifier = Modifier
                        .weight(1f)
                        .padding(start = 12.dp, end = 12.dp)
        ) {
            innerTextField()
            if (text.isEmpty()) {
                Text(
                        text = placeholderText,
                        style = TextStyle(
                                fontSize = 12.sp,
                                color = Color(0xFFBBBBBB)
                        ),
                )
            }
        }
        if (isCounting) {
            Text(
                    text = "${countdown}s",
                    modifier = Modifier
                            .width(40.dp)
                            .padding(end = 12.dp),
                    style = TextStyle(
                            fontSize = 12.sp,
                            lineHeight = 20.sp,
                            fontWeight = FontWeight(600),
                            color = Color(0xFFBBBBBB),
                            textAlign = TextAlign.End,
                    )
            )
        } else {
            Text(text = buttonText,
                    style = TextStyle(
                            fontSize = 12.sp,
                            color = Color(0xFF045FFE),
                            fontWeight = FontWeight.SemiBold
                    ),
                    modifier = Modifier
                            .clickable(onClick = {
                                if (state) {
                                    startCountdown()
                                } else {
                                    setTrue()
                                }
                                onSendClicked()
                            })
                            .clip(RoundedCornerShape(size = 4.dp))
                            .padding(12.dp, 10.dp, 12.dp, 10.dp)
            )
        }
    }
}
  1. useInterval 解构出的 run 函数被连续调用后出现预期外行为;
  2. useInterval 的返回值类型别名不正确;
  3. 使用 ready 控制与手动操作解构出函数带来的额外心智负担;

useRequest请求的参数没有类型提示

我的代码

suspend fun login(s:String): String {
  delay(2000)
  return "response: result success${s}"
}
val (data, _, _, req) = useRequest(requestFn = ::login.asSuspendNoopFn())
req("")// 报错,需要换为arrayOf
req(arrayOf(p)) // 此时p为Any

这是我的设置
image

没有类型还是我的设置有误,是否可以实现一个更基础的方法

以下是我对设想的方法的代码实现,我是个新手,我不清楚是否有性能缺陷

data class AsyncFunResult(val fn: () -> Unit, val loading: Boolean)

@SuppressLint("ComposableNaming")
@Composable
fun useAsyncFun(fn: suspend () -> Unit): AsyncFunResult {
    val (loading, setLoading) = useState(false);
    LaunchedEffect(loading) {
        if (loading) {
            fn().then {
                setLoading(false)
            }
        }
    }
    return AsyncFunResult({
        setLoading(true)
    }, loading)
}
@Composable
fun Layout(){
suspend fun login(s:String): String {
      delay(2000)
      return "response: result success${s}"
   }

  val (request, loading) = useAsyncFun {
     delay(2000);
     login("666")
  }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.