网络父类
BaseNetWorkViewModel、BaseNetWorkUiState 与 BaseNetWorkView 构成了“不分页网络请求 + 三态 UI”的标准方案,文件分别位于:
app/src/main/java/com/joker/kit/core/base/viewmodel/BaseNetWorkViewModel.ktapp/src/main/java/com/joker/kit/core/base/state/BaseNetWorkUiState.ktapp/src/main/java/com/joker/kit/core/ui/component/network/BaseNetWorkView.kt
能力概览
- Flow 即插即用:子类实现
requestApiFlow(),将Flow<NetworkResponse<T>>直接交给基类驱动。 - 统一状态机:
BaseNetWorkUiState只保留最常见的Loading / Success / Error,局部自定义由 UI 决定。 - 最少样板:
executeRequest()、retryRequest()、onRequestStart/Success/Error()处理协程、错误分发、最小加载时间(240 ms,可用enableMinLoadingTime打开)。 - 导航联动:继承自
BaseViewModel,可直接使用类型安全导航、登录拦截、observeRefreshState()等能力。 - 三态视图:
BaseNetWorkView负责加载中、失败、成功三套 UI,错误态内置重试按钮,还允许自定义动画或空态组件。
状态模型:BaseNetWorkUiState
BaseNetWorkUiState<T> 是一个 sealed class,用于描述非分页请求:
| 状态 | 含义 | 默认呈现 |
|---|---|---|
Loading | 进入请求或重试阶段 | PageLoading() |
Success<T> | 成功拿到数据 | content Lambda 收到数据并渲染 |
Error(message, exception) | 网络/业务失败 | EmptyNetwork(),带重试按钮 |
ViewModel 内部通过 MutableStateFlow<BaseNetWorkUiState<T>> 推送状态,视图通过 collectAsState() 即可订阅。
BaseNetWorkViewModel 工作流
重写请求
requestApiFlow()返回真正的网络 Flow,可携带路由参数(借助SavedStateHandle)。触发加载
通过executeRequest()拉取数据,ResultHandler 自动派发 Loading / Success / Error。钩子与选项
showErrorToast:是否在错误时展示默认 Toast。enableMinLoadingTime:详情页常用,避免闪烁。onRequestStart/Success/Error:默认会把数据写入_uiState,若页面有额外副作用(记录足迹、曝光等)可在此覆写,但别忘了调用super以维持状态更新。getSuccessData():在 UI 处于成功状态时直接读数据。observeRefreshState():监听导航返回的刷新信号。
重试
retryRequest()会复位至 Loading 并重新执行请求,通常绑定到BaseNetWorkView的重试按钮。
示例:商品详情
以商品详情为例,ViewModel 负责请求数据并处理足迹、副作用;UI 层遵循模板(context/template/ScreenTemplate.md、ViewModelTemplate.md)所倡导的布局结构:Route -> Screen -> Content,并用默认 Scaffold + BaseNetWorkView 演示三态切换。
kotlin
@HiltViewModel
class GoodsDetailViewModel @Inject constructor(
navigator: AppNavigator,
userState: UserState,
savedStateHandle: SavedStateHandle,
private val repository: GoodsRepository,
) : BaseNetWorkViewModel<GoodsDetail>( // GoodsDetail 是成功态时需要交给 UI 的数据类型
navigator = navigator,
userState = userState,
savedStateHandle = savedStateHandle
) {
// 从路由获取商品 Id
private val goodsId: Long = savedStateHandle.toRoute<GoodsRoutes.Detail>().goodsId
init {
// ViewModel 初始化时立即发起请求
executeRequest()
}
override fun requestApiFlow(): Flow<NetworkResponse<GoodsDetail>> {
// 发起详情接口请求
return repository.getGoodsDetail(goodsId)
}
}布局示例:
kotlin
@Composable
fun GoodsDetailRoute(
viewModel: GoodsDetailViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsState()
Scaffold { innerPadding ->
BaseNetWorkView(
uiState = uiState,
padding = innerPadding,
onRetry = viewModel::retryRequest, // 点击失败页的重试按钮时重新请求
customLoading = { ShimmerGoodsPlaceholder() }, // 可自定义加载动画;不传则使用默认 PageLoading
) { detail -> // detail: GoodsDetail
GoodsDetailScreen(data = detail)
}
}
}BaseNetWorkView内置加载、失败、成功切换;若需要自定义动画或文案,只需提供customLoading/customError。onRetry默认触发retryRequest(),确保状态切回 Loading 并重新请求。GoodsDetailScreen可以继续拆分Content组件,内部遵循designsystem中的尺寸/间距常量。
通过这一套基类 + 模板化布局,业务页面只需定义请求 Flow、编写成功态 UI,即可获得一致的加载/失败/重试体验,并与导航、登录拦截、刷新逻辑无缝衔接。
API 参考
BaseNetWorkViewModel<T>
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
savedStateHandle | SavedStateHandle? | null | 当前路由参数句柄,可读取 URL 参数 |
uiState | StateFlow<BaseNetWorkUiState<T>> | Loading | 供 UI 层订阅的三态数据 |
_uiState | MutableStateFlow<BaseNetWorkUiState<T>> | Loading | 子类可覆写以自定义状态 |
showErrorToast | Boolean | false | 是否在 Error 阶段弹 Toast,protected open |
enableMinLoadingTime | Boolean | false | 是否启用 240ms 最短加载动画 |
requestApiFlow() | 函数 | - | protected abstract fun requestApiFlow(): Flow<NetworkResponse<T>> |
executeRequest() | 函数 | - | 触发请求并派发 Loading/Success/Error |
retryRequest() | 函数 | - | 重置状态后重新执行请求 |
onRequestStart() | 函数 | - | 默认调用 setLoadingState(),可覆写插入逻辑 |
onRequestSuccess(data) | 函数 | - | 默认写入成功状态,可覆写处理副作用 |
onRequestError(message, exception) | 函数 | - | 默认调用 setErrorState() |
setLoadingState() | 函数 | - | 将 _uiState 设为 Loading |
setSuccessState(data) | 函数 | - | 将 _uiState 设为 Success(data) |
setErrorState(message, exception) | 函数 | - | 将 _uiState 设为 Error,可自定义提示 |
getSuccessData() | 函数 | - | 直接读取成功态数据,非成功状态会抛异常 |
observeRefreshState(backStackEntry, key) | 函数 | key = RefreshResultKey | 监听导航返回刷新信号 |
BaseNetWorkView
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
uiState | BaseNetWorkUiState<T> | - | 控制 Loading/Success/Error 展示 |
modifier | Modifier | Modifier | 用于 fillMaxSize()、background() 等 |
padding | PaddingValues | PaddingValues() | 把 Scaffold 的 innerPadding 透传即可 |
onRetry | () -> Unit | {} | 失败页点击后触发的回调 |
customLoading | @Composable (() -> Unit)? | null | 自定义加载态,null 时用 PageLoading() |
customError | @Composable (() -> Unit)? | null | 自定义错误态,默认 EmptyNetwork() |
content | @Composable (data: T) -> Unit | - | 成功态内容,参数为请求结果 |