优化代码以及更新文档说明

This commit is contained in:
Joker.X
2025-12-03 20:23:46 +08:00
parent a26f25fdaa
commit 0309299beb
56 changed files with 698 additions and 21 deletions

1
.gitignore vendored
View File

@@ -16,3 +16,4 @@ local.properties
/.idea/
/android-project-compose-docs/
/context/
/blog_post.md

161
README.md Normal file
View File

@@ -0,0 +1,161 @@
<div align="center">
<img src="docs/images/graphs/logo.svg" width="120" alt="Logo"/>
# AndroidProject-Compose
_一个 Android 快速开发框架_
<!-- 语言切换按钮 -->
<div align="center">
<a href="README_EN.md">🌍 English</a>
</div>
[![GitHub](https://img.shields.io/badge/GitHub-AndroidProjectCompose-blue?style=flat-square&logo=github)](https://github.com/Joker-x-dev/AndroidProject-Compose)
[![Gitee](https://img.shields.io/badge/Gitee-AndroidProjectCompose-red?style=flat-square&logo=gitee)](https://gitee.com/Joker-x-dev/AndroidProject-Compose)
[![Demo](https://img.shields.io/badge/Demo-蒲公英下载-green?style=flat-square&logo=android)](https://www.pgyer.com/AndroidProject-Compose)
[![API](https://img.shields.io/badge/Docs-compose.dusksnow.top-orange?style=flat-square&logo=readthedocs)](https://compose.dusksnow.top)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Joker-x-dev/AndroidProject-Compose)
</div>
## 📖 项目简介
AndroidProject-Compose 是一个包含网络、状态、导航、主题、数据存储、数据库等基础能力的 **Jetpack Compose 单模块脚手架**,并提供少量功能示例页面,方便你“拉仓库 → 运行 → 填业务”地快速搭建或学习现代 Compose 应用。
> 如果项目对您有帮助,请给个 Star 支持 ⭐ 这对我来说很重要,能给我带来长期更新维护的动力!
## 📱 项目预览
<img src="docs/images/preview/page.png" alt="page"/>
### 📍 项目地址
- **GitHub 地址**[https://github.com/Joker-x-dev/AndroidProject-Compose](https://github.com/Joker-x-dev/AndroidProject-Compose)
- **Gitee 地址**[https://gitee.com/Joker-x-dev/AndroidProject-Compose](https://gitee.com/Joker-x-dev/AndroidProject-Compose)
> 本脚手架源于青商城的实践,仅保留基础能力与功能示例;完整电商业务与全量 UI/动效/完整业务示例请查看青商城GitHub https://github.com/Joker-x-dev/CoolMallKotlin Gitee https://gitee.com/Joker-x-dev/CoolMallKotlin
### Demo 下载
**Release 版本2MB**[点击下载体验](https://www.pgyer.com/AndroidProject-Compose)
- **支持系统**Android 6.0 及以上
- **更新说明**:预览版本会不定时更新,可能不会完全同步最新的代码变更
### 说明文档
- **说明文档**[在线查看](https://compose.dusksnow.top)
- **说明**:与代码同步的在线文档,包含快速开始、架构说明、示例路由、常见定制点等,便于理解项目与学习。
## 🛠️ 技术栈
### 核心技术
| 类别 | 技术选型 | 版本号 | 说明 |
|-------|---------------------------|------------|-------------------|
| 编程语言 | Kotlin | 2.2.21 | 100% Kotlin 开发 |
| UI 框架 | Jetpack Compose | 2025.11.01 | 声明式 UI 框架 |
| 依赖注入 | Hilt | 2.57.2 | 基于 Dagger 的依赖注入框架 |
### 功能模块
| 类别 | 技术选型 | 版本号 | 说明 |
|-------|-----------------------|---------------|------------------|
| 导航 | Navigation Compose | 2.9.6 | Compose 导航组件 |
| 网络请求 | Retrofit + OkHttp | 3.0.0 + 5.3.2 | HTTP 客户端 |
### 数据存储
| 类别 | 技术选型 | 版本号 | 说明 |
|------|------|-------|------------|
| 数据库 | Room | 2.8.4 | SQLite 数据库 |
| 本地存储 | MMKV | 2.2.4 | 高性能键值存储 |
### 开发工具
| 类别 | 技术选型 | 版本号 | 说明 |
|------|---------------|--------|-------------|
| 日志框架 | Timber | 5.0.1 | 日志管理 |
| 网络调试 | Chucker | 4.2.0 | 网络请求监控 |
| 内存检测 | LeakCanary | 2.14 | 内存泄漏检测 |
## 📱 功能模块目录
- **主模块 (main)**
- 主页面 (main)
- 基础能力示例 (core-demo)
- 导航示例 (navigation-demo)
- **认证模块 (auth)**
- 登录页 (login)
- **用户模块 (user)**
- 用户信息 (info)
- **示例模块 (demo)**
- 通用网络请求示例 (network-demo)
- 通用分页列表示例 (network-list-demo)
- 数据库示例 (database)
- 本地存储示例 (local-storage)
- 状态管理示例 (state-management)
- 网络请求示例 (network-request)
- 带参跳转示例 (navigation-with-args)
- 结果回传示例 (navigation-result)
## 项目结构
```
├── app/ # 应用入口
├── core/ # 核心
│ ├── base/ # 基础抽象
│ ├── data/ # 数据层
│ ├── database/ # 数据库
│ ├── datastore/ # 数据存储
│ ├── designsystem/ # 设计系统
│ ├── model/ # 数据模型
│ ├── network/ # 网络层
│ ├── result/ # 结果处理
│ ├── state/ # 状态管理
│ ├── ui/ # UI 组件
│ └── util/ # 工具类
├── navigation/ # 导航模块
│ ├── routes/ # 路由定义
│ ├── results/ # 路由返回结果
│ └── extension/ # 导航扩展
├── feature/ # 功能模块
│ ├── main/ # 主模块
│ ├── auth/ # 认证模块
│ ├── user/ # 用户模块
│ └── demo/ # 示例模块
└── MainActivityViewModel.kt # 宿主级共享 ViewModel
```
## 👥 加入群聊
欢迎加入开发者交流群,一起分享学习心得,讨论技术问题!
<div align="left">
<img src="docs/images/group/qq.jpg" width="200" alt="QQ群二维码"/>
<p>扫码或搜索群号加入 QQ 群</p>
</div>
## 🤝 参与贡献
这是一个开放的学习项目,欢迎所有对 Android 开发感兴趣的开发者参与贡献!
### 🎯 贡献方式
- **代码贡献**: 提交 Pull Request完善功能实现或修复问题
- **问题反馈**: 通过 Issue 报告 Bug 或提出功能建议
- **文档优化**: 完善项目文档、添加使用说明或开发指南
- **设计支持**: 提供 UI/UX 设计建议或素材资源
- **测试协助**: 参与功能测试,提供使用反馈和改进建议
### 📋 贡献指南
- 提交代码前请确保遵循项目的编码规范
- 新功能开发建议先创建 Issue 讨论可行性
- 欢迎分享学习心得和技术总结

158
README_EN.md Normal file
View File

@@ -0,0 +1,158 @@
<div align="center">
<img src="docs/images/graphs/logo.svg" width="120" alt="Logo"/>
# AndroidProject-Compose
_A fast-start Android framework built with Jetpack Compose_
<!-- Language Switch Button -->
<div align="center">
<a href="README.md">🌍 中文</a>
</div>
[![GitHub](https://img.shields.io/badge/GitHub-AndroidProjectCompose-blue?style=flat-square&logo=github)](https://github.com/Joker-x-dev/AndroidProject-Compose)
[![Gitee](https://img.shields.io/badge/Gitee-AndroidProjectCompose-red?style=flat-square&logo=gitee)](https://gitee.com/Joker-x-dev/AndroidProject-Compose)
[![Demo](https://img.shields.io/badge/Demo-Download-green?style=flat-square&logo=android)](https://www.pgyer.com/AndroidProject-Compose)
[![API](https://img.shields.io/badge/Docs-compose.dusksnow.top-orange?style=flat-square&logo=readthedocs)](https://compose.dusksnow.top)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Joker-x-dev/AndroidProject-Compose)
</div>
## 📖 Project Overview
AndroidProject-Compose is a **single-module Jetpack Compose scaffold** that ships with the essential building blocks for networking, state, navigation, theming, data storage, and database access. It also includes a few feature demo pages so you can “clone → run → plug in your business logic” to quickly build or learn a modern Compose app.
> If this project helps you, please give it a Star ⭐ It means a lot and keeps me motivated to maintain and update the project long-term!
## 📱 Preview
<img src="docs/images/preview/page.png" alt="page"/>
### 📍 Project Links
- **GitHub**: [https://github.com/Joker-x-dev/AndroidProject-Compose](https://github.com/Joker-x-dev/AndroidProject-Compose)
- **Gitee**: [https://gitee.com/Joker-x-dev/AndroidProject-Compose](https://gitee.com/Joker-x-dev/AndroidProject-Compose)
> This scaffold originates from the CoolMall practice project and keeps only the foundational capabilities plus sample pages. For the full e-commerce business, complete UI/animations, and full-featured demos, see CoolMall: GitHub https://github.com/Joker-x-dev/CoolMallKotlin Gitee https://gitee.com/Joker-x-dev/CoolMallKotlin
### Demo Download
**Release build (2MB)**: [Download](https://www.pgyer.com/AndroidProject-Compose)
- **Supported OS**: Android 6.0 and above
- **Release notes**: Preview builds update occasionally and may not always reflect the latest code changes
### Documentation
- **Docs**: [View online](https://compose.dusksnow.top)
- **Note**: Online docs stay in sync with the code and cover quick start, architecture, sample routes, common customization points, and more.
## 🛠️ Tech Stack
### Core Technologies
| Category | Technology | Version | Description |
|-----------------------|---------------------|------------|------------------------------|
| Programming Language | Kotlin | 2.2.21 | 100% Kotlin |
| UI Framework | Jetpack Compose | 2025.11.01 | Declarative UI framework |
| Dependency Injection | Hilt | 2.57.2 | Dagger-based DI framework |
### Feature Modules
| Category | Technology | Version | Description |
|------------|------------------------|---------------|---------------------------|
| Navigation | Navigation Compose | 2.9.6 | Compose navigation |
| Network | Retrofit + OkHttp | 3.0.0 + 5.3.2 | HTTP client |
### Data Storage
| Category | Technology | Version | Description |
|---------------|------------|---------|----------------------------------|
| Database | Room | 2.8.4 | SQLite database |
| Local Storage | MMKV | 2.2.4 | High-performance key-value store |
### Development Tools
| Category | Technology | Version | Description |
|-------------------|------------|---------|------------------------|
| Logging | Timber | 5.0.1 | Log management |
| Network Debugging | Chucker | 4.2.0 | Network request monitor|
| Memory Leak Check | LeakCanary | 2.14 | Memory leak detection |
## 📱 Feature Module Directory
- **Main Module (main)**
- Main page (main)
- Core capability demos (core-demo)
- Navigation demos (navigation-demo)
- **Auth Module (auth)**
- Login (login)
- **User Module (user)**
- User info (info)
- **Demo Module (demo)**
- Generic network request demo (network-demo)
- Generic paged list demo (network-list-demo)
- Database demo (database)
- Local storage demo (local-storage)
- State management demo (state-management)
- Network request demo (network-request)
- Navigation with args (navigation-with-args)
- Navigation result passing (navigation-result)
## Project Structure
```
├── app/ # App entry
├── core/ # Core
│ ├── base/ # Base abstractions
│ ├── data/ # Data layer
│ ├── database/ # Database
│ ├── datastore/ # Data storage
│ ├── designsystem/ # Design system
│ ├── model/ # Data models
│ ├── network/ # Network layer
│ ├── result/ # Result handling
│ ├── state/ # State management
│ ├── ui/ # UI components
│ └── util/ # Utilities
├── navigation/ # Navigation module
│ ├── routes/ # Route definitions
│ ├── results/ # Route results
│ └── extension/ # Navigation extensions
├── feature/ # Feature modules
│ ├── main/ # Main module
│ ├── auth/ # Auth module
│ ├── user/ # User module
│ └── demo/ # Demo module
└── MainActivityViewModel.kt # Host-level shared ViewModel
```
## 👥 Join the Community
Welcome to the developer group—share learning notes and discuss technical questions together!
<div align="left">
<img src="docs/images/group/qq.jpg" width="200" alt="QQ group QR code"/>
<p>Scan or search the group number to join the QQ group</p>
</div>
## 🤝 Contributing
This is an open learning project. All Android enthusiasts are welcome to contribute!
### 🎯 How to Contribute
- **Code Contributions**: Submit pull requests to improve features or fix issues
- **Issue Feedback**: Report bugs or suggest features via Issues
- **Documentation**: Enhance docs, add usage guides or developer guides
- **Design Support**: Provide UI/UX suggestions or assets
- **Testing Help**: Join feature testing and share feedback
### 📋 Contribution Guidelines
- Please follow the project coding conventions before submitting code
- For new features, create an Issue first to discuss feasibility
- Sharing learning notes and technical takeaways is encouraged

View File

@@ -20,8 +20,13 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# toast 混淆规则
-keep class com.hjq.toast.** {*;}
# xxp权限混淆规则
-keep class com.hjq.permissions.** { *; }
# xxp权限混淆规则 地址: https://github.com/getActivity/XXPermissions/blob/master/library/proguard-permissions.pro
-keepclassmembers interface com.hjq.permissions.start.IStartActivityDelegate {
<methods>;
}
-keepclassmembers interface com.hjq.permissions.fragment.IFragmentMethodNative {
<methods>;
}
-keepclassmembers class androidx.fragment.app.Fragment {
androidx.fragment.app.FragmentActivity getActivity();
}

View File

@@ -1,5 +1,7 @@
package com.joker.kit.core.model.entity
import androidx.annotation.Keep
import kotlinx.serialization.Serializable
/**
@@ -18,6 +20,7 @@ import kotlinx.serialization.Serializable
* @param updateTime 更新时间
* @author Joker.X
*/
@Keep
@Serializable
data class User(

View File

@@ -1,5 +1,7 @@
package com.joker.kit.core.model.network
import androidx.annotation.Keep
import kotlinx.serialization.Serializable
/**
@@ -11,6 +13,7 @@ import kotlinx.serialization.Serializable
* @param message 出错的提示信息
* @author Joker.X
*/
@Keep
@Serializable
data class NetworkResponse<T>(
/**

View File

@@ -1,5 +1,7 @@
package com.joker.kit.core.network.service
import androidx.annotation.Keep
import com.joker.kit.core.model.entity.User
import com.joker.kit.core.model.network.NetworkResponse
import retrofit2.http.GET
@@ -9,6 +11,7 @@ import retrofit2.http.GET
*
* @author Joker.X
*/
@Keep
interface UserInfoService {
/**

View File

@@ -23,12 +23,17 @@ class DemoCounterState @Inject constructor(
/**
* 计数器值
*
* @return 计数器 StateFlow
* @author Joker.X
*/
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count.asStateFlow()
/**
* +1
*
* @author Joker.X
*/
fun increase() {
appScope.launch {
@@ -38,6 +43,8 @@ class DemoCounterState @Inject constructor(
/**
* -1最低为 0
*
* @author Joker.X
*/
fun decrease() {
appScope.launch {
@@ -47,6 +54,8 @@ class DemoCounterState @Inject constructor(
/**
* 重置
*
* @author Joker.X
*/
fun reset() {
appScope.launch {

View File

@@ -182,4 +182,4 @@ class UserState @Inject constructor(
}
)
}
}
}

View File

@@ -12,6 +12,8 @@ import javax.inject.Singleton
/**
* 应用状态模块,提供应用级协程作用域
*
* @author Joker.X
*/
@Module
@InstallIn(SingletonComponent::class)
@@ -20,6 +22,9 @@ object AppStateModule {
/**
* 提供应用级别的协程作用域
* SupervisorJob 确保子协程失败不会终止整个作用域
*
* @return 应用级 CoroutineScope
* @author Joker.X
*/
@ApplicationScope
@Singleton
@@ -31,6 +36,8 @@ object AppStateModule {
/**
* 应用级协程作用域限定符
*
* @author Joker.X
*/
@Retention(AnnotationRetention.RUNTIME)
@Qualifier

View File

@@ -30,6 +30,8 @@ class LoginViewModel @Inject constructor(
/**
* 模拟登录:构造假的 Auth/User写入 UserState演示路由拦截放行。
* 真实项目中,这里会是网络请求,登录成功后会有 token 等信息返回。
*
* @author Joker.X
*/
fun login() {
viewModelScope.launch {
@@ -52,6 +54,8 @@ class LoginViewModel @Inject constructor(
/**
* 模拟退出登录,清空全局 UserState
*
* @author Joker.X
*/
fun logout() {
viewModelScope.launch {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* 数据库示例页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.databaseScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -7,6 +7,10 @@ import androidx.navigation.NavHostController
/**
* Demo 模块导航图
*
* @param navController 导航控制器
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.demoGraph(

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* 本地存储示例页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.localStorageScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* 结果回传示例页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.navigationResultScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* 带参跳转示例页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.navigationWithArgsScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* Network Demo 页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.networkDemoScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* Network List Demo 页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.networkListDemoScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* 网络请求示例页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.networkRequestScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -9,6 +9,9 @@ import com.joker.kit.navigation.routes.DemoRoutes
/**
* 状态管理示例页面导航
*
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.stateManagementScreen(sharedTransitionScope: SharedTransitionScope) {

View File

@@ -53,6 +53,7 @@ import java.util.Locale
* 数据库示例路由
*
* @param viewModel Hilt 注入的 DatabaseViewModel
* @author Joker.X
*/
@Composable
internal fun DatabaseRoute(
@@ -90,6 +91,7 @@ internal fun DatabaseRoute(
* @param onDeleteItem 删除指定记录
* @param onClearAll 清空所有记录
* @param onBackClick 返回按钮回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -132,6 +134,7 @@ internal fun DatabaseScreen(
* @param onAddClick 新增记录回调
* @param onDeleteItem 删除记录回调
* @param onClearAll 清空列表回调
* @author Joker.X
*/
@Composable
private fun DatabaseContent(
@@ -177,6 +180,7 @@ private fun DatabaseContent(
* @param onAddClick 新增记录
* @param onClearAll 清空全部
* @param canClear 是否允许清空按钮启用
* @author Joker.X
*/
@Composable
private fun InputCard(
@@ -249,6 +253,7 @@ private fun InputCard(
*
* @param items Demo 数据列表
* @param onDeleteItem 删除单条记录回调
* @author Joker.X
*/
@Composable
private fun DemoListCard(
@@ -298,6 +303,7 @@ private fun DemoListCard(
*
* @param item Demo 实体
* @param onDeleteItem 删除该条记录回调
* @author Joker.X
*/
@Composable
private fun DemoListItem(
@@ -337,6 +343,8 @@ private fun DemoListItem(
/**
* 数据库界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -352,6 +360,8 @@ private fun DatabasePreview() {
/**
* 数据库界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -367,6 +377,9 @@ private fun DatabasePreviewDark() {
/**
* 预览用 Demo 数据
*
* @return 示例数据列表
* @author Joker.X
*/
private fun previewDemoItems() = listOf(
DemoEntity(id = 1, title = "演示标题 A", description = "这是第一条记录"),

View File

@@ -42,6 +42,7 @@ import com.joker.kit.feature.demo.viewmodel.LocalStorageViewModel
* 本地存储示例路由
*
* @param viewModel Hilt 注入的 LocalStorageViewModel
* @author Joker.X
*/
@Composable
internal fun LocalStorageRoute(
@@ -85,6 +86,7 @@ internal fun LocalStorageRoute(
* @param onClearUser 清除用户信息
* @param onReloadUser 重新读取用户信息
* @param onBackClick 返回按钮回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -104,7 +106,6 @@ internal fun LocalStorageScreen(
AppScaffold(
titleText = "本地存储",
onBackClick = onBackClick,
contentShouldConsumePadding = true
) {
LocalStorageContent(
userId = userId,
@@ -134,6 +135,7 @@ internal fun LocalStorageScreen(
* @param onSaveUser 保存用户
* @param onClearUser 清除用户
* @param onReloadUser 重新读取用户
* @author Joker.X
*/
@Composable
private fun LocalStorageContent(
@@ -183,6 +185,7 @@ private fun LocalStorageContent(
* @param onSaveUser 保存用户
* @param onClearUser 清除用户
* @param onReloadUser 重新读取用户
* @author Joker.X
*/
@Composable
private fun UserCard(
@@ -269,6 +272,8 @@ private fun UserCard(
/**
* 本地存储界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -285,6 +290,8 @@ private fun LocalStoragePreview() {
/**
* 本地存储界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable

View File

@@ -17,6 +17,9 @@ import com.joker.kit.feature.demo.viewmodel.NavigationResultViewModel
/**
* 结果回传示例路由
*
* @param viewModel Hilt 注入的 NavigationResultViewModel
* @author Joker.X
*/
@Composable
internal fun NavigationResultRoute(
@@ -31,7 +34,9 @@ internal fun NavigationResultRoute(
/**
* 结果回传示例界面
*
* @param onSendResult 发送结果并返回回调
* @param onBackClick 返回按钮回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -49,6 +54,9 @@ internal fun NavigationResultScreen(
/**
* 结果回传内容视图
*
* @param onSendResult 发送结果回调
* @author Joker.X
*/
@Composable
private fun NavigationResultContent(onSendResult: () -> Unit) {
@@ -64,6 +72,8 @@ private fun NavigationResultContent(onSendResult: () -> Unit) {
/**
* 结果回传界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -75,6 +85,8 @@ private fun NavigationResultPreview() {
/**
* 结果回传界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable

View File

@@ -11,6 +11,9 @@ import com.joker.kit.feature.demo.viewmodel.NavigationWithArgsViewModel
/**
* 带参跳转示例路由
*
* @param viewModel Hilt 注入的 NavigationWithArgsViewModel
* @author Joker.X
*/
@Composable
internal fun NavigationWithArgsRoute(
@@ -25,7 +28,9 @@ internal fun NavigationWithArgsRoute(
/**
* 带参跳转示例界面
*
* @param goodsId 传入的商品 ID
* @param onBackClick 返回按钮回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -43,6 +48,9 @@ internal fun NavigationWithArgsScreen(
/**
* 带参跳转内容视图
*
* @param goodsId 传入的商品 ID
* @author Joker.X
*/
@Composable
private fun NavigationWithArgsContent(goodsId: Long) {
@@ -51,6 +59,8 @@ private fun NavigationWithArgsContent(goodsId: Long) {
/**
* 带参跳转界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -62,6 +72,8 @@ private fun NavigationWithArgsPreview() {
/**
* 带参跳转界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable

View File

@@ -20,6 +20,9 @@ import com.joker.kit.feature.demo.viewmodel.NetworkDemoViewModel
/**
* Network Demo 路由
*
* @param viewModel Hilt 注入的 NetworkDemoViewModel
* @author Joker.X
*/
@Composable
internal fun NetworkDemoRoute(
@@ -41,6 +44,7 @@ internal fun NetworkDemoRoute(
* @param uiState UI 状态
* @param onBackClick 返回按钮回调
* @param onRetry 重试回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -66,6 +70,7 @@ internal fun NetworkDemoScreen(
* Network Demo 内容视图
*
* @param data 商品数据
* @author Joker.X
*/
@Composable
private fun NetworkDemoContent(data: Goods) {
@@ -79,6 +84,8 @@ private fun NetworkDemoContent(data: Goods) {
/**
* Network Demo 界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -90,6 +97,8 @@ private fun NetworkDemoPreview() {
/**
* Network Demo 界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -101,6 +110,9 @@ private fun NetworkDemoPreviewDark() {
/**
* 模拟商品数据
*
* @return 示例商品
* @author Joker.X
*/
private fun mockGoods() = Goods(
id = 1,

View File

@@ -23,6 +23,9 @@ import com.joker.kit.feature.demo.viewmodel.NetworkListDemoViewModel
/**
* Network List Demo 路由
*
* @param viewModel Hilt 注入的 NetworkListDemoViewModel
* @author Joker.X
*/
@Composable
internal fun NetworkListDemoRoute(
@@ -62,6 +65,7 @@ internal fun NetworkListDemoRoute(
* @param shouldTriggerLoadMore 是否触发加载更多
* @param onBackClick 返回回调
* @param onRetry 重试回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -105,6 +109,7 @@ internal fun NetworkListDemoScreen(
* @param onRefresh 刷新回调
* @param onLoadMore 加载更多回调
* @param shouldTriggerLoadMore 是否触发加载更多
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -131,6 +136,9 @@ private fun NetworkListDemoContent(
/**
* 简单展示商品信息的列表项
*
* @param goods 商品实体
* @author Joker.X
*/
@Composable
private fun GoodsListItem(goods: Goods) {
@@ -146,6 +154,8 @@ private fun GoodsListItem(goods: Goods) {
/**
* Network List Demo 界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -161,6 +171,8 @@ private fun NetworkListDemoPreview() {
/**
* Network List Demo 界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -176,6 +188,9 @@ private fun NetworkListDemoPreviewDark() {
/**
* 预览用商品列表数据
*
* @return 商品预览数据列表
* @author Joker.X
*/
private fun previewGoodsList() = listOf(
Goods(id = 1, title = "小米手机 14", subTitle = "直屏旗舰", price = 3999, sold = 5000),

View File

@@ -24,6 +24,9 @@ import com.joker.kit.feature.demo.viewmodel.NetworkRequestViewModel
/**
* 网络请求示例路由
*
* @param viewModel Hilt 注入的 NetworkRequestViewModel
* @author Joker.X
*/
@Composable
internal fun NetworkRequestRoute(
@@ -45,6 +48,7 @@ internal fun NetworkRequestRoute(
* @param goods 商品信息
* @param onBackClick 返回按钮回调
* @param onRequestClick 请求按钮回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -69,6 +73,7 @@ internal fun NetworkRequestScreen(
*
* @param goods 商品信息
* @param onRequestClick 请求按钮回调
* @author Joker.X
*/
@Composable
private fun NetworkRequestContent(
@@ -99,6 +104,7 @@ private fun NetworkRequestContent(
* 网络请求结果卡片视图
*
* @param goods 商品信息
* @author Joker.X
*/
@Composable
private fun CardResult(goods: Goods) {
@@ -116,6 +122,9 @@ private fun CardResult(goods: Goods) {
/**
* 模拟商品信息
*
* @return 示例商品
* @author Joker.X
*/
private fun mockGoods() = Goods(
id = 1,
@@ -128,6 +137,8 @@ private fun mockGoods() = Goods(
/**
* 网络请求界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -141,6 +152,8 @@ private fun NetworkRequestPreview() {
/**
* 网络请求界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -150,4 +163,4 @@ private fun NetworkRequestPreviewDark() {
goods = mockGoods(),
)
}
}
}

View File

@@ -37,6 +37,7 @@ import com.joker.kit.feature.demo.viewmodel.StateManagementViewModel
* 状态管理示例路由
*
* @param viewModel Hilt 注入的 StateManagementViewModel
* @author Joker.X
*/
@Composable
internal fun StateManagementRoute(
@@ -62,6 +63,7 @@ internal fun StateManagementRoute(
* @param onDecrease -1 回调
* @param onReset 重置回调
* @param onBackClick 返回按钮回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -92,6 +94,7 @@ internal fun StateManagementScreen(
* @param onIncrease 递增回调
* @param onDecrease 递减回调
* @param onReset 重置回调
* @author Joker.X
*/
@Composable
private fun StateManagementContent(
@@ -116,6 +119,11 @@ private fun StateManagementContent(
}
}
/**
* 状态管理介绍卡片
*
* @author Joker.X
*/
@Composable
private fun IntroCard() {
Card(
@@ -147,6 +155,7 @@ private fun IntroCard() {
* @param onIncrease 递增回调
* @param onDecrease 递减回调
* @param onReset 重置回调
* @author Joker.X
*/
@Composable
private fun CounterCard(
@@ -203,6 +212,8 @@ private fun CounterCard(
/**
* 状态管理界面浅色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -214,6 +225,8 @@ private fun StateManagementPreview() {
/**
* 状态管理界面深色主题预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable

View File

@@ -21,6 +21,7 @@ import javax.inject.Inject
* @param navigator 导航管理器
* @param userState 用户状态管理
* @param demoRepository Demo 仓库,封装 DemoDataSource 的增删改查
* @author Joker.X
*/
@HiltViewModel
class DatabaseViewModel @Inject constructor(
@@ -40,6 +41,9 @@ class DatabaseViewModel @Inject constructor(
/**
* Demo 表数据流
* UI 侧直接 collectAsState() 获取最新列表
*
* @return Demo 列表状态流
* @author Joker.X
*/
val items: StateFlow<List<DemoEntity>> = demoRepository
.observeItems()
@@ -53,6 +57,7 @@ class DatabaseViewModel @Inject constructor(
* 更新标题输入
*
* @param value 文本框中的标题
* @author Joker.X
*/
fun onTitleChange(value: String) {
_title.value = value
@@ -62,6 +67,7 @@ class DatabaseViewModel @Inject constructor(
* 更新描述输入
*
* @param value 文本框中的描述
* @author Joker.X
*/
fun onDescriptionChange(value: String) {
_description.value = value
@@ -69,6 +75,8 @@ class DatabaseViewModel @Inject constructor(
/**
* 新增一条记录(仅当标题非空)
*
* @author Joker.X
*/
fun addItem() {
val title = _title.value.trim()
@@ -88,6 +96,7 @@ class DatabaseViewModel @Inject constructor(
* 删除指定记录
*
* @param id 记录主键
* @author Joker.X
*/
fun deleteItem(id: Long) {
viewModelScope.launch {
@@ -97,6 +106,8 @@ class DatabaseViewModel @Inject constructor(
/**
* 清空全部记录
*
* @author Joker.X
*/
fun clearAll() {
viewModelScope.launch {

View File

@@ -21,6 +21,7 @@ import kotlinx.coroutines.launch
* @param navigator 导航管理器
* @param userState 用户状态管理
* @param userInfoStoreRepository 用户信息本地存储仓库
* @author Joker.X
*/
@HiltViewModel
class LocalStorageViewModel @Inject constructor(
@@ -53,6 +54,7 @@ class LocalStorageViewModel @Inject constructor(
* 用户 id 文本更新
*
* @param value 输入的 id 字符串
* @author Joker.X
*/
fun onUserIdChange(value: String) {
_userId.value = value
@@ -62,6 +64,7 @@ class LocalStorageViewModel @Inject constructor(
* 用户昵称输入更新
*
* @param value 昵称文本
* @author Joker.X
*/
fun onNickNameChange(value: String) {
_nickName.value = value
@@ -71,6 +74,7 @@ class LocalStorageViewModel @Inject constructor(
* 头像链接输入更新
*
* @param value 头像 URL
* @author Joker.X
*/
fun onAvatarChange(value: String) {
_avatar.value = value
@@ -78,6 +82,8 @@ class LocalStorageViewModel @Inject constructor(
/**
* 保存用户信息到本地
*
* @author Joker.X
*/
fun saveUser() {
viewModelScope.launch {
@@ -95,6 +101,8 @@ class LocalStorageViewModel @Inject constructor(
/**
* 清除本地用户信息
*
* @author Joker.X
*/
fun clearUser() {
viewModelScope.launch {
@@ -108,6 +116,8 @@ class LocalStorageViewModel @Inject constructor(
/**
* 重新读取用户信息
*
* @author Joker.X
*/
fun loadUser() {
viewModelScope.launch {

View File

@@ -10,6 +10,10 @@ import javax.inject.Inject
/**
* 结果回传示例页 ViewModel
*
* @param navigator 导航管理器
* @param userState 用户状态
* @author Joker.X
*/
@HiltViewModel
class NavigationResultViewModel @Inject constructor(
@@ -19,6 +23,11 @@ class NavigationResultViewModel @Inject constructor(
navigator = navigator,
userState = userState
) {
/**
* 回传结果并返回上一页
*
* @author Joker.X
*/
fun sendResultAndBack() {
popBackStackWithResult(
DemoResultKey,

View File

@@ -11,6 +11,11 @@ import javax.inject.Inject
/**
* 带参跳转示例页 ViewModel
*
* @param navigator 导航管理器
* @param userState 用户状态
* @param savedStateHandle 路由参数存储
* @author Joker.X
*/
@HiltViewModel
class NavigationWithArgsViewModel @Inject constructor(
@@ -23,11 +28,17 @@ class NavigationWithArgsViewModel @Inject constructor(
) {
/**
* 路由参数
* */
*
* @return 路由解析结果
* @author Joker.X
*/
private val route = savedStateHandle.toRoute<DemoRoutes.NavigationWithArgs>()
/**
* 商品ID
* */
*
* @return 传递的商品 ID
* @author Joker.X
*/
val goodsId: Long = route.goodsId
}
}

View File

@@ -16,6 +16,7 @@ import javax.inject.Inject
* @param navigator 导航管理器
* @param userState 用户状态管理
* @param goodsRepository 商品数据仓库
* @author Joker.X
*/
@HiltViewModel
class NetworkDemoViewModel @Inject constructor(
@@ -30,8 +31,11 @@ class NetworkDemoViewModel @Inject constructor(
/**
* 重写请求API Flow获取商品信息
*
* @return 商品信息响应流
* @author Joker.X
*/
override fun requestApiFlow(): Flow<NetworkResponse<Goods>> {
return goodsRepository.getGoodsInfo("1")
}
}
}

View File

@@ -18,6 +18,7 @@ import javax.inject.Inject
* @param navigator 导航器
* @param userState 用户状态管理
* @param goodsRepository 商品数据仓库
* @author Joker.X
*/
@HiltViewModel
class NetworkListDemoViewModel @Inject constructor(
@@ -35,6 +36,9 @@ class NetworkListDemoViewModel @Inject constructor(
/**
* 重写请求API Flow获取商品列表
*
* @return 商品分页数据流
* @author Joker.X
*/
override fun requestListData(): Flow<NetworkResponse<NetworkPageData<Goods>>> {
return goodsRepository.getGoodsPage(

View File

@@ -16,6 +16,11 @@ import javax.inject.Inject
/**
* 网络请求示例页 ViewModel
*
* @param navigator 导航管理器
* @param userState 用户状态
* @param goodsRepository 商品仓库
* @author Joker.X
*/
@HiltViewModel
class NetworkRequestViewModel @Inject constructor(
@@ -29,12 +34,17 @@ class NetworkRequestViewModel @Inject constructor(
/**
* 商品信息
*
* @return 商品信息状态流
* @author Joker.X
*/
private val _goods = MutableStateFlow<Goods?>(null)
val goods: StateFlow<Goods?> = _goods.asStateFlow()
/**
* 发起商品信息请求,示例中固定传 id = 1
*
* @author Joker.X
*/
fun onRequestClick() {
ResultHandler.handleResultWithData(

View File

@@ -10,6 +10,11 @@ import javax.inject.Inject
/**
* 状态管理示例页 ViewModel
*
* @param navigator 应用导航器
* @param userState 全局用户状态
* @param counterState 计数器状态
* @author Joker.X
*/
@HiltViewModel
class StateManagementViewModel @Inject constructor(
@@ -20,21 +25,30 @@ class StateManagementViewModel @Inject constructor(
/**
* 对外暴露的计数器 StateFlow
*
* @return 计数器状态流
* @author Joker.X
*/
val count: StateFlow<Int> = counterState.count
/**
* +1
*
* @author Joker.X
*/
fun increase() = counterState.increase()
/**
* -1
*
* @author Joker.X
*/
fun decrease() = counterState.decrease()
/**
* 重置为 0
*
* @author Joker.X
*/
fun reset() = counterState.reset()
}

View File

@@ -18,6 +18,7 @@ import com.joker.kit.feature.main.model.DemoCardInfo
* @param info 卡片数据
* @param modifier 修饰符
* @param onClick 点击回调
* @author Joker.X
*/
@Composable
fun DemoCard(
@@ -51,7 +52,8 @@ private fun DemoCardPreview() {
DemoCard(
info = DemoCardInfo(
title = "示例组件",
description = "预览展示 Demo 卡片默认样式。"
description = "预览展示 Demo 卡片默认样式。",
route = null
)
)
}

View File

@@ -6,6 +6,8 @@ import com.joker.kit.navigation.routes.UserRoutes
/**
* Demo 卡片静态数据源
*
* @author Joker.X
*/
object DemoCardData {
@@ -59,4 +61,4 @@ object DemoCardData {
route = UserRoutes.Info
)
)
}
}

View File

@@ -5,6 +5,8 @@ package com.joker.kit.feature.main.model
*
* @param title 标题
* @param description 描述内容
* @param route 跳转路由
* @author Joker.X
*/
data class DemoCardInfo(
val title: String,

View File

@@ -11,6 +11,7 @@ import com.joker.kit.navigation.routes.MainRoutes
/**
* 注册主页面路由
*
* @param navController NavHostController
* @param sharedTransitionScope 共享转场作用域
* @author Joker.X
*/

View File

@@ -29,6 +29,7 @@ import com.joker.kit.feature.main.viewmodel.CoreDemoViewModel
* Core Demo 路由
*
* @param viewModel Core Demo ViewModel
* @author Joker.X
*/
@Composable
internal fun CoreDemoRoute(
@@ -49,6 +50,7 @@ internal fun CoreDemoRoute(
* @param cards Demo 卡片列表
* @param counter 全局计数器值,大于 0 时在列表顶部展示
* @param onCardClick 卡片点击回调
* @author Joker.X
*/
@Composable
internal fun CoreDemoScreen(
@@ -81,6 +83,7 @@ internal fun CoreDemoScreen(
* 主页计数器提示
*
* @param counter 当前计数器值
* @author Joker.X
*/
@Composable
private fun CounterBanner(counter: Int) {
@@ -94,6 +97,8 @@ private fun CounterBanner(counter: Int) {
/**
* Core Demo 浅色预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -107,6 +112,8 @@ private fun CoreDemoPreview() {
/**
* Core Demo 深色预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable

View File

@@ -74,6 +74,11 @@ internal fun MainScreen(
/**
* 主页面内容视图,包含底部导航和横向 Pager
*
* @param uiState UI 状态
* @param onTabSelected Tab 切换回调
* @param navController 导航控制器
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
@@ -126,6 +131,11 @@ private fun MainScreenContent(
/**
* 自定义纯文字底部导航栏
*
* @param tabs 底部栏 Tab 列表
* @param currentTab 当前选中 Tab
* @param onTabSelected Tab 选择回调
* @author Joker.X
*/
@Composable
private fun MainBottomBar(

View File

@@ -33,6 +33,8 @@ import com.joker.kit.navigation.results.DemoResultKey
* Navigation Demo 路由
*
* @param viewModel Navigation Demo ViewModel
* @param navController 用于监听结果的 NavController
* @author Joker.X
*/
@Composable
internal fun NavigationDemoRoute(
@@ -60,6 +62,9 @@ internal fun NavigationDemoRoute(
*
* @param cards Demo 卡片列表
* @param isLoggedIn 是否已登录,登录后展示提示
* @param demoResult 回传结果
* @param onCardClick 卡片点击回调
* @author Joker.X
*/
@Composable
internal fun NavigationDemoScreen(
@@ -96,7 +101,9 @@ internal fun NavigationDemoScreen(
/**
* 登录状态提示卡片
* */
*
* @author Joker.X
*/
@Composable
private fun LoginStatusBanner() {
Card(modifier = Modifier.fillMaxWidth()) {
@@ -107,7 +114,12 @@ private fun LoginStatusBanner() {
}
}
/** 回传结果提示卡片 */
/**
* 回传结果提示卡片
*
* @param result 回传结果
* @author Joker.X
*/
@Composable
private fun DemoResultBanner(result: DemoResult) {
Card(modifier = Modifier.fillMaxWidth()) {
@@ -120,6 +132,8 @@ private fun DemoResultBanner(result: DemoResult) {
/**
* Navigation Demo 浅色预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable
@@ -133,6 +147,8 @@ private fun NavigationDemoPreview() {
/**
* Navigation Demo 深色预览
*
* @author Joker.X
*/
@Preview(showBackground = true)
@Composable

View File

@@ -14,6 +14,11 @@ import javax.inject.Inject
/**
* Core Demo ViewModel
*
* @param navigator 导航管理器
* @param userState 用户状态
* @param counterState 计数器状态
* @author Joker.X
*/
@HiltViewModel
class CoreDemoViewModel @Inject constructor(
@@ -28,9 +33,20 @@ class CoreDemoViewModel @Inject constructor(
private val _cards = MutableStateFlow(DemoCardData.coreCards)
val cards: StateFlow<List<DemoCardInfo>> = _cards.asStateFlow()
/** 全局计数器值 */
/**
* 全局计数器值
*
* @return 计数器状态流
* @author Joker.X
*/
val count: StateFlow<Int> = counterState.count
/**
* 处理卡片点击
*
* @param info 被点击的卡片信息
* @author Joker.X
*/
fun onCardClick(info: DemoCardInfo) {
info.route?.let { navigate(it) }
}

View File

@@ -30,6 +30,9 @@ class MainViewModel @Inject constructor(
/**
* 切换底部导航 tab
*
* @param tab 目标 Tab
* @author Joker.X
*/
fun selectTab(tab: MainTab) {
if (tab == _uiState.value.currentTab) return
@@ -41,6 +44,7 @@ class MainViewModel @Inject constructor(
* Main 页面 UI 状态
*
* @param currentTab 当前底部栏 tab
* @author Joker.X
*/
data class MainUiState(
val currentTab: MainTab = MainTab.Core
@@ -48,6 +52,9 @@ data class MainUiState(
/**
* Main 页面底部栏 Tab
*
* @param title Tab 标题
* @author Joker.X
*/
enum class MainTab(val title: String) {
Core("Core"),
@@ -58,6 +65,13 @@ enum class MainTab(val title: String) {
companion object {
val allTabs: List<MainTab> = values().toList()
/**
* 根据索引获取 Tab
*
* @param index 底部栏索引
* @return 对应的 Tab超出范围返回 Core
* @author Joker.X
*/
fun fromIndex(index: Int): MainTab {
return allTabs.getOrElse(index) { Core }
}

View File

@@ -14,6 +14,10 @@ import javax.inject.Inject
/**
* Navigation Demo ViewModel
*
* @param navigator 导航管理器
* @param userState 用户状态
* @author Joker.X
*/
@HiltViewModel
class NavigationDemoViewModel @Inject constructor(
@@ -27,16 +31,33 @@ class NavigationDemoViewModel @Inject constructor(
private val _cards = MutableStateFlow(DemoCardData.navigationCards)
val cards: StateFlow<List<DemoCardInfo>> = _cards.asStateFlow()
/** 全局登录状态 */
/**
* 全局登录状态
*
* @return 登录状态流
* @author Joker.X
*/
val isLoggedIn: StateFlow<Boolean> = userState.isLoggedIn
private val _demoResult = MutableStateFlow<DemoResult?>(null)
val demoResult: StateFlow<DemoResult?> = _demoResult.asStateFlow()
/**
* 处理卡片点击
*
* @param info 卡片信息
* @author Joker.X
*/
fun onCardClick(info: DemoCardInfo) {
info.route?.let { navigate(it) }
}
/**
* 处理回传结果
*
* @param result 回传结果
* @author Joker.X
*/
fun onResultReceived(result: DemoResult) {
_demoResult.value = result
}

View File

@@ -37,6 +37,7 @@ internal fun UserInfoRoute(
* 用户信息页面
*
* @param onBackClick 返回按钮回调
* @param onLogoutClick 退出登录回调
* @author Joker.X
*/
@OptIn(ExperimentalMaterial3Api::class)
@@ -60,6 +61,7 @@ internal fun UserInfoScreen(
* 用户信息内容
*
* @param modifier 修饰符
* @param onLogoutClick 退出登录回调
* @author Joker.X
*/
@Composable

View File

@@ -27,6 +27,8 @@ class UserInfoViewModel @Inject constructor(
/**
* 一键退出登录(本地清空)
*
* @author Joker.X
*/
fun logout() {
viewModelScope.launch {

View File

@@ -9,6 +9,11 @@ import com.joker.kit.navigation.NavigationResultKey
/**
* 监听返回结果扩展
*
* @param key 结果键,定义序列化/反序列化规则
* @param onResult 结果回调
* @param T 结果数据类型
* @author Joker.X
*/
@Composable
fun <T> NavController.observeResult(

View File

@@ -6,12 +6,36 @@ import kotlinx.serialization.json.Json
/**
* Demo 结果回传示例:返回 DemoResult 数据类
*
* @author Joker.X
*/
object DemoResultKey : NavigationResultKey<DemoResult> {
/**
* 序列化结果
*
* @param value 待序列化的结果对象
* @return 序列化后的字符串
* @author Joker.X
*/
override fun serialize(value: DemoResult): Any = Json.encodeToString(value)
/**
* 反序列化结果
*
* @param raw 原始保存的数据
* @return 解析后的结果对象
* @author Joker.X
*/
override fun deserialize(raw: Any): DemoResult = Json.decodeFromString(raw as String)
}
/**
* Demo 结果数据
*
* @param id 结果标识
* @param message 结果信息
* @author Joker.X
*/
@Serializable
data class DemoResult(
val id: Long,

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.CoolMusic" parent="android:Theme.Material.Light.NoActionBar">
<style name="Theme.AndroidProjectCompose" parent="android:Theme.Material.Light.NoActionBar">
<!--启动界面背景颜色-->
<item name="android:windowSplashScreenBackground">@color/black</item>
<!--启动页图标-->

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.CoolMusic" parent="android:Theme.Material.Light.NoActionBar">
<style name="Theme.AndroidProjectCompose" parent="android:Theme.Material.Light.NoActionBar">
<!--启动页背景颜色-->
<item name="android:windowSplashScreenBackground">@color/white</item>
<!--启动页图标-->

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764494784493" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11376" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M373.840702 202.347171H189.591355a67.836327 67.836327 0 0 0-67.617575 67.836327 65.646714 65.646714 0 0 0 65.647756 67.616533h188.190026l-1.968777-135.45286z" fill="#7585FF" p-id="11377" data-spm-anchor-id="a313x.search_index.0.i18.66f43a81iPdE0v" class=""></path><path d="M793.547376 805.21047H189.372602a67.836327 67.836327 0 0 1-67.398822-67.616533V269.744951a67.836327 67.836327 0 0 0 71.993675 68.05508h501.110891s98.470071-8.53449 98.470071 65.646714v401.763725z" fill="#465CFF" p-id="11378" data-spm-anchor-id="a313x.search_index.0.i16.66f43a81iPdE0v" class=""></path><path d="M474.501428 479.818603a37.638005 37.638005 0 1 0 14.44186 72.211386 37.419252 37.419252 0 0 0-14.442902-72.212427z" p-id="11379"></path><path d="M511.700885 565.596838a60.83415 60.83415 0 0 0-34.792133-109.411885h1.968776a57.550772 57.550772 0 0 1 10.722019 0V418.546948a12.035578 12.035578 0 0 0-5.68966-10.723061 11.816825 11.816825 0 0 0-12.034536 0 12.035578 12.035578 0 0 0-5.68966 10.723061v38.294264a61.052903 61.052903 0 0 0-26.697233 109.412926L328.106755 804.335458h72.649933l51.423607-109.412927a26.696192 26.696192 0 0 1 24.071156-15.098119 26.258686 26.258686 0 0 1 23.851362 15.098119l52.955919 109.412927h73.525987L511.699844 565.596838z m77.683334 228.016601a37.638005 37.638005 0 0 1-26.47848-64.115444 37.419252 37.419252 0 0 1 64.115444 26.477439 37.638005 37.638005 0 0 1-37.636964 37.638005z" fill="#3870B2" p-id="11380"></path><path d="M334.015168 235.609076h258.432634v47.485012H334.015168v-47.485012z" fill="#FFFFFF" p-id="11381"></path><path d="M454.368866 479.818603a37.419252 37.419252 0 1 1-26.697233 10.940772 38.075511 38.075511 0 0 1 26.697233-10.941813z m14.880408-21.883627V418.546948a12.035578 12.035578 0 0 0-5.68966-10.723061 11.816825 11.816825 0 0 0-12.035578 0 12.035578 12.035578 0 0 0-5.68966 10.723061v38.294264a61.052903 61.052903 0 0 0-26.696191 109.412926L279.308183 867.138384a33.260863 33.260863 0 0 0 2.407324 33.479616 33.698369 33.698369 0 0 0 30.417075 14.224149 33.04211 33.04211 0 0 0 27.133698-19.694014l93.220001-200.225604a26.915986 26.915986 0 0 1 24.071156-15.098119 26.258686 26.258686 0 0 1 23.851362 15.098119l95.626283 199.131839a33.04211 33.04211 0 1 0 59.521632-28.885804L491.568323 565.596838a60.615397 60.615397 0 0 0 21.882586-60.614355 61.271656 61.271656 0 0 0-43.765171-46.828754" fill="#073042" p-id="11382"></path><path d="M555.02855 277.404429a17.506485 17.506485 0 0 1-12.691837-29.979569 17.725238 17.725238 0 0 1 30.198322 12.473084 17.506485 17.506485 0 0 1-17.506485 17.506485m-194.535944 0a17.506485 17.506485 0 0 1-12.473084-29.979569 17.725238 17.725238 0 1 1 24.946168 24.946168 17.28669 17.28669 0 0 1-12.473084 5.033401M561.374468 171.272796l35.011929-60.83415a7.439683 7.439683 0 0 0-12.691837-7.438641l-35.449434 61.709161a220.575877 220.575877 0 0 0-180.750343 0l-35.667145-61.709161a7.439683 7.439683 0 0 0-6.34696-3.720883 8.096984 8.096984 0 0 0-6.345918 3.720883 7.439683 7.439683 0 0 0 0 7.439683l35.230681 60.83415a208.321545 208.321545 0 0 0-107.661862 166.525151h422.332751A208.321545 208.321545 0 0 0 561.374468 171.273838" fill="#3DDC84" p-id="11383"></path><path d="M732.933021 467.56323h-27.789957a3.501089 3.501089 0 0 0-3.50213 3.501089v438.745471a3.501089 3.501089 0 0 0 3.282336 3.500047h28.667051a60.83415 60.83415 0 0 0 60.614355-60.833108V406.730122a60.83415 60.83415 0 0 1-61.271655 60.83415z" fill="#7585FF" p-id="11384" data-spm-anchor-id="a313x.search_index.0.i19.66f43a81iPdE0v" class=""></path></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
docs/images/group/qq.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 KiB

View File

@@ -1,7 +1,7 @@
#Fri Nov 28 10:19:33 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME