Files
habo/docs/04-ASSETS.md
dazhuang aa69f2a91e feat: initial commit - Habo habit tracking app
- Complete MVP with Repository Pattern, SQLite storage
- Provider + ChangeNotifier state management
- Navigation 2.0 with deep link support
- Habit CRUD with twoDayRule, notifications, categories
- Backup/Restore via JSON
- Statistics with streak tracking
- Material You theme support
- Biometric lock support
- Desktop widget support
- 27 languages i18n structure
- Comprehensive test suite (87/89 passing)
2026-04-13 15:02:30 +00:00

20 KiB
Raw Permalink Blame History

Habo 可复用素材清单

从原项目提取的静态资源、国际化文本、测试用例,作为 AI 复刻项目的起点素材


1. 静态资源清单

1.1 图片资源 (assets/images/)

文件 用途 格式
icon.png Android 应用图标 PNG
ios_icon.jpg iOS 应用图标 JPG
macos_icon.png macOS 应用图标 PNG
app_icon.png 通用应用图标 PNG
splash_icon.png 启动画面图标 PNG
splash_icon2.png 备用启动图标 PNG
android_foreground.png Android 自适应图标前景 PNG
android_background.png Android 自适应图标背景 PNG
android_monochrome.svg Android 单色图标 SVG
emptyList.svg 空列表占位图 SVG
noDataStatistics.svg 统计页空数据占位图 SVG

1.2 引导页图片 (assets/images/onboard/)

文件 用途
1.svg 第 1 步: 定义习惯 (空列表插图)
2.svg 第 2 步: 记录天数 (习惯追踪插图)
3.svg 第 3 步: 观察进步 (进度追踪插图)

1.3 音效资源 (assets/sounds/)

文件 用途 说明
check.wav 打卡完成音效 成功完成的正向音效
click.wav 通用点击音效 失败/跳过等操作的反馈音效
sound_sources.txt 音效来源说明 开源协议信息

1.4 字体资源 (assets/google_fonts/)

字体族: Nunito18 个字重/样式变体)

文件 字重
Nunito-ExtraLight.ttf 200
Nunito-Light.ttf 300
Nunito-Regular.ttf 400
Nunito-Medium.ttf 500
Nunito-SemiBold.ttf 600
Nunito-Bold.ttf 700
Nunito-ExtraBold.ttf 800
Nunito-Black.ttf 900
以及对应的 Italic 变体

许可证: OFL (SIL Open Font License)


2. 国际化文本 (英文基准 ARB)

完整的 intl_en.arb,包含 354 个键,可直接复制为项目的国际化基准文件

{
    "@@locale": "en",
    "habits": "Habits:",
    "statistics": "Statistics",
    "emptyList": "Empty list",
    "noDataAboutHabits": "There is no data about habits.",
    "topStreak": "Top streak",
    "currentStreak": "Current streak",
    "total": "Total",
    "unknown": "Unknown",
    "warning": "Warning",
    "allHabitsWillBeReplaced": "All habits will be replaced with habits from backup.",
    "restore": "Restore",
    "cancel": "Cancel",
    "settings": "Settings",
    "theme": "Theme",
    "firstDayOfWeek": "First day of the week",
    "notifications": "Notifications",
    "notificationTime": "Notification time",
    "soundEffects": "Sound effects",
    "showMonthName": "Show month name",
    "setColors": "Set colors",
    "backup": "Backup",
    "create": "Create",
    "onboarding": "Onboarding",
    "about": "About",
    "habo": "Habo",
    "copyright": "©2023 Habo",
    "termsAndConditions": "Terms and Conditions",
    "privacyPolicy": "Privacy Policy",
    "disclaimer": "Disclaimer",
    "sourceCode": "Source code (GitHub)",
    "ifYouWantToSupport": "If you want to support Habo you can:",
    "buyMeACoffee": "Buy me a coffee",
    "reset": "Reset",
    "done": "Done",
    "congratulationsReward": "Congratulations! Your reward:",
    "ohNoSanction": "Oh no! Your sanction:",
    "month": "Month",
    "week": "Week",
    "habitLoop": "Habit loop",
    "habitLoopDescription": "Habit Loop is a psychological model describing the process of habit formation. It consists of three components: Cue, Routine, and Reward. The Cue triggers the Routine (habitual action), which is then reinforced by the Reward, creating a loop that makes the habit more ingrained and likely to be repeated.",
    "cue": "Cue",
    "cueDescription": "is the trigger that initiates your habit. It could be a specific time, location, feeling, or an event.",
    "routine": "Routine",
    "routineDescription": "is the action you take in response to the cue. This is the habit itself.",
    "reward": "Reward",
    "rewardDescription": "is the benefit or positive feeling you experience after performing the routine. It reinforces the habit.",
    "editHabit": "Edit Habit",
    "createHabit": "Create Habit",
    "delete": "Delete",
    "habitTitleEmptyError": "The habit title can not be empty.",
    "save": "Save",
    "exercise": "Exercise",
    "habit": "Habit",
    "useTwoDayRule": "Use Two day rule",
    "twoDayRule": "Two day rule",
    "twoDayRuleDescription": "With two day rule, you can miss one day and do not lose a streak if the next day is successful.",
    "advancedHabitBuilding": "Advanced habit building",
    "advancedHabitBuildingDescription": "This section helps you better define your habits utilizing the Habit loop. You should define cues, routines, and rewards for every habit.",
    "at7AM": "At 7:00AM",
    "do50PushUps": "Do 50 push ups",
    "fifteenMinOfVideoGames": "15 min. of video games",
    "showReward": "Show reward",
    "remainderOfReward": "The reminder of the reward after a successful routine.",
    "habitContract": "Habit contract",
    "habitContractDescription": "While positive reinforcement is recommended, some people may opt for a habit contract. A habit contract allows you to specify a sanction that will be imposed if you miss your habit, and may involve an accountability partner who helps supervise your goals.",
    "donateToCharity": "Donate 10$ to charity",
    "sanction": "Sanction",
    "showSanction": "Show sanction",
    "remainderOfSanction": "The reminder of the sanction after a unsuccessful routine.",
    "dan": "Dan",
    "accountabilityPartner": "Accountability partner",
    "add": "Add",
    "haboNeedsPermission": "Habo needs permission to send notifications to work properly.",
    "allow": "Allow",
    "date": "Date",
    "check": "Check",
    "fail": "Fail",
    "skip": "Skip",
    "note": "Note",
    "yourCommentHere": "Your note here",
    "close": "Close",
    "createYourFirstHabit": "Create your first habit.",
    "modify": "Modify",
    "backupFailedError": "ERROR: Creating backup failed.",
    "restoreFailedError": "ERROR: Restoring backup failed.",
    "habitDeleted": "Habit deleted.",
    "undo": "Undo",
    "appNotifications": "App notifications",
    "appNotificationsChannel": "Notification channel for application notifications",
    "habitNotifications": "Habit notifications",
    "habitNotificationsChannel": "Notification channel for habit notifications",
    "doNotForgetToCheckYourHabits": "Do not forget to check your habits.",
    "themeSelect": "{theme, select, device {Device} light {Light} dark {Dark} oled {OLED black} materialYou {Material You} other{Device}}",
    "@themeSelect": {
      "placeholders": {
        "theme": {
          "type": "String"
        }
      }
    },
    "defineYourHabits": "Define your habits",
    "defineYourHabitsDescription": "To better stick to your habits, you can define:",
    "cueNumbered": "1. Cue",
    "routineNumbered": "2. Routine",
    "rewardNumbered": "3. Reward",
    "logYourDays": "Log your days",
    "successful": "Successful",
    "notSoSuccessful": "Not so successful",
    "skipDoesNotAffectStreaks": "Skip (does not affect streaks)",
    "observeYourProgress": "Observe your progress",
    "trackYourProgress": "You can track your progress through the calendar view in every habit or on the statistics page.",
    "backupCreatedSuccessfully": "Backup created successfully!",
    "backupFailed": "Backup failed!",
    "restoreCompletedSuccessfully": "Restore completed successfully!",
    "restoreFailed": "Restore failed!",
    "fileNotFound": "File not found",
    "fileTooLarge": "File too large (max 10MB)",
    "invalidBackupFile": "Invalid backup file",
    "progress": "Progress",
    "enterAmount": "Enter amount",
    "complete": "Complete",
    "saveProgress": "Save Progress",
    "currentProgress": "Current: {current} {unit}",
    "@currentProgress": {
      "placeholders": {
        "current": { "type": "String" },
        "unit": { "type": "String" }
      }
    },
    "targetProgress": "Target: {target} {unit}",
    "@targetProgress": {
      "placeholders": {
        "target": { "type": "String" },
        "unit": { "type": "String" }
      }
    },
    "progressOf": "{current} / {target} {unit}",
    "@progressOf": {
      "placeholders": {
        "current": { "type": "String" },
        "target": { "type": "String" },
        "unit": { "type": "String" }
      }
    },
    "numericHabit": "Progressive",
    "targetValue": "Target value",
    "partialValue": "Partial value",
    "unit": "Unit",
    "habitType": "Habit type",
    "booleanHabit": "Checkable (Yes/No)",
    "slider": "Slider",
    "input": "Input",
    "numericHabitDescription": "Numeric habits let you track progress in increments throughout the day.",
    "partialValueDescription": "To track progress in smaller increments",
    "categories": "Categories",
    "addCategory": "Add Category",
    "editCategory": "Edit Category",
    "category": "Category",
    "noCategoriesYet": "No categories yet",
    "createFirstCategory": "Create your first category to organize your habits",
    "pleaseEnterCategoryTitle": "Please enter a category title",
    "categoryAlreadyExists": "Category \"{title}\" already exists",
    "@categoryAlreadyExists": { "placeholders": { "title": { "type": "String" } } },
    "categoryCreatedSuccessfully": "Category \"{title}\" created successfully",
    "@categoryCreatedSuccessfully": { "placeholders": { "title": { "type": "String" } } },
    "categoryUpdatedSuccessfully": "Category \"{title}\" updated successfully",
    "@categoryUpdatedSuccessfully": { "placeholders": { "title": { "type": "String" } } },
    "categoryDeletedSuccessfully": "Category \"{title}\" deleted successfully",
    "@categoryDeletedSuccessfully": { "placeholders": { "title": { "type": "String" } } },
    "failedToSaveCategory": "Failed to save category: {error}",
    "@failedToSaveCategory": { "placeholders": { "error": { "type": "String" } } },
    "failedToDeleteCategory": "Failed to delete category: {error}",
    "@failedToDeleteCategory": { "placeholders": { "error": { "type": "String" } } },
    "selectCategories": "Select Categories",
    "selectedCategories": "Selected Categories ({count})",
    "@selectedCategories": { "placeholders": { "count": { "type": "int" } } },
    "allCategories": "All Categories",
    "deleteCategory": "Delete Category",
    "deleteCategoryConfirmation": "Are you sure you want to delete \"{title}\"?\n\nThis will remove the category from all habits that use it.",
    "@deleteCategoryConfirmation": { "placeholders": { "title": { "type": "String" } } },
    "noHabitsInCategory": "No habits in \"{title}\"",
    "@noHabitsInCategory": { "placeholders": { "title": { "type": "String" } } },
    "createHabitForCategory": "Create a habit and assign it to this category",
    "showCategories": "Show Categories",
    "archive": "Archive",
    "unarchive": "Unarchive",
    "archiveHabit": "Archive habit",
    "unarchiveHabit": "Unarchive habit",
    "archivedHabits": "Archived Habits",
    "noArchivedHabits": "No archived habits",
    "viewArchivedHabits": "View archived habits",
    "habitArchived": "Habit archived",
    "habitUnarchived": "Habit unarchived",
    "biometric": "Biometric",
    "biometricLockEnabled": "Biometric lock enabled",
    "biometricLockDisabled": "Biometric lock disabled",
    "authenticationError": "Authentication error",
    "biometricAuthenticationRequired": "Biometric authentication required",
    "setupFingerprintFaceUnlock": "Please set up your fingerprint or face unlock in device settings",
    "touchSensor": "Touch sensor",
    "biometricNotRecognized": "Biometric not recognized, try again",
    "biometricRequired": "Biometric required",
    "biometricAuthenticationSucceeded": "Biometric authentication succeeded",
    "deviceCredentialsRequired": "Device credentials required",
    "setupDeviceCredentials": "Please set up device credentials in settings",
    "setupTouchIdFaceId": "Please set up your Touch ID or Face ID in device settings",
    "reenableTouchIdFaceId": "Please reenable your Touch ID or Face ID",
    "biometricLock": "Biometric Lock",
    "biometricLockDescription": "Secure app with {authMethod}",
    "@biometricLockDescription": { "placeholders": { "authMethod": { "type": "String" } } },
    "authenticateToEnable": "Authenticate to enable biometric lock",
    "authenticateToAccess": "Please authenticate to access Habo",
    "authenticationRequired": "Authentication Required",
    "authenticationFailedMessage": "Please authenticate to access Habo using {authMethod}",
    "@authenticationFailedMessage": { "placeholders": { "authMethod": { "type": "String" } } },
    "tryAgain": "Try Again",
    "authenticating": "Authenticating…",
    "authenticate": "Authenticate",
    "buildingBetterHabits": "Building Better Habits",
    "authenticationPrompt": "Please authenticate using {authMethod} to access your habits",
    "@authenticationPrompt": { "placeholders": { "authMethod": { "type": "String" } } },
    "devicePinPatternPassword": "Device PIN, Pattern, or Password",
    "fingerprint": "Fingerprint",
    "iris": "Iris",
    "whatsNewTitle": "What's New",
    "whatsNewVersion": "Version {version}",
    "@whatsNewVersion": { "placeholders": { "version": { "type": "String" } } },
    "featureNumericTitle": "Numeric values in habits",
    "featureNumericDesc": "Track counts like glasses of water or pages read",
    "featureDeepLinksTitle": "URL scheme (deep links)",
    "featureDeepLinksDesc": "Open Habo directly to screens like settings or create",
    "featureCategoriesTitle": "Categories",
    "featureCategoriesDesc": "Organize habits with category filters",
    "featureArchiveTitle": "Archive",
    "featureArchiveDesc": "Hide habits you no longer track without deleting",
    "featureMaterialYouTitle": "Material You theme (Android)",
    "featureMaterialYouDesc": "Dynamic colors that match your wallpaper",
    "featureSoundTitle": "New sound engine",
    "featureSoundDesc": "Adjustable volume",
    "featureLockTitle": "Lock feature",
    "featureLockDesc": "Secure the app with Face ID / Touch ID / biometrics",
    "featureIosSoundMixingTitle": "Fixed sound mixing",
    "featureIosSoundMixingDesc": "Habo sounds no longer interrupt your music or podcasts",
    "featureHomescreenWidgetTitle": "Homescreen widget",
    "featureHomescreenWidgetDesc": "View your habit progress at a glance from your home screen (experimental)",
    "featureLongpressCheckTitle": "Longpress check",
    "featureLongpressCheckDesc": "Longpress on habit buttons to quickly change status",
    "haboSyncComingSoon": "Coming Soon",
    "haboSyncDescription": "Sync your habits across all your devices with Habo's end-to-end encrypted cloud service.",
    "haboSyncLearnMore": "Learn more at habo.space/sync",
    "habitsToday": "Habits today",
    "or": "or",
    "oneTapCheck": "Single tap to check",
    "tapCheckLongPressMenu": "Tap to check, long press for menu",
    "categoryName": "Category name",
    "createCategory": "Create category",
    "all": "All",
    "selectIcon": "Pick an icon",
    "searchIcons": "Search"
}

3. 支持的语言 (27 种)

代码 语言
en English (基准)
zh_Hans 中文简体
zh_Hant 中文繁體
es Español
fr Français
de Deutsch
it Italiano
pt Português
pt_BR Português (Brasil)
ru Русский
ja (可能缺失, 需确认)
ar العربية
he עברית
pl Polski
nl Nederlands
sv Svenska
cs Čeština
sk Slovenčina
uk Українська
vi Tiếng Việt
id Bahasa Indonesia
tr Türkçe
ca Català
ta தமிழ்
nb_NO Norsk bokmål
eo Esperanto
ia Interlingua
ckb کوردی

4. 测试用例清单

13 个测试文件,覆盖核心业务逻辑

4.1 单元测试

文件 测试目标 测试用例数
test/habits/habits_manager_test.dart HabitsManager CRUD 13
test/habits/habits_manager_updated_test.dart Repository 模式集成 8
test/habits/habits_manager_fixed_test.dart 归档功能 8
test/habits/habits_manager_notifications_test.dart 通知调度 3
test/habits/backup_enhancement_test.dart 备份格式 4
test/services/backup_service_test.dart 备份服务 7
test/services/backup_feature_comprehensive_test.dart 备份完整性 18
test/services/notification_service_test.dart 通知服务 11
test/app_test.dart 应用初始化 4
test/repositories/repository_test.dart Repository 模式 6

4.2 Widget 测试

文件 测试目标 测试用例数
test/widgets/habit_details_widget_test.dart 习惯详情组件 3
test/widgets/habit_list_widget_test.dart 习惯列表组件 2

4.3 集成测试

文件 测试目标 测试用例数
test/integration/habit_crud_integration_test.dart 完整 CRUD 流程 2

4.4 测试 Mock 基础设施

文件 内容
test/mocks/mock_repositories.dart MockHabitRepository, MockEventRepository, MockCategoryRepository, MockBackupRepository + InMemory 实现用于集成测试

4.5 关键测试场景摘要

HabitsManager 测试:

  • 初始化时从 Repository 加载习惯
  • 空列表正确处理
  • 创建习惯并分配正确位置
  • 编辑更新已有习惯
  • 删除习惯并支持 Undo
  • 归档/取消归档切换
  • 活跃/归档习惯正确过滤
  • 位置排序更新
  • 通知调度触发

备份测试:

  • 正确的 JSON 结构验证
  • 时间戳格式验证
  • 空数据备份处理
  • 事件类型保留
  • 分类关联保留
  • 大数据集处理
  • 损坏数据检测
  • 10MB 文件大小限制
  • 并发操作处理

通知测试:

  • 空习惯列表不崩溃
  • 习惯通知调度
  • 事件添加/删除触发通知
  • 多习惯批量通知
  • 当日/非当日事件区分

5. 备份文件格式规格

5.1 当前格式 (Version 3)

{
  "version": 3,
  "habits": [
    {
      "id": 1,
      "position": 0,
      "title": "Exercise",
      "twoDayRule": false,
      "cue": "At 7:00AM",
      "routine": "Do 50 push ups",
      "reward": "15 min. of video games",
      "showReward": true,
      "advanced": true,
      "notification": true,
      "notTime": {"hour": 7, "minute": 0},
      "events": {
        "2024-01-01 00:00:00.000": [1, ""],
        "2024-01-02 00:00:00.000": [2, "Tired"]
      },
      "sanction": "Donate 10$ to charity",
      "showSanction": true,
      "accountant": "Dan",
      "habitType": 0,
      "targetValue": 1.0,
      "partialValue": 1.0,
      "unit": ""
    }
  ],
  "categories": [
    { "id": 1, "title": "Health", "iconCodePoint": 58718, "fontFamily": "fontAwesomeFlutter" }
  ],
  "habit_categories": [
    { "habit_id": 1, "category_id": 1 }
  ],
  "metadata": {
    "import_timestamp": "2024-01-15T10:30:00.000Z"
  }
}

5.2 旧版兼容格式 (数组)

[
  {
    "id": 1,
    "title": "Exercise",
    "position": 0,
    "events": {},
    ...
  }
]

5.3 事件类型编码

DayType 含义
0 clear 清除/无事件
1 check 完成
2 fail 失败
3 skip 跳过
4 progress 进度(部分完成)

6. pubspec.yaml 关键配置

name: habo
version: 3.1.2+5115

environment:
  sdk: ">=3.11.0 <4.0.0"
  flutter: ">=3.41.1"

flutter:
  uses-material-design: true
  generate: true
  assets:
    - assets/
    - assets/images/
    - assets/images/onboard/
    - assets/sounds/
    - assets/google_fonts/

flutter_intl:
  enabled: true

flutter_native_splash:
  color: "#FAFAFA"
  color_dark: "#000000"
  image: assets/images/splash_icon.png
  image_dark: assets/images/splash_icon.png
  ios_content_mode: center
  android_gravity: center
  fullscreen: false
  android_12:
    color: "#FAFAFA"
    color_dark: "#000000"
    image: assets/images/splash_icon.png
  android: true
  ios: true
  web: false