gpt4 book ai didi

Opening a composable screen from a non-composable callback(从不可合成回调打开可合成屏幕)

转载 作者:bug小助手 更新时间:2023-10-27 20:24:37 28 4
gpt4 key购买 nike



I would like to open the WebView from my callback. But I am getting the following error

我想从我的回调中打开WebView。但我收到了以下错误


@Composable invocations can only happen from the context of a @Composable function

@Composable调用只能从@Composable函数的上下文中进行


This code snippet is the issue

这个代码片段就是问题所在


onNewsLinkedClicked = { newsLink ->
WebViewScreen(webLink = newsLink)
}

I did try and declare my lambda in the NewsScreen function like this.

我确实尝试过在NewsScreen函数中声明我的lambda,如下所示。


onNewsLinkedClicked: @Composable (newsLink: String) -> Unit

onNewsLinkedClicked:@Composable(newsLink:String)-> Unit


That solved the issue below. But then I got another issue, as I launch the callback from a Button onClick event which started to give me an error.

这解决了下面的问题。但后来我遇到了另一个问题,当我从Button onClick事件启动回调时,开始给我一个错误。


Is there any way of doing this for my code below?

对于我下面的代码,有什么方法可以做到这一点吗?


@AndroidEntryPoint
class MainActivity : ComponentActivity() {

@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val newsViewModel: NewsViewModel = hiltViewModel()
val newsHeadLines = newsViewModel.newsPager.collectAsLazyPagingItems()

BeerPagingTheme {
// A surface container using the 'background' color from the theme
val scrollBehavior = enterAlwaysScrollBehavior()

Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
NewsScreen(
newsPagingData = newsHeadLines,
modifier = Modifier
.fillMaxSize()
.nestedScroll(connection = scrollBehavior.nestedScrollConnection),
topAppBarScrollBehavior = scrollBehavior,
onNewsLinkedClicked = { newsLink ->
WebViewScreen(webLink = newsLink)
}
)
}
}
}
}
}

@Composable
fun WebViewScreen(webLink: String) {
AndroidView(
factory = { context ->

WebView(context).apply {
this.layoutParams = ViewGroup.LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT)

loadUrl(webLink)
} },
update = { webView ->
webView.loadUrl(webLink)
},
modifier = Modifier.fillMaxSize())
}

更多回答

You probably want to look at a navigation solution, whether you use the official Navigation for Compose or Appyx or compose-destinations or Voyager or any of the couple of dozen other third-party navigation libraries. Alternatively, your button should mutate some state that causes you to show WebViewScreen() instead of NewsScreen().

您可能想看看导航解决方案,无论是使用Compose、Appyx、Compose-Destination、Voyager的官方导航,还是其他几十个第三方导航库中的任何一个。或者,您的按钮应该改变某种状态,使您显示WebViewScreen()而不是NewsScreen()。

@CommonsWare I am not sure what you mean by this Alternatively, your button should mutate some state that causes you to show WebViewScreen() instead of NewsScreen() Can you give an example?

@CommonsWare我不确定这是什么意思。或者,您的按钮应该改变某种状态,使您显示WebViewScreen()而不是NewsScreen()。您能举个例子吗?

优秀答案推荐

You can use navigation solution.
Alternatively, your button should mutate some state that causes you to show WebViewScreen() instead of NewsScreen() as below:

您可以使用导航解决方案。或者,你的按钮应该改变一些状态,使你显示WebViewScreen()而不是NewsScreen(),如下所示:


var clickedLink by rememberSavable { mutableStateOf("") }// state for saving clicked link
if (clickedLink.isEmpty) {
NewsScreen(
newsPagingData = newsHeadLines,
modifier = Modifier
.fillMaxSize()
.nestedScroll(connection = scrollBehavior.nestedScrollConnection),
topAppBarScrollBehavior = scrollBehavior,
onNewsLinkedClicked = { newsLink ->
clickedLink = newsLink // changing clicked link address
}
)
} else {
WebViewScreen(webLink = clickedLink)
}


For scalability, I recommend using one of the many navigation solutions available for Compose UI. That could be Navigation for Compose, if you want a purely official solution. If you want a wrapper over that, consider compose-destinations. If you want something fully independent, there are Appyx and Voyager and many others.

为了提高可伸缩性,我推荐使用Compose UI可用的众多导航解决方案中的一个。如果你想要一个纯粹的官方解决方案,那可以是合成的导航。如果您想要一个封装器,可以考虑组合目的地。如果你想要完全独立的东西,有Appyx和Voyager和许多其他的。


Tactically, you could do something like:

从战术上讲,您可以这样做:


            BeerPagingTheme {
// A surface container using the 'background' color from the theme
val scrollBehavior = enterAlwaysScrollBehavior()
var newsLink by remember { mutableStateOf<String?>(null) }

Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
if (newsLink == null) {
NewsScreen(
newsPagingData = newsHeadLines,
modifier = Modifier
.fillMaxSize()
.nestedScroll(connection = scrollBehavior.nestedScrollConnection),
topAppBarScrollBehavior = scrollBehavior,
onNewsLinkedClicked = { newsLink = it }
)
} else {
WebViewScreen(webLink = newsLink)
}
}
}

(note: I'm doing this from memory, so there may need to be some adjustments)

(注:我是凭记忆完成这项工作的,因此可能需要进行一些调整)


Compose is a declarative reactive framework. You declare "this is what I want the UI to be, based upon the current state". In this case, the current state is newsLink. You react to changes in that state. So, your onClick lambda updates the state (newsLink = it), causing Compose to re-run all of setContent() and update the UI based upon a fresh declaration.

Compose是一个声明性反应性框架。您声明“这就是我希望的UI,基于当前状态”。在本例中,当前状态为新闻链接。你会对这种状态的变化做出反应。因此,onClick lambda更新状态(新闻链接=it),导致Compose重新运行所有setContent()并基于新的声明更新UI。


更多回答

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com