示例功能

Todos、个人资料、上传、推荐、权限等可替换的产品界面。

这些界面是可运行的产品示例,不是框架层代码。建议保留它们展示的 app pattern,再把文案、字段和业务逻辑替换成你的应用功能。

提供什么

Surface状态实现
HomeExamplefeature/home/HomeScreen.kt + HomeViewModel
TodosExamplefeature/todos/ + TodosRepository
ProfileExamplefeature/profile/ + ProfileRepository
ReferExamplefeature/refer/ReferScreen.kt
PermissionsExamplefeature/permissions/PermissionsScreen.kt
Upload sampleExamplefeature/upload/ + UploadsRepository

Home

Home tab 是 app 内的产品首页示例。它展示 stack overview、基于 entitlement 的 license card、capability preview,以及进入 live demos 的快捷入口。

HomeViewModel 读取两个来源:

来源作用
PurchasesController.isConfigured判断是否配置了 REVENUECAT_ANDROID_KEY
EntitlementController.state判断当前账号是否拥有 Supabase 里的 pro entitlement

这两个来源会映射成 HomeLicenseState.OwnedAvailableUnconfigured。Screen 本身不直接操作导航;HomeRoute 接收 onOpenTodosonOpenUploadsonGetTemplateonManageLicenseonRedeemCode 等 callbacks。新增自己的入口页时,优先复制这种 callback-driven pattern。

"What's inside" preview 指向可删除的 Showcase leaf。相关 seam 用 // Showcase promo (removable leaf) 标记,所以删除 Showcase 时不影响 Home 里的核心 demos。

Todos

Todos 是参考 CRUD 功能。它的 canonical entry 是 Home stack 中的 AppRoutes.Todos,不是底部 tab。

能力代码路径
登录门禁SessionController.currentUser 为空时,TodosScreen 显示 sign-in card
加载/创建/更新/删除TodosViewModel -> TodosRepository -> SupabaseTodosRemoteDataSource
筛选TodosFilter.AllActiveCompleted 渲染为 FilterChip
Inline editRow 内编辑态,带 save/cancel buttons
排序Up/down buttons,先 optimistic UI,再调用 setSortOrders()
错误状态TodosUiState.errorMessage

TodosRepository.clearCompleted() 已存在,但当前 Compose screen 没有暴露 clear-completed 确认弹窗。如果需要这个动作,请在 TodosViewModel 中接线,并在调用 repository helper 前加 Material AlertDialog

Profile

Me tab 是账号中心。未登录用户看到居中的登录提示;已登录用户看到 profile、license、account、notification、permissions、legal 和 sign-out rows。

PersonalInfoScreen 展示可编辑 profile fields 和 app preferences:

字段或设置来源
Full nameprofiles.full_name
Bioprofiles.bio
Phoneprofiles.phone
Locationprofiles.location
Avatar displayprofiles.avatar_url,通过 AppAsyncImage 显示
ThemeThemeController + DataStore
LanguageLocaleController + DataStore

AccountSecurityScreen 展示通常会保留的账号操作:

操作实现
Change email发送 email-change OTP,再验证 OTP
Change password校验新密码,然后调用 Supabase Auth
Biometric app lock通过 BiometricPrompt 确认,再持久化 toggle
Sign out other sessionsauth.signOut(SignOutScope.OTHERS)
Delete account确认弹窗,然后调用 AuthRepository.deleteAccount()

删除账号必须按当前代码诚实理解:它会删除用户的 todos、把 profile 字段置空,并在本机退出登录。它不会删除 Supabase auth.users 行,因为模板还没有附带 delete-account Edge Function。发布到 Google Play 前,请增加服务端删除函数,避免账号删除政策风险。

Refer

ReferScreen 根据 app package name 生成 Play Store URL:

https://play.google.com/store/apps/details?id=<package-name>

它用 Intent.ACTION_SENDIntent.EXTRA_TITLEIntent.EXTRA_TEXT 打开 Android 系统分享面板。分享成功后会调用 RatingController.recordTrigger(activity, "referral_shared"),进入应用内评价的 cooldown 逻辑。

Permissions

Permissions screen 是权限预热页:先解释为什么需要权限,再发起 runtime permission request。

Android 权限说明
NotificationsAndroid 13+ 使用 POST_NOTIFICATIONS;API 33 以下视为已授权FCM 通知显示前需要
CameraCAMERA展示普通 dangerous permission
Trackingcom.google.android.gms.permission.AD_IDAnalytics/attribution SDK 使用的广告 ID 权限

README 仍然提到 Photos,但当前代码没有请求照片/媒体 runtime permission。上传示例使用 Android system photo picker,因此不需要宽泛的相册读取权限。

Upload sample

上传示例是一个实用的 Supabase Storage example:

PickMultipleVisualMedia -> UploadViewModel -> UploadsRepository -> sample-uploads

UploadViewModel 会从选中的 URI 解析 display name、MIME type 和 size,拒绝超过 25 MB 的文件,在主线程外读取 bytes,并把 uploadAsFlow() progress 收集成每个 job 的进度行。成功上传的对象路径是:

<user-id>/<timestamp>-<sanitized-file-name>

Bucket id 硬编码为 UploadsRepository.BUCKET_ID = "sample-uploads"。把它改成真实媒体功能时,请同步替换 bucket、policy 和 object path。

保留这些 pattern

  1. Route composable 放在 feature 边界,把 callbacks 向内传。
  2. Screen composables 尽量保持 stateless。
  3. ViewModel 负责把 repository AppResult 映射成单一 UI state。
  4. 所有 Supabase table access 都走 repository 或 remote data source。
  5. 创建一次性控件前,优先使用 shared component set。
  6. 在产品里诚实标注示例功能,避免 starter/demo copy 意外进入正式产品。

相关页面

On this page