ข้อควรพิจารณาอื่นๆ

แม้ว่าการย้ายข้อมูลจาก Views ไปยัง Compose จะเกี่ยวข้องกับ UI โดยเฉพาะ แต่ก็มีหลายสิ่งที่คุณต้องคำนึงถึงเพื่อทำการย้ายข้อมูลอย่างปลอดภัยและค่อยๆ เป็นค่อยไป หน้านี้มีข้อควรพิจารณาบางประการขณะย้ายข้อมูลแอปที่อิงตาม View ไปยัง Compose

การย้ายข้อมูลธีมของแอป

Material Design เป็นระบบการออกแบบที่แนะนำสำหรับการกำหนดธีมแอป Android

สำหรับแอปที่อิงตาม View จะมี Material 3 เวอร์ชันให้ใช้งาน ดังนี้

  • Material Design 1 ที่ใช้ไลบรารี AppCompat (เช่น Theme.AppCompat.*)
  • Material Design 2 โดยใช้ไลบรารี MDC-Android (เช่น Theme.MaterialComponents.*)
  • Material Design 3 โดยใช้ไลบรารี MDC-Android (เช่น Theme.Material3.*)

สำหรับแอป Compose จะมี Material 2 เวอร์ชันให้ใช้งาน ได้แก่

  • Material Design 2 โดยใช้ไลบรารี Compose Material (เช่น androidx.compose.material.MaterialTheme)
  • Material Design 3 โดยใช้ไลบรารี Compose Material 3 (เช่น androidx.compose.material3.MaterialTheme)

เราขอแนะนำให้ใช้เวอร์ชันล่าสุด (Material 3) หากระบบการออกแบบของแอป พร้อมที่จะทำเช่นนั้น มีคำแนะนำในการย้ายข้อมูลสำหรับทั้ง Views และ Compose ดังนี้

เมื่อสร้างหน้าจอใหม่ใน Compose ไม่ว่าคุณจะใช้ Material Design เวอร์ชันใดก็ตาม ให้ตรวจสอบว่าคุณได้ใช้ MaterialTheme ก่อน Composables ที่ปล่อย UI จากไลบรารี Material ของ Compose คอมโพเนนต์ Material (Button, Text ฯลฯ) ขึ้นอยู่กับMaterialTheme ที่มีอยู่ และจะไม่มีการกำหนดลักษณะการทำงานหากไม่มีคอมโพเนนต์ดังกล่าว

ตัวอย่าง Jetpack Compose ทั้งหมด ใช้ธีม Compose ที่กำหนดเองซึ่งสร้างขึ้นบน MaterialTheme

ดูข้อมูลเพิ่มเติมได้ที่ระบบการออกแบบใน Compose และการย้ายข้อมูลธีม XML ไปยัง Compose

หากคุณใช้คอมโพเนนต์การนำทางในแอป โปรดดูข้อมูลเพิ่มเติมที่การไปยังส่วนต่างๆ ด้วย Compose - การทำงานร่วมกันและย้ายข้อมูลการนำทางของ Jetpack ไปยังการนำทางด้วย Compose

ทดสอบ UI แบบผสม Compose/Views

หลังจากย้ายข้อมูลบางส่วนของแอปไปยัง Compose แล้ว การทดสอบเป็นสิ่งสำคัญเพื่อให้แน่ใจว่า คุณไม่ได้ทำให้สิ่งใดเสียหาย

เมื่อกิจกรรมหรือ Fragment ใช้ Compose คุณต้องใช้ createAndroidComposeRule แทนการใช้ ActivityScenarioRule createAndroidComposeRule ผสานรวม ActivityScenarioRule กับ ComposeTestRule ที่ช่วยให้คุณทดสอบ Compose และ โค้ด View ได้พร้อมกัน

class MyActivityTest {
    @Rule
    @JvmField
    val composeTestRule = createAndroidComposeRule<MyActivity>()

    @Test
    fun testGreeting() {
        val greeting = InstrumentationRegistry.getInstrumentation()
            .targetContext.resources.getString(R.string.greeting)

        composeTestRule.onNodeWithText(greeting).assertIsDisplayed()
    }
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบได้ที่การทดสอบเลย์เอาต์ Compose ดูการทำงานร่วมกับเฟรมเวิร์กการทดสอบ UI ได้ที่การทำงานร่วมกับ Espresso และการทำงานร่วมกับ UiAutomator

การผสานรวม Compose กับสถาปัตยกรรมแอปที่มีอยู่

สถาปัตยกรรมการไหลของข้อมูลแบบทางเดียว (UDF) และรูปแบบต่างๆ ทำงานร่วมกับ Compose ได้อย่างราบรื่น หากแอปใช้รูปแบบสถาปัตยกรรมประเภทอื่นแทน เช่น Model View Presenter (MVP) เราขอแนะนำให้คุณ ย้ายข้อมูลส่วนนั้นของ UI ไปยัง UDF ก่อนหรือขณะใช้ Compose

การใช้ ViewModel ในการเขียน

หากใช้ไลบรารี Architecture Components ViewModel คุณจะเข้าถึง ViewModel จาก Composable ใดก็ได้โดย เรียกใช้ฟังก์ชัน viewModel() ตามที่อธิบายไว้ใน Compose และไลบรารีอื่นๆ

เมื่อใช้ Compose โปรดระมัดระวังในการใช้ประเภท ViewModel เดียวกันใน Composables ที่แตกต่างกัน เนื่องจากองค์ประกอบ ViewModel จะเป็นไปตามขอบเขตวงจรของ View ขอบเขตจะเป็นกิจกรรมโฮสต์ Fragment หรือกราฟการนำทางหากใช้ ไลบรารีการนำทาง

เช่น หากโฮสต์ Composable ในกิจกรรม viewModel() always จะแสดงอินสแตนซ์เดียวกันเสมอ ซึ่งจะล้างเมื่อกิจกรรมเสร็จสิ้นเท่านั้น ในตัวอย่างต่อไปนี้ ผู้ใช้คนเดียวกัน ("user1") จะได้รับการต้อนรับ 2 ครั้งเนื่องจาก มีการนำอินสแตนซ์ GreetingViewModel เดียวกันกลับมาใช้ซ้ำใน Composable ทั้งหมดภายใต้ กิจกรรมโฮสต์ ระบบจะนำอินสแตนซ์ ViewModel แรกที่สร้างขึ้นมาใช้ซ้ำใน Composable อื่นๆ

class GreetingActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            MaterialTheme {
                Column {
                    GreetingScreen("user1")
                    GreetingScreen("user2")
                }
            }
        }
    }
}

@Composable
fun GreetingScreen(
    userId: String,
    viewModel: GreetingViewModel = viewModel(  
        factory = GreetingViewModelFactory(userId)  
    )
) {
    val messageUser by viewModel.message.observeAsState("")
    Text(messageUser)
}

class GreetingViewModel(private val userId: String) : ViewModel() {
    private val _message = MutableLiveData("Hi $userId")
    val message: LiveData<String> = _message
}

class GreetingViewModelFactory(private val userId: String) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return GreetingViewModel(userId) as T
    }
}

เนื่องจากกราฟการนำทางยังกำหนดขอบเขตองค์ประกอบ ViewModel ด้วย Composable ที่เป็นปลายทางในกราฟการนำทางจึงมีอินสแตนซ์ ViewModel ที่แตกต่างกัน ในกรณีนี้ ViewModel จะกำหนดขอบเขตตามวงจรของปลายทาง และ จะล้างเมื่อนำปลายทางออกจาก Backstack ในตัวอย่างต่อไปนี้ เมื่อผู้ใช้นําทางไปยังหน้าจอโปรไฟล์ ระบบจะสร้างอินสแตนซ์ใหม่ของ GreetingViewModel

@Composable
fun MyApp() {
    NavHost(rememberNavController(), startDestination = "profile/{userId}") {
        /* ... */
        composable("profile/{userId}") { backStackEntry ->
            GreetingScreen(backStackEntry.arguments?.getString("userId") ?: "")
        }
    }
}

แหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียว

เมื่อใช้ Compose ในส่วนหนึ่งของ UI ก็เป็นไปได้ที่ Compose และโค้ดระบบ View จะต้องแชร์ข้อมูล เราขอแนะนำให้คุณ ห่อหุ้มสถานะที่แชร์นั้นไว้ในอีกคลาสหนึ่งที่ทำตามแนวทางปฏิบัติแนะนำของ UDF ซึ่งทั้ง 2 แพลตฟอร์มใช้ เช่น ใน ViewModel ที่แสดงสตรีมของ ข้อมูลที่แชร์เพื่อส่งการอัปเดตข้อมูล

อย่างไรก็ตาม การดำเนินการดังกล่าวอาจเป็นไปไม่ได้เสมอไปหากข้อมูลที่จะแชร์มีการเปลี่ยนแปลงได้หรือเชื่อมโยงกับองค์ประกอบ UI อย่างใกล้ชิด ในกรณีดังกล่าว ระบบหนึ่งต้องเป็นแหล่งข้อมูลที่เชื่อถือได้ และระบบนั้นต้องแชร์การอัปเดตข้อมูลกับอีกระบบหนึ่ง โดยทั่วไปแล้ว แหล่งข้อมูลที่เชื่อถือได้ควรเป็นขององค์ประกอบที่อยู่ใกล้กับรูทของลำดับชั้น UI มากที่สุด

Compose เป็นแหล่งข้อมูลที่ถูกต้องเพียงแหล่งเดียว

ใช้ SideEffect ที่ประกอบได้เพื่อเผยแพร่สถานะ Compose ไปยังโค้ดที่ไม่ใช่ Compose ในกรณีนี้ เราจะเก็บ แหล่งข้อมูลที่เชื่อถือได้ไว้ใน Composable ซึ่งจะส่งการอัปเดตสถานะ

ตัวอย่างเช่น ไลบรารีการวิเคราะห์อาจช่วยให้คุณแบ่งกลุ่มประชากรผู้ใช้ได้โดยการแนบข้อมูลเมตาที่กำหนดเอง (พร็อพเพอร์ตี้ผู้ใช้ในตัวอย่างนี้) เข้ากับเหตุการณ์การวิเคราะห์ทั้งหมดที่ตามมา หากต้องการสื่อสารประเภทผู้ใช้ของ ผู้ใช้ปัจจุบันไปยังไลบรารีการวิเคราะห์ ให้ใช้ SideEffect เพื่ออัปเดตค่า

@Composable
fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics {
    val analytics: FirebaseAnalytics = remember {
        FirebaseAnalytics()
    }

    // On every successful composition, update FirebaseAnalytics with
    // the userType from the current User, ensuring that future analytics
    // events have this metadata attached
    SideEffect {
        analytics.setUserProperty("userType", user.userType)
    }
    return analytics
}

ดูข้อมูลเพิ่มเติมได้ที่ผลข้างเคียงใน Compose

ดูระบบเป็นแหล่งข้อมูลที่ถูกต้อง

หากระบบ View เป็นเจ้าของสถานะและแชร์กับ Compose เราขอแนะนำให้ คุณห่อหุ้มสถานะในออบเจ็กต์ mutableStateOf เพื่อให้ Compose ทำงานได้อย่างปลอดภัยในหลายเธรด หากใช้วิธีนี้ ฟังก์ชันที่ประกอบได้จะง่ายขึ้นเนื่องจาก ไม่มีแหล่งข้อมูลความจริงอีกต่อไป แต่ระบบ View ต้องอัปเดต สถานะที่เปลี่ยนแปลงได้และ View ที่ใช้สถานะนั้น

ในตัวอย่างต่อไปนี้ CustomViewGroup มี TextView และ ComposeView ที่มี TextField ที่ใช้ร่วมกันได้อยู่ภายใน TextView ต้องแสดงเนื้อหาที่ผู้ใช้พิมพ์ใน TextField

class CustomViewGroup @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : LinearLayout(context, attrs, defStyle) {

    // Source of truth in the View system as mutableStateOf
    // to make it thread-safe for Compose
    private var text by mutableStateOf("")

    private val textView: TextView

    init {
        orientation = VERTICAL

        textView = TextView(context)
        val composeView = ComposeView(context).apply {
            setContent {
                MaterialTheme {
                    TextField(value = text, onValueChange = { updateState(it) })
                }
            }
        }

        addView(textView)
        addView(composeView)
    }

    // Update both the source of truth and the TextView
    private fun updateState(newValue: String) {
        text = newValue
        textView.text = newValue
    }
}

การย้ายข้อมูล UI ที่แชร์

หากค่อยๆ ย้ายข้อมูลไปยัง Compose คุณอาจต้องใช้ UI ที่แชร์ ในทั้ง Compose และระบบ View เช่น หากแอปมีคอมโพเนนต์ CallToActionButton ที่กำหนดเอง คุณอาจต้องใช้ทั้งในหน้าจอที่อิงตาม Compose และ View

ใน Compose องค์ประกอบ UI ที่แชร์จะกลายเป็น Composable ที่นำกลับมาใช้ใหม่ได้ทั่วทั้งแอป ไม่ว่าองค์ประกอบนั้นจะได้รับการจัดรูปแบบโดยใช้ XML หรือเป็นมุมมองที่กำหนดเองก็ตาม เช่น คุณจะสร้าง CallToActionButtonComposable สำหรับคอมโพเนนต์ข้อความกระตุ้นให้ดำเนินการ (Call-To-Action) ที่กำหนดเองButton

หากต้องการใช้ Composable ในหน้าจอที่อิงตาม View ให้สร้าง View Wrapper ที่กำหนดเองซึ่ง ขยายจาก AbstractComposeView ใน ContentComposable ที่ลบล้าง ให้วาง Composable ที่คุณสร้างไว้ในธีม Compose ดังที่แสดงใน ตัวอย่างด้านล่าง

@Composable
fun CallToActionButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            containerColor = MaterialTheme.colorScheme.secondary
        ),
        onClick = onClick,
        modifier = modifier,
    ) {
        Text(text)
    }
}

class CallToActionViewButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {

    var text by mutableStateOf("")
    var onClick by mutableStateOf({})

    @Composable
    override fun Content() {
        YourAppTheme {
            CallToActionButton(text, onClick)
        }
    }
}

โปรดทราบว่าพารามิเตอร์ที่ประกอบได้จะกลายเป็นตัวแปรที่เปลี่ยนแปลงได้ภายในมุมมองที่กำหนดเอง ซึ่งจะทำให้มุมมอง CallToActionViewButton ที่กำหนดเองสามารถขยายและใช้งานได้ เหมือนกับมุมมองแบบดั้งเดิม ดูตัวอย่างของกรณีนี้ได้ที่การเชื่อมโยงมุมมอง ด้านล่าง

class ViewBindingActivity : ComponentActivity() {

    private lateinit var binding: ActivityExampleBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityExampleBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.callToAction.apply {
            text = getString(R.string.greeting)
            onClick = { /* Do something */ }
        }
    }
}

หากคอมโพเนนต์ที่กำหนดเองมีสถานะที่เปลี่ยนแปลงได้ โปรดดูแหล่งที่มาของความจริงของสถานะ

จัดลำดับความสำคัญของการแยกสถานะออกจากงานนำเสนอ

โดยปกติแล้ว View จะมีสถานะ View จัดการฟิลด์ที่อธิบายสิ่งที่จะแสดง รวมถึงวิธีแสดง เมื่อแปลง View เป็น Compose ให้แยกข้อมูลที่แสดงผลเพื่อให้ได้โฟลว์ข้อมูลแบบทิศทางเดียว ดังที่อธิบายเพิ่มเติมในการยกระดับสถานะ

เช่น View มีพร็อพเพอร์ตี้ visibility ที่อธิบายว่าพร็อพเพอร์ตี้นั้น มองเห็นได้ มองไม่เห็น หรือหายไป ซึ่งเป็นคุณสมบัติโดยธรรมชาติของ View แม้ว่าโค้ดส่วนอื่นๆ อาจเปลี่ยนระดับการเข้าถึงของ View แต่มีเพียง View เท่านั้นที่รู้ว่าระดับการเข้าถึงปัจจุบันของตนเองคืออะไร ตรรกะในการตรวจสอบว่า View แสดงอยู่หรือไม่นั้นอาจเกิดข้อผิดพลาดได้ และมักจะเชื่อมโยงกับ View เอง

ในทางตรงกันข้าม Compose ช่วยให้แสดง Composable ที่แตกต่างกันโดยสิ้นเชิงได้ง่ายๆ โดยใช้ตรรกะแบบมีเงื่อนไขใน Kotlin ดังนี้

@Composable
fun MyComposable(showCautionIcon: Boolean) {
    if (showCautionIcon) {
        CautionIcon(/* ... */)
    }
}

CautionIcon ไม่จำเป็นต้องทราบหรือสนใจว่าเหตุใดจึงมีการแสดงผล และไม่มีแนวคิดของ visibility: โดยจะอยู่ในองค์ประกอบหรือไม่อยู่ ก็ได้

การแยกการจัดการสถานะและตรรกะการนำเสนออย่างชัดเจนช่วยให้คุณเปลี่ยนวิธีแสดงเนื้อหาเป็น Conversion ของสถานะเป็น UI ได้อย่างอิสระมากขึ้น การยกสถานะขึ้นเมื่อจำเป็นยังช่วยให้ Composable นำกลับมาใช้ซ้ำได้มากขึ้น เนื่องจากความเป็นเจ้าของสถานะมีความยืดหยุ่นมากขึ้น

โปรโมตคอมโพเนนต์ที่แคปซูลและนำกลับมาใช้ใหม่ได้

องค์ประกอบ View มักจะมีแนวคิดเกี่ยวกับตำแหน่งขององค์ประกอบนั้นๆ เช่น ภายใน Activity, Dialog, Fragment หรือที่ใดที่หนึ่งภายในลำดับชั้น View อื่น เนื่องจากมักจะขยายจากไฟล์เลย์เอาต์แบบคงที่ โครงสร้างโดยรวมของ View จึงมักจะมีความยืดหยุ่นน้อยมาก ซึ่งจะส่งผลให้มีการเชื่อมโยงที่แน่นแฟ้นยิ่งขึ้น และทำให้View เปลี่ยนแปลงหรือนำกลับมาใช้ใหม่ได้ยากขึ้น

ตัวอย่างเช่น Viewที่กำหนดเองอาจถือว่ามีมุมมองย่อยของ ประเภทหนึ่งๆ ที่มีรหัสหนึ่งๆ และเปลี่ยนพร็อพเพอร์ตี้โดยตรงเพื่อตอบสนองต่อการดำเนินการบางอย่าง ซึ่งจะเชื่อมโยงองค์ประกอบ View เหล่านั้นเข้าด้วยกันอย่างแน่นแฟ้น กล่าวคือ View ที่กำหนดเองอาจขัดข้องหรือใช้งานไม่ได้หากไม่พบองค์ประกอบย่อย และองค์ประกอบย่อยมักจะนำกลับมาใช้ใหม่ไม่ได้หากไม่มีองค์ประกอบหลัก View ที่กำหนดเอง

ปัญหานี้จะลดลงใน Compose เมื่อใช้ Composable ที่นำกลับมาใช้ใหม่ได้ ผู้ปกครองสามารถ ระบุสถานะและฟังก์ชันเรียกกลับได้อย่างง่ายดาย คุณจึงเขียน Composable ที่นำกลับมาใช้ใหม่ได้ โดยไม่ต้องทราบตำแหน่งที่แน่นอนที่จะใช้

@Composable
fun AScreen() {
    var isEnabled by rememberSaveable { mutableStateOf(false) }

    Column {
        ImageWithEnabledOverlay(isEnabled)
        ControlPanelWithToggle(
            isEnabled = isEnabled,
            onEnabledChanged = { isEnabled = it }
        )
    }
}

ในตัวอย่างด้านบน ทั้ง 3 ส่วนจะมีการห่อหุ้มมากขึ้นและมีการเชื่อมโยงกันน้อยลง

  • ImageWithEnabledOverlay เพียงแค่ต้องรู้ว่าisEnabled สถานะปัจจุบันคืออะไร ไม่จำเป็นต้องรู้ว่าControlPanelWithToggle มีอยู่ หรือ แม้กระทั่งวิธีควบคุม

  • ControlPanelWithToggle ไม่รู้ว่ามี ImageWithEnabledOverlay อยู่ isEnabled อาจแสดงได้หลายวิธี และ ControlPanelWithToggle ไม่จำเป็นต้องเปลี่ยนแปลง

  • สำหรับองค์ประกอบระดับบนสุด ไม่ว่า ImageWithEnabledOverlay หรือ ControlPanelWithToggle จะซ้อนกันลึกแค่ไหนก็ตาม เด็กๆ อาจเคลื่อนไหวการเปลี่ยนแปลง สลับเนื้อหา หรือส่งต่อเนื้อหาให้เด็กคนอื่นๆ

รูปแบบนี้เรียกว่าการผกผันของการควบคุม ซึ่งคุณสามารถอ่านข้อมูลเพิ่มเติมได้ในCompositionLocalเอกสารประกอบ

การจัดการการเปลี่ยนแปลงขนาดหน้าจอ

การมีแหล่งข้อมูลที่แตกต่างกันสำหรับขนาดหน้าต่างที่แตกต่างกันเป็นวิธีหลักวิธีหนึ่งในการ สร้างViewเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ แม้ว่าทรัพยากรที่มีคุณสมบัติเหมาะสมจะยังคงเป็นตัวเลือก สำหรับการตัดสินใจเกี่ยวกับเลย์เอาต์ระดับหน้าจอ แต่ Compose ก็ช่วยให้การเปลี่ยน เลย์เอาต์ทั้งหมดในโค้ดด้วยตรรกะแบบมีเงื่อนไขปกติเป็นเรื่องง่ายขึ้นมาก ดูข้อมูลเพิ่มเติมได้ที่ใช้คลาสขนาดหน้าต่าง

นอกจากนี้ โปรดดูรองรับขนาดการแสดงผลต่างๆ เพื่อดูข้อมูลเกี่ยวกับเทคนิคที่ Compose มีให้ในการสร้าง UI แบบปรับเปลี่ยนได้

การเลื่อนที่ฝังไว้ด้วย View

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีเปิดใช้การทำงานร่วมกันของการเลื่อนที่ซ้อนกันระหว่าง องค์ประกอบ View ที่เลื่อนได้และ Composable ที่เลื่อนได้ ซึ่งซ้อนกันทั้ง 2 ทิศทาง ได้ที่การทำงานร่วมกันของการเลื่อนที่ซ้อนกัน

เขียนใน RecyclerView

Composable ใน RecyclerView มีประสิทธิภาพตั้งแต่ RecyclerView เวอร์ชัน 1.3.0-alpha02 ตรวจสอบว่าคุณใช้ RecyclerView เวอร์ชัน 1.3.0-alpha02 เป็นอย่างน้อยเพื่อดูสิทธิประโยชน์เหล่านั้น

WindowInsets การทำงานร่วมกันกับ Views

คุณอาจต้องลบล้างระยะขอบเริ่มต้นเมื่อหน้าจอมีทั้ง View และโค้ด Compose ในลำดับชั้นเดียวกัน ในกรณีนี้ คุณต้องระบุอย่างชัดเจนว่า เลย์เอาต์ใดควรใช้ระยะขอบ และเลย์เอาต์ใดควรละเว้น

เช่น หากเลย์เอาต์ชั้นนอกสุดเป็นเลย์เอาต์ Android View คุณควร ใช้ Inset ในระบบ View และไม่สนใจ Inset สำหรับ Compose หรือหากเลย์เอาต์ชั้นนอกสุดเป็น Composable คุณควรใช้ ระยะขอบใน Compose และเว้นที่ว่างสำหรับ Composable AndroidView ตามนั้น

โดยค่าเริ่มต้น ComposeView แต่ละรายการจะใช้ขอบทั้งหมดที่ระดับการใช้งาน WindowInsetsCompat หากต้องการเปลี่ยนลักษณะการทำงานเริ่มต้นนี้ ให้ตั้งค่า ComposeView.consumeWindowInsets เป็น false

อ่านข้อมูลเพิ่มเติมได้ในเอกสารประกอบเรื่อง WindowInsets ใน Compose