news 2026/4/3 4:16:03

Jetpack Compose 多页面架构实战:从 Splash 到底部导航,每个 Tab 拥有独立 ViewModel

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Jetpack Compose 多页面架构实战:从 Splash 到底部导航,每个 Tab 拥有独立 ViewModel

在构建现代 Android 应用时,清晰的页面结构和合理的状态管理是保证项目可维护性和可扩展性的关键。Jetpack Compose 提供了声明式 UI 的强大能力,而Navigation+ViewModel的组合,则是实现复杂多页面应用的黄金搭档。

本文将通过一个完整、可运行、生产级风格的案例,带你一步步实现:

  • 启动页(Splash)
  • 登录页(Login)
  • 主页(带底部导航:首页 / 通讯录 / 我的)
  • 每个 Tab 拥有自己独立的 ViewModel
  • 全局登录状态统一管理

并深入探讨页面拆分、导航设计、状态隔离等核心工程实践。

🎯 最终效果预览


所有页面逻辑分离,职责清晰,切换 Tab 不会丢失状态!

📁 推荐项目结构

良好的目录结构是大型项目的基石:

com.example.myapp/├── MyApp.kt// App 根组件├── navigation/NavGraph.kt //全局导航图 ├── viewmodel/AuthViewModel.kt //全局登录状态 ├── route/ModuleRoute.kt //路由常量 └── ui/├── splash/SplashScreen.kt ├── login/LoginScreen.kt └── main/├── MainScreen.kt// 底部导航容器├── home/HomeTab.kt+HomeViewModel.kt ├── contacts/ContactsTab.kt+ContactsViewModel.kt └── profile/ProfileTab.kt+ProfileViewModel.kt

🔑 核心依赖

implementation("androidx.navigation:navigation-compose:2.8.0")implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0")implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.0")implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0")

1️⃣ 全局状态:AuthViewModel

用于管理用户是否已登录,供整个 App 使用:

classAuthViewModel:ViewModel(){privateval_flowLogin=MutableStateFlow(false)valflowLogin=_flowLogin.asStateFlow()// 模拟登录成功funlogin(){_flowLogin.value=true}funloginOut(){_flowLogin.value=false}}

在 MyApp.kt 中通过 viewModel() 获取单例实例。

2️⃣ 页面专属 ViewModel:解耦业务逻辑

通讯录 ViewModel

dataclassContact(valid:Int,valname:String,valphone:String)classContactsViewModel:ViewModel(){privateval_contacts=MutableStateFlow<MutableList<Contact>>(mutableListOf())valcontacts:StateFlow<List<Contact>>=_contacts.asStateFlow()privatevar_isLoading=MutableStateFlow(true)valisLoading=_isLoading.asStateFlow()init{loadContacts()}privatefunloadContacts(){viewModelScope.launch{_isLoading.value=truedelay(1000)// 模拟网络请求_contacts.value=mutableListOf(Contact(1,"张三","138****1234"),Contact(2,"李四","139****5678"))_isLoading.value=false}}funrefresh(){loadContacts()}}

💡 优势:

  • 数据加载、错误处理、刷新逻辑全部封装在 ViewModel
  • UI 层只负责展示,完全无业务逻辑
  • 切换 Tab 时,ViewModel 实例由 Navigation 自动保存(只要 route 不变)

3️⃣ 页面 UI:自动注入 ViewModel

在 Composable 中直接使用viewModel()获取专属实例:

@ComposablefunContactsTab(contactsViewModel:ContactsViewModel=viewModel(),modifier:Modifier=Modifier,){valcontactsbycontactsViewModel.contacts.collectAsStateWithLifecycle()valisLoadingbycontactsViewModel.isLoading.collectAsStateWithLifecycle()Box(modifier=modifier.fillMaxSize(),contentAlignment=Alignment.TopStart){Text("📇 通讯录",fontSize=24.sp)}if(isLoading){Box(modifier=Modifier.fillMaxSize(),contentAlignment=Alignment.Center){CircularProgressIndicator()}}else{LazyColumn(modifier=Modifier.fillMaxSize().padding(top=56.dp),contentPadding=PaddingValues(16.dp)){items(contacts.size){contact->Card(modifier=Modifier.fillMaxWidth().padding(vertical=4.dp),elevation=CardDefaults.cardElevation(defaultElevation=2.dp)){Row(modifier=Modifier.padding(16.dp),verticalAlignment=Alignment.CenterVertically){Column{Text(contacts[contact].name,style=MaterialTheme.typography.titleMedium)Text(contacts[contact].phone,style=MaterialTheme.typography.bodySmall)}}}}}}}

✅ collectAsStateWithLifecycle() 会自动在 Composable 进入后台时暂停收集,避免内存泄漏。

4️⃣ 导航设计:嵌套路由 + 状态清理

全局导航图(NavGraph.kt)

@ComposablefunNavGraph(navigationControl:NavHostController,authViewModel:AuthViewModel){NavHost(navController=navigationControl,startDestination=ModuleRoute.Splash){composable<ModuleRoute.Splash>{SplashScreen(onTimeOut={if(authViewModel.flowLogin.value){navigationControl.navigate(ModuleRoute.Main){popUpTo(ModuleRoute.Splash){inclusive=true}}}else{navigationControl.navigate(ModuleRoute.Login){popUpTo(ModuleRoute.Splash){inclusive=true}}}})}composable<ModuleRoute.Login>{LoginScreen(onLoginClick={authViewModel.login()navigationControl.navigate(ModuleRoute.Main){popUpTo(ModuleRoute.Login){inclusive=true}}})}composable<ModuleRoute.Main>{MainScreen(onLogout={authViewModel.loginOut()navigationControl.navigate(ModuleRoute.Login){popUpTo(ModuleRoute.Main){inclusive=true}}})}}}

主页内部:嵌套 NavHost 实现底部导航

@ComposablefunMainScreen(onLogout:()->Unit,modifier:Modifier=Modifier,navController:NavHostController=rememberNavController(),){// 定义底部tabvalitems=listOf(BottomNavItem.Home,BottomNavItem.Contacts,BottomNavItem.Profile)BackHandler(enabled=true){Log.e("test","进入BackHandler")}Scaffold(bottomBar={NavigationBar{valnavBackStackEntrybynavController.currentBackStackEntryAsState()valcurrentRoute=navBackStackEntry?.destination?.route items.forEach{item->NavigationBarItem(icon={Icon(item.icon,contentDescription=null)},label={Text(item.title)},selected=currentRoute==item.route,onClick={navController.navigate(item.route){// 避免重复入栈popUpTo(navController.graph.id){saveState=trueinclusive=false}launchSingleTop=truerestoreState=true}})}}},){innerPadding->NavHost(navController=navController,startDestination=BottomNavItem.Home.route,modifier=modifier.padding(innerPadding)){composable(BottomNavItem.Home.route){HomeTab()}composable(BottomNavItem.Contacts.route){ContactsTab()}composable(BottomNavItem.Profile.route){ProfileTab(onLogout=onLogout)}}}}// 定义底部导航项sealedclassBottomNavItem(valroute:String,valtitle:String,valicon:ImageVector){dataobjectHome:BottomNavItem("home","首页",Icons.Default.Home)dataobjectContacts:BottomNavItem("contacts","通讯录",Icons.Default.Person)dataobjectProfile:BottomNavItem("profile","我的",Icons.Default.AccountCircle)}

🌟 为什么用嵌套路由?

官方推荐做法!避免底部 Tab 切换时重建整个页面,同时支持每个 Tab 内部继续跳转子页面(如联系人详情)。

5️⃣ 关键技巧总结

场景解决方案
页面太多?拆分到不同文件,按功能模块组织目录
状态混乱?全局状态用共享 ViewModel,局部状态用页面专属 ViewModel
返回栈错乱?使用 popUpTo(…) + inclusive = true 清理历史
Tab 切换重建?确保使用嵌套路由,Navigation 会自动保存状态
内存泄漏?用 collectAsStateWithLifecycle() 替代 collectAsState()

✅ 为什么这样做是“最佳实践”?

  • 高内聚低耦合:每个页面只关心自己的数据和 UI
  • 易于测试:ViewModel 可单元测试,UI 可 Preview 预览
  • 团队协作友好:多人开发不同 Tab 互不干扰
  • 可扩展性强:未来添加“消息”Tab 只需复制模式
  • 符合官方架构指南:遵循 Guide to app architecture

📚 结语

Jetpack Compose 不只是“新 UI 框架”,更是一种全新的应用架构思维。通过合理拆分页面、隔离状态、规范导航,我们可以构建出既简洁又强大的现代化 Android 应用。

记住:好的架构不是一开始就完美,而是在演进中保持清晰。

代码后续补充…

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 8:40:18

预设性能约束下的航天器编队事件触发姿态跟踪控制探索

预设性能约束下的航天器编队事件触发姿态跟踪控制 预设性能控制 编队控制 事件触发控制 姿态控制在航天器编队飞行领域&#xff0c;姿态控制是确保任务成功的关键一环。而预设性能控制、编队控制以及事件触发控制&#xff0c;这几个关键技术的融合&#xff0c;正为航天器姿态跟…

作者头像 李华
网站建设 2026/3/30 22:57:32

ARM汇编概述:Cortex-M3/M4实战指南

一、核心寄存器&#xff08;汇编的"操作对象"&#xff09; Cortex-M3/M4的寄存器是汇编指令的直接操作对象&#xff0c;无需记忆所有寄存器&#xff0c;仅需掌握以下高频核心寄存器&#xff0c;即可覆盖绝大多数嵌入式开发场景。 1. 通用寄存器 寄存器 别名 / 功能…

作者头像 李华
网站建设 2026/3/28 18:56:56

黑客网站整理大全(2025版),收藏这一篇就够了!

今天给大家分享一些学习网络安全的好去处。对于网络安全的学习&#xff0c;多逛论坛、阅读他人的技术分析帖是非常重要的。但有时候&#xff0c;初学者可能会感到迷茫&#xff0c;不知道去哪里寻找这些技术分析帖&#xff0c;也不知道有哪些相关的论坛或网站。所以&#xff0c;…

作者头像 李华
网站建设 2026/3/13 3:47:40

Python modbus-tk在配电物联网边缘网关的应用

配电物联网作为智能电网建设的核心组成部分&#xff0c;正在经历从传统监控向智能化、边缘化方向的深刻变革。Python modbus-tk 作为一个强大的 Modbus 协议实现库&#xff0c;在配电物联网边缘网关的应用中发挥着越来越重要的作用。本研究旨在深入分析 modbus-tk 在配电物联网…

作者头像 李华