Я смотрел это руководство и мне было интересно, есть ли способ отправить параметры из приложения на экран Voyager, и если да, то как? Или как-то совместить эту ImagePicker
реализацию с нижней навигацией?
Я пробовал много вещей, но на самом деле ничего не помогало: либо пустой экран, либо вылет с сообщением
java.lang.RuntimeException: Parcelable обнаружил исключение IOException, записывающее сериализуемый объект
Код:
actual class ImagePickerFactory {
@Composable
actual fun createPicker(): ImagePicker {
val activity = LocalContext.current as ComponentActivity
return remember(activity) { ImagePicker(activity) }
}
}
actual class ImagePicker(private val activity: ComponentActivity) {
private lateinit var getContent: ActivityResultLauncher<String>
@Composable
actual fun RegisterPicker(onImagePicked: (ByteArray) -> Unit) {
getContent =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
uri?.let {
activity.contentResolver.openInputStream(uri)?.use {
onImagePicked(it.readBytes())
}
}
}
}
actual fun ShowImagePicker() {
getContent.launch("image/*")
}
}
Это мои классы выбора изображений на стороне Android. Общий код выглядит следующим образом:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App(ImagePickerFactory().createPicker())
}
}
}
@Composable
@Preview
fun App(imagePicker: ImagePicker) {
initKoin()
MaterialTheme {
TabNavigator(
tab = HomeTab
) {
Scaffold(
modifier = Modifier.fillMaxSize(),
bottomBar = {
BottomNavigation(
backgroundColor = Color(0xFF3F54BE)
) {
TabNavigationItem(HomeTab)
TabNavigationItem(SearchTab)
TabNavigationItem(ProfileTab(imagePicker))
}
},
content = { CurrentTab() }
)
}
}
}
@Composable
private fun RowScope.TabNavigationItem(tab: Tab) {
val tabNavigator: TabNavigator = LocalTabNavigator.current
BottomNavigationItem(
modifier = Modifier.padding(top = 24.dp),
selected = tabNavigator.current == tab,
onClick = { tabNavigator.current = tab },
icon = {
tab.options.icon?.let { icon ->
Icon(
painter = icon,
contentDescription = null,
tint = if (tabNavigator.current == tab) Color.White else Color.Gray
)
}
},
label = {
Text(text = tab.options.title)
}
)
}
data class ProfileTab(private val imagePicker: ImagePicker) : Tab {
@Composable
override fun Content() {
Navigator(screen = ProfileScreen(imagePicker)) { navigator ->
SlideTransition(navigator = navigator)
}
}
override val options: TabOptions
@Composable
get() {
val index: UShort = 2u
return TabOptions(
icon = rememberVectorPainter(Icons.Default.Person),
index = index,
title = ""
)
}
}
class ProfileScreen(private val imagePicker: ImagePicker) : Screen {
@Composable
override fun Content() {
val viewModel = getScreenModel<ProfileViewModel>()
imagePicker.RegisterPicker { imageBytes -> viewModel.saveUserImage(imageBytes) }
ProfileScreenRoot(viewModel) { imagePicker.ShowImagePicker() }
}
}
@Composable
fun ProfileScreenRoot(viewModel: ProfileViewModel, onImagePicked: () -> Unit) {
//code unrelated to this issue
}
Я предполагаю, что это только на Android, исключением является исключение Java, похоже, что это сбой сериализации, который возникает, когда экраны не определены должным образом. В Voyager встроено восстановление состояния с использованием Java Serializable. Возможно, один из параметров вашего экрана не соответствует Java Serializable, и это является причиной сбоя.
Ознакомьтесь с документацией о том, как правильно определить экраны, соответствующие ретарации/сериализации состояния https://voyager.adriel.cafe/state-restoration/
В конечном итоге я «исправил» это, заменив ImagePicker этой классной библиотекой, которая используется как для Android, так и для iOS, под названием peekaboo. Это довольно просто и аккуратно, я рекомендую использовать его вместо реализации средства выбора изображений отдельно на обеих платформах, потому что это намного проще, и вы не столкнетесь с такими проблемами, как необходимость передачи контекста на стороне Android для создания фабрики выбора изображений, передачи его в экраны из приложения и т. д.
Спасибо за ответ! Я думаю, это потому, что я пытался передать ImagePicker в качестве аргумента ProfileTab, и мне нужно было это сделать, потому что ImagePicker нужен контекст на Android, чтобы он мог работать. Я опубликую код здесь, в моем комментарии выше. Если у вас есть идеи, как найти обходной путь для его прохождения, поделитесь своими мыслями!