Jetpack Compose中PullToRefreshBox返回或滑动后消失问题求助
问题描述
- 返回页面后异常:离开当前页面再返回时,若刷新任务正在进行,滚动或点击屏幕会导致刷新指示器立即消失;
- 滑动异常:上下滑动屏幕时,刷新指示器会意外消失。
问题演示:
相关代码
@AndroidEntryPoint class TestActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyApp() } } } @Composable fun MyApp() { // 定义路由及对应图标 val topLevelRoutes = listOf( TopLevelRoute("Profile", "profile", Icons.Filled.AccountCircle), TopLevelRoute("Friends", "friends", Icons.Filled.People) ) // 创建NavController val navController = rememberNavController() Scaffold( bottomBar = { BottomNavigation { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination topLevelRoutes.forEach { topLevelRoute -> BottomNavigationItem( icon = { Icon( topLevelRoute.icon, contentDescription = topLevelRoute.name ) }, label = { Text(topLevelRoute.name) }, selected = currentDestination?.route == topLevelRoute.route, onClick = { navController.navigate(topLevelRoute.route) { popUpTo(navController.graph.findStartDestination().id) { saveState = true } launchSingleTop = true restoreState = true } } ) } } } ) { innerPadding -> NavHost(navController, startDestination = "profile", Modifier.padding(innerPadding)) { composable("profile") { ProfileScreen() } composable("friends") { FriendsScreen() } } } } data class TopLevelRoute(val name: String, val route: String, val icon: ImageVector) @OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable fun ProfileScreen() { var isRefreshing by rememberSaveable { mutableStateOf(false) } // 刷新开始时启动协程 if (isRefreshing) { LaunchedEffect(Unit) { delay(10000) // 模拟网络请求 isRefreshing = false // 重置刷新状态 } } // 个人资料页面内容 Surface { PullToRefreshBox( onRefresh = { isRefreshing = true }, isRefreshing = isRefreshing, modifier = Modifier .fillMaxSize() ) { Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { // 主内容区 Text("Profile Screen", style = MaterialTheme.typography.h4) } } } } @Composable fun FriendsScreen() { // 好友页面内容 Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("Friends Screen") } }
解决方案
问题根源
- 原代码中只要
isRefreshing为true就会创建LaunchedEffect(Unit),页面重建(比如返回页面)时会重复启动协程,导致isRefreshing被提前置为false; Column + verticalScroll的滚动手势与PullToRefreshBox的下拉刷新手势存在冲突,滑动时会干扰刷新指示器的状态维持。
修复步骤
1. 正确管理刷新协程
将LaunchedEffect的触发条件改为isRefreshing,确保每次仅启动一个协程执行刷新逻辑,避免重复触发:
LaunchedEffect(isRefreshing) { if (isRefreshing) { delay(10000) // 模拟网络请求 isRefreshing = false } }
替换原代码中if (isRefreshing) { LaunchedEffect(Unit) { ... } }的结构。
2. 解决手势冲突
使用LazyColumn替代Column + verticalScroll,PullToRefreshBox与LazyColumn的手势兼容性更好,能避免滑动时的状态干扰:
PullToRefreshBox( onRefresh = { isRefreshing = true }, isRefreshing = isRefreshing, modifier = Modifier.fillMaxSize() ) { LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { item { Text("Profile Screen", style = MaterialTheme.typography.h4) } // 可添加更多列表项 } }
完整修复后的ProfileScreen代码
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable fun ProfileScreen() { var isRefreshing by rememberSaveable { mutableStateOf(false) } LaunchedEffect(isRefreshing) { if (isRefreshing) { delay(10000) // 模拟网络请求 isRefreshing = false } } Surface { PullToRefreshBox( onRefresh = { isRefreshing = true }, isRefreshing = isRefreshing, modifier = Modifier.fillMaxSize() ) { LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { item { Text("Profile Screen", style = MaterialTheme.typography.h4) } } } } }
内容的提问来源于stack exchange,提问作者Santhosh Kumar
相关产品推荐
相关产品推荐

