ตั้งแต่ Navigation 3 1.2.0 เป็นต้นไป คุณจะแสดงผลลัพธ์
จากปลายทางได้โดยใช้ ResultEventBus
API
ResultEventBus มีรูปแบบการสื่อสาร 2 รูปแบบ ได้แก่
- ผลลัพธ์ตามเหตุการณ์: สำหรับเหตุการณ์แบบครั้งเดียวที่เกิดขึ้นชั่วคราว (เช่น การแสดง
แถบแสดงข้อความยืนยันหรือการเรียกใช้ผลข้างเคียง) ให้ใช้
ResultEffect - ผลลัพธ์ตามสถานะ: สำหรับการสังเกตผลลัพธ์ล่าสุดขณะเขียน
Stateโดยใช้conflateAsState
ตั้งค่าบัสเหตุการณ์ผลลัพธ์
หากต้องการให้ ResultEventBus พร้อมใช้งานในปลายทางที่ประกอบได้ ให้เพิ่ม
rememberResultEventBusNavEntryDecorator ลงในรายการ
ของตัวตกแต่งที่ส่งไปยัง NavDisplay ซึ่งจะช่วยให้เนื้อหาของแต่ละ
จุดหมายมีLocalResultEventBus
องค์ประกอบในพื้นที่
NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberResultEventBusNavEntryDecorator() ) )
คีย์ผลลัพธ์
ResultEventBus ระบุและกำหนดเส้นทางผลลัพธ์แต่ละรายการโดยใช้คีย์ ผู้ส่งและผู้รับจะจับคู่ผลลัพธ์โดยใช้คีย์เดียวกัน
คุณระบุคีย์ผลลัพธ์ได้ 2 วิธีดังนี้
- คีย์ที่ชัดเจน: คุณระบุคีย์ที่ชัดเจนได้ (เช่น
resultKey = "pickup_address") ใช้คีย์ที่ชัดเจนเมื่อแสดงผลประเภททั่วไป (เช่นString,Booleanหรือ Primitive) หรือเมื่อปลายทางหลายแห่งแสดงผลอินสแตนซ์ที่แตกต่างกันของข้อมูลประเภทเดียวกัน - คีย์ที่ได้จากประเภท: เมื่อคุณไม่ได้ระบุคีย์อย่างชัดเจน
ResultEventBusจะสร้างคีย์โดยอัตโนมัติโดยใช้การแสดงtoStringKClassของประเภทผลลัพธ์ (เช่นContact::class.toString()) ใช้คีย์ที่ได้จากประเภทสำหรับประเภทข้อมูลที่แตกต่างกันและเฉพาะเจาะจงโดเมน
แสดงผลลัพธ์จากปลายทาง
หากต้องการให้ Composable ของหน้าจอใช้ซ้ำและทดสอบได้ อย่าเข้าถึง
LocalResultEventBus โดยตรงภายใน UI ของหน้าจอ แต่ให้แสดงผลลัพธ์ของฟังก์ชันเรียกกลับ
แลมบ์ดาจากหน้าจอแทน ใน entryProvider ให้จัดการการเรียกกลับโดย
ส่งผลลัพธ์โดยใช้
LocalResultEventBus.current และย้อนกลับ
คุณส่งผลลัพธ์ได้โดยใช้คีย์ผลลัพธ์ที่ชัดเจน ดังนี้
import androidx.compose.runtime.Composable import androidx.navigation3.runtime.result.LocalResultEventBus entry<AddressPickerRoute> { val resultBus = LocalResultEventBus.current AddressPickerScreen( onAddressSelected = { selectedAddress: Address -> resultBus.sendResult( resultKey = "pickup_address", result = selectedAddress ) navigator.goBack() } ) }
นอกจากนี้ คุณยังส่งผลลัพธ์โดยใช้คีย์ที่ได้จากประเภทได้ด้วย โดยทำดังนี้
import androidx.compose.runtime.Composable import androidx.navigation3.runtime.result.LocalResultEventBus entry<ContactPickerRoute> { val resultBus = LocalResultEventBus.current ContactPickerScreen( onContactSelected = { selectedContact: Contact -> resultBus.sendResult(result = selectedContact) navigator.goBack() } ) }
รับผลลัพธ์
ปลายทางสามารถใช้ผลลัพธ์ได้โดยใช้เอฟเฟกต์ตามเหตุการณ์หรือ Observable ตามสถานะ
| API | ลักษณะการทำงาน | กรณีการใช้งานที่แนะนำ |
|---|---|---|
ResultEffect |
อยู่ในคิว: ประมวลผลผลลัพธ์ทั้งหมดที่ปล่อยออกมาสำหรับคีย์ตามลำดับ | เหตุการณ์แบบครั้งเดียวและผลข้างเคียง (เช่น การแสดงแถบแสดงข้อความหรือการส่งต่อข้อมูลไปยัง ViewModel) |
conflateAsState |
รวม: ทิ้งผลลัพธ์ระดับกลางและเก็บเฉพาะผลลัพธ์ล่าสุดไว้เป็น Compose State |
ตัวแก้ไขสถานะ UI ที่มีน้ำหนักเบา (เช่น แท็กตัวกรองที่ใช้งานอยู่หรือการลบล้างการเลือก) |
จัดการกิจกรรมแบบครั้งเดียวด้วย ResultEffect
ใช้ ResultEffect เมื่อจัดการเหตุการณ์แบบครั้งเดียว เช่น
การทริกเกอร์ Analytics, การแสดงแถบแสดงข้อความ หรือการส่งต่อผลลัพธ์ไปยัง
ViewModel
ResultEffect จะดูแลคิวสำหรับผลลัพธ์ที่เข้ามา หากมีการส่งผลลัพธ์หลายรายการสำหรับคีย์หนึ่งๆ ResultEffect จะประมวลผลผลลัพธ์แต่ละรายการตามลำดับที่ส่ง นอกจากนี้ ResultEffect ยังทำงานในขอบเขตของโครูทีน ซึ่งช่วยให้คุณ
เรียกฟังก์ชันที่ระงับได้ภายในเนื้อหาของเอฟเฟกต์โดยตรง
คุณฟังผลลัพธ์ที่เชื่อมโยงกับคีย์ผลลัพธ์ที่ชัดเจนได้โดยทำดังนี้
import androidx.compose.runtime.Composable import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation3.runtime.result.ResultEffect @Composable fun RideSummaryScreen( onOpenAddressPicker: (key: String) -> Unit, viewModel: RideSummaryViewModel = viewModel() ) { ResultEffect<Address>(resultKey = "pickup_address") { address -> viewModel.onPickupAddressSelected(address) } ResultEffect<Address>(resultKey = "destination_address") { address -> viewModel.onDestinationAddressSelected(address) } RideSummaryContent( pickupAddress = viewModel.pickupAddress, destinationAddress = viewModel.destinationAddress, onPickPickup = { onOpenAddressPicker("pickup_address") }, onPickDestination = { onOpenAddressPicker("destination_address") } ) }
นอกจากนี้ คุณยังฟังผลลัพธ์โดยใช้คีย์ที่ได้จากประเภทได้ด้วย โดยทำดังนี้
import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation3.runtime.result.ResultEffect @Composable fun ComposeMessageScreen( onPickContact: () -> Unit, snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, viewModel: ComposeMessageViewModel = viewModel() ) { ResultEffect<Contact> { contact -> // Suspending calls are supported directly in the effect body snackbarHostState.showSnackbar("Selected ${contact.name}") viewModel.onRecipientSelected(contact) } ComposeMessageContent( recipient = viewModel.recipient, onPickContact = onPickContact ) }
เมื่อนำทางระหว่างจุดหมาย ResultEffect จะดำเนินการตามลำดับวงจรต่อไปนี้
- ผู้ส่งส่ง: ปลายทางของผู้ส่งจะส่งผลลัพธ์โดยใช้
resultBus.sendResult(resultKey = "pickup_address", address)และนำ ออกจาก Back Stack - ผู้รับเข้าสู่การเรียบเรียง: ปลายทางของผู้รับจะกลายเป็นหน้าจอที่ใช้งานอยู่ และ
ResultEffectจะเริ่มฟังผลลัพธ์ - ผู้รับประมวลผลผลลัพธ์:
ResultEffectรับและดำเนินการกับเนื้อหาเอฟเฟกต์ สำหรับแต่ละผลลัพธ์ที่ส่งสำหรับคีย์นั้น โดยประมวลผลการปล่อยทั้งหมดตาม ลำดับที่ส่ง - Receiver leaves composition: เมื่อมีการป๊อปปลายทางของ Receiver ออกจาก Back Stack
ResultEffectจะออกจาก Composition และหยุดฟังผลลัพธ์
สังเกตผลลัพธ์ล่าสุดในสถานะที่มี conflateAsState
เมื่อคุณต้องการค่าผลลัพธ์ล่าสุดเท่านั้นเพื่อแก้ไขหรือกรองสถานะ UI ท้องถิ่นโดยตรง และต้องการให้ Compose จัดองค์ประกอบใหม่โดยอัตโนมัติทุกครั้งที่ผลลัพธ์อัปเดต ให้เรียกใช้ conflateAsState ใน ResultEventBus
คุณสามารถดูผลลัพธ์ที่เชื่อมโยงกับคีย์ผลลัพธ์ที่ชัดเจนได้โดยทำดังนี้
import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color import androidx.navigation3.runtime.result.LocalResultEventBus @Composable fun ThemePreviewScreen( onOpenColorPicker: (key: String) -> Unit ) { val resultBus = LocalResultEventBus.current val primaryColor by resultBus.conflateAsState<Color>( resultKey = "primary_color", defaultValue = MaterialTheme.colorScheme.primary ) val accentColor by resultBus.conflateAsState<Color>( resultKey = "accent_color", defaultValue = MaterialTheme.colorScheme.tertiary ) ThemePreviewContent( primaryColor = primaryColor, accentColor = accentColor, onPickPrimary = { onOpenColorPicker("primary_color") }, onPickAccent = { onOpenColorPicker("accent_color") } ) }
นอกจากนี้ คุณยังดูผลลัพธ์ได้โดยใช้คีย์ที่ได้จากประเภท ดังนี้
import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.navigation3.runtime.result.LocalResultEventBus @Composable fun FilterableProductListScreen( initialFilter: ProductFilter = ProductFilter.All, onOpenFilterPicker: () -> Unit ) { val resultBus = LocalResultEventBus.current // Observe latest filter result as Compose State, starting with initialFilter val activeFilter by resultBus.conflateAsState<ProductFilter>( defaultValue = initialFilter ) ProductListContent( activeFilter = activeFilter, onOpenFilterPicker = onOpenFilterPicker ) }
Hoist ResultEventBus
โดยค่าเริ่มต้น rememberResultEventBusNavEntryDecorator จะสร้างและจดจำResultEventBusของตัวเองภายในโดยใช้ rememberResultEventBus
คุณสร้างและยกเลิกการฝัง ResultEventBus ได้อย่างชัดเจนเมื่อต้องการทำสิ่งต่อไปนี้
- ส่งอินสแตนซ์
ResultEventBusไปยังคอมโพเนนต์ที่ไม่สามารถเขียนร่วมกันได้ หรือกราฟการแทรกการอ้างอิงโดยตรง - ส่งหรือสังเกตผลลัพธ์จากการจัดโครงสร้างแอปในระดับบนสุด (เช่น แถบแอป หรือลิ้นชักการนำทาง) นอกลำดับชั้นปลายทาง
หากต้องการยก ResultEventBus ให้สร้างโดยใช้ rememberResultEventBus แล้วส่ง
ไปยัง rememberResultEventBusNavEntryDecorator(resultEventBus) ดังนี้
import androidx.compose.runtime.Composable import androidx.navigation3.runtime.result.rememberResultEventBus import androidx.navigation3.runtime.result.rememberResultEventBusNavEntryDecorator import androidx.navigation3.ui.NavDisplay // Hoist the ResultEventBus at the top level val resultEventBus = rememberResultEventBus() // Pass the hoisted bus to the decorator val resultEventBusNavEntryDecorator = rememberResultEventBusNavEntryDecorator<NavKey>( resultEventBus = resultEventBus ) NavDisplay( /* ... */ entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), resultEventBusNavEntryDecorator ) )
จัดการและล้างผลลัพธ์
เมื่อปลายทางใช้ผลลัพธ์แบบครั้งเดียวแล้ว ให้ล้างผลลัพธ์ออกจาก Event Bus โดยใช้
removeResult ซึ่งจะป้องกันไม่ให้บัสส่งเหตุการณ์ที่ผ่านมาซ้ำไปยังผู้สังเกตการณ์ใหม่เมื่อปลายทางกลับมาอยู่ในองค์ประกอบอีกครั้ง
import androidx.compose.runtime.Composable import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation3.runtime.result.LocalResultEventBus import androidx.navigation3.runtime.result.ResultEffect @Composable fun NotificationSettingsScreen( viewModel: NotificationViewModel = viewModel() ) { val resultBus = LocalResultEventBus.current ResultEffect<ConfirmationResult>(resultKey = "confirm_permission") { confirmation -> viewModel.onPermissionConfirmed(confirmation) // Clear the result after consumption to prevent re-delivery resultBus.removeResult(resultKey = "confirm_permission") } }
คุณล้างผลลัพธ์ได้โดยใช้คีย์ที่ชัดเจน (resultBus.removeResult(resultKey)) หรือ
คีย์ที่ได้จากประเภท (resultBus.removeResult<T>()) ดูรายละเอียดเกี่ยวกับ
การจับคู่คีย์ได้ที่คีย์ผลลัพธ์
สูตรอาหาร
ดูตัวอย่างโค้ดที่สมบูรณ์และเรียกใช้ได้ซึ่งแสดงกลยุทธ์การส่งผลลัพธ์ต่างๆ ได้ที่สูตรต่อไปนี้