297 Commits

Author SHA1 Message Date
住京华
b8ce69011c feat(video): use ffmpeg as video backend (#241)
* chore(deps): update dependencies

* chore(deps): update dependencies

* fix: update ios and macos platform version

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* feat: add argon2 support

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* chore(deps): update deps

* feat(video): use ffmpeg as video backend

* fix(video): simplify video view invocation and improve button interaction

* feat(button): add customizable color option to PageBackButton

[skip ci]
2025-05-19 00:18:51 +08:00
住京华
4d93fe259b Merge pull request #240 from liruohrh/feat/ci_win
ci: add ci for windows
2025-05-13 14:58:39 +08:00
liruohrh
466a664157 fix(rust): new zip, use std::os::unix only in unix 2025-05-12 19:41:07 +08:00
liruohrh
7885b68415 ci: github actions for windows 2025-05-11 23:00:56 +08:00
github-actions[bot]
8ef72433eb Merge pull request #238 from ZhuJHua/changelog-update
Update changelog
2025-04-18 04:06:47 +00:00
github-actions[bot]
073e483112 Update changelog 2025-04-18 04:06:41 +00:00
住京华
967f652d9a Merge pull request #237 from ZhuJHua/develop
fix: qweather api error
2025-04-18 12:06:14 +08:00
ZhuJHua
64ca18ef44 fix: qweather api error
(cherry picked from commit 2e206a9335)
2025-04-18 12:02:41 +08:00
github-actions[bot]
c1948f888e Merge pull request #236 from ZhuJHua/changelog-update
Update changelog
2025-04-18 04:01:43 +00:00
github-actions[bot]
2b7f750b4c Update changelog 2025-04-18 04:01:39 +00:00
住京华
ef3f0270a6 Merge pull request #235 from ZhuJHua/develop
feat: integrate Jieba for keyword extraction and search optimization
2025-04-18 12:01:29 +08:00
ZhuJHua
8d9f380ec5 Revert "fix: qweather api error"
This reverts commit 2e206a9335.
2025-04-18 11:58:51 +08:00
ZhuJHua
2e206a9335 fix: qweather api error 2025-04-18 11:54:16 +08:00
ZhuJHua
f642530bca feat: integrate Jieba for keyword extraction and search optimization 2025-04-18 11:19:01 +08:00
github-actions[bot]
13e3dc1bb2 Merge pull request #234 from ZhuJHua/changelog-update
Update changelog
2025-04-17 03:51:56 +00:00
github-actions[bot]
ce319374a2 Update changelog 2025-04-17 03:51:50 +00:00
住京华
eee3cc03d8 Merge pull request #233 from ZhuJHua/develop
feat: optimize image processing capabilities
2025-04-17 11:51:40 +08:00
ZhuJHua
372678fa82 feat: optimize image processing capabilities 2025-04-17 11:41:41 +08:00
ZhuJHua
9b3757dea1 ci: add test module to lib.rs 2025-04-17 11:20:11 +08:00
ZhuJHua
cc4cee941d ci: update rust test ci 2025-04-17 11:15:48 +08:00
ZhuJHua
3be9f90575 chore(deps): update dependencies 2025-04-16 23:56:59 +08:00
github-actions[bot]
87a7af08ff Merge pull request #231 from ZhuJHua/changelog-update
Update changelog
2025-04-14 07:34:22 +00:00
github-actions[bot]
cf97917117 Update changelog 2025-04-14 07:34:17 +00:00
住京华
487972451c Merge pull request #230 from ZhuJHua/develop
feat: add code scanning to import support
2025-04-14 15:34:06 +08:00
ZhuJHua
2eb6ab7409 feat: add code scanning to import support 2025-04-14 15:30:23 +08:00
github-actions[bot]
f106154fe4 Merge pull request #229 from ZhuJHua/changelog-update
Update changelog
2025-04-13 13:42:42 +00:00
github-actions[bot]
9524193261 Update changelog 2025-04-13 13:42:36 +00:00
住京华
59346795ae Merge pull request #228 from ZhuJHua/develop
refactor: optimize the code structure
2025-04-13 21:42:24 +08:00
ZhuJHua
96682cb593 refactor: optimize the code structure 2025-04-13 21:36:41 +08:00
ZhuJHua
06b52fce5d chore(deps): update permission_handler and syncfusion_flutter_sliders versions 2025-04-09 23:54:58 +08:00
ZhuJHua
9edc55a6c8 chore(deps): update flutter_map_cache and flutter_native_splash versions 2025-04-07 23:57:58 +08:00
ZhuJHua
d25231c413 feat(widget): add utility functions to capture widget as image 2025-04-05 23:56:40 +08:00
ZhuJHua
6584cca855 chore(deps): update project configuration and dependencies 2025-04-02 23:58:59 +08:00
ZhuJHua
12e4e808da chore(deps): update project configuration and dependencies 2025-04-01 23:52:46 +08:00
ZhuJHua
3266d9f438 chore(deps): update project configuration and dependencies 2025-03-28 23:56:46 +08:00
ZhuJHua
c39f5b30f8 chore(deps): update project configuration and dependencies 2025-03-27 23:35:59 +08:00
ZhuJHua
d5e5b94607 chore(deps): update project configuration and dependencies 2025-03-25 22:34:00 +08:00
github-actions[bot]
ddb6d8f2db Merge pull request #221 from ZhuJHua/changelog-update
Update changelog
2025-03-22 21:55:31 +00:00
github-actions[bot]
a7f7601408 Update changelog 2025-03-22 21:55:27 +00:00
住京华
5edaf2f6cf Merge pull request #220 from ZhuJHua/develop
chore: remove custom_lint to fix dart analyzer slow
2025-03-23 05:55:15 +08:00
ZhuJHua
b274622006 chore: remove custom_lint to fix dart analyzer slow 2025-03-23 05:36:05 +08:00
github-actions[bot]
6de1968ddf Merge pull request #219 from ZhuJHua/changelog-update
Update changelog
2025-03-22 20:35:36 +00:00
github-actions[bot]
aeef1e511c Update changelog 2025-03-22 20:35:32 +00:00
住京华
9589dc9e54 Merge pull request #218 from ZhuJHua/develop
fix(share): fix capture and share methods
2025-03-23 04:35:20 +08:00
ZhuJHua
4abb85ad42 fix: remove workspace 2025-03-23 04:30:17 +08:00
ZhuJHua
f9f2056d04 fix(share): fix capture and share methods 2025-03-22 23:38:38 +08:00
ZhuJHua
182008a678 chore(deps): update isar_flutter_libs dependency to use git source 2025-03-22 23:22:57 +08:00
ZhuJHua
0b1a9bf4c8 chore(deps): update package versions 2025-03-21 23:58:26 +08:00
ZhuJHua
cd51e8922b chore(readme): update Flutter and Dart versions in documentation 2025-03-20 17:27:07 +08:00
github-actions[bot]
8c9fe08128 Merge pull request #214 from ZhuJHua/changelog-update
Update changelog
2025-03-19 15:32:33 +00:00
github-actions[bot]
8dc18c92cd Update changelog 2025-03-19 15:32:25 +00:00
住京华
2a9900556e Merge pull request #213 from ZhuJHua/develop
chore(deps): update dependencies to latest versions
2025-03-19 23:32:08 +08:00
ZhuJHua
0e46a830af chore(deps): update dependencies to latest versions 2025-03-19 23:27:42 +08:00
ZhuJHua
41709c8832 chore(deps): update dependencies to latest versions 2025-03-18 23:57:10 +08:00
住京华
316fa1119f Merge pull request #212
docs: update sponsor list
2025-03-16 21:23:46 +08:00
ZhuJHua
0df58eed05 docs: update sponsor list
[skip ci]
2025-03-16 21:23:19 +08:00
github-actions[bot]
7d936ac1c3 Merge pull request #211 from ZhuJHua/changelog-update
Update changelog
2025-03-16 12:56:57 +00:00
github-actions[bot]
4a1c2609ab Update changelog 2025-03-16 12:56:53 +00:00
住京华
28dc3e1114 Merge pull request #210 from ZhuJHua/develop
feat(zip): use rust to increase data export speed
2025-03-16 20:56:41 +08:00
ZhuJHua
cd78b4d64d chore(deps): fix library error 2025-03-16 20:53:58 +08:00
ZhuJHua
e32bb156fe chore(deps): remove unnecessary dependencies 2025-03-16 20:47:47 +08:00
ZhuJHua
ab8f40b98f chore(ci): remove redundant dependency installation for Rust builder 2025-03-16 20:22:45 +08:00
ZhuJHua
235f632670 feat(zip): use rust to increase data export speed 2025-03-16 20:18:47 +08:00
ZhuJHua
af3bf29139 chore(deps): use dart workspace 2025-03-16 20:14:58 +08:00
ZhuJHua
1afe59ac70 chore(deps): update dependencies 2025-03-16 00:03:53 +08:00
ZhuJHua
406e042411 chore(deps): update dependencies 2025-03-16 00:03:53 +08:00
ZhuJHua
8d95476d04 chore(deps): update dependencies 2025-03-16 00:03:52 +08:00
ZhuJHua
83049f598e chore(deps): update dependencies in pubspec.lock and pubspec.yaml 2025-03-16 00:03:52 +08:00
住京华
047a3ac576 Merge pull request #203 from ZhuJHua/develop
ci: fix build
2025-03-03 00:50:39 +08:00
ZhuJHua
fe2f29c939 ci: fix build
[skip ci]
2025-03-03 00:49:58 +08:00
住京华
c631636472 Merge pull request #202 from ZhuJHua/develop
ci: fix build error
2025-03-03 00:22:22 +08:00
ZhuJHua
ca9c553df1 ci(fix build error):
[skip ci]
2025-03-03 00:20:57 +08:00
github-actions[bot]
4ca93f15ca Merge pull request #201 from ZhuJHua/changelog-update
Update changelog
2025-03-02 16:17:45 +00:00
github-actions[bot]
9906d8c1af Update changelog 2025-03-02 16:17:42 +00:00
住京华
3a00ffa04f Merge pull request #200 from ZhuJHua/develop
feat: enhance diary card views with icons and improve page transitions
2025-03-03 00:17:30 +08:00
ZhuJHua
5328649b67 feat: enhance diary card views with icons and improve page transitions 2025-03-03 00:06:51 +08:00
github-actions[bot]
b3b3f526b4 Merge pull request #199 from ZhuJHua/changelog-update
Update changelog
2025-03-02 15:32:10 +00:00
github-actions[bot]
1f438e5c52 Update changelog 2025-03-02 15:32:06 +00:00
住京华
c4f5cf7d96 Merge pull request #198 from ZhuJHua/develop
feat: add an abstract sync interface
2025-03-02 23:31:54 +08:00
ZhuJHua
443de5d35a feat: add an abstract sync interface 2025-03-02 23:26:17 +08:00
github-actions[bot]
c424839453 Merge pull request #197 from ZhuJHua/changelog-update
Update changelog
2025-03-01 23:39:26 +00:00
github-actions[bot]
6d9489076e Update changelog 2025-03-01 23:39:22 +00:00
住京华
cb43f50ea5 Merge pull request #196 from ZhuJHua/develop
chore: update NDK and plugin versions in build configuration
2025-03-02 07:39:10 +08:00
ZhuJHua
b6e116418b chore: update .gitignore 2025-03-02 07:35:42 +08:00
ZhuJHua
574e7090cc chore: update NDK and plugin versions in build configuration 2025-03-02 07:31:21 +08:00
github-actions[bot]
77b9bd5005 Merge pull request #195 from ZhuJHua/changelog-update
Update changelog
2025-03-01 23:23:13 +00:00
github-actions[bot]
7c6f704895 Update changelog 2025-03-01 23:23:10 +00:00
住京华
7ed54ddf95 Merge pull request #194 from ZhuJHua/develop
refactor: update tile caching implementation
2025-03-02 07:22:59 +08:00
ZhuJHua
d992cc173d refactor: remove Objectbox plugin references and update tile caching implementation 2025-03-02 07:05:48 +08:00
github-actions[bot]
1671c3c770 Merge pull request #193 from ZhuJHua/changelog-update
Update changelog
2025-03-01 20:53:40 +00:00
github-actions[bot]
89f9045e30 Update changelog 2025-03-01 20:53:35 +00:00
住京华
80ad276634 Merge pull request #192 from ZhuJHua/develop
refactor: streamline theme management and dynamic color support
2025-03-02 04:53:20 +08:00
ZhuJHua
3fb5573a7c refactor: streamline theme management and dynamic color support 2025-03-02 04:47:50 +08:00
ZhuJHua
9b72ceaa6e chore: update dependencies in pubspec.yaml to latest versions 2025-03-01 23:59:33 +08:00
github-actions[bot]
8047c391b3 Merge pull request #189 from ZhuJHua/changelog-update
Update changelog
2025-02-24 04:18:21 +00:00
github-actions[bot]
ea053f5a73 Update changelog 2025-02-24 04:18:17 +00:00
住京华
31560b783f Merge pull request #188 from ZhuJHua/develop
fix: update diary_view to use Get.put for DiaryLogic instantiation
2025-02-24 12:18:05 +08:00
ZhuJHua
d25d8c8269 fix: update diary_view to use Get.put for DiaryLogic instantiation 2025-02-24 12:14:55 +08:00
github-actions[bot]
fae6316f55 Merge pull request #187 from ZhuJHua/changelog-update
Update changelog
2025-02-23 13:55:23 +00:00
github-actions[bot]
63ba3c2b68 Update changelog 2025-02-23 13:55:18 +00:00
住京华
827fd89ddc Merge pull request #186 from ZhuJHua/develop
ci: fix cliff ci
2025-02-23 21:54:36 +08:00
ZhuJHua
5bf5f36f9d ci: fix cliff ci
[skip ci]
2025-02-23 21:53:45 +08:00
住京华
84d1bb5802 Merge pull request #183 from ZhuJHua/develop
ci: improve ci
2025-02-23 21:51:00 +08:00
ZhuJHua
ee1e240f6d chore: upgrade actions/checkout to v4 across workflow files 2025-02-23 21:46:55 +08:00
ZhuJHua
5f5e6b90ed feat: update changelog workflow to check PR merge status before generating changelog 2025-02-23 21:41:04 +08:00
ZhuJHua
512cd622f7 fix: correct syntax for workflow_run trigger in auto-merge.yml 2025-02-23 21:29:29 +08:00
住京华
5b9ca30c95 Merge pull request #181 from ZhuJHua/develop
feat: add workflow_run trigger for Git Cliff completion
2025-02-23 21:28:53 +08:00
github-actions[bot]
348fe883ab Update changelog (#182) 2025-02-23 13:28:26 +00:00
ZhuJHua
64a65f39b6 feat: add workflow_run trigger for Git Cliff completion 2025-02-23 21:23:47 +08:00
github-actions[bot]
acca62a525 Update changelog (#180) 2025-02-23 13:10:27 +00:00
住京华
cd78ab72c4 feat: enhance UI and improve desktop experience (#179) 2025-02-23 13:06:39 +00:00
github-actions[bot]
9c20ee729c Update changelog (#178) 2025-02-23 09:57:20 +00:00
住京华
a2c92ef947 fix: ci (#177) 2025-02-23 09:56:44 +00:00
住京华
42fe3ab33b Merge pull request #174 from ZhuJHua/dev
ci: add git-cliff to generate changelog
2025-02-23 17:42:54 +08:00
ZhuJHua
738d13d67d ci: add git-cliff to generate changelog 2025-02-23 17:41:08 +08:00
ZhuJHua
e7ea753e78 ci: add git-cliff to generate changelog 2025-02-23 17:34:33 +08:00
ZhuJHua
39e52878e1 ci: add git-cliff to generate changelog 2025-02-23 17:27:23 +08:00
ZhuJHua
744e7464e1 ci: add git-cliff to generate changelog
[Skip ci]
2025-02-23 17:12:23 +08:00
住京华
a6c91f2316 Merge pull request #173 from ZhuJHua/dev
ci: add git-cliff to generate changelog
2025-02-23 17:04:30 +08:00
ZhuJHua
10e0da1861 ci: add git-cliff to generate changelog 2025-02-23 16:56:46 +08:00
住京华
ccf2ec85dd Merge pull request #172 from ZhuJHua/dev
feat: enhance media library appearance and animations
2025-02-23 16:41:20 +08:00
ZhuJHua
bcdbabc8d4 fix: update dependencies and remove unused localization imports 2025-02-23 16:36:48 +08:00
ZhuJHua
80cd02e0fe fix: update localization configuration to include synthetic package option 2025-02-23 16:35:00 +08:00
ZhuJHua
abbd48d600 feat: add WindowsBar and MoveTitle widgets for improved window management 2025-02-23 16:34:04 +08:00
ZhuJHua
f172e0791a refactor: remove video page routing and update video state initialization 2025-02-23 16:28:41 +08:00
ZhuJHua
e5578bdeae feat: improve image handling with enhanced hero transitions and dynamic sizing 2025-02-23 02:17:17 +08:00
ZhuJHua
b9d694ca50 fix: update application name and improve debug configuration 2025-02-23 02:14:41 +08:00
ZhuJHua
827c98d177 feat: enhance image viewing experience with hero transitions and loading states 2025-02-18 23:58:46 +08:00
ZhuJHua
e9992128c7 feat: optimize image preview 2025-02-17 23:59:13 +08:00
ZhuJHua
12e5dbdaad fix: route error 2025-02-16 23:57:40 +08:00
ZhuJHua
cdf39298f9 feat: use the new route transition 2025-02-14 23:54:06 +08:00
ZhuJHua
fe6267bf29 chore(*): upgrade flutter version to 3.29.0 2025-02-13 23:57:51 +08:00
ZhuJHua
e53c379502 chore(*): upgrade flutter and frb version 2025-02-12 23:56:07 +08:00
住京华
b3d1b53fb1 Merge pull request #166 from ZhuJHua/dev
test: add test for utils
2025-02-09 00:01:38 +08:00
ZhuJHua
1c374b7a84 test(add test for utils): 2025-02-08 23:56:26 +08:00
住京华
7597ff5194 Merge pull request #164 from ZhuJHua/dev
feat: supports data encryption using aes-gcm
2025-02-07 16:56:35 +08:00
ZhuJHua
6ce64f8b46 feat: add secure storage to store user key 2025-02-06 18:51:07 +08:00
ZhuJHua
545891fe55 feat(aes): add aes-gcm encryption algorithm 2025-02-06 18:49:42 +08:00
ZhuJHua
610b983e12 feat(font): clear cache when font style change 2025-02-05 17:18:53 +08:00
住京华
905a398e7e Merge pull request #163
fix(log): log did not work in release mode
2025-02-05 16:45:05 +08:00
ZhuJHua
6b1aa8c07d fix(log): log did not work in release mode 2025-02-05 16:44:30 +08:00
住京华
2176e96c2d Merge pull request #162 from ZhuJHua/dev
feat(font): enhance font loading capabilities
2025-02-05 16:43:31 +08:00
ZhuJHua
c0fe3d31a3 fix(font): fix font is not loaded when startup 2025-02-05 16:02:18 +08:00
ZhuJHua
75dfb05e6c feat(font): enhance font loading capabilities 2025-02-05 15:38:17 +08:00
住京华
9ba802a6ae Merge pull request #161 from ZhuJHua/dev
feat(diary): enhance text handling
2025-02-05 15:05:53 +08:00
住京华
0029932e85 Merge pull request #160 from ZhuJHua/readme
chore: update readme
2025-02-04 22:31:45 +08:00
ZhuJHua
09b979493c chore: update readme 2025-02-04 22:27:42 +08:00
ZhuJHua
97dd44856c feat(diary): enhance text handling by trimming titles and removing line breaks from content 2025-02-04 14:07:17 +08:00
住京华
cebab37e0a Merge pull request #159 from ZhuJHua/dev
fix(font): fix adding the font may cause the application crash
2025-02-04 05:10:08 +08:00
ZhuJHua
3006aa2dda fix(font): fix adding the font may cause the application crash 2025-02-04 05:07:06 +08:00
ZhuJHua
527767e1ab feat(font): add caching for font name and weight axis retrieval 2025-02-04 05:05:45 +08:00
住京华
1f08c317b9 Merge pull request #158 from ZhuJHua/dev
perf: improve application performance
2025-02-04 04:41:50 +08:00
ZhuJHua
5dc836ecce feat(font): streamline font loading 2025-02-04 04:38:07 +08:00
ZhuJHua
5154db3269 feat(cache): implement LRUCache and AsyncLRUCache for efficient data management 2025-02-04 04:13:36 +08:00
ZhuJHua
b5be93fa56 refactor: rename rust library from rust_lib_mood_diary to moodiary_rust 2025-02-04 03:10:37 +08:00
住京华
cb1bb4b891 Merge pull request #157 from ZhuJHua/dev
feat(text): introduce AdaptiveText and EllipsisText components
2025-02-04 02:38:31 +08:00
ZhuJHua
5ded722d5c chore: update imports and refresh package version to 3.0.0 2025-02-04 02:34:12 +08:00
ZhuJHua
6ae4b4a45f feat(about): replace Text with AnimatedText for dynamic app info display 2025-02-04 02:34:00 +08:00
ZhuJHua
7acbc7854d feat(text): introduce AdaptiveText and EllipsisText components for improved text handling 2025-02-04 02:33:47 +08:00
住京华
1374ae8a64 Merge pull request #156
chore: update flutter_rust_bridge to version 2.7.1
2025-02-01 00:26:06 +08:00
ZhuJHua
4436162bd7 chore: update flutter_rust_bridge to version 2.7.1 and adjust generated files 2025-01-31 21:36:32 +08:00
ZhuJHua
e3066748d4 feat(llama): add LlamaUtil class and update dependencies 2025-01-30 23:40:50 +08:00
ZhuJHua
f6fbdf8c4c build(*): upgrade dependencies 2025-01-29 23:59:00 +08:00
住京华
7523cd0f6b Merge pull request #155 from ZhuJHua/dev
Dev
2025-01-27 17:30:47 +08:00
ZhuJHua
a1ad810e0f chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 17:29:36 +08:00
ZhuJHua
e9498646e2 chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 17:27:32 +08:00
住京华
4e05adca5d Merge pull request #154 from ZhuJHua/dev
chore(*): adjust the output format of outdated dependencies in the Te…
2025-01-27 17:18:25 +08:00
ZhuJHua
ac23cdaeb1 chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 17:11:52 +08:00
住京华
74b8f4db3b Merge pull request #153 from ZhuJHua/dev
chore(*): adjust the output format of outdated dependencies in the Te…
2025-01-27 17:09:04 +08:00
ZhuJHua
9daf5dfe4b chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 17:08:36 +08:00
住京华
bfb5429f76 Merge pull request #152 from ZhuJHua/dev
chore(*): adjust the output format of outdated dependencies in the Te…
2025-01-27 17:07:02 +08:00
ZhuJHua
c3aac55752 chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 17:06:37 +08:00
住京华
86f3ecbfb9 Merge pull request #151 from ZhuJHua/dev
chore(*): adjust the output format of outdated dependencies in the Te…
2025-01-27 17:02:17 +08:00
ZhuJHua
13348fd880 chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 17:00:26 +08:00
住京华
77bfab6584 Merge pull request #150 from ZhuJHua/dev
chore(*): adjust the output format of outdated dependencies in the Te…
2025-01-27 16:57:10 +08:00
ZhuJHua
6649f04ae3 chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 16:56:39 +08:00
住京华
a1379eb474 Merge pull request #149 from ZhuJHua/dev
chore(*): adjust the output format of outdated dependencies in the Te…
2025-01-27 16:49:51 +08:00
ZhuJHua
28bffec383 chore(*): adjust the output format of outdated dependencies in the Telegram bot workflow
[skip ci]
2025-01-27 16:48:45 +08:00
住京华
9ee9e78974 Merge pull request #148 from ZhuJHua/dev
chore(*): update telegram bot workflow
2025-01-27 16:46:02 +08:00
ZhuJHua
e563afc43a chore(*): update telegram bot workflow
Update the telegram bot workflow to include OUTDATED_DEPENDENCIES variable in the environment.
2025-01-27 16:45:20 +08:00
住京华
c6bc93fdbd Merge pull request #147 from ZhuJHua/dev
feat(telegram-bot): enhance release notification workflow
2025-01-27 16:43:17 +08:00
ZhuJHua
ac01624def feat(telegram-bot): enhance release notification workflow
- Enable manual triggering of the workflow via `workflow_dispatch`
- Update the workflow to obtain release information using the GitHub API
- Update the format of the message to the user
2025-01-27 16:42:28 +08:00
住京华
4e662f9b96 Merge pull request #146 from ZhuJHua/dev
feat(telegram-bot): enhance release notification workflow
2025-01-27 16:40:08 +08:00
ZhuJHua
9a5b85cc02 feat(telegram-bot): enhance release notification workflow
- Enable manual triggering of the workflow via `workflow_dispatch`
- Update the workflow to obtain release information using the GitHub API
- Update the format of the message to the user
2025-01-27 16:39:28 +08:00
住京华
676ea4d763 Merge pull request #145 from ZhuJHua/dev
chore(*): bump version to 2.7.2+72
2025-01-27 15:48:40 +08:00
ZhuJHua
fb77e91a33 chore(*): bump version to 2.7.2+72
Update the application version in pubspec.yaml from 2.7.1+71 to 2.7.2+72.
2025-01-27 15:47:43 +08:00
住京华
1e220f67c3 Merge pull request #144 from ZhuJHua/dev
refactor(media): reconstruct the media library
2025-01-27 15:42:45 +08:00
ZhuJHua
5f62a228f0 refactor(media): reconstruct the media library, with more animation and better performance 2025-01-27 15:39:34 +08:00
ZhuJHua
be911fbd17 feat(ui): enhance image viewing and navigation
-   Improve image viewing experience by adding hero animations to images within `media_image_view.dart`, `image_embed.dart` for both quill and markdown, and `image_view.dart`.
-   Refactor diary title display by removing the arrow icon and using a single text widget in `diary_view.dart`.
-  Set  `showCupertinoParallax: false`  to false in all routes.
-   Change default transition to  `Transition.native` in `main.dart`.
2025-01-26 11:31:04 +08:00
ZhuJHua
509d8f17e2 feat(home): add shadow option for the HomeFab component
This commit introduces a `showShadow` option to the `HomeFabComponent`, enabling the display of a shadow effect for the floating action button. The `HomeView` is updated to set `showShadow` to true.
2025-01-26 11:00:19 +08:00
住京华
c6022db034 Merge pull request #143 from ZhuJHua/dev
feat(markdown): support markdown editing and embed rendering
2025-01-26 10:58:25 +08:00
ZhuJHua
686bd4f41a feat(markdown): support markdown editing and embed rendering
- Implement markdown editing feature with toolbar.
- Add rendering for image, audio, and video embeds in markdown content.
- Add markdown content preview.
- Refactor and improve drawing and photo picking features.
- Implement rendering of markdown syntax highlighting.
- Add markdown to text plain text conversion.
- Add markdown type for diary.
2025-01-26 10:57:37 +08:00
住京华
042da7d659 Merge pull request #142 from ZhuJHua/dev
feat(ui): optimize UI for desktop platform
2025-01-25 22:31:12 +08:00
ZhuJHua
00395a27ac feat(ui): enhance diary card text visibility and format
- Changed the color of the time text in both `grid_diary_card_view` and `list_diary_card_view` to `onSurfaceVariant` for better visibility.
- Adjusted the `maxLines` property in `list_diary_card_view` based on whether the diary has a title, enhancing content readability.
- Changed the format of the time in list_diary_card_view to `yMMMMEEEEd().add_Hms()`.
2025-01-25 22:28:13 +08:00
ZhuJHua
93a4325aec feat(pubspec): add logo path for desktop
Add `logo_path` to pubspec.yaml for specifying the desktop application logo.
2025-01-25 22:28:02 +08:00
ZhuJHua
0b5aa5f762 feat(msix): update msix config for store publishing
Update `identity_name`, `publisher`, and `publisher_display_name` in `msix_config` for Microsoft Store publishing.
2025-01-25 16:43:14 +08:00
ZhuJHua
c9fd39561f feat(msix): update msix configuration for store
Update `display_name`, `publisher_display_name`, `identity_name`, and `publisher` in `msix_config` for store publishing.
2025-01-25 16:40:48 +08:00
ZhuJHua
ad32aa616a chore(*): adjust msix_config settings in pubspec.yaml
Refactor the msix_config in `pubspec.yaml` by changing the indentation of the settings.
2025-01-25 16:37:55 +08:00
ZhuJHua
41e6113a0a feat(msix): add msix configuration for store publishing
Adds display name, publisher display name, identity name and capabilities for publishing to the Microsoft Store.
2025-01-25 16:36:01 +08:00
ZhuJHua
5cba840f80 feat(ui): add simple window button support for windows 2025-01-25 16:08:18 +08:00
ZhuJHua
2478c9c932 feat(ui): refine window buttons and UI components
- Replaced `IconButton` with `IconButton.filled` and adjusted icon sizes in `HomeFabView`.
- Added tooltips to buttons in `HomeFabView` for clarity.
- Improved `WindowButtons` widget to only display on Windows and provide an integrated dragging area, removed unnecessary code.
- Adjusted padding and spacing for MacOS in the navigation rail in `HomePage`.
- Modified `AdaptiveBackground` to simplify layout and remove `appWindow.startDragging()` from the background, use `GestureDetector` instead.
- Added `WindowButtons` at the top of the main application window for better integration.
2025-01-25 15:48:11 +08:00
ZhuJHua
67bf72284f style: code clean 2025-01-25 14:19:31 +08:00
ZhuJHua
022c4bb60d fix(*): remove pull to refresh at desktop platform 2025-01-25 14:19:03 +08:00
ZhuJHua
5fe6d66d48 fix(ui): add microsoft yahei ui as default font at windows 2025-01-25 14:18:14 +08:00
ZhuJHua
3584cca5c9 feat(text): optimize ellipsis text rendering
This commit optimizes the rendering of ellipsis text by:

-   Implementing a more efficient algorithm for truncating text.
-   Using `TextPainter.didExceedMaxLines` to detect overflow.
-   Adding support for custom ellipsis strings.
-   Fixing the layout logic for text width calculation.
-   Removing unnecessary `context` and adding `textStyle` optional parameters.
-   Updating code comments to be clearer.
2025-01-25 11:25:44 +08:00
ZhuJHua
58ca34bcf9 feat(about): enhance confetti effect and update duration
- Increased the duration of the confetti animation from 2 to 4 seconds.
- Updated confetti colors to a wider range, including red, orange, yellow, green, cyan, blue, and purple.
- Refactor code to remove extra `SafeArea` widget.
2025-01-25 11:25:18 +08:00
ZhuJHua
3c05149bed chore: update shared_preferences_android to 2.4.3
Update the version of `shared_preferences_android` from 2.4.2 to 2.4.3 in `pubspec.lock`.
2025-01-25 09:56:06 +08:00
ZhuJHua
4e525f4d30 feat(lint): add more lint rules 2025-01-25 09:49:17 +08:00
ZhuJHua
6ff78e4538 feat(markdown): add markdown preview support 2025-01-25 09:21:13 +08:00
ZhuJHua
502df877c1 feat(media): optimize UI layout 2025-01-25 09:20:07 +08:00
ZhuJHua
875edb6dba feat(about): Add easter eggs 2025-01-25 09:18:54 +08:00
ZhuJHua
7a2fe21a21 chore(deps): bump shared_preferences, waterfall_flow, slang_build_runner and other dependencies
Bumps [shared_preferences](https://pub.dev/packages/shared_preferences) from 2.3.5 to 2.4.0.
Bumps [waterfall_flow](https://pub.dev/packages/waterfall_flow) from 3.1.0 to 3.1.1.
Bumps [slang_build_runner](https://pub.dev/packages/slang_build_runner) from 4.4.0 to 4.4.2.
Also bumps other dependencies:
- glob from 2.1.2 to 2.1.3
- image_picker_macos from 0.2.1+1 to 0.2.1+2
2025-01-24 18:11:15 +08:00
ZhuJHua
c9d626ed3d fix(ui): improve desktop page switching performance
Add a GlobalKey to the AnimatedSwitcher to prevent unnecessary rebuilds and optimize desktop page switching performance.
2025-01-23 13:29:47 +08:00
ZhuJHua
0d0c95b78c feat(ui): optimize UI details
- Improve scrollbar theme.
- Add overlay color to TabBar.
- Add icon to PopupMenuButton.
- Update HomeFab icons and add label.
- Update placeholder and empty view in DiaryTabView.
- Update NavigationBar icons.
2025-01-23 02:47:45 +08:00
ZhuJHua
aa40bdb9ab feat(ui): optimize home page ui
Optimize the home page UI for desktop and mobile, including:
- Added a background for desktop.
- Added a draggable area at the top for desktop.
- Added FABs with animations for diary creation.
- Added a navigation bar for mobile.
- Added a to-top button for both desktop and mobile.
2025-01-23 01:09:45 +08:00
ZhuJHua
f22231ff33 refactor(*): optimize project structure 2025-01-23 01:06:57 +08:00
住京华
feed024495 Merge pull request #139 from ZhuJHua/dev
chore: update Flutter version to 3.27.3
2025-01-22 14:30:22 +08:00
ZhuJHua
c94499ad43 chore: update Flutter version to 3.27.3
Update the Flutter version to 3.27.3 and adjust the minimum SDK constraints in pubspec.yaml. Update the Flutter version badge in the README files.
2025-01-22 14:29:22 +08:00
住京华
c36a771405 Merge pull request #138
chore: refine release notification workflow
2025-01-22 11:21:25 +08:00
ZhuJHua
c67b13ecd3 chore: refine release notification workflow
Refine the release notification workflow to:
- Remove manual workflow dispatch trigger.
- Extract release information directly from GitHub event data.
- Format the Telegram message using markdown for better readability.
2025-01-22 11:21:07 +08:00
住京华
630849e189 Merge pull request #137
chore(ci): enhance telegram notification with release details
2025-01-22 10:38:58 +08:00
ZhuJHua
2ce60b0a21 chore(ci): enhance telegram notification with release details
Update the telegram notification workflow to fetch and include the latest release details in the message.
This includes the release name, tag, notes, and URL, providing more context for users.
2025-01-22 10:38:36 +08:00
住京华
47a6eb8baa Merge pull request #136
chore: simplify telegram release notification
2025-01-22 10:33:57 +08:00
ZhuJHua
cd4e699878 chore: simplify telegram release notification
Simplified the Telegram release notification by removing the assets list and reformatting the message for better readability.
2025-01-22 10:33:34 +08:00
住京华
b5978de312 Merge pull request #135
feat(*): add workflow to send release notifications to Telegram
2025-01-22 10:21:00 +08:00
ZhuJHua
bc5f9ee3b8 feat(*): add workflow to send release notifications to Telegram
This change introduces a new workflow that sends release notifications to a specified Telegram channel using the `appleboy/telegram-action`.
The workflow is triggered on new releases and includes the release name, tag, release notes, assets, and a link to the release page in the notification message.
2025-01-22 10:20:12 +08:00
住京华
246b17ab68 Merge pull request #134
feat(category): init support nested multi-level classification
2025-01-22 10:17:10 +08:00
ZhuJHua
d525ddaa70 feat(category): init support nested multi-level classification
Added `parentId` and `level` fields to the Category model to support nested multi-level classification. Also added an index for the `level` field.
2025-01-22 10:16:49 +08:00
住京华
0354e1af37 Merge pull request #133
chore(deps): bump flutter_quill from 11.0.0-dev.20 to 11.0.0-dev.21
2025-01-22 10:16:01 +08:00
ZhuJHua
df1020b7ee chore(deps): bump flutter_quill from 11.0.0-dev.20 to 11.0.0-dev.21
Remove unused dependencies:
- fading_edge_scrollview
- google_fonts
- http_status_code
- pinput
- simple_gesture_detector
- table_calendar
2025-01-22 10:15:21 +08:00
住京华
ca0f81a09c Merge pull request #132 from ZhuJHua/dev
feat(readme): update Flutter and Dart version requirements
2025-01-21 17:16:51 +08:00
ZhuJHua
7fee9ae8ae feat(readme): update Flutter and Dart version requirements
- Updated Flutter SDK requirement to >= 3.27.2 Stable.
- Updated Dart requirement to >= 3.6.1.
- Added a note encouraging the use of the latest Flutter version and recommending FVM for Flutter version management.
2025-01-21 17:16:20 +08:00
住京华
17a7ee7ed9 Merge pull request #131 from ZhuJHua/dev
feat(dependencies): update SDK and package versions
2025-01-21 17:07:07 +08:00
ZhuJHua
f7da792bdb feat(dependencies): update SDK and package versions
- Updates the Dart SDK version to `>=3.6.1`.
- Updates the Flutter version to `>=3.27.2`.
- Updates the `syncfusion_flutter_sliders` package to version `28.1.41`.
- Adds `msix_config` for application configuration.
2025-01-21 17:06:20 +08:00
住京华
1de5ae87ae Merge pull request #130 from ZhuJHua/dev
feat(android): update NDK and Kotlin versions
2025-01-21 13:28:20 +08:00
ZhuJHua
f84f636529 feat(android): update NDK and Kotlin versions
- Updates the NDK version to "27.2.12479018" in `android/app/build.gradle`.
- Updates the Kotlin Android plugin version to "2.1.0" in `android/settings.gradle`.
- Add abiFilters 'arm64-v8a'
2025-01-21 13:27:46 +08:00
住京华
3709b4fd57 Merge pull request #129 from ZhuJHua/dev
chore(readme): update readme for new features and improvements
2025-01-21 13:27:05 +08:00
ZhuJHua
6eba9a1a1e chore(readme): update readme for new features and improvements 2025-01-21 13:26:45 +08:00
住京华
865234514e Merge pull request #128 from ZhuJHua/dev
refactor(*): organize the project structure
2025-01-21 10:02:53 +08:00
ZhuJHua
c4f5780dab refactor(*): organize the project structure and use absolute paths uniformly 2025-01-21 10:00:44 +08:00
住京华
b31c7415a9 Merge pull request #127 from ZhuJHua/dev
feat(markdown): add support for markdown diary
2025-01-21 09:47:30 +08:00
ZhuJHua
581655b2dd feat(markdown): add support for markdown diary
- Add markdown_bar component for markdown editing.
- Update edit page to support markdown diary type.
- Update home page floating button to include markdown diary.
- Update edit_logic to include markdown controller.
- Update markdown and remove markdown_toolbar package.
- Add support to preview markdown.
- Add support to markdown_bar for markdown editing.
- Add support to markdown parser for markdown editing.
- Update home page floating button to include markdown diary.
2025-01-21 09:47:04 +08:00
住京华
3c5dea31ab Merge pull request #124 from ZhuJHua/dev
feat(ui): adjust lock page and correct localization
2025-01-19 21:44:40 +08:00
ZhuJHua
e4c356560d feat(ui): adjust lock page and correct localization
-   Adjusted the lock page's `AppBar` to remove the leading widget and prevent auto-implication.
-   Made the lock page's `AppBar` to extend behind the body.
-   Corrected the "weather" localization to "Weather".
2025-01-19 21:44:02 +08:00
住京华
e55358cfbf Merge pull request #123 from ZhuJHua/dev
chore(*): update flutter & dependent versions
2025-01-19 16:53:42 +08:00
ZhuJHua
93d378637e chore(deps): update flutter_quill to 11.0.0-dev.20 2025-01-19 16:50:24 +08:00
ZhuJHua
589a87b30d chore(flutter): update flutter version to 3.27.2 2025-01-19 16:50:12 +08:00
ZhuJHua
51e87f9ae6 build(macos): update code signing and provisioning settings 2025-01-19 16:49:42 +08:00
住京华
c0d3d60674 Merge pull request #122 from ZhuJHua/dev
chore(*): bump version to 2.7.1+71
2025-01-19 07:16:07 +08:00
ZhuJHua
08df9bba04 chore(*): bump version to 2.7.1+71
The version number in `pubspec.yaml` has been updated from `2.7.0+70` to `2.7.1+71`.
2025-01-19 07:15:12 +08:00
住京华
1e4b75b414 Merge pull request #121 from ZhuJHua/dev
chore(readme): update screenshots for different languages
2025-01-19 07:13:37 +08:00
ZhuJHua
137a1f3c98 chore(readme): update screenshots for different languages
- Replace `mobile_dark.webp`, `mobile_light.webp`, `desktop_dark.webp`, and `desktop_light.webp` with language-specific screenshots in the `res/screenshot/` directory.
- Update the references to these screenshots in `README.md` and `README.zh.md` to include language indicators.
2025-01-19 07:13:00 +08:00
住京华
59caeb3f28 Merge pull request #120 from ZhuJHua/dev
fix(calendar): filter visible diaries
2025-01-19 07:08:55 +08:00
ZhuJHua
042a715595 fix(calendar): filter visible diaries
Modify Isar database query to only get visible diaries.
2025-01-19 07:07:51 +08:00
住京华
3a55579f33 Merge pull request #119 from ZhuJHua/dev
feat(l10n): add localization for diary operations and data sync
2025-01-19 07:05:57 +08:00
ZhuJHua
7736c72780 feat(l10n): add localization for diary operations and data sync
- Add `diaryDelete`, `diaryEdit`, `diaryShare`, `diaryCount` and `dataSync` translations to `intl_en.arb` and `intl_zh.arb`.
- Update diary list and diary details to use new localization strings.
- Add `sensors_plus` plugin to Podfile.lock.
- Update `diary_view` with localized strings
- update `diary_details_view` with localized strings
2025-01-19 07:05:16 +08:00
住京华
cabb760599 Merge pull request #118 from ZhuJHua/dev
feat(edit): add audio file selection
2025-01-19 06:06:24 +08:00
ZhuJHua
86f337c0a2 feat(edit): add audio file selection 2025-01-19 06:05:09 +08:00
住京华
784297f007 Merge pull request #117 from ZhuJHua/dev
feat(l10n): add full localization support
2025-01-19 05:33:53 +08:00
ZhuJHua
5af6d9080b feat(l10n): add full localization support 2025-01-19 05:32:41 +08:00
住京华
074b2e0631 Merge pull request #116 from ZhuJHua/dev
chore(ci): add dependency installation step for rust builder
2025-01-19 01:57:18 +08:00
ZhuJHua
4f12267b3f chore(ci): add dependency installation step for rust builder
Adds a step to install dependencies for the Rust Builder project within the Flutter analyze workflow. This ensures all necessary packages are available for the analyze process.
2025-01-19 01:56:50 +08:00
住京华
9e1ffc49e3 Merge pull request #115 from ZhuJHua/dev
ci(workflow): update flutter analyze workflow
2025-01-19 01:52:30 +08:00
ZhuJHua
b3dddaabda ci(workflow): update flutter analyze workflow
- Add `workflow_dispatch` trigger.
- Change `cwd` to `working-directory`.
2025-01-19 01:51:33 +08:00
住京华
81657dbe69 Merge pull request #114 from ZhuJHua/dev
chore(ci): install dependencies for rust builder project
2025-01-19 01:47:35 +08:00
ZhuJHua
76365ece09 chore(ci): install dependencies for rust builder project in flutter analyze workflow
The `flutter analyze` workflow has been updated to install dependencies for the `rust_builder` project, ensuring that the analysis process is comprehensive.
2025-01-19 01:45:43 +08:00
住京华
671aa29b3c Merge pull request #113
chore(ci): adjust flutter analyze trigger
2025-01-19 01:37:43 +08:00
ZhuJHua
d285b74131 chore(ci): adjust flutter analyze trigger
Update the workflow to only trigger on pull requests to `master`. Removed push trigger.
2025-01-19 01:37:21 +08:00
住京华
3b7c46c815 Merge pull request #112
ci(*): add flutter analyze workflow and dynamic flutter version support
2025-01-19 01:35:58 +08:00
ZhuJHua
0222f7b352 ci(*): add flutter analyze workflow and dynamic flutter version support
- Introduced a new workflow, `flutter-analyze.yml`, to perform Flutter code analysis.
- Added dynamic Flutter version selection based on the `.fvmrc` file or defaulted to the latest stable version.
- Modified the `build.yml` workflow to also use the dynamic Flutter version.
- Added the installation of `jq` to read the Flutter version from `.fvmrc`.
2025-01-19 01:35:25 +08:00
住京华
c30b84f668 Merge pull request #111
build(android): upgrade AGP to 8.8.0
2025-01-19 01:12:24 +08:00
ZhuJHua
04c37d3465 Merge remote-tracking branch 'origin/dev' into dev 2025-01-19 01:11:56 +08:00
ZhuJHua
b05c71a69c build(android): upgrade AGP to 8.8.0 2025-01-19 01:11:03 +08:00
ZhuJHua
2cd0661ee9 build(android): upgrade AGP to 8.8.0 2025-01-19 01:09:59 +08:00
住京华
ca40667068 Merge pull request #110
* ci(*): optimize release note generation logic
2025-01-19 01:02:07 +08:00
ZhuJHua
7a5df7605e ci(*): optimize release note generation logic 2025-01-19 00:59:19 +08:00
住京华
d6012c6499 Merge pull request #109 from ZhuJHua/dev
fix(*): fix ci
2025-01-17 23:30:36 +08:00
ZhuJHua
be5814c6a0 fix(*): fix ci
[skip ci]
2025-01-17 23:30:10 +08:00
住京华
3aab7784c6 Merge pull request #108 from ZhuJHua/dev
fix(*): fix ci
2025-01-17 23:13:56 +08:00
ZhuJHua
df95b1e252 fix(*): fix ci
[skip ci]
2025-01-17 23:12:45 +08:00
住京华
e103f3fd8b fix(ci): fix ci (#107)
* build(pub): upgrade dependencies

* feat(feedback): add new feedback

* fix(ci): fix work flows

now the work flows only build apk

* build(pub): upgrade dependencies

* fix(media_view): fix file time sorting bug in media library

* feat(lock): automatically invoke biometrics on startup

* refactor(ui): improve ui

* fix(sync): fix local sync exception

* style(*): code clean

* feat(edit): add multi image picker support

* refactor(ui): limit the maximum height of media files in rich text to 300

* feat(webdav): added more option for webdav

* fix(*): fix unexpected issue causing immediate lockup

* chore(*): release at 2.7.0

* build(action): fix workflow

* fix(ci): add release drafter

[skip ci]

* chore(*): update readme

[skip ci]

* fix(*): fix ci

[skip ci]

* fix(*): fix ci

[skip ci]

* fix(*): fix ci

[skip ci]
2025-01-17 22:38:22 +08:00
住京华
e951a59d9d fix(ci): fix ci (#106)
* build(pub): upgrade dependencies

* feat(feedback): add new feedback

* fix(ci): fix work flows

now the work flows only build apk

* build(pub): upgrade dependencies

* fix(media_view): fix file time sorting bug in media library

* feat(lock): automatically invoke biometrics on startup

* refactor(ui): improve ui

* fix(sync): fix local sync exception

* style(*): code clean

* feat(edit): add multi image picker support

* refactor(ui): limit the maximum height of media files in rich text to 300

* feat(webdav): added more option for webdav

* fix(*): fix unexpected issue causing immediate lockup

* chore(*): release at 2.7.0

* build(action): fix workflow

* fix(ci): add release drafter

[skip ci]

* chore(*): update readme

[skip ci]

* fix(*): fix ci

[skip ci]

* fix(*): fix ci

[skip ci]
2025-01-17 22:00:27 +08:00
ZhuJHua
b66ff57c07 fix(*): fix ci
[skip ci]
2025-01-17 21:59:31 +08:00
ZhuJHua
8250c7a0bf Merge remote-tracking branch 'origin/dev' into dev 2025-01-17 21:54:22 +08:00
ZhuJHua
822719bdeb fix(*): fix ci
[skip ci]
2025-01-17 21:53:56 +08:00
住京华
f3e735b886 fix(ci): fix workflow error (#105)
* build(pub): upgrade dependencies

* feat(feedback): add new feedback

* fix(ci): fix work flows

now the work flows only build apk

* build(pub): upgrade dependencies

* fix(media_view): fix file time sorting bug in media library

* feat(lock): automatically invoke biometrics on startup

* refactor(ui): improve ui

* fix(sync): fix local sync exception

* style(*): code clean

* feat(edit): add multi image picker support

* refactor(ui): limit the maximum height of media files in rich text to 300

* feat(webdav): added more option for webdav

* fix(*): fix unexpected issue causing immediate lockup

* chore(*): release at 2.7.0

* build(action): fix workflow

* fix(ci): add release drafter

[skip ci]

* chore(*): update readme

[skip ci]

* fix(*): fix ci

[skip ci]
2025-01-17 21:18:48 +08:00
住京华
3e0f437a0e Merge branch 'master' into dev 2025-01-17 21:18:30 +08:00
ZhuJHua
e3d27db92e fix(*): fix ci
[skip ci]
2025-01-17 21:16:52 +08:00
住京华
25d0858a76 Dev (#104)
* build(pub): upgrade dependencies

* feat(feedback): add new feedback

* fix(ci): fix work flows

now the work flows only build apk

* build(pub): upgrade dependencies

* fix(media_view): fix file time sorting bug in media library

* feat(lock): automatically invoke biometrics on startup

* refactor(ui): improve ui

* fix(sync): fix local sync exception

* style(*): code clean

* feat(edit): add multi image picker support

* refactor(ui): limit the maximum height of media files in rich text to 300

* feat(webdav): added more option for webdav

* fix(*): fix unexpected issue causing immediate lockup

* chore(*): release at 2.7.0

* build(action): fix workflow

* fix(ci): add release drafter

[skip ci]

* chore(*): update readme

[skip ci]
2025-01-17 19:56:02 +08:00
ZhuJHua
8365641f74 chore(*): update readme
[skip ci]
2025-01-17 19:55:20 +08:00
ZhuJHua
691ff9a65d fix(ci): add release drafter
[skip ci]
2025-01-17 19:47:32 +08:00
住京华
793c248b2a Fix ci (#103)
* build(pub): upgrade dependencies

* feat(feedback): add new feedback

* fix(ci): fix work flows

now the work flows only build apk

* build(pub): upgrade dependencies

* fix(media_view): fix file time sorting bug in media library

* feat(lock): automatically invoke biometrics on startup

* refactor(ui): improve ui

* fix(sync): fix local sync exception

* style(*): code clean

* feat(edit): add multi image picker support

* refactor(ui): limit the maximum height of media files in rich text to 300

* feat(webdav): added more option for webdav

* fix(*): fix unexpected issue causing immediate lockup

* chore(*): release at 2.7.0

* build(action): fix workflow
2025-01-17 19:06:20 +08:00
住京华
123d1842dc Merge branch 'master' into dev 2025-01-17 19:06:11 +08:00
ZhuJHua
b7906f0114 build(action): fix workflow 2025-01-17 19:04:06 +08:00
住京华
2b279f59af Release at 2.7.0 (#102)
* build(pub): upgrade dependencies

* feat(feedback): add new feedback

* fix(ci): fix work flows

now the work flows only build apk

* build(pub): upgrade dependencies

* fix(media_view): fix file time sorting bug in media library

* feat(lock): automatically invoke biometrics on startup

* refactor(ui): improve ui

* fix(sync): fix local sync exception

* style(*): code clean

* feat(edit): add multi image picker support

* refactor(ui): limit the maximum height of media files in rich text to 300

* feat(webdav): added more option for webdav

* fix(*): fix unexpected issue causing immediate lockup

* chore(*): release at 2.7.0
2025-01-17 18:28:34 +08:00
住京华
685bb4e2dd Update README.en.md (#101) 2025-01-15 14:46:16 +08:00
住京华
66de7b3daf Update README.md (#100) 2025-01-15 14:44:10 +08:00
411 changed files with 38761 additions and 14704 deletions

2
.fvmrc
View File

@@ -1,3 +1,3 @@
{
"flutter": "3.27.1"
"flutter": "3.29.3"
}

49
.github/release-drafter.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
categories:
- title: ✨ Features
labels:
- feat
- title: 🐛 Bug Fixes
labels:
- fix
- title: 🛠️ Chores
labels:
- chore
- title: 📚 Documentation Updates
labels:
- docs
- title: 🎨 Code Style Updates
labels:
- style
- title: 🔨 Refactors
labels:
- refactor
- title: ⚡ Performance Improvements
labels:
- perf
- title: ✅ Tests
labels:
- test
- title: 🏗️ Build System
labels:
- build
- title: 🧹 CI/CD Changes
labels:
- ci
- title: ⏪ Reverts
labels:
- revert
- title: ❓ What is This
labels:
- '?'
change-template: '- $TITLE (#$NUMBER) by @$AUTHOR'
template: |
# 🔥 What's Changed
$CHANGES
---
🔖 Compare Changes: [View Changes](https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION)

33
.github/workflows/auto-merge.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Auto Merge
on:
pull_request:
types:
- labeled
- unlabeled
- synchronize
- opened
- edited
- ready_for_review
- reopened
- unlocked
pull_request_review:
types:
- submitted
check_suite:
types:
- completed
status: { }
jobs:
automerge:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- id: automerge
name: automerge
uses: "pascalgn/automerge-action@v0.16.4"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_METHOD: "squash"
MERGE_DELETE_BRANCH: "true"

View File

@@ -1,9 +1,6 @@
name: Build & Release
on:
pull_request:
branches:
- master
workflow_dispatch:
jobs:
@@ -13,7 +10,7 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set Up Java
uses: actions/setup-java@v3
@@ -21,10 +18,32 @@ jobs:
distribution: 'temurin'
java-version: '21'
- name: Install jq
run: sudo apt-get install -y jq
- name: Read Flutter Version
id: flutter_version
run: |
if [ -f .fvmrc ]; then
FLUTTER_VERSION=$(cat .fvmrc | jq -r '.flutter')
echo "Flutter version detected: $FLUTTER_VERSION"
echo "flutter-version=$FLUTTER_VERSION" >> $GITHUB_ENV
else
echo ".fvmrc not found. Defaulting to latest Flutter version."
echo "flutter-version=stable" >> $GITHUB_ENV
fi
- name: Set Up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.1'
flutter-version: ${{ env.flutter-version }}
- name: Set Up Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: Install Dependencies
run: flutter pub get
@@ -42,137 +61,122 @@ jobs:
run: flutter build apk --release --obfuscate --split-debug-info=splitMap
- name: Upload APK Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: android-apk
path: build/app/outputs/flutter-apk/app-release.apk
build-windows:
runs-on: windows-latest
name: Build Windows
# build-msix:
# runs-on: windows-latest
# name: Build MSIX
# needs: build-apk
#
# steps:
# - name: Checkout Repository
# uses: actions/checkout@v3
#
# - name: Set Up Flutter
# uses: subosito/flutter-action@v2
# with:
# flutter-version: '3.27.1'
#
# - name: Install Dependencies
# run: flutter pub get
#
# - name: Build MSIX
# run: flutter build windows --release
#
# - name: Upload MSIX Artifact
# uses: actions/upload-artifact@v3
# with:
# name: windows-msix
# path: build/windows/runner/Release/*.msix
steps:
- name: Checkout Repository
uses: actions/checkout@v4
# build-ios-macos:
# runs-on: macos-latest
# name: Build iOS & macOS
# needs: build-apk
#
# steps:
# - name: Checkout Repository
# uses: actions/checkout@v3
#
# - name: Set Up Flutter
# uses: subosito/flutter-action@v2
# with:
# flutter-version: '3.27.0-0.2.pre'
#
# - name: Install Dependencies
# run: flutter pub get
#
# - name: Set Up Certificates and Profiles
# run: |
# echo "${{ secrets.CERTIFICATE }}" | base64 --decode > signing_certificate.p12
# echo "${{ secrets.PROFILE }}" | base64 --decode > provisioning_profile.mobileprovision
#
# - name: Build iOS
# run: flutter build ipa --release
#
# - name: Build macOS
# run: flutter build macos --release
#
# - name: Upload iOS Artifact
# uses: actions/upload-artifact@v3
# with:
# name: ios-ipa
# path: build/ios/ipa/*.ipa
#
# - name: Upload macOS Artifact
# uses: actions/upload-artifact@v3
# with:
# name: macos-app
# path: build/macos/Build/Products/Release/*.app
- name: Set Up Java
uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: "21"
- name: Read Flutter Version
id: flutter_version
run: |
if (Test-Path .fvmrc) {
$FLUTTER_VERSION = (Get-Content .fvmrc | ConvertFrom-Json).flutter
echo "Flutter version detected: $FLUTTER_VERSION"
echo "flutter-version=$FLUTTER_VERSION" >> $env:GITHUB_ENV
} else {
echo ".fvmrc not found. Defaulting to latest Flutter version."
echo "flutter-version=stable" >> $env:GITHUB_ENV
}
- name: Set Up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.flutter-version }}
- name: Set Up Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: Install Dependencies
run: flutter pub get
- name: Build Windows
run: flutter build windows --release
- name: Create ZIP Archive
run: |
Compress-Archive -Path build\windows\x64\runner\Release\* -DestinationPath build\windows\moodiary-windows.zip
- name: Upload Windows Artifact
uses: actions/upload-artifact@v4
with:
name: windows-app
path: build\windows\moodiary-windows.zip
release:
runs-on: ubuntu-latest
name: Publish Release
needs: [ build-apk ]
needs: [ build-apk, build-windows ]
steps:
- name: Checkout Repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# 下载构建产物
- name: Download APK Artifact
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: android-apk
path: artifacts/android
# - name: Download MSIX Artifact
# uses: actions/download-artifact@v3
# with:
# name: windows-msix
# path: artifacts/windows
- name: Download Windows Artifact
uses: actions/download-artifact@v4
with:
name: windows-app
path: artifacts/windows
# - name: Download iOS Artifact
# uses: actions/download-artifact@v3
# with:
# name: ios-ipa
# path: artifacts/ios
#
# - name: Download macOS Artifact
# uses: actions/download-artifact@v3
# with:
# name: macos-app
# path: artifacts/macos
- name: Get Latest Tag
id: latest_tag
uses: actions-ecosystem/action-get-latest-tag@v1.6.0
with:
fetch-all-tags: true
sort-tags: true
# 提取版本号
- name: Extract Version
id: extract_version
- name: Extract Tag Version
id: extract_tag_version
run: |
version=$(grep '^version: ' pubspec.yaml | cut -d ' ' -f 2 | tr -d '\r')
tag="${{ steps.latest_tag.outputs.tag }}"
version="${tag#v}"
echo "VERSION=$version" >> $GITHUB_ENV
# 重命名构建产物
- name: Rename Artifacts
run: |
mkdir -p renamed-artifacts
mv artifacts/android/app-release.apk renamed-artifacts/moodiary-${{ env.VERSION }}-android-arm64.apk
# mv artifacts/windows/*.msix renamed-artifacts/moodiary-${{ env.VERSION }}-windows-x64.msix
# mv artifacts/ios/*.ipa renamed-artifacts/moodiary-${{ env.VERSION }}-ios.ipa
# mv artifacts/macos/*.app renamed-artifacts/moodiary-${{ env.VERSION }}-macos.app
mv artifacts/windows/moodiary-windows.zip renamed-artifacts/moodiary-${{ env.VERSION }}-windows-x64.zip
# 生成 Release Notes
- name: Generate Release Notes
id: release_notes
uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter.yml
tag: v${{ env.VERSION }}
publish: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# 发布 Release
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: renamed-artifacts/*
tag: v${{ env.VERSION }}
tag: v${{ env.VERSION }}
name: v${{ env.VERSION }}
body: ${{ steps.release_notes.outputs.body }}
draft: true
prerelease: false
token: ${{ secrets.GITHUB_TOKEN }}

28
.github/workflows/cargo-ci.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Cargo CI
on:
workflow_dispatch:
pull_request:
branches:
- master
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: Run cargo test
working-directory: rust
run: cargo test
- name: Fail on Errors
if: failure()
run: exit 1

57
.github/workflows/cliff.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: Git Cliff
on:
pull_request:
branches:
- master
types:
- closed
workflow_dispatch:
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if PR is merged
run: |
if [ "${{ github.event.pull_request.merged }}" == "true" ]; then
echo "PR is merged, proceeding with changelog update."
else
echo "PR is not merged, skipping changelog update."
exit 0
fi
- name: Generate changelog using Git Cliff
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --verbose
env:
OUTPUT: CHANGELOG.md
GITHUB_REPO: ${{ github.repository }}
- name: Commit and push changelog changes
run: |
git checkout -b changelog-update
git config user.name 'github-actions[bot]'
git config user.email 'github-actions[bot]@users.noreply.github.com'
git add CHANGELOG.md
git commit -m "Update changelog"
git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git changelog-update
- name: Create Pull Request
run: |
gh pr create --base master --head changelog-update --title "Update changelog" --body "This PR updates the changelog based on recent changes." --label bot
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Auto Merge Pull Request
run: |
gh pr merge --merge --delete-branch "changelog-update"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

49
.github/workflows/flutter-ci.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Flutter CI
on:
workflow_dispatch:
pull_request:
branches:
- master
jobs:
flutter-ci:
name: Analyze and Run Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Install jq
run: sudo apt-get install -y jq
- name: Read Flutter Version
id: flutter_version
run: |
if [ -f .fvmrc ]; then
FLUTTER_VERSION=$(cat .fvmrc | jq -r '.flutter')
echo "Flutter version detected: $FLUTTER_VERSION"
echo "flutter-version=$FLUTTER_VERSION" >> $GITHUB_ENV
else
echo ".fvmrc not found. Defaulting to latest Flutter version."
echo "flutter-version=stable" >> $GITHUB_ENV
fi
- name: Set Up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.flutter-version }}
- name: Install Dependencies
run: flutter pub get
- name: Run Flutter Analyze
run: flutter analyze
- name: Run Flutter Tests
run: flutter test
- name: Fail on Errors
if: failure()
run: exit 1

View File

@@ -2,7 +2,7 @@ name: Issue Labeled
on:
issues:
types: [labeled]
types: [ labeled ]
jobs:
issue-labeled:

54
.github/workflows/label-pr.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Label PR Based on Conventional Commits
on:
pull_request:
types: [ opened, synchronize, reopened ]
jobs:
label-pr:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Analyze PR Title
id: analyze_title
run: |
pr_title="${{ github.event.pull_request.title }}"
label=""
# Match against Conventional Commits
if [[ "$pr_title" == feat* ]]; then
label="feat"
elif [[ "$pr_title" == fix* ]]; then
label="fix"
elif [[ "$pr_title" == chore* ]]; then
label="chore"
elif [[ "$pr_title" == docs* ]]; then
label="docs"
elif [[ "$pr_title" == style* ]]; then
label="style"
elif [[ "$pr_title" == refactor* ]]; then
label="refactor"
elif [[ "$pr_title" == perf* ]]; then
label="perf"
elif [[ "$pr_title" == test* ]]; then
label="test"
elif [[ "$pr_title" == build* ]]; then
label="build"
elif [[ "$pr_title" == ci* ]]; then
label="ci"
elif [[ "$pr_title" == revert* ]]; then
label="revert"
else
label="?"
fi
echo "label=$label" >> $GITHUB_ENV
- name: Add Label to PR
uses: actions-ecosystem/action-add-labels@v1
with:
labels: ${{ env.label }}
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,83 +0,0 @@
name: "Build"
on:
pull_request:
branches:
- master
workflow_dispatch: # 添加手动触发
jobs:
build:
name: Build & Release
runs-on: macos-latest
steps:
#1 Checkout Repository
- name: Checkout Repository
uses: actions/checkout@v3
#2 Setup Java
- name: Set Up Java
uses: actions/setup-java@v3.12.0
with:
distribution: 'oracle'
java-version: '17'
#3 Setup Flutter
- name: Set Up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.27.0-0.1.pre'
channel: 'beta'
#4 Install Dependencies
- name: Install Dependencies
run: flutter pub get
#5 Setup Keystore
- name: Decode Keystore
run: |
echo "${{ secrets.ANDROID_KJS }}" | base64 --decode > android/app/key.jks
- name: Create local.properties
run: |
echo "storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}" > android/local.properties
echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/local.properties
#6 Building APK
- name: Build APK
run: flutter build apk --obfuscate --split-debug-info=splitMap
#10 Extract Version
- name: Extract version from pubspec.yaml
id: extract_version
run: |
version=$(grep '^version: ' pubspec.yaml | cut -d ' ' -f 2 | tr -d '\r')
echo "VERSION=$version" >> $GITHUB_ENV
#11 Check if Tag Exists
- name: Check if Tag Exists
id: check_tag
run: |
if git rev-parse "v${{ env.VERSION }}" >/dev/null 2>&1; then
echo "TAG_EXISTS=true" >> $GITHUB_ENV
else
echo "TAG_EXISTS=false" >> $GITHUB_ENV
fi
#12 Modify Tag if it Exists
- name: Modify Tag
if: env.TAG_EXISTS == 'true'
id: modify_tag
run: |
new_version="${{ env.VERSION }}-build-${{ github.run_number }}"
echo "VERSION=$new_version" >> $GITHUB_ENV
#13 Create Release
- name: Create Release
uses: ncipollo/release-action@v1
with:
artifacts: "build/app/outputs/flutter-apk/app-release.apk"
tag: v${{ env.VERSION }}
token: ${{ secrets.TOKEN }}

54
.github/workflows/telegram-bot.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: Release Notification
on:
release:
types:
- released
workflow_dispatch:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
notify-telegram:
name: Notify Telegram
runs-on: ubuntu-latest
steps:
- name: Extract Release Info
id: extract_release
run: |
response=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/ZhuJHua/moodiary/releases/latest")
# Parse the response to extract needed fields
RELEASE_TAG=$(echo "$response" | jq -r '.tag_name')
RELEASE_NAME=$(echo "$response" | jq -r '.name')
RELEASE_NOTES=$(echo "$response" | jq -r '.body')
RELEASE_URL=$(echo "$response" | jq -r '.html_url')
# Export as environment variables
echo "RELEASE_TAG=$RELEASE_TAG" >> $GITHUB_ENV
echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV
{
echo "RELEASE_NOTES<<EOF"
echo "$RELEASE_NOTES"
echo "EOF"
} >> $GITHUB_ENV
echo "RELEASE_URL=$RELEASE_URL" >> $GITHUB_ENV
- name: Send Telegram Message
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
format: html
message: |
🎉 A new release is live! 🎉
Version: ${{ env.RELEASE_TAG }}
Release Notes:
${{ env.RELEASE_NOTES }}
🔗 View Release: ${{ env.RELEASE_URL }}

1
.gitignore vendored
View File

@@ -43,6 +43,7 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
/ios/build
# FVM Version Cache
.fvm/

691
CHANGELOG.md Normal file
View File

@@ -0,0 +1,691 @@
# Changelog
All notable changes to this project will be documented in this file.
## [unreleased]
### 🚀 Features
- *(zip)* Use rust to increase data export speed
- *(widget)* Add utility functions to capture widget as image
- Add code scanning to import support
- Optimize image processing capabilities
- Integrate Jieba for keyword extraction and search optimization
### 🐛 Bug Fixes
- *(share)* Fix capture and share methods
- Remove workspace
- Qweather api error
- Qweather api error
### 🚜 Refactor
- Optimize the code structure
### 📚 Documentation
- Update sponsor list
### ⚙️ Miscellaneous Tasks
- Fix build
- *(ci)* Remove redundant dependency installation for Rust builder
- Remove custom_lint to fix dart analyzer slow
- Update rust test ci
- Add test module to lib.rs
## [2.7.3] - 2025-03-02
### 🚀 Features
- *(telegram-bot)* Enhance release notification workflow
- *(telegram-bot)* Enhance release notification workflow
- *(llama)* Add LlamaUtil class and update dependencies
- *(text)* Introduce AdaptiveText and EllipsisText components for improved text handling
- *(about)* Replace Text with AnimatedText for dynamic app info display
- *(cache)* Implement LRUCache and AsyncLRUCache for efficient data management
- *(font)* Streamline font loading
- *(font)* Add caching for font name and weight axis retrieval
- *(diary)* Enhance text handling by trimming titles and removing line breaks from content
- *(font)* Enhance font loading capabilities
- *(font)* Clear cache when font style change
- *(aes)* Add aes-gcm encryption algorithm
- Add secure storage to store user key
- Use the new route transition
- Optimize image preview
- Enhance image viewing experience with hero transitions and loading states
- Improve image handling with enhanced hero transitions and dynamic sizing
- Add WindowsBar and MoveTitle widgets for improved window management
- Enhance UI and improve desktop experience (#179)
- Add workflow_run trigger for Git Cliff completion
- Update changelog workflow to check PR merge status before generating changelog
- Add an abstract sync interface
- Enhance diary card views with icons and improve page transitions
### 🐛 Bug Fixes
- *(font)* Fix adding the font may cause the application crash
- *(font)* Fix font is not loaded when startup
- *(log)* Log did not work in release mode
- Route error
- Update application name and improve debug configuration
- Update localization configuration to include synthetic package option
- Update dependencies and remove unused localization imports
- Ci (#177)
- Correct syntax for workflow_run trigger in auto-merge.yml
- Update diary_view to use Get.put for DiaryLogic instantiation
### 💼 Other
- *(*)* Upgrade dependencies
### 🚜 Refactor
- Rename rust library from rust_lib_mood_diary to moodiary_rust
- Remove video page routing and update video state initialization
- Streamline theme management and dynamic color support
- Remove Objectbox plugin references and update tile caching implementation
### ⚙️ Miscellaneous Tasks
- *(*)* Update telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- *(*)* Adjust the output format of outdated dependencies in the Telegram bot workflow
- Update flutter_rust_bridge to version 2.7.1 and adjust generated files
- Update imports and refresh package version to 3.0.0
- Update readme
- *(*)* Upgrade flutter and frb version
- *(*)* Upgrade flutter version to 3.29.0
- Add git-cliff to generate changelog
- Add git-cliff to generate changelog
- Add git-cliff to generate changelog
- Add git-cliff to generate changelog
- Add git-cliff to generate changelog
- Upgrade actions/checkout to v4 across workflow files
- Fix cliff ci
- Update dependencies in pubspec.yaml to latest versions
- Update NDK and plugin versions in build configuration
- Update .gitignore
- *(fix build error)* [skip ci]
## [2.7.2] - 2025-01-27
### 🚀 Features
- *(ui)* Adjust lock page and correct localization
- *(markdown)* Add support for markdown diary
- *(android)* Update NDK and Kotlin versions
- *(dependencies)* Update SDK and package versions
- *(readme)* Update Flutter and Dart version requirements
- *(category)* Init support nested multi-level classification
- *(*)* Add workflow to send release notifications to Telegram
- *(ui)* Optimize home page ui
- *(ui)* Optimize UI details
- *(about)* Add easter eggs
- *(media)* Optimize UI layout
- *(markdown)* Add markdown preview support
- *(lint)* Add more lint rules
- *(about)* Enhance confetti effect and update duration
- *(text)* Optimize ellipsis text rendering
- *(ui)* Refine window buttons and UI components
- *(ui)* Add simple window button support for windows
- *(msix)* Add msix configuration for store publishing
- *(msix)* Update msix configuration for store
- *(msix)* Update msix config for store publishing
- *(pubspec)* Add logo path for desktop
- *(ui)* Enhance diary card text visibility and format
- *(markdown)* Support markdown editing and embed rendering
- *(home)* Add shadow option for the HomeFab component
- *(ui)* Enhance image viewing and navigation
### 🐛 Bug Fixes
- *(ui)* Improve desktop page switching performance
- *(ui)* Add microsoft yahei ui as default font at windows
- *(*)* Remove pull to refresh at desktop platform
### 💼 Other
- *(macos)* Update code signing and provisioning settings
### 🚜 Refactor
- *(*)* Organize the project structure and use absolute paths uniformly
- *(*)* Optimize project structure
- *(media)* Reconstruct the media library, with more animation and better performance
### 🎨 Styling
- Code clean
### ⚙️ Miscellaneous Tasks
- *(flutter)* Update flutter version to 3.27.2
- Simplify telegram release notification
- *(ci)* Enhance telegram notification with release details
- Refine release notification workflow
- Update Flutter version to 3.27.3
- Update shared_preferences_android to 2.4.3
- *(*)* Adjust msix_config settings in pubspec.yaml
- *(*)* Bump version to 2.7.2+72
## [2.7.1] - 2025-01-18
### 🚀 Features
- *(l10n)* Add full localization support
- *(edit)* Add audio file selection
- *(l10n)* Add localization for diary operations and data sync
### 🐛 Bug Fixes
- *(ci)* Add release drafter
- *(*)* Fix ci
- *(*)* Fix ci
- *(*)* Fix ci
- *(ci)* Fix ci (#107)
- *(*)* Fix ci
- *(*)* Fix ci
- *(calendar)* Filter visible diaries
### 💼 Other
- *(action)* Fix workflow
- *(android)* Upgrade AGP to 8.8.0
- *(android)* Upgrade AGP to 8.8.0
### ⚙️ Miscellaneous Tasks
- *(*)* Update readme
- *(*)* Optimize release note generation logic
- *(*)* Add flutter analyze workflow and dynamic flutter version support
- *(ci)* Adjust flutter analyze trigger
- *(ci)* Install dependencies for rust builder project in flutter analyze workflow
- *(workflow)* Update flutter analyze workflow
- *(ci)* Add dependency installation step for rust builder
- *(*)* Bump version to 2.7.1+71
## [2.7.0] - 2025-01-17
### 🚀 Features
- *(diary)* Init support for multi diary
- *(private)* Add private mode
- *(feedback)* Add new feedback
- *(lock)* Automatically invoke biometrics on startup
- *(edit)* Add multi image picker support
- *(webdav)* Added more option for webdav
### 🐛 Bug Fixes
- *(webdav)* Fix webdav error
- *(logger)* Fix logger can't export
- *(log)* Fix logger
- *(ci)* Fix workflow error (#105)
- *(ci)* Fix ci (#106)
- *(ci)* Fix work flows
- *(media_view)* Fix file time sorting bug in media library
- *(sync)* Fix local sync exception
- *(*)* Fix unexpected issue causing immediate lockup
### 💼 Other
- *(pub)* Update dependencies
- *(pub)* Upgrade dependencies
- *(pub)* Upgrade dependencies
- *(pub)* Upgrade dependencies
### 🚜 Refactor
- *(ui)* Improve ui
- *(ui)* Improve ui
- *(*)* Use refreshed to replace getx &use in app webview
- *(ui)* Improve ui
- *(ui)* Improve ui
- *(ui)* Limit the maximum height of media files in rich text to 300
### 🎨 Styling
- *(*)* Code clean up
- *(*)* Code clean
- *(*)* Code clean
### ⚙️ Miscellaneous Tasks
- *(*)* Update readme
- *(*)* Update readme
- *(*)* Update readme
- *(*)* Update readme
- *(*)* Release at 2.7.0
## [2.6.5] - 2025-01-02
### 🚀 Features
- *(category)* Init support nested multi-level classification
- *(font)* Add support for custom font
- *(ui)* Optimize image and video viewing
- *(ui)* Optimize desktop operating experience
- *(fvm)* Add fvm to manager flutter version
- *(*)* Improve category manager
- *(hitokoto)* Add hitokoto in title
- *(ui)* Add marquee to fix long text
- *(*)* Add dev mode
### 🐛 Bug Fixes
- *(fix display error when local send)* Closes #80
- *(android)* Fix release build error on android platform
- *(logger)* Fix can't catch error log at release mode
- *(webdav)* Fix webdav error when creating dir
- *(ui)* Fix text painter did not match custom text scale
- *(font)* Fix variable font misans vf
- *(action)* Fix github action
### 🚜 Refactor
- *(*)* Add some notice when catching error
- *(ui)* New bottom sheet ui & new theme color picker
- *(home)* New ui for home page
- *(ui)* Add animated switcher to fade
- *(ui)* Remove useless widget & add try catch for hitokoto
### 🎨 Styling
- *(code)* Cleanup the code
- *(*)* Code cleanup
### ⚙️ Miscellaneous Tasks
- *(*)* Update readme
- *(*)* Update readme
- *(*)* Fix isar build error
- *(*)* Release at 2.6.5
## [2.6.4] - 2024-12-15
### 🐛 Bug Fixes
- *(data)* Fix date merge error
- *(data)* Fix data merge error to 2.6.3
- *(diary)* Home page did not remove deleted diary
### 🚜 Refactor
- *(ui)* Improve ui
### ⚙️ Miscellaneous Tasks
- *(*)* Release at 2.6.4 as hotfix
## [2.6.3] - 2024-12-15
### 🚀 Features
- *(icon)* Add new icon for desktop
- *(webdav)* Improve webdav behavior
- *(*)* Add try catch
- *(sync)* Add local sync at home page
- *(webdav)* Improve webdav util
- *(save_image)* Add save image to local
- *(media)* Improve media library
- *(media)* Improve media
### 🐛 Bug Fixes
- *(layout)* Fix home page layout overflow
- *(desktop)* Improve desktop experience
- *(layout)* Fix layout
- *(logic)* Fix did not jump to new category tabview when add category
- *(webdav)* Local did not delete
- *(android)* Fix splash screen not full
- *(lab)* Key did not update after input
- *(log)* Error log did not write into file
- *(local_sync)* Category sync error
- *(location)* Fix can't get location on macos
- *(toast)* Remove flutter hook
### 🚜 Refactor
- *(*)* Remove hitokoto
- *(calendar)* Better calendar view
- *(l10n)* Refactor localization
- *(ui)* Refactor ui
- *(ui)* Refactor rich text adding media
- *(ui)* Make record_sheet_view higher
### ⚡ Performance
- *(image)* Improve image performance
- *(*)* Improve performance
- *(*)* Use isolate to improve performance
### 🎨 Styling
- *(*)* Code clean
### ⚙️ Miscellaneous Tasks
- *(*)* Hotfix at 2.6.1
- *(*)* Update readme
- *(isar)* Gen code
- *(*)* Release at 2.6.3
## [2.6.1] - 2024-12-11
### 🐛 Bug Fixes
- *(data)* Fix data merge error
- *(webdav)* Behavior when has no options
- *(rust)* Frb version
- *(*)* Can't start on some devices
### 🚜 Refactor
- *(*)* Remove font setting
- *(diary_detail)* Remove squad
## [2.6.0] - 2024-12-10
### 🚀 Features
- *(server)* Init server support
- *(kmp)* Add kmp search use rust
- *(webdav)* Init webdav util
- *(server)* Init server
- *(*)* Add rich text and media editor
- *(util)* Add keyboard listener
- *(icon)* Use new app icon
- *(webdav)* Support webdav sync
- *(webdav)* Improve webdav support
- *(edit)* Add auto select category
- *(*)* Add data merge to 2.6.0
### 🐛 Bug Fixes
- *(rust)* Fix string error in kmp
- *(ui)* Fixed layout issues with three-button navigation mode
- *(image_compress)* Fix can't compress heif image
- *(local_send)* Fix can't find ip at ethernet mode
### 💼 Other
- *(pubspec)* Upgrade dependencies
- *(rust)* Update dependencies
- *(rust)* Generated rust builder
- *(*)* Platform build option
- *(pubsepc)* Remove unused dependencies
### 🚜 Refactor
- *(ui)* Improve ui
- *(ui)* Improve ui
- *(*)* Use the new flutter api to catch error
### ⚡ Performance
- *(*)* Improve perf at home page
### ⚙️ Miscellaneous Tasks
- *(git)* Add attributes
- *(*)* Add workflows
- *(release)* Prepare release version 2.6.0
## [2.4.9] - 2024-11-19
### 🐛 Bug Fixes
- *(password)* Remove password error
- *(windows)* Fix biometric auth error in windows
- *(windows)* Fix get video thumbnail error in windows
- *(home)* Fix pagination error
### 🚜 Refactor
- *(image)* Use rust to compress image
- *(ui)* Add some animation
- *(ui)* Improve layout for better user experience
- *(ui)* Improve layout for better user experience
- *(*)* Add category manager
### 🎨 Styling
- *(*)* Code clean
### ⚙️ Miscellaneous Tasks
- *(*)* Release 2.4.9
## [2.4.8] - 2024-11-12
### 🐛 Bug Fixes
- *(*)* Fix many bugs
## [2.4.8-rc] - 2024-11-12
### 🚀 Features
- *(*)* Initial localization and LAN synchronization
- *(macos)* Prepare for macos support
- *(map)* Add map support for viewing and managing diary entry locations
- *(local_send)* Add local send support
- *(local_send)* More improve about local send
- *(*)* Add more function in home page
### 🐛 Bug Fixes
- *(search)* Add title search support
- *(*)* Fix title bar in macos
- *(local_send)* Fix modifying list in loop
- *(map)* Fix map cache error
### 💼 Other
- *(android)* Remove unused dependencies
### 🚜 Refactor
- *(*)* Improve performance
### 🎨 Styling
- *(*)* Code clean
### ⚙️ Miscellaneous Tasks
- *(*)* Release 2.4.8
## [2.4.7] - 2024-10-27
### 🚀 Features
- *(edit)* Add automatic weather getting
- *(page/map)* Map
### 🐛 Bug Fixes
- *(edit)* Fix tooltip position error
- *(edit)* Fix tooltip position
- *(category)* Fix category not be able to deselect
- *(file_util)* Fix share and data export error on windows
### ⚡ Performance
- *(page/setting)* Improve performance
### 🎨 Styling
- *(*)* Code clean
### ⚙️ Miscellaneous Tasks
- *(*)* Release 2.4.7
## [2.4.6] - 2024-10-18
### 🚀 Features
- *(update)* Add check update in app
### 🐛 Bug Fixes
- *(location)* Fix location error
- *(app)* Fix title display on some devices
- *(file_util)* Fix data import and export
## [2.4.5] - 2024-10-17
### 🚀 Features
- *(video)* Add support for video player
### 🐛 Bug Fixes
- *(*)* Landscape cannot be used on Android tablets
- *(weather)* Weather cannot be obtained due to positioning problems
### 💼 Other
- *(android)* Update gradle version
- *(*)* Update version
### 🚜 Refactor
- *(*)* Refactor directory structure
- *(*)* Refactor for performance
### ⚙️ Miscellaneous Tasks
- *(*)* Code clean
## [2.4.4] - 2024-10-01
### 🚀 Features
- *(ask_question)* Add SQuAD task in diary
- *(media_view)* Add media view in navigator bar
- *(media_view)* Add image manager
### 💼 Other
- *(android)* Change the minSdk 26
- *(pubspec)* Update dependencies
### 🚜 Refactor
- *(*)* Optimize the color scheme
### ⚡ Performance
- *(setting_view)* Cache the list reduces unnecessary rebuild
### 🎨 Styling
- *(*)* Code clean
### ⚙️ Miscellaneous Tasks
- *(lint)* Add lint rules
- *(lfs)* Add lfs support
## [2.4.3] - 2024-09-23
### 🚀 Features
- *(calendar)* Add timeline view in calendar page
- *(home)* Auto hidden navigation bar
- *(calendar_view)* Add calendar view
- *(About)* Add about page
- *(Calendar)* Add expansion panel in calendar view
### 🐛 Bug Fixes
- *(isar)* Fix error when importing data
- *(home)* Fix scrolling error
- *(record_sheet)* Can not start recording after stop recording
- *(calendar)* Unnecessary build in calendar page
- *(*)* Diary is not change after edit or delete
### 💼 Other
- *(pubspec)* Update dependencies
- *(pubspec)* Update dependencies
- *(pubspec)* Update dependencies
### 🚜 Refactor
- *(wave_form)* Make wave form componentized
- *(*)* Make the code clear
- *(*)* Extract the border radius settings
- *(isar)* Add equal method in diary
- *(isar)* Rename the method
- *(pref)* Change the default color when start up
- *(*)* Remove useless method
### ⚙️ Miscellaneous Tasks
- *(theme)* New theme color
- *(*)* Code clean
- *(intl)* More localization
- *(intl)* More localization
## [2.4.2] - 2024-09-19
### 🚀 Features
- *(windows)* Prepare for windows platform
- *(assistant)* Prepare for assistant page
### 🐛 Bug Fixes
- *(diary_details)* Fix banner height
- *(home)* Fix multi scroll in home page
- *(permission_util)* Fix get location on android platform
- *(diary_card)* Fix an unexpected jump after modify a diary
### 💼 Other
- *(pubspec)* Update dependencies
- *(android)* Remove unused abiFilters
- *(pubspec)* Update dependencies
- *(*)* Remove unused dependencies
- *(pubsepc)* Update dependencies
- *(android)* Update agp version
### 🚜 Refactor
- *(*)* There are so many changes, I don't know what changes, so fuck it
### ⚡ Performance
- *(*)* Improve performance in some cases
### ⚙️ Miscellaneous Tasks
- *(intl)* More localization
## [2.4.1] - 2024-08-26
### 🐛 Bug Fixes
- *(record_sheet)* Fix wave form width anomalies
- *(notice_util)* Something wrong when catching bugs
### 💼 Other
- *(pubspec)* Update dependencies
### ⚡ Performance
- *(home)* Improve performance in home view
<!-- generated by git-cliff -->

View File

@@ -1,180 +0,0 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/banner/dark_en.svg">
<source media="(prefers-color-scheme: light)" srcset="res/banner/light_en.svg">
<img alt="The preview for moodiary." src="res/banner/light_en.svg">
</picture>
<p align="center"><a href="README.md">简体中文</a> | English</p>
<p align="center">QQ Group: 760014526</p>
<div align="center">
<img src="https://img.shields.io/badge/Flutter-3.27.0-blue?style=for-the-badge">
<img src="https://img.shields.io/github/repo-size/ZhuJHua/moodiary?style=for-the-badge&color=ff7070">
<img src="https://img.shields.io/github/stars/ZhuJHua/moodiary?style=for-the-badge&color=965f8a">
<img src="https://img.shields.io/github/v/release/ZhuJHua/moodiary?style=for-the-badge&color=4f5e7f">
<img src="https://img.shields.io/github/license/ZhuJHua/moodiary?style=for-the-badge&color=4ac6b7">
</div>
## ✨ Feature
- **Cross-platform support**:🌍 Compatible with Android, iOS\*, Windows\*, MacOS\*, Linux.
- **Material Design**:🎨 The interface is intuitive and user-friendly, and follows the Material Design specifications.
- **Rich text editing**:📝 Supports text editing in bold, italic, underline and other formats.
- **Multimedia accessories**:📷 You can add pictures, audio, video or even draw a picture to your diary.
- **Search and classification**:🔍 Easily manage your diary by full-text search and categorization.
- **Custom theme**:🌈 Supports light and dark modes, as well as a variety of color schemes.
- **Data security**:🔒 Keep your diary safe with a password, supports biometric unlocking.
- **Export and share**:🧾 Support all data import/export, as well as single diary sharing.
- **Backup and synchronization**:☁ Support for LAN synchronization and WebDav backup.
- **Footprint Map** : 🗺️ See your footprints on a map. Every step of your life is worth documenting.
- **Intelligent assistant**:💬 Supports access to third-party large models, provides Q&A, sentiment analysis and other functions.
- **Local Natural Language Processing (NLP)**:🤖 A more secure intelligent assistant that lets your diary know you better.
(Note: Cross-platform capabilities are provided by Flutter, platforms with * may need to configure your own signatures)
## 🔧 Main Technology stack
- [Flutter](https://github.com/flutter/flutter) ( Cross-platform UI framework )
- [Isar](https://github.com/isar/isar) ( High performance local database )
- [GetX](https://github.com/jonataslaw/getx) ( State management framework )
## 📸 Application screenshot
> The application is constantly updated, and the interface may change slightly in the new version
### Mobile
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/mobile_dark.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/mobile_light.webp">
<img alt="The mobile screenshot for moodiary." src="res/screenshot/mobile_light.webp">
</picture>
### Desktop
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/desktop_dark.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/desktop_light.webp">
<img alt="The desktop screenshot for moodiary." src="res/screenshot/desktop_light.webp">
</picture>
## 🚀 Installation guide
### Third party SDK
Some capabilities need to apply for third-party SDKS, and the following service providers provide free versions, and the obtained keys are configured in the lab.
#### Weather service
- [QWeather](https://dev.qweather.com/docs/api/)
#### Map service
- [Tianditu](http://lbs.tianditu.gov.cn/server/MapService.html)
#### Intelligent assistant
- [Tencent Hunyuan](https://cloud.tencent.com/document/product/1729/97731)
### Direct mounting
Use it by downloading the compiled installation package in Release, or manually compiling it if you don't have the platform you need.
### Manual compilation
#### Environmental requirement
- Flutter SDK (>= 3.27.0)
- Dart (>= 3.6.0)
- Rust Toolchain (Latest)
- Clang/LLVM (Latest)
- Compatible ides (e.g. Android Studio, Visual Studio Code)
#### Installation procedure
> Note: When packaging, you need to modify the corresponding platform configuration file, such as Android platform build.gradle
1. **Clone Repo**
```bash
git clone https://github.com/ZhuJHua/moodiary.git
cd moodiary
```
2. **Installation dependency**
```bash
flutter pub get
```
3. **Running application**
```bash
flutter run
```
4. **Package release**
- Android: `flutter build apk`
- iOS: `flutter build ipa`
- Windows: `flutter build windows`
- MacOS: `flutter build macos`
## 📝 More description
### Natural Language Processing (NLP)
> In the experimental stage
Today, more and more industry products are beginning to incorporate AI technology, which undoubtedly greatly improves our experience. However, for diary applications, it is not acceptable to hand over the data to a large model because it is not certain that the data will be used for training. Therefore, a better approach is to adopt a local model. Although local models may not be as powerful as large models due to size limitations, they can still provide necessary help to a certain extent.
Currently, I have the following tasks integrated into the source code:
#### SQuAD task based on Bert pre-trained model
I used MobileBert for the SQuAD task, which is a simple machine reading comprehension task. You can ask it questions and it will return the answers you need. The model files are in the `.tflite` format required by TensorFlow Lite, so you can add your own model files to the `assets/tflite` directory.
Thanks to the following open source projects:
- [Chinese MobileBERT](https://github.com/ymcui/Chinese-MobileBERT)
- [Mobilebert](https://github.com/google-research/google-research/tree/master/mobilebert)
- [ChineseSquad](https://github.com/junzeng-pluto/ChineseSquad)
## 🤝 Contribution guide
Contributions are welcome! Please follow these steps to contribute:
1. Fork this repository.
2. Create a new branch(`git checkout -b feature-branch-name`)。
3. Commit your changes(`git commit -am 'Add some feature'`)。
4. Push to branch(`git push origin feature-branch-name`)。
5. Create a Pull Request.
Please make sure that your code to follow [Flutter style guide](https://flutter.dev/docs/development/tools/formatting) and include the appropriate tests.
## 📄 License
This project is licensed under the AGPL-3.0 LICENSE, see the [LICENSE](LICENSE) file for details.
## 💖 Thanks
- Thanks to the Flutter team for the excellent framework.
- Special thanks to the open source community for their valuable contributions.
## 🥪 Sponsor
You can buy me a sandwich to keep me motivated to continue developing.
<img src="res/sponsor/wechat.jpg" style="width:300px" alt="Sponsor"/>
### List of sponsors
If you want to be on the list, you can leave me a message, in no particular order, and the list will be updated regularly.
| Sponsor | Price | Sponsor | Price |
|---------------------------------------|----------|---------|--------|
| [dsxksss](https://github.com/dsxksss) | 50 CNY | 十一 | 20 CNY |
| 沭飏秋 | 10 CNY | 朱东杰 | 60 CNY |
| 匿名 | 5 CNY | wu | 10 CNY |
| 云烨 | 2.76 CNY | | |

190
README.md
View File

@@ -1,180 +1,188 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/banner/dark_zh.svg">
<source media="(prefers-color-scheme: light)" srcset="res/banner/light_zh.svg">
<img alt="The preview for moodiary." src="res/banner/light_zh.svg">
<source media="(prefers-color-scheme: dark)" srcset="res/banner/dark_en.svg">
<source media="(prefers-color-scheme: light)" srcset="res/banner/light_en.svg">
<img alt="The preview for moodiary." src="res/banner/light_en.svg">
</picture>
<p align="center"><a href="README.zh.md">简体中文</a> | English</p>
<p align="center">简体中文 | <a href="README.en.md">English</a></p>
<p align="center">QQ 交流群760014526</p>
<p align="center"><a href="https://answer.moodiary.net" target="_blank">Official forum</a>丨QQ Group: <a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=xGr0TNp_X1z3XEn09_iE_iGSLolQwl6Y&jump_from=webapi&authKey=ZmSb2oEd94FSXxBXRBq53hgTjjvcfmgkQrduB3uL12XtRylPmRlO2OdFz6R25tIo">760014526</a>丨Telegram: <a target="_blank" href="https://t.me/openmoodiary">openmoodiary</a></p>
<div align="center">
<img src="https://img.shields.io/badge/Flutter-3.27.0-blue?style=for-the-badge">
<img src="https://img.shields.io/badge/Flutter-3.29.2-blue?style=for-the-badge">
<img src="https://img.shields.io/github/repo-size/ZhuJHua/moodiary?style=for-the-badge&color=ff7070">
<img src="https://img.shields.io/github/stars/ZhuJHua/moodiary?style=for-the-badge&color=965f8a">
<img src="https://img.shields.io/github/v/release/ZhuJHua/moodiary?style=for-the-badge&color=4f5e7f">
<img src="https://img.shields.io/github/license/ZhuJHua/moodiary?style=for-the-badge&color=4ac6b7">
</div>
## ✨ 功能特性
- **跨平台支持**:🌍 兼容 Android、iOS\*、Windows\*、MacOS\*、Linux。
- **Material Design**:🎨 界面直观且用户友好,遵循 Material Design 设计规范。
- **富文本编辑**:📝 支持加粗、斜体、下划线等多种格式的文本编辑。
- **多媒体附件**:📷 可以为你的日记添加图片、音频、视频甚至画一张画。
- **搜索和分类**:🔍 轻松通过全文搜索及分类管理你的日记。
- **自定义主题**:🌈 支持浅色和深色模式,以及多种配色的主题。
- **数据安全**:🔒 通过密码来保障你的日记安全,支持通过生物识别解锁。
- **导出和分享**:🧾 支持所有数据的导入/导出,以及单篇日记的分享。
- **备份与同步**:☁ 支持局域网同步,快速在设备间同步数据,以及 WebDav 备份。
- **足迹地图**:🗺️ 在地图上查看你足迹,生活中的每一步都值得被记录。
- **智能助手**:💬 支持接入第三方大模型,提供问答、情绪分析等功能。
- **本地自然语言处理NLP**:🤖 更安全的智能助手,让你的日记更懂你。
## ✨ Feature
(注:跨平台能力由 Flutter 提供,带 * 号的平台可能需要自行配置签名)
- **Cross-platform support**: 🌍 Compatible with Android, iOS, Windows, MacOS, Linux\*.
- **Material Design**: 🎨 The interface is intuitive and user-friendly, and follows the Material Design specifications.
- **Multiple editors**: 📝 supports markdown, plain text, rich text and other forms of text editing.
- **Multimedia accessories**: 📷 You can add pictures, audio, video or even draw a picture to your diary.
- **Search and classification**: 🔍 Easily manage your diary by full-text search and categorization.
- **Custom theme**: 🌈 Supports light and dark modes, as well as a variety of color schemes.
- **Custom fonts**: ✍️ Supports importing different fonts, and supports variable fonts.
- **Data security**: 🔒 Keep your diary safe with a password, supports biometric unlocking.
- **Export and share**: 🧾 Support all data import/export, as well as single diary sharing.
- **Backup and synchronization**: ☁ Support for LAN synchronization and WebDav backup.
- **Trail Map**: 🗺️ See your footprints on a map. Every step of your life is worth documenting.
- **Intelligent assistant**: 💬 Supports access to third-party large models, provides Q&A, sentiment analysis and other functions.
- **Local Natural Language Processing (NLP)**: 🤖 A more secure intelligent assistant that lets your diary know you better.
## 🔧 主要技术栈
(Note: Cross-platform capabilities are provided by Flutter, and platforms with * marks may require more testing)
- [Flutter](https://github.com/flutter/flutter)(跨平台 UI 框架)
- [Isar](https://github.com/isar/isar)(高性能本地数据库)
- [GetX](https://github.com/jonataslaw/getx)(状态管理框架)
## 🔧 Main Technology stack
## 📸 应用截图
- [Flutter](https://github.com/flutter/flutter) ( Cross-platform UI framework )
- [Isar](https://github.com/isar/isar) ( High performance local database )
- [GetX](https://github.com/jonataslaw/getx) ( State management framework )
> 应用持续更新中,新版本界面可能稍有变化
## 📸 Application screenshot
### 移动端
> The application is constantly updated, and the interface may change slightly in the new version
### Mobile
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/mobile_dark.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/mobile_light.webp">
<img alt="The mobile screenshot for moodiary." src="res/screenshot/mobile_light.webp">
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/mobile_dark_en.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/mobile_light_en.webp">
<img alt="The mobile screenshot for moodiary." src="res/screenshot/mobile_light_en.webp">
</picture>
### 桌面端
### Desktop
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/desktop_dark.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/desktop_light.webp">
<img alt="The desktop screenshot for moodiary." src="res/screenshot/desktop_light.webp">
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/desktop_dark_en.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/desktop_light_en.webp">
<img alt="The desktop screenshot for moodiary." src="res/screenshot/desktop_light_en.webp">
</picture>
## 🚀 安装指南
## 🚀 Installation guide
### 第三方 SDK
### Third party SDK
某些能力需要自行申请第三方 SDK下列服务商均提供免费的版本获取到的 Key 在实验室中配置。
Some capabilities need to apply for third-party SDKS, and the following service providers provide free versions, and the obtained keys are configured in the lab.
#### 天气服务
#### Weather service
- [和风天气](https://dev.qweather.com/docs/api/)
- [QWeather](https://dev.qweather.com/docs/api/)
#### 地图服务
#### Map service
- [天地图](http://lbs.tianditu.gov.cn/server/MapService.html)
- [Tianditu](http://lbs.tianditu.gov.cn/server/MapService.html)
#### 智能助手
#### Intelligent assistant
- [腾讯混元大模型](https://cloud.tencent.com/document/product/1729/97731)
- [Tencent Hunyuan](https://cloud.tencent.com/document/product/1729/97731)
### 直接安装
### Direct install
通过下载 Release 中已编译好的安装包来使用,如果没有你所需要的平台,请使用手动编译。
Use it by downloading the compiled installation package in Release, or manually compiling it if you don't have the platform you need.
### 手动编译
### Manual compilation
#### 环境要求
#### Environmental requirement
- Flutter SDK (>= 3.27.0)
- Dart (>= 3.6.0)
- Rust 工具链 (Latest)
- Clang/LLVM (Latest)
- 兼容的 IDE如 Android Studio、Visual Studio Code
> I always use the latest Flutter version (if possible), using newer versions will bring more features and better performance improvements, never use older versions unless you want your code to become a piece of 💩
#### 安装步骤
- Flutter SDK (>= 3.29.0 Stable) (It is recommended to use FVM to manage the Flutter version)
- Dart (>= 3.7.0)
- Rust Toolchain (Nightly)
- Clang/LLVM
- Compatible IDE (e.g. Android Studio, Visual Studio Code)
> 注意:当打包时,需要自己修改对应平台的配置文件,例如安卓平台的 build.gradle
#### Installation procedure
1. **克隆仓库**
> Note: For security reasons, I did not include my signature in the code base, when you need to manually package, you need to modify the configuration file of the corresponding platform, such as build.gradle on the Android platform, and package after modifying the package name, thank you for your understanding.
1. **Clone Repo**
```bash
git clone https://github.com/ZhuJHua/moodiary.git
cd moodiary
```
2. **安装依赖**
2. **Installation dependency**
```bash
flutter pub get
```
3. **运行应用**
3. **Running application**
```bash
flutter run
```
4. **打包发布**
4. **Package release**
- Android: `flutter build apk`
- iOS: `flutter build ipa`
- Windows: `flutter build windows`
- MacOS: `flutter build macos`
## 📝 更多说明
## 📝 More description
### 自然语言处理NLP
### Natural Language Processing (NLP)
> 处于实验阶段
> In the experimental stage
如今,越来越多的行业产品开始融入 AI 技术,这无疑极大地提升了我们的使用体验。然而,对于日记应用来说,将数据交给大型模型处理并不可接受,因为无法确定这些数据是否会被用于训练。因此,更好的方法是采用本地模型。虽然由于体积限制,本地模型的能力可能不如大型模型强大,但在一定程度上仍能为我们提供必要的帮助。
Today, more and more industry products are beginning to incorporate AI technology, which undoubtedly greatly improves our experience. However, for diary applications, it is not acceptable to hand over the data to a large model because it is not certain that the data will be used for training. Therefore, a better approach is to adopt a local model. Although local models may not be as powerful as large models due to size limitations, they can still provide necessary help to a certain extent.
目前,我在源码中集成了以下任务:
Currently, I have the following tasks integrated into the source code:
#### 基于 Bert 预训练模型的 SQuAD 任务
#### SQuAD task based on Bert pre-trained model
我采用了 MobileBert 来处理 SQuAD 任务,这是一个简单的机器阅读理解任务。你可以向它提出问题,它会返回你需要的答案。模型文件采用 TensorFlow Lite 所需的 `.tflite` 格式,所以你可以添加自己的模型文件到 `assets/tflite` 目录下。
I used MobileBert for the SQuAD task, which is a simple machine reading comprehension task. You can ask it questions and it will return the answers you need. The model files are in the `.tflite` format required by TensorFlow Lite, so you can add your own model files to the `assets/tflite` directory.
感谢以下开源项目:
Thanks to the following open source projects:
- [Chinese MobileBERT](https://github.com/ymcui/Chinese-MobileBERT)
- [Mobilebert](https://github.com/google-research/google-research/tree/master/mobilebert)
- [ChineseSquad](https://github.com/junzeng-pluto/ChineseSquad)
## 🤝 贡献指南
## 🤝 Contribution guide
欢迎贡献!请按照以下步骤进行贡献:
Contributions are welcome! Please follow these steps to contribute:
1. Fork 本仓库。
2. 创建一个新分支(`git checkout -b feature-branch-name`
3. 提交你的修改(`git commit -am 'Add some feature'`
4. 推送到分支(`git push origin feature-branch-name`
5. 创建一个 Pull Request
1. Fork this repository.
2. Create a new branch(`git checkout -b feature-branch-name`)
3. Commit your changes(`git commit -am 'Add some feature'`)
4. Push to branch(`git push origin feature-branch-name`)
5. Create a Pull Request.
请确保你的代码遵循 [Flutter 风格指南](https://flutter.dev/docs/development/tools/formatting) 并包含适当的测试。
Please make sure that your code to follow [Flutter style guide](https://flutter.dev/docs/development/tools/formatting) and include the appropriate tests.
## 📄 许可证
## 📄 License
此项目基于 AGPL-3.0 许可证进行许可,详情请参阅 [LICENSE](LICENSE) 文件。
This project is licensed under the AGPL-3.0 LICENSE, see the [LICENSE](LICENSE) file for details.
## 💖 鸣谢
## 💖 Thanks
- 感谢 Flutter 团队提供出色的框架。
- 特别感谢开源社区的宝贵贡献。
- Thanks to the Flutter team for the excellent framework.
- Special thanks to the open source community for their valuable contributions.
## 🥪 捐助
## 🥪 Sponsor
可以给我买一个三明治,让我更有动力继续开发。
You can buy me a sandwich to keep me motivated to continue developing.
<img src="res/sponsor/wechat.jpg" style="width:300px" alt="Sponsor"/>
<img src="res/sponsor/wechat.jpg" style="width:300px" alt="Sponsor"/>
### 捐助者名单
### List of sponsors
如果您想要出现在名单中,可以给我留言,排名不分先后,名单会定期更新。
If you want to appear on the list, you can leave your Github username in the comment, in no particular order, and the list will be updated regularly.
| 捐助者 | 金额 | 捐助者 | 金额 |
|---------------------------------------|----------|-----|--------|
| [dsxksss](https://github.com/dsxksss) | 50 CNY | 十 | 20 CNY |
| 沭飏秋 | 10 CNY | 朱东杰 | 60 CNY |
| 匿名 | 5 CNY | wu | 10 CNY |
| 云 | 2.76 CNY | | |
| Sponsor | Price | Sponsor | Price |
|-----------------------------------| -------- | ------------------------------------------------ | ------ |
| [dsxksss](https://github.com/dsxksss) | 50 CNY | 十* | 20 CNY |
| 沭** | 10 CNY | 朱东杰 | 60 CNY |
| *Person* | 5 CNY | wu* | 10 CNY |
| 云* | 2.76 CNY | 不对味的雪碧 | 10 CNY |
| w** | 6.6 CNY | [帕斯卡的芦苇](https://github.com/xiaoxianzi-99) | 10 CNY |
| 不** | 20 CNY | 曾** | 20 CNY |
| *Person* | 20 CNY | *Person* | 18.88 CNY |
| Lucci | 9.9 CNY | *Person* | 5 CNY |
| 宋** | 5 CNY | 翰** | 5 CNY |

189
README.zh.md Normal file
View File

@@ -0,0 +1,189 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/banner/dark_zh.svg">
<source media="(prefers-color-scheme: light)" srcset="res/banner/light_zh.svg">
<img alt="The preview for moodiary." src="res/banner/light_zh.svg">
</picture>
<p align="center">简体中文 | <a href="README.md">English</a></p>
<p align="center"><a href="https://answer.moodiary.net" target="_blank">官方论坛</a>丨QQ群: <a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=xGr0TNp_X1z3XEn09_iE_iGSLolQwl6Y&jump_from=webapi&authKey=ZmSb2oEd94FSXxBXRBq53hgTjjvcfmgkQrduB3uL12XtRylPmRlO2OdFz6R25tIo">760014526</a>丨Telegram: <a target="_blank" href="https://t.me/openmoodiary">openmoodiary</a></p>
<div align="center">
<img src="https://img.shields.io/badge/Flutter-3.29.2-blue?style=for-the-badge">
<img src="https://img.shields.io/github/repo-size/ZhuJHua/moodiary?style=for-the-badge&color=ff7070">
<img src="https://img.shields.io/github/stars/ZhuJHua/moodiary?style=for-the-badge&color=965f8a">
<img src="https://img.shields.io/github/v/release/ZhuJHua/moodiary?style=for-the-badge&color=4f5e7f">
<img src="https://img.shields.io/github/license/ZhuJHua/moodiary?style=for-the-badge&color=4ac6b7">
</div>
## ✨ 功能特性
- **跨平台支持**:🌍 兼容 Android、iOS、Windows、MacOS、Linux\*。
- **Material Design**:🎨 界面直观且用户友好,遵循 Material Design 设计规范。
- **多种编辑器**:📝 支持 Markdown 、纯文本、富文本等多种形式的文本编辑。
- **多媒体附件**:📷 可以为你的日记添加图片、音频、视频甚至画一张画。
- **搜索和分类**:🔍 轻松通过全文搜索及分类管理你的日记。
- **自定义主题**:🌈 支持浅色和深色模式,以及多种配色的主题。
- **自定义字体**:✍️ 支持导入不同的字体,并支持可变字体。
- **数据安全**:🔒 通过密码来保障你的日记安全,支持通过生物识别解锁。
- **导出和分享**:🧾 支持所有数据的导入/导出,以及单篇日记的分享。
- **备份与同步**:☁ 支持局域网同步,快速在设备间同步数据,以及 WebDav 备份。
- **足迹地图**:🗺️ 在地图上查看你足迹,生活中的每一步都值得被记录。
- **智能助手**:💬 支持接入第三方大模型,提供问答、情绪分析等功能。
- **本地自然语言处理NLP**:🤖 更安全的智能助手,让你的日记更懂你。
(注:跨平台能力由 Flutter 提供,带 * 号的平台可能需要更多测试)
## 🔧 主要技术栈
- [Flutter](https://github.com/flutter/flutter)(跨平台 UI 框架)
- [Isar](https://github.com/isar/isar)(高性能本地数据库)
- [GetX](https://github.com/jonataslaw/getx)(状态管理框架)
## 📸 应用截图
> 应用持续更新中,新版本界面可能稍有变化
### 移动端
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/mobile_dark_zh.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/mobile_light_zh.webp">
<img alt="The mobile screenshot for moodiary." src="res/screenshot/mobile_light_zh.webp">
</picture>
### 桌面端
<picture>
<source media="(prefers-color-scheme: dark)" srcset="res/screenshot/desktop_dark_zh.webp">
<source media="(prefers-color-scheme: light)" srcset="res/screenshot/desktop_light_zh.webp">
<img alt="The desktop screenshot for moodiary." src="res/screenshot/desktop_light_zh.webp">
</picture>
## 🚀 安装指南
### 第三方 SDK
某些能力需要自行申请第三方 SDK下列服务商均提供免费的版本获取到的 Key 在实验室中配置。
#### 天气服务
- [和风天气](https://dev.qweather.com/docs/api/)
#### 地图服务
- [天地图](http://lbs.tianditu.gov.cn/server/MapService.html)
#### 智能助手
- [腾讯混元大模型](https://cloud.tencent.com/document/product/1729/97731)
### 直接安装
通过下载 Release 中已编译好的安装包来使用,如果没有你所需要的平台,请使用手动编译。
### 手动编译
#### 环境要求
> 我总是会使用最新的 Flutter 版本(如果可能的话),使用新版本可以带来更多的功能和更好的性能提升,永远不要使用老版本除非你希望代码变成一坨 💩
- Flutter SDK (>= 3.29.0 Stable)(建议使用 fvm 来管理 flutter 版本)
- Dart (>= 3.7.0)
- Rust 工具链Nightly
- Clang/LLVM
- 兼容的 IDE如 Android Studio、Visual Studio Code
#### 安装步骤
> 注意:出于安全考虑,我并没有在代码库中包含我的签名,当您需要手动打包时,需要自己修改对应平台的配置文件,例如安卓平台的 build.gradle修改包名后打包感谢您的理解
1. **克隆仓库**
```bash
git clone https://github.com/ZhuJHua/moodiary.git
cd moodiary
```
2. **安装依赖**
```bash
flutter pub get
```
3. **运行应用**
```bash
flutter run
```
4. **打包发布**
- Android: `flutter build apk`
- iOS: `flutter build ipa`
- Windows: `flutter build windows`
- MacOS: `flutter build macos`
## 📝 更多说明
### 自然语言处理NLP
> 处于实验阶段
如今,越来越多的行业产品开始融入 AI 技术,这无疑极大地提升了我们的使用体验。然而,对于日记应用来说,将数据交给大型模型处理并不可接受,因为无法确定这些数据是否会被用于训练。因此,更好的方法是采用本地模型。虽然由于体积限制,本地模型的能力可能不如大型模型强大,但在一定程度上仍能为我们提供必要的帮助。
目前,我在源码中集成了以下任务:
#### 基于 Bert 预训练模型的 SQuAD 任务
我采用了 MobileBert 来处理 SQuAD 任务,这是一个简单的机器阅读理解任务。你可以向它提出问题,它会返回你需要的答案。模型文件采用 TensorFlow Lite 所需的 `.tflite` 格式,所以你可以添加自己的模型文件到 `assets/tflite` 目录下。
感谢以下开源项目:
- [Chinese MobileBERT](https://github.com/ymcui/Chinese-MobileBERT)
- [Mobilebert](https://github.com/google-research/google-research/tree/master/mobilebert)
- [ChineseSquad](https://github.com/junzeng-pluto/ChineseSquad)
## 🤝 贡献指南
欢迎贡献!请按照以下步骤进行贡献:
1. Fork 本仓库。
2. 创建一个新分支(`git checkout -b feature-branch-name`)。
3. 提交你的修改(`git commit -am 'Add some feature'`)。
4. 推送到分支(`git push origin feature-branch-name`)。
5. 创建一个 Pull Request。
请确保你的代码遵循 [Flutter 风格指南](https://flutter.dev/docs/development/tools/formatting) 并包含适当的测试。
## 📄 许可证
此项目基于 AGPL-3.0 许可证进行许可,详情请参阅 [LICENSE](LICENSE) 文件。
## 💖 鸣谢
- 感谢 Flutter 团队提供出色的框架。
- 特别感谢开源社区的宝贵贡献。
## 🥪 捐助
可以给我买一个三明治,让我更有动力继续开发。
<img src="res/sponsor/wechat.jpg" style="width:300px" alt="Sponsor"/>
### 捐助者名单
如果您想要出现在名单中,可以在留言中留下您的 Github 用户名,排名不分先后,名单会定期更新。
| 捐助者 | 金额 | 捐助者 | 金额 |
| ------------------------------------- | -------- | ------------------------------------------------ | --------- |
| [dsxksss](https://github.com/dsxksss) | 50 CNY | 十* | 20 CNY |
| 沭** | 10 CNY | 朱东杰 | 60 CNY |
| *人* | 5 CNY | wu* | 10 CNY |
| 云* | 2.76 CNY | 不对味的雪碧 | 10 CNY |
| w** | 6.6 CNY | [帕斯卡的芦苇](https://github.com/xiaoxianzi-99) | 10 CNY |
| 不** | 20 CNY | 曾** | 20 CNY |
| *人* | 20 CNY | *人* | 18.88 CNY |
| Lucci | 9.9 CNY | *人* | 5 CNY |
| 宋** | 5 CNY | 翰** | 5 CNY |

View File

@@ -1,33 +1,24 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
prefer_const_constructors: true
prefer_const_literals_to_create_immutables: true
prefer_const_declarations: true
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
prefer_final_in_for_each: true
prefer_final_fields: true
prefer_final_locals: true
analyzer:
plugins:
- custom_lint
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
- "**/*.gr.dart"
- "**/*.mocks.dart"
- "rust_builder/**"
- "ios/**"
- "android/**"
- "windows/**"
- "macos/**"
errors:
invalid_annotation_target: ignore

4
android/.gitignore vendored
View File

@@ -1,8 +1,10 @@
gradle-wrapper.jar
/.gradle
/captures/
/app/.cxx
/.kotlin
/gradlew
/gradlew.bat
/gradlew.bats
/local.properties
GeneratedPluginRegistrant.java

View File

@@ -23,9 +23,9 @@ if (flutterVersionName == null) {
}
android {
namespace = "cn.yooss.mood_diary"
compileSdk = 35
ndkVersion = "27.1.12297006"
namespace = "cn.yooss.moodiary"
compileSdk = 36
ndkVersion = "28.0.13004108"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
@@ -51,10 +51,11 @@ android {
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = 26
targetSdk = 35
targetSdk = 36
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
ndk {
//noinspection ChromeOsAbiSupport
abiFilters 'arm64-v8a'
}
}
@@ -68,6 +69,9 @@ android {
}
debug {
signingConfig = signingConfigs.config
resValue "string", "app_name", "Moodiary Debug"
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
}
profile {
signingConfig = signingConfigs.config

View File

@@ -33,9 +33,9 @@
</queries>
<application
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon"
android:label="Moodiary"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/launcher_icon"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">

View File

@@ -1,15 +0,0 @@
package cn.yooss.mood_diary
import com.github.gzuliyujiang.oaid.IGetter
import io.flutter.plugin.common.MethodChannel
import java.lang.Exception
class HandleGetOAID(private var resultCallback: MethodChannel.Result) : IGetter {
override fun onOAIDGetComplete(result: String) {
resultCallback.success(result)
}
override fun onOAIDGetError(error: Exception?) {
resultCallback.success(null)
}
}

View File

@@ -1,16 +1,14 @@
package cn.yooss.mood_diary
package cn.yooss.moodiary
import android.os.Bundle
import com.github.gzuliyujiang.oaid.DeviceID
import com.github.gzuliyujiang.oaid.IGetter
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
@@ -27,15 +25,24 @@ class MainActivity : FlutterFragmentActivity() {
}
}
}
}
private fun getOAID(resultCallback: MethodChannel.Result) {
if (DeviceID.supportedOAID(application)) {
DeviceID.getOAID(application, HandleGetOAID(resultCallback));
DeviceID.getOAID(application, HandleGetOAID(resultCallback))
} else {
resultCallback.success(null)
}
}
}
class HandleGetOAID(private var resultCallback: MethodChannel.Result) : IGetter {
override fun onOAIDGetComplete(result: String) {
resultCallback.success(result)
}
override fun onOAIDGetError(error: Exception?) {
resultCallback.success(null)
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Moodiary</string>
</resources>

View File

@@ -22,8 +22,8 @@ subprojects {
if (project.plugins.hasPlugin("com.android.application") ||
project.plugins.hasPlugin("com.android.library")) {
project.android {
compileSdkVersion 35
buildToolsVersion "35.0.0"
compileSdkVersion 36
buildToolsVersion "36.0.0"
}
}
if (project.hasProperty("android")) {

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.9-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip

90
android/gradlew.bat vendored Executable file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -25,8 +25,8 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.7.3' apply false
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
id "com.android.application" version '8.9.1' apply false
id "org.jetbrains.kotlin.android" version "2.1.20" apply false
}
include ":app"

19
build.yaml Normal file
View File

@@ -0,0 +1,19 @@
targets:
$default:
builders:
json_serializable:
options:
any_map: false
checked: false
constructor: ""
create_factory: true
create_field_map: false
create_json_keys: false
create_per_field_to_json: false
create_to_json: true
disallow_unrecognized_keys: false
explicit_to_json: false
field_rename: none
generic_argument_factories: false
ignore_unannotated: false
include_if_null: false

88
cliff.toml Normal file
View File

@@ -0,0 +1,88 @@
# git-cliff ~ default configuration file
# https://git-cliff.org/docs/configuration
#
# Lines starting with "#" are comments.
# Configuration options are organized into tables and keys.
# See documentation for more information on available options.
[output]
file = "CHANGELOG.md"
[changelog]
# template for the changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# template for the changelog footer
footer = """
<!-- generated by git-cliff -->
"""
# remove the leading and trailing s
trim = true
# postprocessors
postprocessors = [
# { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL
]
# render body even when there are no releases to process
# render_always = true
# output file path
# output = "test.md"
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = true
# process each line of a commit as an individual commit
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
# Replace issue numbers
#{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
# Check spelling of the commit with https://github.com/crate-ci/typos
# If the spelling is incorrect, it will be automatically fixed.
#{ pattern = '.*', replace_command = 'typos --write-changes -' },
]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(deps.*\\)", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore\\(readme\\)", skip = true },
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
{ message = ".*", group = "<!-- 10 -->💼 Other" },
]
# filter out the commits that are not matched by commit parsers
filter_commits = false
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"

View File

@@ -1,3 +1,4 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
- shared_preferences: true

View File

@@ -1,3 +1,4 @@
rust_input: crate::api
rust_root: rust/
dart_output: lib/src/rust
dart_output: lib/src/rust
web: false

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '18.2'
platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -5,7 +5,6 @@ PODS:
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- FlutterMacOS
- device_info_plus (0.0.1):
- Flutter
- DKImagePickerController/Core (4.3.9):
@@ -62,14 +61,19 @@ PODS:
- Flutter
- flutter_native_splash (2.4.3):
- Flutter
- fluttertoast (0.0.2):
- flutter_secure_storage_darwin (10.0.0):
- Flutter
- Toast
- FlutterMacOS
- fvp (0.31.2):
- Flutter
- FlutterMacOS
- mdk (~> 0.32.0)
- gal (1.0.0):
- Flutter
- FlutterMacOS
- geolocator_apple (1.2.0):
- Flutter
- FlutterMacOS
- image_picker_ios (0.0.1):
- Flutter
- isar_flutter_libs (1.0.0):
@@ -92,18 +96,14 @@ PODS:
- Mantle (2.2.0):
- Mantle/extobjc (= 2.2.0)
- Mantle/extobjc (2.2.0)
- media_kit_libs_ios_video (1.0.4):
- mdk (0.32.0)
- mobile_scanner (7.0.0):
- Flutter
- media_kit_native_event_loop (1.0.0):
- Flutter
- media_kit_video (0.0.1):
- FlutterMacOS
- moodiary_rust (0.0.1):
- Flutter
- network_info_plus (0.0.1):
- Flutter
- ObjectBox (4.0.1)
- objectbox_flutter_libs (0.0.1):
- Flutter
- ObjectBox (= 4.0.1)
- OrderedSet (6.0.3)
- package_info_plus (0.4.5):
- Flutter
@@ -114,13 +114,7 @@ PODS:
- Flutter
- quill_native_bridge_ios (0.0.1):
- Flutter
- record_darwin (1.0.0):
- Flutter
- rive_common (0.0.1):
- Flutter
- rust_lib_mood_diary (0.0.1):
- Flutter
- screen_brightness_ios (0.1.0):
- record_ios (1.0.0):
- Flutter
- SDWebImage (5.19.7):
- SDWebImage/Core (= 5.19.7)
@@ -159,21 +153,18 @@ PODS:
- TensorFlowLiteSwift (= 2.12.0)
- TensorFlowLiteSwift/CoreML (= 2.12.0)
- TensorFlowLiteSwift/Metal (= 2.12.0)
- Toast (4.1.1)
- url_launcher_ios (0.0.1):
- Flutter
- video_player_avfoundation (0.0.1):
- Flutter
- FlutterMacOS
- volume_controller (0.0.1):
- Flutter
- wakelock_plus (0.0.1):
- Flutter
DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- fc_native_video_thumbnail (from `.symlinks/plugins/fc_native_video_thumbnail/darwin`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
@@ -182,32 +173,27 @@ DEPENDENCIES:
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_keyboard_visibility_temp_fork (from `.symlinks/plugins/flutter_keyboard_visibility_temp_fork/ios`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- flutter_secure_storage_darwin (from `.symlinks/plugins/flutter_secure_storage_darwin/darwin`)
- fvp (from `.symlinks/plugins/fvp/darwin`)
- gal (from `.symlinks/plugins/gal/darwin`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
- moodiary_rust (from `.symlinks/plugins/moodiary_rust/ios`)
- network_info_plus (from `.symlinks/plugins/network_info_plus/ios`)
- objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- quill_native_bridge_ios (from `.symlinks/plugins/quill_native_bridge_ios/ios`)
- record_darwin (from `.symlinks/plugins/record_darwin/ios`)
- rive_common (from `.symlinks/plugins/rive_common/ios`)
- rust_lib_mood_diary (from `.symlinks/plugins/rust_lib_mood_diary/ios`)
- screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`)
- record_ios (from `.symlinks/plugins/record_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- tflite_flutter (from `.symlinks/plugins/tflite_flutter/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
- volume_controller (from `.symlinks/plugins/volume_controller/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
SPEC REPOS:
@@ -216,14 +202,13 @@ SPEC REPOS:
- DKPhotoGallery
- libwebp
- Mantle
- ObjectBox
- mdk
- OrderedSet
- SDWebImage
- SDWebImageWebPCoder
- SwiftyGif
- TensorFlowLiteC
- TensorFlowLiteSwift
- Toast
EXTERNAL SOURCES:
app_links:
@@ -231,7 +216,7 @@ EXTERNAL SOURCES:
audioplayers_darwin:
:path: ".symlinks/plugins/audioplayers_darwin/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/darwin"
:path: ".symlinks/plugins/connectivity_plus/ios"
device_info_plus:
:path: ".symlinks/plugins/device_info_plus/ios"
fc_native_video_thumbnail:
@@ -248,28 +233,26 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_keyboard_visibility_temp_fork/ios"
flutter_native_splash:
:path: ".symlinks/plugins/flutter_native_splash/ios"
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
flutter_secure_storage_darwin:
:path: ".symlinks/plugins/flutter_secure_storage_darwin/darwin"
fvp:
:path: ".symlinks/plugins/fvp/darwin"
gal:
:path: ".symlinks/plugins/gal/darwin"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/ios"
:path: ".symlinks/plugins/geolocator_apple/darwin"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
isar_flutter_libs:
:path: ".symlinks/plugins/isar_flutter_libs/ios"
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
media_kit_libs_ios_video:
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
media_kit_native_event_loop:
:path: ".symlinks/plugins/media_kit_native_event_loop/ios"
media_kit_video:
:path: ".symlinks/plugins/media_kit_video/ios"
mobile_scanner:
:path: ".symlinks/plugins/mobile_scanner/darwin"
moodiary_rust:
:path: ".symlinks/plugins/moodiary_rust/ios"
network_info_plus:
:path: ".symlinks/plugins/network_info_plus/ios"
objectbox_flutter_libs:
:path: ".symlinks/plugins/objectbox_flutter_libs/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
@@ -278,14 +261,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/permission_handler_apple/ios"
quill_native_bridge_ios:
:path: ".symlinks/plugins/quill_native_bridge_ios/ios"
record_darwin:
:path: ".symlinks/plugins/record_darwin/ios"
rive_common:
:path: ".symlinks/plugins/rive_common/ios"
rust_lib_mood_diary:
:path: ".symlinks/plugins/rust_lib_mood_diary/ios"
screen_brightness_ios:
:path: ".symlinks/plugins/screen_brightness_ios/ios"
record_ios:
:path: ".symlinks/plugins/record_ios/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
@@ -298,48 +275,42 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/url_launcher_ios/ios"
video_player_avfoundation:
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
volume_controller:
:path: ".symlinks/plugins/volume_controller/ios"
wakelock_plus:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
app_links: 3da4c36b46cac3bf24eb897f1a6ce80bda109874
app_links: 76b66b60cc809390ca1ad69bfd66b998d2387ac7
audioplayers_darwin: ccf9c770ee768abb07e26d90af093f7bab1c12ab
connectivity_plus: 2256d3e20624a7749ed21653aafe291a46446fee
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
fc_native_video_thumbnail: ea75aa9d0f7e7b58215d2ad99ac9dddafc038bc9
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_keyboard_visibility_temp_fork: 95b2d534bacf6ac62e7fcbe5c2a9e2c2a17ce06f
flutter_native_splash: 6cad9122ea0fad137d23137dd14b937f3e90b145
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_secure_storage_darwin: ce237a8775b39723566dc72571190a3769d70468
fvp: 89275776efede62b7fde1762afa518974e6d6750
gal: baecd024ebfd13c441269ca7404792a7152fde89
geolocator_apple: 1560c3c875af2a412242c7a923e15d0d401966ff
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
isar_flutter_libs: 9fc2cfb928c539e1b76c481ba5d143d556d94920
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_native_event_loop: 5fba1a849a6c87a34985f1e178a0de5bd444a0cf
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
mdk: a726c85fd49c002b83d586b278d452f387750889
mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
moodiary_rust: e75b3fb63e53d3ba5cfed0edf0b6df5f98c4c5f1
network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
ObjectBox: 0bc4bb75eea85f6af06b369148b334c2056bbc29
objectbox_flutter_libs: 3af037f7cc35e687acca01f1f6da6cb6c2abc22b
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
quill_native_bridge_ios: f47af4b14e7757968486641656c5d23250cee521
record_darwin: fb1f375f1d9603714f55b8708a903bbb91ffdb0a
rive_common: dd421daaf9ae69f0125aa761dd96abd278399952
rust_lib_mood_diary: ea9f784d50426d5994e53cecee71cd23d965542a
screen_brightness_ios: 5ed898fa50fa82a26171c086ca5e28228f932576
record_ios: fee1c924aa4879b882ebca2b4bce6011bcfc3d8b
SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
@@ -349,12 +320,10 @@ SPEC CHECKSUMS:
TensorFlowLiteC: 20785a69299185a379ba9852b6625f00afd7984a
TensorFlowLiteSwift: 3a4928286e9e35bdd3e17970f48e53c80d25e793
tflite_flutter: 64b192e11352fe36943ab6656e1d49207f1a5595
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
volume_controller: ca1cde542ee70fad77d388f82e9616488110942b
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
PODFILE CHECKSUM: 9752b5340b4d3f9618318fcd530d907790e2a9f5
PODFILE CHECKSUM: ed22e19af5eb4a72710862041c4be54584bcf797
COCOAPODS: 1.16.2

View File

@@ -7,15 +7,15 @@
objects = {
/* Begin PBXBuildFile section */
10D3882531AED19DB670FEB7 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F2698D5B078302CF0E4E6EE6 /* Pods_RunnerTests.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
306627225710E691EF0E0A35 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A1A8341695BDC0741989253 /* Pods_Runner.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7F6D40933C7341CBD11FBDD8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51E3DDEC08B33FD4E6BEE95D /* Pods_RunnerTests.framework */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
FE9916AC13415D97E045D776 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E85B5AB729F0B2360835644 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -42,29 +42,31 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
132767BCEC59ECA2C27AAF55 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2A1A8341695BDC0741989253 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
18106D637ED786D29FE68004 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
51E3DDEC08B33FD4E6BEE95D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
481FF39ABA9170A968D00D47 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7D210E37B46E2650FA945AB1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
8E85B5AB729F0B2360835644 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D3787A842552CCA621630495 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
D73569CF22DB2A16EAE0220D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
E231F0C855BB3DD103DA44E9 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
E7AEAE59248BEA6C8378B114 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
E913EDBE59656A0E8EB14DDA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
9F8B7E7E836FBA71F1E2767D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
A910B4CF2D4C960600C998E7 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; };
A910B4D02D4C960600C998E7 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
A910B4D12D4C961C00C998E7 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = "zh-Hans"; path = "zh-Hans.lproj/Main.storyboard"; sourceTree = "<group>"; };
A910B4D22D4C961C00C998E7 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.storyboard"; sourceTree = "<group>"; };
D0CBE1B7A5C3D8E84DECC981 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
F2698D5B078302CF0E4E6EE6 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FD54B3E6C9A9278793818CA8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -72,7 +74,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
306627225710E691EF0E0A35 /* Pods_Runner.framework in Frameworks */,
FE9916AC13415D97E045D776 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -80,22 +82,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7F6D40933C7341CBD11FBDD8 /* Pods_RunnerTests.framework in Frameworks */,
10D3882531AED19DB670FEB7 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
32F89EBD1A567B14B95A803E /* Frameworks */ = {
isa = PBXGroup;
children = (
2A1A8341695BDC0741989253 /* Pods_Runner.framework */,
51E3DDEC08B33FD4E6BEE95D /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@@ -107,12 +100,12 @@
4358F1D645E2BE879D24A551 /* Pods */ = {
isa = PBXGroup;
children = (
D3787A842552CCA621630495 /* Pods-Runner.debug.xcconfig */,
E913EDBE59656A0E8EB14DDA /* Pods-Runner.release.xcconfig */,
7D210E37B46E2650FA945AB1 /* Pods-Runner.profile.xcconfig */,
D73569CF22DB2A16EAE0220D /* Pods-RunnerTests.debug.xcconfig */,
E7AEAE59248BEA6C8378B114 /* Pods-RunnerTests.release.xcconfig */,
E231F0C855BB3DD103DA44E9 /* Pods-RunnerTests.profile.xcconfig */,
D0CBE1B7A5C3D8E84DECC981 /* Pods-Runner.debug.xcconfig */,
FD54B3E6C9A9278793818CA8 /* Pods-Runner.release.xcconfig */,
132767BCEC59ECA2C27AAF55 /* Pods-Runner.profile.xcconfig */,
9F8B7E7E836FBA71F1E2767D /* Pods-RunnerTests.debug.xcconfig */,
18106D637ED786D29FE68004 /* Pods-RunnerTests.release.xcconfig */,
481FF39ABA9170A968D00D47 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@@ -136,7 +129,7 @@
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
4358F1D645E2BE879D24A551 /* Pods */,
32F89EBD1A567B14B95A803E /* Frameworks */,
B0AE19443A2FC97A6908AB80 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -164,6 +157,15 @@
path = Runner;
sourceTree = "<group>";
};
B0AE19443A2FC97A6908AB80 /* Frameworks */ = {
isa = PBXGroup;
children = (
8E85B5AB729F0B2360835644 /* Pods_Runner.framework */,
F2698D5B078302CF0E4E6EE6 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -171,7 +173,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
802002D8FFADD5D24C50A9FA /* [CP] Check Pods Manifest.lock */,
1C96070FD74708494C91C45C /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
CE18F12EDBB0BF2FD5BF0F0E /* Frameworks */,
@@ -190,15 +192,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
3190980419AFCAD7AC11C68D /* [CP] Check Pods Manifest.lock */,
6FA29CD45882489C42CB69A1 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
DDBAC86E6B3F372170C284A0 /* [CP] Embed Pods Frameworks */,
B482CF705AC6985E445540F2 /* [CP] Copy Pods Resources */,
523FF08A9B798D86BA8B9DD9 /* Frameworks */,
24F448645ED8BCE73BE4BB9F /* [CP] Embed Pods Frameworks */,
E28A122442FA326B99260C5B /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -231,11 +233,11 @@
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
developmentRegion = "zh-Hans";
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
"zh-Hans",
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
@@ -270,45 +272,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3190980419AFCAD7AC11C68D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
802002D8FFADD5D24C50A9FA /* [CP] Check Pods Manifest.lock */ = {
1C96070FD74708494C91C45C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -330,6 +294,61 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
24F448645ED8BCE73BE4BB9F /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
6FA29CD45882489C42CB69A1 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -345,7 +364,7 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
B482CF705AC6985E445540F2 /* [CP] Copy Pods Resources */ = {
E28A122442FA326B99260C5B /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -362,23 +381,6 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
DDBAC86E6B3F372170C284A0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -413,7 +415,8 @@
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
A910B4CF2D4C960600C998E7 /* en */,
A910B4D12D4C961C00C998E7 /* zh-Hans */,
);
name = Main.storyboard;
sourceTree = "<group>";
@@ -421,7 +424,8 @@
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
A910B4D02D4C960600C998E7 /* en */,
A910B4D22D4C961C00C998E7 /* zh-Hans */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
@@ -434,6 +438,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -467,6 +472,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = s;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -495,9 +501,10 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3XA29H789G;
ENABLE_BITCODE = NO;
FLUTTER_BUILD_MODE = release;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.lifestyle";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -505,16 +512,21 @@
PRODUCT_BUNDLE_IDENTIFIER = cn.yooss.moodiary;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "moodiary-dev";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "moodiary-ios";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = D73569CF22DB2A16EAE0220D /* Pods-RunnerTests.debug.xcconfig */;
baseConfigurationReference = 9F8B7E7E836FBA71F1E2767D /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Manual;
@@ -534,7 +546,7 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E7AEAE59248BEA6C8378B114 /* Pods-RunnerTests.release.xcconfig */;
baseConfigurationReference = 18106D637ED786D29FE68004 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Manual;
@@ -552,7 +564,7 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E231F0C855BB3DD103DA44E9 /* Pods-RunnerTests.profile.xcconfig */;
baseConfigurationReference = 481FF39ABA9170A968D00D47 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Manual;
@@ -573,6 +585,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -631,6 +644,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -664,6 +678,8 @@
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = s;
GCC_PREPROCESSOR_DEFINITIONS = "DISABLE_PUSH_NOTIFICATIONS=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -673,7 +689,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
@@ -694,9 +710,10 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3XA29H789G;
ENABLE_BITCODE = NO;
FLUTTER_BUILD_MODE = debug;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.lifestyle";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -704,10 +721,15 @@
PRODUCT_BUNDLE_IDENTIFIER = cn.yooss.moodiary;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "moodiary-dev";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "moodiary-ios";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@@ -725,9 +747,10 @@
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 3XA29H789G;
ENABLE_BITCODE = NO;
FLUTTER_BUILD_MODE = release;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.lifestyle";
IPHONEOS_DEPLOYMENT_TARGET = 15.6;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -735,9 +758,14 @@
PRODUCT_BUNDLE_IDENTIFIER = cn.yooss.moodiary;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "moodiary-dev";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "moodiary-ios";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;

View File

@@ -51,7 +51,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
@@ -59,6 +59,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
showGraphicsOverview = "Yes"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -20,24 +20,38 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppleMusicUsageDescription</key>
<string>应用需要您的同意,才能访问媒体资料库</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>应用需要您的同意,才能使用蓝牙</string>
<key>NSCameraUsageDescription</key>
<string>使用你的摄像头拍摄媒体</string>
<string>应用需要您的同意,才能访问相机</string>
<key>NSContactsUsageDescription</key>
<string>应用需要您的同意,才能访问通讯录</string>
<key>NSFaceIDUsageDescription</key>
<string>使用你的面部数据解锁</string>
<string>应用需要您的同意,才能使用人脸识别</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>应用需要您的同意,才能访问位置</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>访问你的位置信息</string>
<string>应用需要您的同意,才能访问位置</string>
<key>NSMicrophoneUsageDescription</key>
<string>使用你的麦克风来录制声音</string>
<string>应用需要您的同意,才能使用麦克风</string>
<key>NSMotionUsageDescription</key>
<string>应用需要您的同意,才能访问运动与健身</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>访问你的媒体数据</string>
<string>应用需要您的同意,才能访问照片</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>访问你的照片</string>
<string>应用需要您的同意,才能访问照片</string>
<key>NSSiriUsageDescription</key>
<string>应用需要您的同意才能访问Siri</string>
<key>NSSpeechRecognitionUsageDescription</key>
<string>应用需要您的同意,才能访问语音识别</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
@@ -49,8 +63,6 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
@@ -61,5 +73,7 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>keychain-access-groups</key>
<array/>
</dict>
</plist>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/>
<constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/>
<constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/>
<constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="1152" height="1152"/>
<image name="LaunchBackground" width="1" height="1"/>
</resources>
</document>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-26" y="-77"/>
</scene>
</scenes>
</document>

View File

@@ -1,3 +1,4 @@
arb-dir: lib/l10n
template-arb-file: intl_zh.arb
output-localization-file: app_localizations.dart
output-localization-file: app_localizations.dart
synthetic-package: false

View File

@@ -5,25 +5,28 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
import 'package:mood_diary/common/models/geo.dart';
import 'package:mood_diary/common/models/github.dart';
import 'package:mood_diary/common/models/hitokoto.dart';
import 'package:mood_diary/common/models/hunyuan.dart';
import 'package:mood_diary/common/models/image.dart';
import 'package:mood_diary/common/models/weather.dart';
import 'package:mood_diary/utils/http_util.dart';
import 'package:mood_diary/utils/notice_util.dart';
import 'package:mood_diary/utils/signature_util.dart';
import 'package:refreshed/refreshed.dart';
import '../utils/data/pref.dart';
import 'package:moodiary/common/models/geo.dart';
import 'package:moodiary/common/models/github.dart';
import 'package:moodiary/common/models/hitokoto.dart';
import 'package:moodiary/common/models/hunyuan.dart';
import 'package:moodiary/common/models/image.dart';
import 'package:moodiary/common/models/weather.dart';
import 'package:moodiary/l10n/l10n.dart';
import 'package:moodiary/persistence/pref.dart';
import 'package:moodiary/utils/http_util.dart';
import 'package:moodiary/utils/notice_util.dart';
import 'package:moodiary/utils/signature_util.dart';
class Api {
static Future<Stream<String>?> getHunYuan(
String id, String key, List<Message> messages, int model) async {
String id,
String key,
List<Message> messages,
int model,
) async {
//获取时间戳
var timestamp = DateTime.now().millisecondsSinceEpoch;
var hunyuanModel = switch (model) {
final timestamp = DateTime.now().millisecondsSinceEpoch;
final hunyuanModel = switch (model) {
0 => 'hunyuan-lite',
1 => 'hunyuan-standard',
2 => 'hunyuan-pro',
@@ -31,66 +34,82 @@ class Api {
_ => 'hunyuan-lite',
};
//请求正文
var body = {
final body = {
'Model': hunyuanModel,
'Messages': messages.map((value) => value.toMap()).toList(),
'Messages': messages.map((value) => value.toJson()).toList(),
'Stream': true,
};
//获取签名
var authorization =
SignatureUtil.generateSignature(id, key, timestamp, body);
final authorization = SignatureUtil.generateSignature(
id,
key,
timestamp,
body,
);
//构造请求头
var header = PublicHeader(
'ChatCompletions', timestamp ~/ 1000, '2023-09-01', authorization);
final header = PublicHeader(
action: 'ChatCompletions',
timestamp: timestamp ~/ 1000,
version: '2023-09-01',
authorization: authorization,
);
//发起请求
return await HttpUtil().postStream('https://hunyuan.tencentcloudapi.com',
header: header.toMap(), data: body);
return await HttpUtil().postStream(
'https://hunyuan.tencentcloudapi.com',
header: header.toJson(),
data: body,
);
}
static Future<Uint8List?> getImageData(String url) async {
return (await HttpUtil().get(url, type: ResponseType.bytes)).data;
}
static Future<List<String>?> updatePosition() async {
static Future<List<String>?> updatePosition(BuildContext context) async {
Position? position;
var permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
NoticeUtil.showToast('请开启定位权限');
if (permission == LocationPermission.denied && context.mounted) {
toast.info(message: context.l10n.noticeEnableLocation);
return null;
}
if (permission == LocationPermission.deniedForever) {
NoticeUtil.showToast('请前往设置开启定位权限');
if (permission == LocationPermission.deniedForever && context.mounted) {
toast.info(message: context.l10n.noticeEnableLocation2);
return null;
}
}
if (await Geolocator.isLocationServiceEnabled()) {
position = await Geolocator.getLastKnownPosition(
forceAndroidLocationManager: true);
forceAndroidLocationManager: true,
);
position ??= await Geolocator.getCurrentPosition(
locationSettings: AndroidSettings(forceLocationManager: true));
locationSettings: AndroidSettings(forceLocationManager: true),
);
}
if (position != null) {
var local = Localizations.localeOf(Get.context!);
var parameters = {
if (position != null && context.mounted) {
final local = Localizations.localeOf(context);
final parameters = {
'location':
'${double.parse(position.longitude.toStringAsFixed(2))},${double.parse(position.latitude.toStringAsFixed(2))}',
'key': PrefUtil.getValue<String>('qweatherKey'),
'lang': local
'lang': local,
};
var res = await HttpUtil().get(
'https://geoapi.qweather.com/v2/city/lookup',
parameters: parameters);
var geo =
await compute(GeoResponse.fromJson, res.data as Map<String, dynamic>);
final res = await HttpUtil().get(
'https://${PrefUtil.getValue<String>('qweatherApiHost')}/geo/v2/city/lookup',
parameters: parameters,
);
final geo = await compute(
GeoResponse.fromJson,
res.data as Map<String, dynamic>,
);
if (geo.location != null && geo.location!.isNotEmpty) {
var city = geo.location!.first;
final city = geo.location!.first;
return [
position.latitude.toString(),
position.longitude.toString(),
'${city.adm2} ${city.name}'
'${city.adm2} ${city.name}',
];
} else {
return null;
@@ -100,52 +119,63 @@ class Api {
}
}
static Future<List<String>?> updateWeather({required LatLng position}) async {
var local = Localizations.localeOf(Get.context!);
var parameters = {
static Future<List<String>?> updateWeather({
required BuildContext context,
required LatLng position,
}) async {
final local = Localizations.localeOf(context);
final parameters = {
'location':
'${double.parse(position.longitude.toStringAsFixed(2))},${double.parse(position.latitude.toStringAsFixed(2))}',
'key': PrefUtil.getValue<String>('qweatherKey'),
'lang': local
'lang': local,
};
var res = await HttpUtil().get('https://devapi.qweather.com/v7/weather/now',
parameters: parameters);
var weather = await compute(
WeatherResponse.fromJson, res.data as Map<String, dynamic>);
final res = await HttpUtil().get(
'https://${PrefUtil.getValue<String>('qweatherApiHost')}/v7/weather/now',
parameters: parameters,
);
final weather = await compute(
WeatherResponse.fromJson,
res.data as Map<String, dynamic>,
);
if (weather.now != null) {
return [
weather.now!.icon!,
weather.now!.temp!,
weather.now!.text!,
];
return [weather.now!.icon!, weather.now!.temp!, weather.now!.text!];
} else {
return null;
}
}
static Future<GithubRelease?> getGithubRelease() async {
var res = await HttpUtil()
.get('https://api.github.com/repos/ZhuJHua/moodiary/releases/latest');
final res = await HttpUtil().get(
'https://api.github.com/repos/ZhuJHua/moodiary/releases/latest',
);
if (res.data != null) {
var githubRelease = await compute(
GithubRelease.fromJson, res.data as Map<String, dynamic>);
final githubRelease = await compute(
GithubRelease.fromJson,
res.data as Map<String, dynamic>,
);
return githubRelease;
}
return null;
}
static Future<List<String>?> updateHitokoto() async {
var res = await HttpUtil().get('https://v1.hitokoto.cn');
var hitokoto = await compute(
HitokotoResponse.fromJson, res.data as Map<String, dynamic>);
final res = await HttpUtil().get('https://v1.hitokoto.cn');
final hitokoto = await compute(
HitokotoResponse.fromJson,
res.data as Map<String, dynamic>,
);
return [hitokoto.hitokoto!];
}
static Future<List<String>?> updateImageUrl() async {
var res = await HttpUtil()
.get('https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1');
BingImage bingImage =
await compute(BingImage.fromJson, res.data as Map<String, dynamic>);
final res = await HttpUtil().get(
'https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1',
);
final BingImage bingImage = await compute(
BingImage.fromJson,
res.data as Map<String, dynamic>,
);
return ['https://cn.bing.com${bingImage.images?[0].url}'];
}
}

View File

@@ -1,110 +1,45 @@
class GeoResponse {
String? code;
List<Location>? location;
Refer? refer;
import 'package:freezed_annotation/freezed_annotation.dart';
GeoResponse({this.code, this.location, this.refer});
part 'geo.freezed.dart';
part 'geo.g.dart';
GeoResponse.fromJson(Map<String, dynamic> json) {
code = json["code"];
location = json["location"] == null
? null
: (json["location"] as List).map((e) => Location.fromJson(e)).toList();
refer = json["refer"] == null ? null : Refer.fromJson(json["refer"]);
}
@freezed
abstract class GeoResponse with _$GeoResponse {
const factory GeoResponse({
String? code,
List<Location>? location,
Refer? refer,
}) = _GeoResponse;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["code"] = code;
data["location"] = location?.map((e) => e.toJson()).toList();
data["refer"] = refer?.toJson();
return data;
}
factory GeoResponse.fromJson(Map<String, dynamic> json) =>
_$GeoResponseFromJson(json);
}
class Refer {
List<String>? sources;
List<String>? license;
@freezed
abstract class Refer with _$Refer {
const factory Refer({List<String>? sources, List<String>? license}) = _Refer;
Refer({this.sources, this.license});
Refer.fromJson(Map<String, dynamic> json) {
sources =
json["sources"] == null ? null : List<String>.from(json["sources"]);
license =
json["license"] == null ? null : List<String>.from(json["license"]);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["sources"] = sources;
data["license"] = license;
return data;
}
factory Refer.fromJson(Map<String, dynamic> json) => _$ReferFromJson(json);
}
class Location {
String? name;
String? id;
String? lat;
String? lon;
String? adm2;
String? adm1;
String? country;
String? tz;
String? utcOffset;
String? isDst;
String? type;
String? rank;
String? fxLink;
@freezed
abstract class Location with _$Location {
const factory Location({
String? name,
String? id,
String? lat,
String? lon,
String? adm2,
String? adm1,
String? country,
String? tz,
String? utcOffset,
String? isDst,
String? type,
String? rank,
String? fxLink,
}) = _Location;
Location({
this.name,
this.id,
this.lat,
this.lon,
this.adm2,
this.adm1,
this.country,
this.tz,
this.utcOffset,
this.isDst,
this.type,
this.rank,
this.fxLink,
});
Location.fromJson(Map<String, dynamic> json) {
name = json["name"];
id = json["id"];
lat = json["lat"];
lon = json["lon"];
adm2 = json["adm2"];
adm1 = json["adm1"];
country = json["country"];
tz = json["tz"];
utcOffset = json["utcOffset"];
isDst = json["isDst"];
type = json["type"];
rank = json["rank"];
fxLink = json["fxLink"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["name"] = name;
data["id"] = id;
data["lat"] = lat;
data["lon"] = lon;
data["adm2"] = adm2;
data["adm1"] = adm1;
data["country"] = country;
data["tz"] = tz;
data["utcOffset"] = utcOffset;
data["isDst"] = isDst;
data["type"] = type;
data["rank"] = rank;
data["fxLink"] = fxLink;
return data;
}
factory Location.fromJson(Map<String, dynamic> json) =>
_$LocationFromJson(json);
}

View File

@@ -0,0 +1,768 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'geo.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$GeoResponse {
String? get code;
List<Location>? get location;
Refer? get refer;
/// Create a copy of GeoResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$GeoResponseCopyWith<GeoResponse> get copyWith =>
_$GeoResponseCopyWithImpl<GeoResponse>(this as GeoResponse, _$identity);
/// Serializes this GeoResponse to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is GeoResponse &&
(identical(other.code, code) || other.code == code) &&
const DeepCollectionEquality().equals(other.location, location) &&
(identical(other.refer, refer) || other.refer == refer));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, code, const DeepCollectionEquality().hash(location), refer);
@override
String toString() {
return 'GeoResponse(code: $code, location: $location, refer: $refer)';
}
}
/// @nodoc
abstract mixin class $GeoResponseCopyWith<$Res> {
factory $GeoResponseCopyWith(GeoResponse value,
$Res Function(GeoResponse) _then) = _$GeoResponseCopyWithImpl;
@useResult
$Res call({
String? code, List<Location>? location, Refer? refer
});
$ReferCopyWith<$Res>? get refer;
}
/// @nodoc
class _$GeoResponseCopyWithImpl<$Res>
implements $GeoResponseCopyWith<$Res> {
_$GeoResponseCopyWithImpl(this._self, this._then);
final GeoResponse _self;
final $Res Function(GeoResponse) _then;
/// Create a copy of GeoResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? code = freezed, Object? location = freezed, Object? refer = freezed,}) {
return _then(_self.copyWith(
code: freezed == code
? _self.code
: code // ignore: cast_nullable_to_non_nullable
as String?,
location: freezed == location
? _self.location
: location // ignore: cast_nullable_to_non_nullable
as List<Location>?,
refer: freezed == refer
? _self.refer
: refer // ignore: cast_nullable_to_non_nullable
as Refer?,
));
}
/// Create a copy of GeoResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ReferCopyWith<$Res>? get refer {
if (_self.refer == null) {
return null;
}
return $ReferCopyWith<$Res>(_self.refer!, (value) {
return _then(_self.copyWith(refer: value));
});
}
}
/// @nodoc
@JsonSerializable()
class _GeoResponse implements GeoResponse {
const _GeoResponse({this.code, final List<Location>? location, this.refer})
: _location = location;
factory _GeoResponse.fromJson(Map<String, dynamic> json) =>
_$GeoResponseFromJson(json);
@override final String? code;
final List<Location>? _location;
@override List<Location>? get location {
final value = _location;
if (value == null) return null;
if (_location is EqualUnmodifiableListView) return _location;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override final Refer? refer;
/// Create a copy of GeoResponse
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$GeoResponseCopyWith<_GeoResponse> get copyWith =>
__$GeoResponseCopyWithImpl<_GeoResponse>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$GeoResponseToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _GeoResponse &&
(identical(other.code, code) || other.code == code) &&
const DeepCollectionEquality().equals(other._location, _location) &&
(identical(other.refer, refer) || other.refer == refer));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, code, const DeepCollectionEquality().hash(_location), refer);
@override
String toString() {
return 'GeoResponse(code: $code, location: $location, refer: $refer)';
}
}
/// @nodoc
abstract mixin class _$GeoResponseCopyWith<$Res>
implements $GeoResponseCopyWith<$Res> {
factory _$GeoResponseCopyWith(_GeoResponse value,
$Res Function(_GeoResponse) _then) = __$GeoResponseCopyWithImpl;
@override
@useResult
$Res call({
String? code, List<Location>? location, Refer? refer
});
@override $ReferCopyWith<$Res>? get refer;
}
/// @nodoc
class __$GeoResponseCopyWithImpl<$Res>
implements _$GeoResponseCopyWith<$Res> {
__$GeoResponseCopyWithImpl(this._self, this._then);
final _GeoResponse _self;
final $Res Function(_GeoResponse) _then;
/// Create a copy of GeoResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? code = freezed, Object? location = freezed, Object? refer = freezed,}) {
return _then(_GeoResponse(
code: freezed == code
? _self.code
: code // ignore: cast_nullable_to_non_nullable
as String?,
location: freezed == location
? _self._location
: location // ignore: cast_nullable_to_non_nullable
as List<Location>?,
refer: freezed == refer
? _self.refer
: refer // ignore: cast_nullable_to_non_nullable
as Refer?,
));
}
/// Create a copy of GeoResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ReferCopyWith<$Res>? get refer {
if (_self.refer == null) {
return null;
}
return $ReferCopyWith<$Res>(_self.refer!, (value) {
return _then(_self.copyWith(refer: value));
});
}
}
/// @nodoc
mixin _$Refer {
List<String>? get sources;
List<String>? get license;
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ReferCopyWith<Refer> get copyWith =>
_$ReferCopyWithImpl<Refer>(this as Refer, _$identity);
/// Serializes this Refer to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Refer &&
const DeepCollectionEquality().equals(other.sources, sources) &&
const DeepCollectionEquality().equals(other.license, license));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, const DeepCollectionEquality().hash(sources),
const DeepCollectionEquality().hash(license));
@override
String toString() {
return 'Refer(sources: $sources, license: $license)';
}
}
/// @nodoc
abstract mixin class $ReferCopyWith<$Res> {
factory $ReferCopyWith(Refer value,
$Res Function(Refer) _then) = _$ReferCopyWithImpl;
@useResult
$Res call({
List<String>? sources, List<String>? license
});
}
/// @nodoc
class _$ReferCopyWithImpl<$Res>
implements $ReferCopyWith<$Res> {
_$ReferCopyWithImpl(this._self, this._then);
final Refer _self;
final $Res Function(Refer) _then;
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? sources = freezed, Object? license = freezed,}) {
return _then(_self.copyWith(
sources: freezed == sources
? _self.sources
: sources // ignore: cast_nullable_to_non_nullable
as List<String>?,
license: freezed == license
? _self.license
: license // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _Refer implements Refer {
const _Refer({final List<String>? sources, final List<String>? license})
: _sources = sources,
_license = license;
factory _Refer.fromJson(Map<String, dynamic> json) => _$ReferFromJson(json);
final List<String>? _sources;
@override List<String>? get sources {
final value = _sources;
if (value == null) return null;
if (_sources is EqualUnmodifiableListView) return _sources;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
final List<String>? _license;
@override List<String>? get license {
final value = _license;
if (value == null) return null;
if (_license is EqualUnmodifiableListView) return _license;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ReferCopyWith<_Refer> get copyWith =>
__$ReferCopyWithImpl<_Refer>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ReferToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _Refer &&
const DeepCollectionEquality().equals(other._sources, _sources) &&
const DeepCollectionEquality().equals(other._license, _license));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, const DeepCollectionEquality().hash(_sources),
const DeepCollectionEquality().hash(_license));
@override
String toString() {
return 'Refer(sources: $sources, license: $license)';
}
}
/// @nodoc
abstract mixin class _$ReferCopyWith<$Res> implements $ReferCopyWith<$Res> {
factory _$ReferCopyWith(_Refer value,
$Res Function(_Refer) _then) = __$ReferCopyWithImpl;
@override
@useResult
$Res call({
List<String>? sources, List<String>? license
});
}
/// @nodoc
class __$ReferCopyWithImpl<$Res>
implements _$ReferCopyWith<$Res> {
__$ReferCopyWithImpl(this._self, this._then);
final _Refer _self;
final $Res Function(_Refer) _then;
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call({Object? sources = freezed, Object? license = freezed,}) {
return _then(_Refer(
sources: freezed == sources
? _self._sources
: sources // ignore: cast_nullable_to_non_nullable
as List<String>?,
license: freezed == license
? _self._license
: license // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}
}
/// @nodoc
mixin _$Location {
String? get name;
String? get id;
String? get lat;
String? get lon;
String? get adm2;
String? get adm1;
String? get country;
String? get tz;
String? get utcOffset;
String? get isDst;
String? get type;
String? get rank;
String? get fxLink;
/// Create a copy of Location
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LocationCopyWith<Location> get copyWith =>
_$LocationCopyWithImpl<Location>(this as Location, _$identity);
/// Serializes this Location to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Location &&
(identical(other.name, name) || other.name == name) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.lat, lat) || other.lat == lat) &&
(identical(other.lon, lon) || other.lon == lon) &&
(identical(other.adm2, adm2) || other.adm2 == adm2) &&
(identical(other.adm1, adm1) || other.adm1 == adm1) &&
(identical(other.country, country) || other.country == country) &&
(identical(other.tz, tz) || other.tz == tz) &&
(identical(other.utcOffset, utcOffset) ||
other.utcOffset == utcOffset) &&
(identical(other.isDst, isDst) || other.isDst == isDst) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.rank, rank) || other.rank == rank) &&
(identical(other.fxLink, fxLink) || other.fxLink == fxLink));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
name,
id,
lat,
lon,
adm2,
adm1,
country,
tz,
utcOffset,
isDst,
type,
rank,
fxLink);
@override
String toString() {
return 'Location(name: $name, id: $id, lat: $lat, lon: $lon, adm2: $adm2, adm1: $adm1, country: $country, tz: $tz, utcOffset: $utcOffset, isDst: $isDst, type: $type, rank: $rank, fxLink: $fxLink)';
}
}
/// @nodoc
abstract mixin class $LocationCopyWith<$Res> {
factory $LocationCopyWith(Location value,
$Res Function(Location) _then) = _$LocationCopyWithImpl;
@useResult
$Res call({
String? name, String? id, String? lat, String? lon, String? adm2, String? adm1, String? country, String? tz, String? utcOffset, String? isDst, String? type, String? rank, String? fxLink
});
}
/// @nodoc
class _$LocationCopyWithImpl<$Res>
implements $LocationCopyWith<$Res> {
_$LocationCopyWithImpl(this._self, this._then);
final Location _self;
final $Res Function(Location) _then;
/// Create a copy of Location
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? name = freezed, Object? id = freezed, Object? lat = freezed, Object? lon = freezed, Object? adm2 = freezed, Object? adm1 = freezed, Object? country = freezed, Object? tz = freezed, Object? utcOffset = freezed, Object? isDst = freezed, Object? type = freezed, Object? rank = freezed, Object? fxLink = freezed,}) {
return _then(_self.copyWith(
name: freezed == name
? _self.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String?,
lat: freezed == lat
? _self.lat
: lat // ignore: cast_nullable_to_non_nullable
as String?,
lon: freezed == lon
? _self.lon
: lon // ignore: cast_nullable_to_non_nullable
as String?,
adm2: freezed == adm2
? _self.adm2
: adm2 // ignore: cast_nullable_to_non_nullable
as String?,
adm1: freezed == adm1
? _self.adm1
: adm1 // ignore: cast_nullable_to_non_nullable
as String?,
country: freezed == country
? _self.country
: country // ignore: cast_nullable_to_non_nullable
as String?,
tz: freezed == tz ? _self.tz : tz // ignore: cast_nullable_to_non_nullable
as String?,
utcOffset: freezed == utcOffset
? _self.utcOffset
: utcOffset // ignore: cast_nullable_to_non_nullable
as String?,
isDst: freezed == isDst
? _self.isDst
: isDst // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _self.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
rank: freezed == rank
? _self.rank
: rank // ignore: cast_nullable_to_non_nullable
as String?,
fxLink: freezed == fxLink
? _self.fxLink
: fxLink // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _Location implements Location {
const _Location(
{this.name, this.id, this.lat, this.lon, this.adm2, this.adm1, this.country, this.tz, this.utcOffset, this.isDst, this.type, this.rank, this.fxLink});
factory _Location.fromJson(Map<String, dynamic> json) =>
_$LocationFromJson(json);
@override final String? name;
@override final String? id;
@override final String? lat;
@override final String? lon;
@override final String? adm2;
@override final String? adm1;
@override final String? country;
@override final String? tz;
@override final String? utcOffset;
@override final String? isDst;
@override final String? type;
@override final String? rank;
@override final String? fxLink;
/// Create a copy of Location
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LocationCopyWith<_Location> get copyWith =>
__$LocationCopyWithImpl<_Location>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$LocationToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _Location &&
(identical(other.name, name) || other.name == name) &&
(identical(other.id, id) || other.id == id) &&
(identical(other.lat, lat) || other.lat == lat) &&
(identical(other.lon, lon) || other.lon == lon) &&
(identical(other.adm2, adm2) || other.adm2 == adm2) &&
(identical(other.adm1, adm1) || other.adm1 == adm1) &&
(identical(other.country, country) || other.country == country) &&
(identical(other.tz, tz) || other.tz == tz) &&
(identical(other.utcOffset, utcOffset) ||
other.utcOffset == utcOffset) &&
(identical(other.isDst, isDst) || other.isDst == isDst) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.rank, rank) || other.rank == rank) &&
(identical(other.fxLink, fxLink) || other.fxLink == fxLink));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
name,
id,
lat,
lon,
adm2,
adm1,
country,
tz,
utcOffset,
isDst,
type,
rank,
fxLink);
@override
String toString() {
return 'Location(name: $name, id: $id, lat: $lat, lon: $lon, adm2: $adm2, adm1: $adm1, country: $country, tz: $tz, utcOffset: $utcOffset, isDst: $isDst, type: $type, rank: $rank, fxLink: $fxLink)';
}
}
/// @nodoc
abstract mixin class _$LocationCopyWith<$Res>
implements $LocationCopyWith<$Res> {
factory _$LocationCopyWith(_Location value,
$Res Function(_Location) _then) = __$LocationCopyWithImpl;
@override
@useResult
$Res call({
String? name, String? id, String? lat, String? lon, String? adm2, String? adm1, String? country, String? tz, String? utcOffset, String? isDst, String? type, String? rank, String? fxLink
});
}
/// @nodoc
class __$LocationCopyWithImpl<$Res>
implements _$LocationCopyWith<$Res> {
__$LocationCopyWithImpl(this._self, this._then);
final _Location _self;
final $Res Function(_Location) _then;
/// Create a copy of Location
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? name = freezed, Object? id = freezed, Object? lat = freezed, Object? lon = freezed, Object? adm2 = freezed, Object? adm1 = freezed, Object? country = freezed, Object? tz = freezed, Object? utcOffset = freezed, Object? isDst = freezed, Object? type = freezed, Object? rank = freezed, Object? fxLink = freezed,}) {
return _then(_Location(
name: freezed == name
? _self.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String?,
lat: freezed == lat
? _self.lat
: lat // ignore: cast_nullable_to_non_nullable
as String?,
lon: freezed == lon
? _self.lon
: lon // ignore: cast_nullable_to_non_nullable
as String?,
adm2: freezed == adm2
? _self.adm2
: adm2 // ignore: cast_nullable_to_non_nullable
as String?,
adm1: freezed == adm1
? _self.adm1
: adm1 // ignore: cast_nullable_to_non_nullable
as String?,
country: freezed == country
? _self.country
: country // ignore: cast_nullable_to_non_nullable
as String?,
tz: freezed == tz ? _self.tz : tz // ignore: cast_nullable_to_non_nullable
as String?,
utcOffset: freezed == utcOffset
? _self.utcOffset
: utcOffset // ignore: cast_nullable_to_non_nullable
as String?,
isDst: freezed == isDst
? _self.isDst
: isDst // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _self.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
rank: freezed == rank
? _self.rank
: rank // ignore: cast_nullable_to_non_nullable
as String?,
fxLink: freezed == fxLink
? _self.fxLink
: fxLink // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on

View File

@@ -0,0 +1,70 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'geo.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_GeoResponse _$GeoResponseFromJson(Map<String, dynamic> json) => _GeoResponse(
code: json['code'] as String?,
location:
(json['location'] as List<dynamic>?)
?.map((e) => Location.fromJson(e as Map<String, dynamic>))
.toList(),
refer:
json['refer'] == null
? null
: Refer.fromJson(json['refer'] as Map<String, dynamic>),
);
Map<String, dynamic> _$GeoResponseToJson(_GeoResponse instance) =>
<String, dynamic>{
if (instance.code case final value?) 'code': value,
if (instance.location case final value?) 'location': value,
if (instance.refer case final value?) 'refer': value,
};
_Refer _$ReferFromJson(Map<String, dynamic> json) => _Refer(
sources:
(json['sources'] as List<dynamic>?)?.map((e) => e as String).toList(),
license:
(json['license'] as List<dynamic>?)?.map((e) => e as String).toList(),
);
Map<String, dynamic> _$ReferToJson(_Refer instance) => <String, dynamic>{
if (instance.sources case final value?) 'sources': value,
if (instance.license case final value?) 'license': value,
};
_Location _$LocationFromJson(Map<String, dynamic> json) => _Location(
name: json['name'] as String?,
id: json['id'] as String?,
lat: json['lat'] as String?,
lon: json['lon'] as String?,
adm2: json['adm2'] as String?,
adm1: json['adm1'] as String?,
country: json['country'] as String?,
tz: json['tz'] as String?,
utcOffset: json['utcOffset'] as String?,
isDst: json['isDst'] as String?,
type: json['type'] as String?,
rank: json['rank'] as String?,
fxLink: json['fxLink'] as String?,
);
Map<String, dynamic> _$LocationToJson(_Location instance) => <String, dynamic>{
if (instance.name case final value?) 'name': value,
if (instance.id case final value?) 'id': value,
if (instance.lat case final value?) 'lat': value,
if (instance.lon case final value?) 'lon': value,
if (instance.adm2 case final value?) 'adm2': value,
if (instance.adm1 case final value?) 'adm1': value,
if (instance.country case final value?) 'country': value,
if (instance.tz case final value?) 'tz': value,
if (instance.utcOffset case final value?) 'utcOffset': value,
if (instance.isDst case final value?) 'isDst': value,
if (instance.type case final value?) 'type': value,
if (instance.rank case final value?) 'rank': value,
if (instance.fxLink case final value?) 'fxLink': value,
};

View File

@@ -1,336 +1,107 @@
class GithubRelease {
String? url;
String? assetsUrl;
String? uploadUrl;
String? htmlUrl;
int? id;
Author? author;
String? nodeId;
String? tagName;
String? targetCommitish;
String? name;
bool? draft;
bool? prerelease;
String? createdAt;
String? publishedAt;
List<Assets>? assets;
String? tarballUrl;
String? zipballUrl;
String? body;
import 'package:freezed_annotation/freezed_annotation.dart';
GithubRelease(
{this.url,
this.assetsUrl,
this.uploadUrl,
this.htmlUrl,
this.id,
this.author,
this.nodeId,
this.tagName,
this.targetCommitish,
this.name,
this.draft,
this.prerelease,
this.createdAt,
this.publishedAt,
this.assets,
this.tarballUrl,
this.zipballUrl,
this.body});
part 'github.freezed.dart';
part 'github.g.dart';
GithubRelease.fromJson(Map<String, dynamic> json) {
url = json["url"];
assetsUrl = json["assets_url"];
uploadUrl = json["upload_url"];
htmlUrl = json["html_url"];
id = json["id"];
author = json["author"] == null ? null : Author.fromJson(json["author"]);
nodeId = json["node_id"];
tagName = json["tag_name"];
targetCommitish = json["target_commitish"];
name = json["name"];
draft = json["draft"];
prerelease = json["prerelease"];
createdAt = json["created_at"];
publishedAt = json["published_at"];
assets = json["assets"] == null
? null
: (json["assets"] as List).map((e) => Assets.fromJson(e)).toList();
tarballUrl = json["tarball_url"];
zipballUrl = json["zipball_url"];
body = json["body"];
}
@freezed
abstract class GithubRelease with _$GithubRelease {
const factory GithubRelease({
String? url,
@JsonKey(name: 'assets_url') String? assetsUrl,
@JsonKey(name: 'upload_url') String? uploadUrl,
@JsonKey(name: 'html_url') String? htmlUrl,
int? id,
Author? author,
@JsonKey(name: 'node_id') String? nodeId,
@JsonKey(name: 'tag_name') String? tagName,
@JsonKey(name: 'target_commitish') String? targetCommitish,
String? name,
bool? draft,
bool? prerelease,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'published_at') String? publishedAt,
List<Assets>? assets,
@JsonKey(name: 'tarball_url') String? tarballUrl,
@JsonKey(name: 'zipball_url') String? zipballUrl,
String? body,
}) = _GithubRelease;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["url"] = url;
data["assets_url"] = assetsUrl;
data["upload_url"] = uploadUrl;
data["html_url"] = htmlUrl;
data["id"] = id;
if (author != null) {
data["author"] = author?.toJson();
}
data["node_id"] = nodeId;
data["tag_name"] = tagName;
data["target_commitish"] = targetCommitish;
data["name"] = name;
data["draft"] = draft;
data["prerelease"] = prerelease;
data["created_at"] = createdAt;
data["published_at"] = publishedAt;
if (assets != null) {
data["assets"] = assets?.map((e) => e.toJson()).toList();
}
data["tarball_url"] = tarballUrl;
data["zipball_url"] = zipballUrl;
data["body"] = body;
return data;
}
factory GithubRelease.fromJson(Map<String, dynamic> json) =>
_$GithubReleaseFromJson(json);
}
class Assets {
String? url;
int? id;
String? nodeId;
String? name;
dynamic label;
Uploader? uploader;
String? contentType;
String? state;
int? size;
int? downloadCount;
String? createdAt;
String? updatedAt;
String? browserDownloadUrl;
@freezed
abstract class Assets with _$Assets {
const factory Assets({
String? url,
int? id,
@JsonKey(name: 'node_id') String? nodeId,
String? name,
dynamic label,
Uploader? uploader,
@JsonKey(name: 'content_type') String? contentType,
String? state,
int? size,
@JsonKey(name: 'download_count') int? downloadCount,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
@JsonKey(name: 'browser_download_url') String? browserDownloadUrl,
}) = _Assets;
Assets(
{this.url,
this.id,
this.nodeId,
this.name,
this.label,
this.uploader,
this.contentType,
this.state,
this.size,
this.downloadCount,
this.createdAt,
this.updatedAt,
this.browserDownloadUrl});
Assets.fromJson(Map<String, dynamic> json) {
url = json["url"];
id = json["id"];
nodeId = json["node_id"];
name = json["name"];
label = json["label"];
uploader =
json["uploader"] == null ? null : Uploader.fromJson(json["uploader"]);
contentType = json["content_type"];
state = json["state"];
size = json["size"];
downloadCount = json["download_count"];
createdAt = json["created_at"];
updatedAt = json["updated_at"];
browserDownloadUrl = json["browser_download_url"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["url"] = url;
data["id"] = id;
data["node_id"] = nodeId;
data["name"] = name;
data["label"] = label;
if (uploader != null) {
data["uploader"] = uploader?.toJson();
}
data["content_type"] = contentType;
data["state"] = state;
data["size"] = size;
data["download_count"] = downloadCount;
data["created_at"] = createdAt;
data["updated_at"] = updatedAt;
data["browser_download_url"] = browserDownloadUrl;
return data;
}
factory Assets.fromJson(Map<String, dynamic> json) => _$AssetsFromJson(json);
}
class Uploader {
String? login;
int? id;
String? nodeId;
String? avatarUrl;
String? gravatarId;
String? url;
String? htmlUrl;
String? followersUrl;
String? followingUrl;
String? gistsUrl;
String? starredUrl;
String? subscriptionsUrl;
String? organizationsUrl;
String? reposUrl;
String? eventsUrl;
String? receivedEventsUrl;
String? type;
String? userViewType;
bool? siteAdmin;
@freezed
abstract class Uploader with _$Uploader {
const factory Uploader({
String? login,
int? id,
@JsonKey(name: 'node_id') String? nodeId,
@JsonKey(name: 'avatar_url') String? avatarUrl,
@JsonKey(name: 'gravatar_id') String? gravatarId,
String? url,
@JsonKey(name: 'html_url') String? htmlUrl,
@JsonKey(name: 'followers_url') String? followersUrl,
@JsonKey(name: 'following_url') String? followingUrl,
@JsonKey(name: 'gists_url') String? gistsUrl,
@JsonKey(name: 'starred_url') String? starredUrl,
@JsonKey(name: 'subscriptions_url') String? subscriptionsUrl,
@JsonKey(name: 'organizations_url') String? organizationsUrl,
@JsonKey(name: 'repos_url') String? reposUrl,
@JsonKey(name: 'events_url') String? eventsUrl,
@JsonKey(name: 'received_events_url') String? receivedEventsUrl,
String? type,
@JsonKey(name: 'user_view_type') String? userViewType,
@JsonKey(name: 'site_admin') bool? siteAdmin,
}) = _Uploader;
Uploader(
{this.login,
this.id,
this.nodeId,
this.avatarUrl,
this.gravatarId,
this.url,
this.htmlUrl,
this.followersUrl,
this.followingUrl,
this.gistsUrl,
this.starredUrl,
this.subscriptionsUrl,
this.organizationsUrl,
this.reposUrl,
this.eventsUrl,
this.receivedEventsUrl,
this.type,
this.userViewType,
this.siteAdmin});
Uploader.fromJson(Map<String, dynamic> json) {
login = json["login"];
id = json["id"];
nodeId = json["node_id"];
avatarUrl = json["avatar_url"];
gravatarId = json["gravatar_id"];
url = json["url"];
htmlUrl = json["html_url"];
followersUrl = json["followers_url"];
followingUrl = json["following_url"];
gistsUrl = json["gists_url"];
starredUrl = json["starred_url"];
subscriptionsUrl = json["subscriptions_url"];
organizationsUrl = json["organizations_url"];
reposUrl = json["repos_url"];
eventsUrl = json["events_url"];
receivedEventsUrl = json["received_events_url"];
type = json["type"];
userViewType = json["user_view_type"];
siteAdmin = json["site_admin"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["login"] = login;
data["id"] = id;
data["node_id"] = nodeId;
data["avatar_url"] = avatarUrl;
data["gravatar_id"] = gravatarId;
data["url"] = url;
data["html_url"] = htmlUrl;
data["followers_url"] = followersUrl;
data["following_url"] = followingUrl;
data["gists_url"] = gistsUrl;
data["starred_url"] = starredUrl;
data["subscriptions_url"] = subscriptionsUrl;
data["organizations_url"] = organizationsUrl;
data["repos_url"] = reposUrl;
data["events_url"] = eventsUrl;
data["received_events_url"] = receivedEventsUrl;
data["type"] = type;
data["user_view_type"] = userViewType;
data["site_admin"] = siteAdmin;
return data;
}
factory Uploader.fromJson(Map<String, dynamic> json) =>
_$UploaderFromJson(json);
}
class Author {
String? login;
int? id;
String? nodeId;
String? avatarUrl;
String? gravatarId;
String? url;
String? htmlUrl;
String? followersUrl;
String? followingUrl;
String? gistsUrl;
String? starredUrl;
String? subscriptionsUrl;
String? organizationsUrl;
String? reposUrl;
String? eventsUrl;
String? receivedEventsUrl;
String? type;
String? userViewType;
bool? siteAdmin;
@freezed
abstract class Author with _$Author {
const factory Author({
String? login,
int? id,
@JsonKey(name: 'node_id') String? nodeId,
@JsonKey(name: 'avatar_url') String? avatarUrl,
@JsonKey(name: 'gravatar_id') String? gravatarId,
String? url,
@JsonKey(name: 'html_url') String? htmlUrl,
@JsonKey(name: 'followers_url') String? followersUrl,
@JsonKey(name: 'following_url') String? followingUrl,
@JsonKey(name: 'gists_url') String? gistsUrl,
@JsonKey(name: 'starred_url') String? starredUrl,
@JsonKey(name: 'subscriptions_url') String? subscriptionsUrl,
@JsonKey(name: 'organizations_url') String? organizationsUrl,
@JsonKey(name: 'repos_url') String? reposUrl,
@JsonKey(name: 'events_url') String? eventsUrl,
@JsonKey(name: 'received_events_url') String? receivedEventsUrl,
String? type,
@JsonKey(name: 'user_view_type') String? userViewType,
@JsonKey(name: 'site_admin') bool? siteAdmin,
}) = _Author;
Author(
{this.login,
this.id,
this.nodeId,
this.avatarUrl,
this.gravatarId,
this.url,
this.htmlUrl,
this.followersUrl,
this.followingUrl,
this.gistsUrl,
this.starredUrl,
this.subscriptionsUrl,
this.organizationsUrl,
this.reposUrl,
this.eventsUrl,
this.receivedEventsUrl,
this.type,
this.userViewType,
this.siteAdmin});
Author.fromJson(Map<String, dynamic> json) {
login = json["login"];
id = json["id"];
nodeId = json["node_id"];
avatarUrl = json["avatar_url"];
gravatarId = json["gravatar_id"];
url = json["url"];
htmlUrl = json["html_url"];
followersUrl = json["followers_url"];
followingUrl = json["following_url"];
gistsUrl = json["gists_url"];
starredUrl = json["starred_url"];
subscriptionsUrl = json["subscriptions_url"];
organizationsUrl = json["organizations_url"];
reposUrl = json["repos_url"];
eventsUrl = json["events_url"];
receivedEventsUrl = json["received_events_url"];
type = json["type"];
userViewType = json["user_view_type"];
siteAdmin = json["site_admin"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["login"] = login;
data["id"] = id;
data["node_id"] = nodeId;
data["avatar_url"] = avatarUrl;
data["gravatar_id"] = gravatarId;
data["url"] = url;
data["html_url"] = htmlUrl;
data["followers_url"] = followersUrl;
data["following_url"] = followingUrl;
data["gists_url"] = gistsUrl;
data["starred_url"] = starredUrl;
data["subscriptions_url"] = subscriptionsUrl;
data["organizations_url"] = organizationsUrl;
data["repos_url"] = reposUrl;
data["events_url"] = eventsUrl;
data["received_events_url"] = receivedEventsUrl;
data["type"] = type;
data["user_view_type"] = userViewType;
data["site_admin"] = siteAdmin;
return data;
}
factory Author.fromJson(Map<String, dynamic> json) => _$AuthorFromJson(json);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'github.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_GithubRelease _$GithubReleaseFromJson(Map<String, dynamic> json) =>
_GithubRelease(
url: json['url'] as String?,
assetsUrl: json['assets_url'] as String?,
uploadUrl: json['upload_url'] as String?,
htmlUrl: json['html_url'] as String?,
id: (json['id'] as num?)?.toInt(),
author:
json['author'] == null
? null
: Author.fromJson(json['author'] as Map<String, dynamic>),
nodeId: json['node_id'] as String?,
tagName: json['tag_name'] as String?,
targetCommitish: json['target_commitish'] as String?,
name: json['name'] as String?,
draft: json['draft'] as bool?,
prerelease: json['prerelease'] as bool?,
createdAt: json['created_at'] as String?,
publishedAt: json['published_at'] as String?,
assets:
(json['assets'] as List<dynamic>?)
?.map((e) => Assets.fromJson(e as Map<String, dynamic>))
.toList(),
tarballUrl: json['tarball_url'] as String?,
zipballUrl: json['zipball_url'] as String?,
body: json['body'] as String?,
);
Map<String, dynamic> _$GithubReleaseToJson(_GithubRelease instance) =>
<String, dynamic>{
if (instance.url case final value?) 'url': value,
if (instance.assetsUrl case final value?) 'assets_url': value,
if (instance.uploadUrl case final value?) 'upload_url': value,
if (instance.htmlUrl case final value?) 'html_url': value,
if (instance.id case final value?) 'id': value,
if (instance.author case final value?) 'author': value,
if (instance.nodeId case final value?) 'node_id': value,
if (instance.tagName case final value?) 'tag_name': value,
if (instance.targetCommitish case final value?) 'target_commitish': value,
if (instance.name case final value?) 'name': value,
if (instance.draft case final value?) 'draft': value,
if (instance.prerelease case final value?) 'prerelease': value,
if (instance.createdAt case final value?) 'created_at': value,
if (instance.publishedAt case final value?) 'published_at': value,
if (instance.assets case final value?) 'assets': value,
if (instance.tarballUrl case final value?) 'tarball_url': value,
if (instance.zipballUrl case final value?) 'zipball_url': value,
if (instance.body case final value?) 'body': value,
};
_Assets _$AssetsFromJson(Map<String, dynamic> json) => _Assets(
url: json['url'] as String?,
id: (json['id'] as num?)?.toInt(),
nodeId: json['node_id'] as String?,
name: json['name'] as String?,
label: json['label'],
uploader:
json['uploader'] == null
? null
: Uploader.fromJson(json['uploader'] as Map<String, dynamic>),
contentType: json['content_type'] as String?,
state: json['state'] as String?,
size: (json['size'] as num?)?.toInt(),
downloadCount: (json['download_count'] as num?)?.toInt(),
createdAt: json['created_at'] as String?,
updatedAt: json['updated_at'] as String?,
browserDownloadUrl: json['browser_download_url'] as String?,
);
Map<String, dynamic> _$AssetsToJson(_Assets instance) => <String, dynamic>{
if (instance.url case final value?) 'url': value,
if (instance.id case final value?) 'id': value,
if (instance.nodeId case final value?) 'node_id': value,
if (instance.name case final value?) 'name': value,
if (instance.label case final value?) 'label': value,
if (instance.uploader case final value?) 'uploader': value,
if (instance.contentType case final value?) 'content_type': value,
if (instance.state case final value?) 'state': value,
if (instance.size case final value?) 'size': value,
if (instance.downloadCount case final value?) 'download_count': value,
if (instance.createdAt case final value?) 'created_at': value,
if (instance.updatedAt case final value?) 'updated_at': value,
if (instance.browserDownloadUrl case final value?)
'browser_download_url': value,
};
_Uploader _$UploaderFromJson(Map<String, dynamic> json) => _Uploader(
login: json['login'] as String?,
id: (json['id'] as num?)?.toInt(),
nodeId: json['node_id'] as String?,
avatarUrl: json['avatar_url'] as String?,
gravatarId: json['gravatar_id'] as String?,
url: json['url'] as String?,
htmlUrl: json['html_url'] as String?,
followersUrl: json['followers_url'] as String?,
followingUrl: json['following_url'] as String?,
gistsUrl: json['gists_url'] as String?,
starredUrl: json['starred_url'] as String?,
subscriptionsUrl: json['subscriptions_url'] as String?,
organizationsUrl: json['organizations_url'] as String?,
reposUrl: json['repos_url'] as String?,
eventsUrl: json['events_url'] as String?,
receivedEventsUrl: json['received_events_url'] as String?,
type: json['type'] as String?,
userViewType: json['user_view_type'] as String?,
siteAdmin: json['site_admin'] as bool?,
);
Map<String, dynamic> _$UploaderToJson(_Uploader instance) => <String, dynamic>{
if (instance.login case final value?) 'login': value,
if (instance.id case final value?) 'id': value,
if (instance.nodeId case final value?) 'node_id': value,
if (instance.avatarUrl case final value?) 'avatar_url': value,
if (instance.gravatarId case final value?) 'gravatar_id': value,
if (instance.url case final value?) 'url': value,
if (instance.htmlUrl case final value?) 'html_url': value,
if (instance.followersUrl case final value?) 'followers_url': value,
if (instance.followingUrl case final value?) 'following_url': value,
if (instance.gistsUrl case final value?) 'gists_url': value,
if (instance.starredUrl case final value?) 'starred_url': value,
if (instance.subscriptionsUrl case final value?) 'subscriptions_url': value,
if (instance.organizationsUrl case final value?) 'organizations_url': value,
if (instance.reposUrl case final value?) 'repos_url': value,
if (instance.eventsUrl case final value?) 'events_url': value,
if (instance.receivedEventsUrl case final value?)
'received_events_url': value,
if (instance.type case final value?) 'type': value,
if (instance.userViewType case final value?) 'user_view_type': value,
if (instance.siteAdmin case final value?) 'site_admin': value,
};
_Author _$AuthorFromJson(Map<String, dynamic> json) => _Author(
login: json['login'] as String?,
id: (json['id'] as num?)?.toInt(),
nodeId: json['node_id'] as String?,
avatarUrl: json['avatar_url'] as String?,
gravatarId: json['gravatar_id'] as String?,
url: json['url'] as String?,
htmlUrl: json['html_url'] as String?,
followersUrl: json['followers_url'] as String?,
followingUrl: json['following_url'] as String?,
gistsUrl: json['gists_url'] as String?,
starredUrl: json['starred_url'] as String?,
subscriptionsUrl: json['subscriptions_url'] as String?,
organizationsUrl: json['organizations_url'] as String?,
reposUrl: json['repos_url'] as String?,
eventsUrl: json['events_url'] as String?,
receivedEventsUrl: json['received_events_url'] as String?,
type: json['type'] as String?,
userViewType: json['user_view_type'] as String?,
siteAdmin: json['site_admin'] as bool?,
);
Map<String, dynamic> _$AuthorToJson(_Author instance) => <String, dynamic>{
if (instance.login case final value?) 'login': value,
if (instance.id case final value?) 'id': value,
if (instance.nodeId case final value?) 'node_id': value,
if (instance.avatarUrl case final value?) 'avatar_url': value,
if (instance.gravatarId case final value?) 'gravatar_id': value,
if (instance.url case final value?) 'url': value,
if (instance.htmlUrl case final value?) 'html_url': value,
if (instance.followersUrl case final value?) 'followers_url': value,
if (instance.followingUrl case final value?) 'following_url': value,
if (instance.gistsUrl case final value?) 'gists_url': value,
if (instance.starredUrl case final value?) 'starred_url': value,
if (instance.subscriptionsUrl case final value?) 'subscriptions_url': value,
if (instance.organizationsUrl case final value?) 'organizations_url': value,
if (instance.reposUrl case final value?) 'repos_url': value,
if (instance.eventsUrl case final value?) 'events_url': value,
if (instance.receivedEventsUrl case final value?)
'received_events_url': value,
if (instance.type case final value?) 'type': value,
if (instance.userViewType case final value?) 'user_view_type': value,
if (instance.siteAdmin case final value?) 'site_admin': value,
};

View File

@@ -1,60 +1,25 @@
class HitokotoResponse {
int? id;
String? uuid;
String? hitokoto;
String? type;
String? from;
String? fromWho;
String? creator;
int? creatorUid;
int? reviewer;
String? commitFrom;
String? createdAt;
int? length;
import 'package:freezed_annotation/freezed_annotation.dart';
HitokotoResponse(
{this.id,
this.uuid,
this.hitokoto,
this.type,
this.from,
this.fromWho,
this.creator,
this.creatorUid,
this.reviewer,
this.commitFrom,
this.createdAt,
this.length});
part 'hitokoto.freezed.dart';
part 'hitokoto.g.dart';
HitokotoResponse.fromJson(Map<String, dynamic> json) {
id = json["id"];
uuid = json["uuid"];
hitokoto = json["hitokoto"];
type = json["type"];
from = json["from"];
fromWho = json["from_who"];
creator = json["creator"];
creatorUid = json["creator_uid"];
reviewer = json["reviewer"];
commitFrom = json["commit_from"];
createdAt = json["created_at"];
length = json["length"];
}
@freezed
abstract class HitokotoResponse with _$HitokotoResponse {
const factory HitokotoResponse({
int? id,
String? uuid,
String? hitokoto,
String? type,
String? from,
@JsonKey(name: 'from_who') String? fromWho,
String? creator,
@JsonKey(name: 'creator_uid') int? creatorUid,
int? reviewer,
@JsonKey(name: 'commit_from') String? commitFrom,
@JsonKey(name: 'created_at') String? createdAt,
int? length,
}) = _HitokotoResponse;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["id"] = id;
data["uuid"] = uuid;
data["hitokoto"] = hitokoto;
data["type"] = type;
data["from"] = from;
data["from_who"] = fromWho;
data["creator"] = creator;
data["creator_uid"] = creatorUid;
data["reviewer"] = reviewer;
data["commit_from"] = commitFrom;
data["created_at"] = createdAt;
data["length"] = length;
return data;
}
factory HitokotoResponse.fromJson(Map<String, dynamic> json) =>
_$HitokotoResponseFromJson(json);
}

View File

@@ -0,0 +1,370 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'hitokoto.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$HitokotoResponse {
int? get id;
String? get uuid;
String? get hitokoto;
String? get type;
String? get from;
@JsonKey(name: 'from_who') String? get fromWho;
String? get creator;
@JsonKey(name: 'creator_uid') int? get creatorUid;
int? get reviewer;
@JsonKey(name: 'commit_from') String? get commitFrom;
@JsonKey(name: 'created_at') String? get createdAt;
int? get length;
/// Create a copy of HitokotoResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$HitokotoResponseCopyWith<HitokotoResponse> get copyWith =>
_$HitokotoResponseCopyWithImpl<HitokotoResponse>(
this as HitokotoResponse, _$identity);
/// Serializes this HitokotoResponse to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is HitokotoResponse &&
(identical(other.id, id) || other.id == id) &&
(identical(other.uuid, uuid) || other.uuid == uuid) &&
(identical(other.hitokoto, hitokoto) ||
other.hitokoto == hitokoto) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.from, from) || other.from == from) &&
(identical(other.fromWho, fromWho) || other.fromWho == fromWho) &&
(identical(other.creator, creator) || other.creator == creator) &&
(identical(other.creatorUid, creatorUid) ||
other.creatorUid == creatorUid) &&
(identical(other.reviewer, reviewer) ||
other.reviewer == reviewer) &&
(identical(other.commitFrom, commitFrom) ||
other.commitFrom == commitFrom) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.length, length) || other.length == length));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
id,
uuid,
hitokoto,
type,
from,
fromWho,
creator,
creatorUid,
reviewer,
commitFrom,
createdAt,
length);
@override
String toString() {
return 'HitokotoResponse(id: $id, uuid: $uuid, hitokoto: $hitokoto, type: $type, from: $from, fromWho: $fromWho, creator: $creator, creatorUid: $creatorUid, reviewer: $reviewer, commitFrom: $commitFrom, createdAt: $createdAt, length: $length)';
}
}
/// @nodoc
abstract mixin class $HitokotoResponseCopyWith<$Res> {
factory $HitokotoResponseCopyWith(HitokotoResponse value,
$Res Function(HitokotoResponse) _then) = _$HitokotoResponseCopyWithImpl;
@useResult
$Res call({
int? id, String? uuid, String? hitokoto, String? type, String? from, @JsonKey(
name: 'from_who') String? fromWho, String? creator, @JsonKey(
name: 'creator_uid') int? creatorUid, int? reviewer, @JsonKey(
name: 'commit_from') String? commitFrom, @JsonKey(
name: 'created_at') String? createdAt, int? length
});
}
/// @nodoc
class _$HitokotoResponseCopyWithImpl<$Res>
implements $HitokotoResponseCopyWith<$Res> {
_$HitokotoResponseCopyWithImpl(this._self, this._then);
final HitokotoResponse _self;
final $Res Function(HitokotoResponse) _then;
/// Create a copy of HitokotoResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? id = freezed, Object? uuid = freezed, Object? hitokoto = freezed, Object? type = freezed, Object? from = freezed, Object? fromWho = freezed, Object? creator = freezed, Object? creatorUid = freezed, Object? reviewer = freezed, Object? commitFrom = freezed, Object? createdAt = freezed, Object? length = freezed,}) {
return _then(_self.copyWith(
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as int?,
uuid: freezed == uuid
? _self.uuid
: uuid // ignore: cast_nullable_to_non_nullable
as String?,
hitokoto: freezed == hitokoto
? _self.hitokoto
: hitokoto // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _self.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
from: freezed == from
? _self.from
: from // ignore: cast_nullable_to_non_nullable
as String?,
fromWho: freezed == fromWho
? _self.fromWho
: fromWho // ignore: cast_nullable_to_non_nullable
as String?,
creator: freezed == creator
? _self.creator
: creator // ignore: cast_nullable_to_non_nullable
as String?,
creatorUid: freezed == creatorUid
? _self.creatorUid
: creatorUid // ignore: cast_nullable_to_non_nullable
as int?,
reviewer: freezed == reviewer
? _self.reviewer
: reviewer // ignore: cast_nullable_to_non_nullable
as int?,
commitFrom: freezed == commitFrom
? _self.commitFrom
: commitFrom // ignore: cast_nullable_to_non_nullable
as String?,
createdAt: freezed == createdAt
? _self.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
length: freezed == length
? _self.length
: length // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
@JsonSerializable()
class _HitokotoResponse implements HitokotoResponse {
const _HitokotoResponse(
{this.id, this.uuid, this.hitokoto, this.type, this.from, @JsonKey(
name: 'from_who') this.fromWho, this.creator, @JsonKey(
name: 'creator_uid') this.creatorUid, this.reviewer, @JsonKey(
name: 'commit_from') this.commitFrom, @JsonKey(
name: 'created_at') this.createdAt, this.length});
factory _HitokotoResponse.fromJson(Map<String, dynamic> json) =>
_$HitokotoResponseFromJson(json);
@override final int? id;
@override final String? uuid;
@override final String? hitokoto;
@override final String? type;
@override final String? from;
@override
@JsonKey(name: 'from_who')
final String? fromWho;
@override final String? creator;
@override
@JsonKey(name: 'creator_uid')
final int? creatorUid;
@override final int? reviewer;
@override
@JsonKey(name: 'commit_from')
final String? commitFrom;
@override
@JsonKey(name: 'created_at')
final String? createdAt;
@override final int? length;
/// Create a copy of HitokotoResponse
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$HitokotoResponseCopyWith<_HitokotoResponse> get copyWith =>
__$HitokotoResponseCopyWithImpl<_HitokotoResponse>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$HitokotoResponseToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _HitokotoResponse &&
(identical(other.id, id) || other.id == id) &&
(identical(other.uuid, uuid) || other.uuid == uuid) &&
(identical(other.hitokoto, hitokoto) ||
other.hitokoto == hitokoto) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.from, from) || other.from == from) &&
(identical(other.fromWho, fromWho) || other.fromWho == fromWho) &&
(identical(other.creator, creator) || other.creator == creator) &&
(identical(other.creatorUid, creatorUid) ||
other.creatorUid == creatorUid) &&
(identical(other.reviewer, reviewer) ||
other.reviewer == reviewer) &&
(identical(other.commitFrom, commitFrom) ||
other.commitFrom == commitFrom) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.length, length) || other.length == length));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
id,
uuid,
hitokoto,
type,
from,
fromWho,
creator,
creatorUid,
reviewer,
commitFrom,
createdAt,
length);
@override
String toString() {
return 'HitokotoResponse(id: $id, uuid: $uuid, hitokoto: $hitokoto, type: $type, from: $from, fromWho: $fromWho, creator: $creator, creatorUid: $creatorUid, reviewer: $reviewer, commitFrom: $commitFrom, createdAt: $createdAt, length: $length)';
}
}
/// @nodoc
abstract mixin class _$HitokotoResponseCopyWith<$Res>
implements $HitokotoResponseCopyWith<$Res> {
factory _$HitokotoResponseCopyWith(_HitokotoResponse value,
$Res Function(_HitokotoResponse) _then) = __$HitokotoResponseCopyWithImpl;
@override
@useResult
$Res call({
int? id, String? uuid, String? hitokoto, String? type, String? from, @JsonKey(
name: 'from_who') String? fromWho, String? creator, @JsonKey(
name: 'creator_uid') int? creatorUid, int? reviewer, @JsonKey(
name: 'commit_from') String? commitFrom, @JsonKey(
name: 'created_at') String? createdAt, int? length
});
}
/// @nodoc
class __$HitokotoResponseCopyWithImpl<$Res>
implements _$HitokotoResponseCopyWith<$Res> {
__$HitokotoResponseCopyWithImpl(this._self, this._then);
final _HitokotoResponse _self;
final $Res Function(_HitokotoResponse) _then;
/// Create a copy of HitokotoResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? id = freezed, Object? uuid = freezed, Object? hitokoto = freezed, Object? type = freezed, Object? from = freezed, Object? fromWho = freezed, Object? creator = freezed, Object? creatorUid = freezed, Object? reviewer = freezed, Object? commitFrom = freezed, Object? createdAt = freezed, Object? length = freezed,}) {
return _then(_HitokotoResponse(
id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as int?,
uuid: freezed == uuid
? _self.uuid
: uuid // ignore: cast_nullable_to_non_nullable
as String?,
hitokoto: freezed == hitokoto
? _self.hitokoto
: hitokoto // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _self.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
from: freezed == from
? _self.from
: from // ignore: cast_nullable_to_non_nullable
as String?,
fromWho: freezed == fromWho
? _self.fromWho
: fromWho // ignore: cast_nullable_to_non_nullable
as String?,
creator: freezed == creator
? _self.creator
: creator // ignore: cast_nullable_to_non_nullable
as String?,
creatorUid: freezed == creatorUid
? _self.creatorUid
: creatorUid // ignore: cast_nullable_to_non_nullable
as int?,
reviewer: freezed == reviewer
? _self.reviewer
: reviewer // ignore: cast_nullable_to_non_nullable
as int?,
commitFrom: freezed == commitFrom
? _self.commitFrom
: commitFrom // ignore: cast_nullable_to_non_nullable
as String?,
createdAt: freezed == createdAt
? _self.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
length: freezed == length
? _self.length
: length // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
// dart format on

View File

@@ -0,0 +1,39 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'hitokoto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_HitokotoResponse _$HitokotoResponseFromJson(Map<String, dynamic> json) =>
_HitokotoResponse(
id: (json['id'] as num?)?.toInt(),
uuid: json['uuid'] as String?,
hitokoto: json['hitokoto'] as String?,
type: json['type'] as String?,
from: json['from'] as String?,
fromWho: json['from_who'] as String?,
creator: json['creator'] as String?,
creatorUid: (json['creator_uid'] as num?)?.toInt(),
reviewer: (json['reviewer'] as num?)?.toInt(),
commitFrom: json['commit_from'] as String?,
createdAt: json['created_at'] as String?,
length: (json['length'] as num?)?.toInt(),
);
Map<String, dynamic> _$HitokotoResponseToJson(_HitokotoResponse instance) =>
<String, dynamic>{
if (instance.id case final value?) 'id': value,
if (instance.uuid case final value?) 'uuid': value,
if (instance.hitokoto case final value?) 'hitokoto': value,
if (instance.type case final value?) 'type': value,
if (instance.from case final value?) 'from': value,
if (instance.fromWho case final value?) 'from_who': value,
if (instance.creator case final value?) 'creator': value,
if (instance.creatorUid case final value?) 'creator_uid': value,
if (instance.reviewer case final value?) 'reviewer': value,
if (instance.commitFrom case final value?) 'commit_from': value,
if (instance.createdAt case final value?) 'created_at': value,
if (instance.length case final value?) 'length': value,
};

View File

@@ -1,136 +1,74 @@
class PublicHeader {
String? action;
int? timestamp;
String? version;
String? authorization;
import 'package:freezed_annotation/freezed_annotation.dart';
Map<String, dynamic> toMap() {
return {
'X-TC-Action': action,
'X-TC-Timestamp': timestamp,
'X-TC-Version': version,
'Authorization': authorization,
};
}
part 'hunyuan.freezed.dart';
part 'hunyuan.g.dart';
PublicHeader.fromMap(Map<String, dynamic> map) {
action = map['X-TC-Action'];
timestamp = map['X-TC-Timestamp'];
version = map['X-TC-Version'];
authorization = map['Authorization'];
}
@freezed
abstract class PublicHeader with _$PublicHeader {
const factory PublicHeader({
@JsonKey(name: 'X-TC-Action') String? action,
@JsonKey(name: 'X-TC-Timestamp') int? timestamp,
@JsonKey(name: 'X-TC-Version') String? version,
@JsonKey(name: 'Authorization') String? authorization,
}) = _PublicHeader;
PublicHeader(this.action, this.timestamp, this.version, this.authorization);
factory PublicHeader.fromJson(Map<String, dynamic> json) =>
_$PublicHeaderFromJson(json);
}
class Message {
late String role;
late String content;
@freezed
abstract class Message with _$Message {
const factory Message({
@JsonKey(name: 'Role') required String role,
@JsonKey(name: 'Content') required String content,
}) = _Message;
Message(this.role, this.content);
Map<String, dynamic> toMap() {
return {'Role': role, 'Content': content};
}
Message.fromMap(Map<String, dynamic> map) {
role = map['Role'];
content = map['Content'];
}
factory Message.fromJson(Map<String, dynamic> json) =>
_$MessageFromJson(json);
}
class HunyuanResponse {
String? note;
List<Choices>? choices;
int? created;
String? id;
Usage? usage;
@freezed
abstract class HunyuanResponse with _$HunyuanResponse {
const factory HunyuanResponse({
@JsonKey(name: 'Note') String? note,
@JsonKey(name: 'Choices') List<Choices>? choices,
@JsonKey(name: 'Created') int? created,
@JsonKey(name: 'Id') String? id,
@JsonKey(name: 'Usage') Usage? usage,
}) = _HunyuanResponse;
HunyuanResponse({this.note, this.choices, this.created, this.id, this.usage});
HunyuanResponse.fromJson(Map<String, dynamic> json) {
note = json["Note"];
choices = json["Choices"] == null
? null
: (json["Choices"] as List).map((e) => Choices.fromJson(e)).toList();
created = json["Created"];
id = json["Id"];
usage = json["Usage"] == null ? null : Usage.fromJson(json["Usage"]);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["Note"] = note;
if (choices != null) {
data["Choices"] = choices?.map((e) => e.toJson()).toList();
}
data["Created"] = created;
data["Id"] = id;
if (usage != null) {
data["Usage"] = usage?.toJson();
}
return data;
}
factory HunyuanResponse.fromJson(Map<String, dynamic> json) =>
_$HunyuanResponseFromJson(json);
}
class Usage {
int? promptTokens;
int? completionTokens;
int? totalTokens;
@freezed
abstract class Usage with _$Usage {
const factory Usage({
@JsonKey(name: 'PromptTokens') int? promptTokens,
@JsonKey(name: 'CompletionTokens') int? completionTokens,
@JsonKey(name: 'TotalTokens') int? totalTokens,
}) = _Usage;
Usage({this.promptTokens, this.completionTokens, this.totalTokens});
Usage.fromJson(Map<String, dynamic> json) {
promptTokens = json["PromptTokens"];
completionTokens = json["CompletionTokens"];
totalTokens = json["TotalTokens"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["PromptTokens"] = promptTokens;
data["CompletionTokens"] = completionTokens;
data["TotalTokens"] = totalTokens;
return data;
}
factory Usage.fromJson(Map<String, dynamic> json) => _$UsageFromJson(json);
}
class Choices {
String? finishReason;
Delta? delta;
@freezed
abstract class Choices with _$Choices {
const factory Choices({
@JsonKey(name: 'FinishReason') String? finishReason,
@JsonKey(name: 'Delta') Delta? delta,
}) = _Choices;
Choices({this.finishReason, this.delta});
Choices.fromJson(Map<String, dynamic> json) {
finishReason = json["FinishReason"];
delta = json["Delta"] == null ? null : Delta.fromJson(json["Delta"]);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["FinishReason"] = finishReason;
if (delta != null) {
data["Delta"] = delta?.toJson();
}
return data;
}
factory Choices.fromJson(Map<String, dynamic> json) =>
_$ChoicesFromJson(json);
}
class Delta {
String? role;
String? content;
@freezed
abstract class Delta with _$Delta {
const factory Delta({
@JsonKey(name: 'Role') String? role,
@JsonKey(name: 'Content') String? content,
}) = _Delta;
Delta({this.role, this.content});
Delta.fromJson(Map<String, dynamic> json) {
role = json["Role"];
content = json["Content"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["Role"] = role;
data["Content"] = content;
return data;
}
factory Delta.fromJson(Map<String, dynamic> json) => _$DeltaFromJson(json);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,88 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'hunyuan.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_PublicHeader _$PublicHeaderFromJson(Map<String, dynamic> json) =>
_PublicHeader(
action: json['X-TC-Action'] as String?,
timestamp: (json['X-TC-Timestamp'] as num?)?.toInt(),
version: json['X-TC-Version'] as String?,
authorization: json['Authorization'] as String?,
);
Map<String, dynamic> _$PublicHeaderToJson(_PublicHeader instance) =>
<String, dynamic>{
if (instance.action case final value?) 'X-TC-Action': value,
if (instance.timestamp case final value?) 'X-TC-Timestamp': value,
if (instance.version case final value?) 'X-TC-Version': value,
if (instance.authorization case final value?) 'Authorization': value,
};
_Message _$MessageFromJson(Map<String, dynamic> json) =>
_Message(role: json['Role'] as String, content: json['Content'] as String);
Map<String, dynamic> _$MessageToJson(_Message instance) => <String, dynamic>{
'Role': instance.role,
'Content': instance.content,
};
_HunyuanResponse _$HunyuanResponseFromJson(Map<String, dynamic> json) =>
_HunyuanResponse(
note: json['Note'] as String?,
choices:
(json['Choices'] as List<dynamic>?)
?.map((e) => Choices.fromJson(e as Map<String, dynamic>))
.toList(),
created: (json['Created'] as num?)?.toInt(),
id: json['Id'] as String?,
usage:
json['Usage'] == null
? null
: Usage.fromJson(json['Usage'] as Map<String, dynamic>),
);
Map<String, dynamic> _$HunyuanResponseToJson(_HunyuanResponse instance) =>
<String, dynamic>{
if (instance.note case final value?) 'Note': value,
if (instance.choices case final value?) 'Choices': value,
if (instance.created case final value?) 'Created': value,
if (instance.id case final value?) 'Id': value,
if (instance.usage case final value?) 'Usage': value,
};
_Usage _$UsageFromJson(Map<String, dynamic> json) => _Usage(
promptTokens: (json['PromptTokens'] as num?)?.toInt(),
completionTokens: (json['CompletionTokens'] as num?)?.toInt(),
totalTokens: (json['TotalTokens'] as num?)?.toInt(),
);
Map<String, dynamic> _$UsageToJson(_Usage instance) => <String, dynamic>{
if (instance.promptTokens case final value?) 'PromptTokens': value,
if (instance.completionTokens case final value?) 'CompletionTokens': value,
if (instance.totalTokens case final value?) 'TotalTokens': value,
};
_Choices _$ChoicesFromJson(Map<String, dynamic> json) => _Choices(
finishReason: json['FinishReason'] as String?,
delta:
json['Delta'] == null
? null
: Delta.fromJson(json['Delta'] as Map<String, dynamic>),
);
Map<String, dynamic> _$ChoicesToJson(_Choices instance) => <String, dynamic>{
if (instance.finishReason case final value?) 'FinishReason': value,
if (instance.delta case final value?) 'Delta': value,
};
_Delta _$DeltaFromJson(Map<String, dynamic> json) =>
_Delta(role: json['Role'] as String?, content: json['Content'] as String?);
Map<String, dynamic> _$DeltaToJson(_Delta instance) => <String, dynamic>{
if (instance.role case final value?) 'Role': value,
if (instance.content case final value?) 'Content': value,
};

View File

@@ -1,128 +1,50 @@
class BingImage {
List<Images>? images;
Tooltips? tooltips;
import 'package:freezed_annotation/freezed_annotation.dart';
BingImage({this.images, this.tooltips});
part 'image.freezed.dart';
part 'image.g.dart';
BingImage.fromJson(Map<String, dynamic> json) {
images = json["images"] == null
? null
: (json["images"] as List).map((e) => Images.fromJson(e)).toList();
tooltips =
json["tooltips"] == null ? null : Tooltips.fromJson(json["tooltips"]);
}
@freezed
abstract class BingImage with _$BingImage {
const factory BingImage({List<Images>? images, Tooltips? tooltips}) =
_BingImage;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (images != null) {
data["images"] = images?.map((e) => e.toJson()).toList();
}
if (tooltips != null) {
data["tooltips"] = tooltips?.toJson();
}
return data;
}
factory BingImage.fromJson(Map<String, dynamic> json) =>
_$BingImageFromJson(json);
}
class Tooltips {
String? loading;
String? previous;
String? next;
String? walle;
String? walls;
@freezed
abstract class Tooltips with _$Tooltips {
const factory Tooltips({
String? loading,
String? previous,
String? next,
String? walle,
String? walls,
}) = _Tooltips;
Tooltips({this.loading, this.previous, this.next, this.walle, this.walls});
Tooltips.fromJson(Map<String, dynamic> json) {
loading = json["loading"];
previous = json["previous"];
next = json["next"];
walle = json["walle"];
walls = json["walls"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["loading"] = loading;
data["previous"] = previous;
data["next"] = next;
data["walle"] = walle;
data["walls"] = walls;
return data;
}
factory Tooltips.fromJson(Map<String, dynamic> json) =>
_$TooltipsFromJson(json);
}
class Images {
String? startdate;
String? fullstartdate;
String? enddate;
String? url;
String? urlbase;
String? copyright;
String? copyrightlink;
String? title;
String? quiz;
bool? wp;
String? hsh;
int? drk;
int? top;
int? bot;
List<dynamic>? hs;
@freezed
abstract class Images with _$Images {
const factory Images({
String? startdate,
String? fullstartdate,
String? enddate,
String? url,
String? urlbase,
String? copyright,
String? copyrightlink,
String? title,
String? quiz,
bool? wp,
String? hsh,
int? drk,
int? top,
int? bot,
List<dynamic>? hs,
}) = _Images;
Images(
{this.startdate,
this.fullstartdate,
this.enddate,
this.url,
this.urlbase,
this.copyright,
this.copyrightlink,
this.title,
this.quiz,
this.wp,
this.hsh,
this.drk,
this.top,
this.bot,
this.hs});
Images.fromJson(Map<String, dynamic> json) {
startdate = json["startdate"];
fullstartdate = json["fullstartdate"];
enddate = json["enddate"];
url = json["url"];
urlbase = json["urlbase"];
copyright = json["copyright"];
copyrightlink = json["copyrightlink"];
title = json["title"];
quiz = json["quiz"];
wp = json["wp"];
hsh = json["hsh"];
drk = json["drk"];
top = json["top"];
bot = json["bot"];
hs = json["hs"] ?? [];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["startdate"] = startdate;
data["fullstartdate"] = fullstartdate;
data["enddate"] = enddate;
data["url"] = url;
data["urlbase"] = urlbase;
data["copyright"] = copyright;
data["copyrightlink"] = copyrightlink;
data["title"] = title;
data["quiz"] = quiz;
data["wp"] = wp;
data["hsh"] = hsh;
data["drk"] = drk;
data["top"] = top;
data["bot"] = bot;
if (hs != null) {
data["hs"] = hs;
}
return data;
}
factory Images.fromJson(Map<String, dynamic> json) => _$ImagesFromJson(json);
}

View File

@@ -0,0 +1,825 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'image.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$BingImage {
List<Images>? get images;
Tooltips? get tooltips;
/// Create a copy of BingImage
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$BingImageCopyWith<BingImage> get copyWith =>
_$BingImageCopyWithImpl<BingImage>(this as BingImage, _$identity);
/// Serializes this BingImage to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is BingImage &&
const DeepCollectionEquality().equals(other.images, images) &&
(identical(other.tooltips, tooltips) ||
other.tooltips == tooltips));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, const DeepCollectionEquality().hash(images), tooltips);
@override
String toString() {
return 'BingImage(images: $images, tooltips: $tooltips)';
}
}
/// @nodoc
abstract mixin class $BingImageCopyWith<$Res> {
factory $BingImageCopyWith(BingImage value,
$Res Function(BingImage) _then) = _$BingImageCopyWithImpl;
@useResult
$Res call({
List<Images>? images, Tooltips? tooltips
});
$TooltipsCopyWith<$Res>? get tooltips;
}
/// @nodoc
class _$BingImageCopyWithImpl<$Res>
implements $BingImageCopyWith<$Res> {
_$BingImageCopyWithImpl(this._self, this._then);
final BingImage _self;
final $Res Function(BingImage) _then;
/// Create a copy of BingImage
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? images = freezed, Object? tooltips = freezed,}) {
return _then(_self.copyWith(
images: freezed == images
? _self.images
: images // ignore: cast_nullable_to_non_nullable
as List<Images>?,
tooltips: freezed == tooltips
? _self.tooltips
: tooltips // ignore: cast_nullable_to_non_nullable
as Tooltips?,
));
}
/// Create a copy of BingImage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TooltipsCopyWith<$Res>? get tooltips {
if (_self.tooltips == null) {
return null;
}
return $TooltipsCopyWith<$Res>(_self.tooltips!, (value) {
return _then(_self.copyWith(tooltips: value));
});
}
}
/// @nodoc
@JsonSerializable()
class _BingImage implements BingImage {
const _BingImage({final List<Images>? images, this.tooltips})
: _images = images;
factory _BingImage.fromJson(Map<String, dynamic> json) =>
_$BingImageFromJson(json);
final List<Images>? _images;
@override List<Images>? get images {
final value = _images;
if (value == null) return null;
if (_images is EqualUnmodifiableListView) return _images;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@override final Tooltips? tooltips;
/// Create a copy of BingImage
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$BingImageCopyWith<_BingImage> get copyWith =>
__$BingImageCopyWithImpl<_BingImage>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$BingImageToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _BingImage &&
const DeepCollectionEquality().equals(other._images, _images) &&
(identical(other.tooltips, tooltips) ||
other.tooltips == tooltips));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, const DeepCollectionEquality().hash(_images), tooltips);
@override
String toString() {
return 'BingImage(images: $images, tooltips: $tooltips)';
}
}
/// @nodoc
abstract mixin class _$BingImageCopyWith<$Res>
implements $BingImageCopyWith<$Res> {
factory _$BingImageCopyWith(_BingImage value,
$Res Function(_BingImage) _then) = __$BingImageCopyWithImpl;
@override
@useResult
$Res call({
List<Images>? images, Tooltips? tooltips
});
@override $TooltipsCopyWith<$Res>? get tooltips;
}
/// @nodoc
class __$BingImageCopyWithImpl<$Res>
implements _$BingImageCopyWith<$Res> {
__$BingImageCopyWithImpl(this._self, this._then);
final _BingImage _self;
final $Res Function(_BingImage) _then;
/// Create a copy of BingImage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call({Object? images = freezed, Object? tooltips = freezed,}) {
return _then(_BingImage(
images: freezed == images
? _self._images
: images // ignore: cast_nullable_to_non_nullable
as List<Images>?,
tooltips: freezed == tooltips
? _self.tooltips
: tooltips // ignore: cast_nullable_to_non_nullable
as Tooltips?,
));
}
/// Create a copy of BingImage
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TooltipsCopyWith<$Res>? get tooltips {
if (_self.tooltips == null) {
return null;
}
return $TooltipsCopyWith<$Res>(_self.tooltips!, (value) {
return _then(_self.copyWith(tooltips: value));
});
}
}
/// @nodoc
mixin _$Tooltips {
String? get loading;
String? get previous;
String? get next;
String? get walle;
String? get walls;
/// Create a copy of Tooltips
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TooltipsCopyWith<Tooltips> get copyWith =>
_$TooltipsCopyWithImpl<Tooltips>(this as Tooltips, _$identity);
/// Serializes this Tooltips to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Tooltips &&
(identical(other.loading, loading) || other.loading == loading) &&
(identical(other.previous, previous) ||
other.previous == previous) &&
(identical(other.next, next) || other.next == next) &&
(identical(other.walle, walle) || other.walle == walle) &&
(identical(other.walls, walls) || other.walls == walls));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, loading, previous, next, walle, walls);
@override
String toString() {
return 'Tooltips(loading: $loading, previous: $previous, next: $next, walle: $walle, walls: $walls)';
}
}
/// @nodoc
abstract mixin class $TooltipsCopyWith<$Res> {
factory $TooltipsCopyWith(Tooltips value,
$Res Function(Tooltips) _then) = _$TooltipsCopyWithImpl;
@useResult
$Res call({
String? loading, String? previous, String? next, String? walle, String? walls
});
}
/// @nodoc
class _$TooltipsCopyWithImpl<$Res>
implements $TooltipsCopyWith<$Res> {
_$TooltipsCopyWithImpl(this._self, this._then);
final Tooltips _self;
final $Res Function(Tooltips) _then;
/// Create a copy of Tooltips
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? loading = freezed, Object? previous = freezed, Object? next = freezed, Object? walle = freezed, Object? walls = freezed,}) {
return _then(_self.copyWith(
loading: freezed == loading
? _self.loading
: loading // ignore: cast_nullable_to_non_nullable
as String?,
previous: freezed == previous
? _self.previous
: previous // ignore: cast_nullable_to_non_nullable
as String?,
next: freezed == next
? _self.next
: next // ignore: cast_nullable_to_non_nullable
as String?,
walle: freezed == walle
? _self.walle
: walle // ignore: cast_nullable_to_non_nullable
as String?,
walls: freezed == walls
? _self.walls
: walls // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _Tooltips implements Tooltips {
const _Tooltips(
{this.loading, this.previous, this.next, this.walle, this.walls});
factory _Tooltips.fromJson(Map<String, dynamic> json) =>
_$TooltipsFromJson(json);
@override final String? loading;
@override final String? previous;
@override final String? next;
@override final String? walle;
@override final String? walls;
/// Create a copy of Tooltips
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TooltipsCopyWith<_Tooltips> get copyWith =>
__$TooltipsCopyWithImpl<_Tooltips>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$TooltipsToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _Tooltips &&
(identical(other.loading, loading) || other.loading == loading) &&
(identical(other.previous, previous) ||
other.previous == previous) &&
(identical(other.next, next) || other.next == next) &&
(identical(other.walle, walle) || other.walle == walle) &&
(identical(other.walls, walls) || other.walls == walls));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, loading, previous, next, walle, walls);
@override
String toString() {
return 'Tooltips(loading: $loading, previous: $previous, next: $next, walle: $walle, walls: $walls)';
}
}
/// @nodoc
abstract mixin class _$TooltipsCopyWith<$Res>
implements $TooltipsCopyWith<$Res> {
factory _$TooltipsCopyWith(_Tooltips value,
$Res Function(_Tooltips) _then) = __$TooltipsCopyWithImpl;
@override
@useResult
$Res call({
String? loading, String? previous, String? next, String? walle, String? walls
});
}
/// @nodoc
class __$TooltipsCopyWithImpl<$Res>
implements _$TooltipsCopyWith<$Res> {
__$TooltipsCopyWithImpl(this._self, this._then);
final _Tooltips _self;
final $Res Function(_Tooltips) _then;
/// Create a copy of Tooltips
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? loading = freezed, Object? previous = freezed, Object? next = freezed, Object? walle = freezed, Object? walls = freezed,}) {
return _then(_Tooltips(
loading: freezed == loading
? _self.loading
: loading // ignore: cast_nullable_to_non_nullable
as String?,
previous: freezed == previous
? _self.previous
: previous // ignore: cast_nullable_to_non_nullable
as String?,
next: freezed == next
? _self.next
: next // ignore: cast_nullable_to_non_nullable
as String?,
walle: freezed == walle
? _self.walle
: walle // ignore: cast_nullable_to_non_nullable
as String?,
walls: freezed == walls
? _self.walls
: walls // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
mixin _$Images {
String? get startdate;
String? get fullstartdate;
String? get enddate;
String? get url;
String? get urlbase;
String? get copyright;
String? get copyrightlink;
String? get title;
String? get quiz;
bool? get wp;
String? get hsh;
int? get drk;
int? get top;
int? get bot;
List<dynamic>? get hs;
/// Create a copy of Images
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ImagesCopyWith<Images> get copyWith =>
_$ImagesCopyWithImpl<Images>(this as Images, _$identity);
/// Serializes this Images to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Images &&
(identical(other.startdate, startdate) ||
other.startdate == startdate) &&
(identical(other.fullstartdate, fullstartdate) ||
other.fullstartdate == fullstartdate) &&
(identical(other.enddate, enddate) || other.enddate == enddate) &&
(identical(other.url, url) || other.url == url) &&
(identical(other.urlbase, urlbase) || other.urlbase == urlbase) &&
(identical(other.copyright, copyright) ||
other.copyright == copyright) &&
(identical(other.copyrightlink, copyrightlink) ||
other.copyrightlink == copyrightlink) &&
(identical(other.title, title) || other.title == title) &&
(identical(other.quiz, quiz) || other.quiz == quiz) &&
(identical(other.wp, wp) || other.wp == wp) &&
(identical(other.hsh, hsh) || other.hsh == hsh) &&
(identical(other.drk, drk) || other.drk == drk) &&
(identical(other.top, top) || other.top == top) &&
(identical(other.bot, bot) || other.bot == bot) &&
const DeepCollectionEquality().equals(other.hs, hs));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
startdate,
fullstartdate,
enddate,
url,
urlbase,
copyright,
copyrightlink,
title,
quiz,
wp,
hsh,
drk,
top,
bot,
const DeepCollectionEquality().hash(hs));
@override
String toString() {
return 'Images(startdate: $startdate, fullstartdate: $fullstartdate, enddate: $enddate, url: $url, urlbase: $urlbase, copyright: $copyright, copyrightlink: $copyrightlink, title: $title, quiz: $quiz, wp: $wp, hsh: $hsh, drk: $drk, top: $top, bot: $bot, hs: $hs)';
}
}
/// @nodoc
abstract mixin class $ImagesCopyWith<$Res> {
factory $ImagesCopyWith(Images value,
$Res Function(Images) _then) = _$ImagesCopyWithImpl;
@useResult
$Res call({
String? startdate, String? fullstartdate, String? enddate, String? url, String? urlbase, String? copyright, String? copyrightlink, String? title, String? quiz, bool? wp, String? hsh, int? drk, int? top, int? bot, List<
dynamic>? hs
});
}
/// @nodoc
class _$ImagesCopyWithImpl<$Res>
implements $ImagesCopyWith<$Res> {
_$ImagesCopyWithImpl(this._self, this._then);
final Images _self;
final $Res Function(Images) _then;
/// Create a copy of Images
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? startdate = freezed, Object? fullstartdate = freezed, Object? enddate = freezed, Object? url = freezed, Object? urlbase = freezed, Object? copyright = freezed, Object? copyrightlink = freezed, Object? title = freezed, Object? quiz = freezed, Object? wp = freezed, Object? hsh = freezed, Object? drk = freezed, Object? top = freezed, Object? bot = freezed, Object? hs = freezed,}) {
return _then(_self.copyWith(
startdate: freezed == startdate
? _self.startdate
: startdate // ignore: cast_nullable_to_non_nullable
as String?,
fullstartdate: freezed == fullstartdate
? _self.fullstartdate
: fullstartdate // ignore: cast_nullable_to_non_nullable
as String?,
enddate: freezed == enddate
? _self.enddate
: enddate // ignore: cast_nullable_to_non_nullable
as String?,
url: freezed == url
? _self.url
: url // ignore: cast_nullable_to_non_nullable
as String?,
urlbase: freezed == urlbase
? _self.urlbase
: urlbase // ignore: cast_nullable_to_non_nullable
as String?,
copyright: freezed == copyright
? _self.copyright
: copyright // ignore: cast_nullable_to_non_nullable
as String?,
copyrightlink: freezed == copyrightlink
? _self.copyrightlink
: copyrightlink // ignore: cast_nullable_to_non_nullable
as String?,
title: freezed == title
? _self.title
: title // ignore: cast_nullable_to_non_nullable
as String?,
quiz: freezed == quiz
? _self.quiz
: quiz // ignore: cast_nullable_to_non_nullable
as String?,
wp: freezed == wp ? _self.wp : wp // ignore: cast_nullable_to_non_nullable
as bool?,
hsh: freezed == hsh
? _self.hsh
: hsh // ignore: cast_nullable_to_non_nullable
as String?,
drk: freezed == drk
? _self.drk
: drk // ignore: cast_nullable_to_non_nullable
as int?,
top: freezed == top
? _self.top
: top // ignore: cast_nullable_to_non_nullable
as int?,
bot: freezed == bot
? _self.bot
: bot // ignore: cast_nullable_to_non_nullable
as int?,
hs: freezed == hs ? _self.hs : hs // ignore: cast_nullable_to_non_nullable
as List<dynamic>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _Images implements Images {
const _Images(
{this.startdate, this.fullstartdate, this.enddate, this.url, this.urlbase, this.copyright, this.copyrightlink, this.title, this.quiz, this.wp, this.hsh, this.drk, this.top, this.bot, final List<
dynamic>? hs}) : _hs = hs;
factory _Images.fromJson(Map<String, dynamic> json) => _$ImagesFromJson(json);
@override final String? startdate;
@override final String? fullstartdate;
@override final String? enddate;
@override final String? url;
@override final String? urlbase;
@override final String? copyright;
@override final String? copyrightlink;
@override final String? title;
@override final String? quiz;
@override final bool? wp;
@override final String? hsh;
@override final int? drk;
@override final int? top;
@override final int? bot;
final List<dynamic>? _hs;
@override List<dynamic>? get hs {
final value = _hs;
if (value == null) return null;
if (_hs is EqualUnmodifiableListView) return _hs;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
/// Create a copy of Images
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ImagesCopyWith<_Images> get copyWith =>
__$ImagesCopyWithImpl<_Images>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ImagesToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _Images &&
(identical(other.startdate, startdate) ||
other.startdate == startdate) &&
(identical(other.fullstartdate, fullstartdate) ||
other.fullstartdate == fullstartdate) &&
(identical(other.enddate, enddate) || other.enddate == enddate) &&
(identical(other.url, url) || other.url == url) &&
(identical(other.urlbase, urlbase) || other.urlbase == urlbase) &&
(identical(other.copyright, copyright) ||
other.copyright == copyright) &&
(identical(other.copyrightlink, copyrightlink) ||
other.copyrightlink == copyrightlink) &&
(identical(other.title, title) || other.title == title) &&
(identical(other.quiz, quiz) || other.quiz == quiz) &&
(identical(other.wp, wp) || other.wp == wp) &&
(identical(other.hsh, hsh) || other.hsh == hsh) &&
(identical(other.drk, drk) || other.drk == drk) &&
(identical(other.top, top) || other.top == top) &&
(identical(other.bot, bot) || other.bot == bot) &&
const DeepCollectionEquality().equals(other._hs, _hs));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
startdate,
fullstartdate,
enddate,
url,
urlbase,
copyright,
copyrightlink,
title,
quiz,
wp,
hsh,
drk,
top,
bot,
const DeepCollectionEquality().hash(_hs));
@override
String toString() {
return 'Images(startdate: $startdate, fullstartdate: $fullstartdate, enddate: $enddate, url: $url, urlbase: $urlbase, copyright: $copyright, copyrightlink: $copyrightlink, title: $title, quiz: $quiz, wp: $wp, hsh: $hsh, drk: $drk, top: $top, bot: $bot, hs: $hs)';
}
}
/// @nodoc
abstract mixin class _$ImagesCopyWith<$Res> implements $ImagesCopyWith<$Res> {
factory _$ImagesCopyWith(_Images value,
$Res Function(_Images) _then) = __$ImagesCopyWithImpl;
@override
@useResult
$Res call({
String? startdate, String? fullstartdate, String? enddate, String? url, String? urlbase, String? copyright, String? copyrightlink, String? title, String? quiz, bool? wp, String? hsh, int? drk, int? top, int? bot, List<
dynamic>? hs
});
}
/// @nodoc
class __$ImagesCopyWithImpl<$Res>
implements _$ImagesCopyWith<$Res> {
__$ImagesCopyWithImpl(this._self, this._then);
final _Images _self;
final $Res Function(_Images) _then;
/// Create a copy of Images
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? startdate = freezed, Object? fullstartdate = freezed, Object? enddate = freezed, Object? url = freezed, Object? urlbase = freezed, Object? copyright = freezed, Object? copyrightlink = freezed, Object? title = freezed, Object? quiz = freezed, Object? wp = freezed, Object? hsh = freezed, Object? drk = freezed, Object? top = freezed, Object? bot = freezed, Object? hs = freezed,}) {
return _then(_Images(
startdate: freezed == startdate
? _self.startdate
: startdate // ignore: cast_nullable_to_non_nullable
as String?,
fullstartdate: freezed == fullstartdate
? _self.fullstartdate
: fullstartdate // ignore: cast_nullable_to_non_nullable
as String?,
enddate: freezed == enddate
? _self.enddate
: enddate // ignore: cast_nullable_to_non_nullable
as String?,
url: freezed == url
? _self.url
: url // ignore: cast_nullable_to_non_nullable
as String?,
urlbase: freezed == urlbase
? _self.urlbase
: urlbase // ignore: cast_nullable_to_non_nullable
as String?,
copyright: freezed == copyright
? _self.copyright
: copyright // ignore: cast_nullable_to_non_nullable
as String?,
copyrightlink: freezed == copyrightlink
? _self.copyrightlink
: copyrightlink // ignore: cast_nullable_to_non_nullable
as String?,
title: freezed == title
? _self.title
: title // ignore: cast_nullable_to_non_nullable
as String?,
quiz: freezed == quiz
? _self.quiz
: quiz // ignore: cast_nullable_to_non_nullable
as String?,
wp: freezed == wp ? _self.wp : wp // ignore: cast_nullable_to_non_nullable
as bool?,
hsh: freezed == hsh
? _self.hsh
: hsh // ignore: cast_nullable_to_non_nullable
as String?,
drk: freezed == drk
? _self.drk
: drk // ignore: cast_nullable_to_non_nullable
as int?,
top: freezed == top
? _self.top
: top // ignore: cast_nullable_to_non_nullable
as int?,
bot: freezed == bot
? _self.bot
: bot // ignore: cast_nullable_to_non_nullable
as int?,
hs: freezed == hs
? _self._hs
: hs // ignore: cast_nullable_to_non_nullable
as List<dynamic>?,
));
}
}
// dart format on

View File

@@ -0,0 +1,76 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'image.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_BingImage _$BingImageFromJson(Map<String, dynamic> json) => _BingImage(
images:
(json['images'] as List<dynamic>?)
?.map((e) => Images.fromJson(e as Map<String, dynamic>))
.toList(),
tooltips:
json['tooltips'] == null
? null
: Tooltips.fromJson(json['tooltips'] as Map<String, dynamic>),
);
Map<String, dynamic> _$BingImageToJson(_BingImage instance) =>
<String, dynamic>{
if (instance.images case final value?) 'images': value,
if (instance.tooltips case final value?) 'tooltips': value,
};
_Tooltips _$TooltipsFromJson(Map<String, dynamic> json) => _Tooltips(
loading: json['loading'] as String?,
previous: json['previous'] as String?,
next: json['next'] as String?,
walle: json['walle'] as String?,
walls: json['walls'] as String?,
);
Map<String, dynamic> _$TooltipsToJson(_Tooltips instance) => <String, dynamic>{
if (instance.loading case final value?) 'loading': value,
if (instance.previous case final value?) 'previous': value,
if (instance.next case final value?) 'next': value,
if (instance.walle case final value?) 'walle': value,
if (instance.walls case final value?) 'walls': value,
};
_Images _$ImagesFromJson(Map<String, dynamic> json) => _Images(
startdate: json['startdate'] as String?,
fullstartdate: json['fullstartdate'] as String?,
enddate: json['enddate'] as String?,
url: json['url'] as String?,
urlbase: json['urlbase'] as String?,
copyright: json['copyright'] as String?,
copyrightlink: json['copyrightlink'] as String?,
title: json['title'] as String?,
quiz: json['quiz'] as String?,
wp: json['wp'] as bool?,
hsh: json['hsh'] as String?,
drk: (json['drk'] as num?)?.toInt(),
top: (json['top'] as num?)?.toInt(),
bot: (json['bot'] as num?)?.toInt(),
hs: json['hs'] as List<dynamic>?,
);
Map<String, dynamic> _$ImagesToJson(_Images instance) => <String, dynamic>{
if (instance.startdate case final value?) 'startdate': value,
if (instance.fullstartdate case final value?) 'fullstartdate': value,
if (instance.enddate case final value?) 'enddate': value,
if (instance.url case final value?) 'url': value,
if (instance.urlbase case final value?) 'urlbase': value,
if (instance.copyright case final value?) 'copyright': value,
if (instance.copyrightlink case final value?) 'copyrightlink': value,
if (instance.title case final value?) 'title': value,
if (instance.quiz case final value?) 'quiz': value,
if (instance.wp case final value?) 'wp': value,
if (instance.hsh case final value?) 'hsh': value,
if (instance.drk case final value?) 'drk': value,
if (instance.top case final value?) 'top': value,
if (instance.bot case final value?) 'bot': value,
if (instance.hs case final value?) 'hs': value,
};

View File

@@ -17,11 +17,7 @@ class Category {
Category();
Map<String, dynamic> toJson() {
return {
'id': id,
'categoryName': categoryName,
'parentId': parentId,
};
return {'id': id, 'categoryName': categoryName, 'parentId': parentId};
}
factory Category.fromJson(Map<String, dynamic> json) {

View File

@@ -20,16 +20,19 @@ const CategorySchema = IsarGeneratedSchema(
idName: 'id',
embedded: false,
properties: [
IsarPropertySchema(
name: 'id',
type: IsarType.string,
),
IsarPropertySchema(
name: 'categoryName',
type: IsarType.string,
IsarPropertySchema(name: 'id', type: IsarType.string),
IsarPropertySchema(name: 'categoryName', type: IsarType.string),
IsarPropertySchema(name: 'parentId', type: IsarType.string),
IsarPropertySchema(name: 'level', type: IsarType.string),
],
indexes: [
IsarIndexSchema(
name: 'level',
properties: ["level"],
unique: false,
hash: false,
),
],
indexes: [],
),
converter: IsarObjectConverter<String, Category>(
serialize: serializeCategory,
@@ -43,6 +46,15 @@ const CategorySchema = IsarGeneratedSchema(
int serializeCategory(IsarWriter writer, Category object) {
IsarCore.writeString(writer, 1, object.id);
IsarCore.writeString(writer, 2, object.categoryName);
{
final value = object.parentId;
if (value == null) {
IsarCore.writeNull(writer, 3);
} else {
IsarCore.writeString(writer, 3, value);
}
}
IsarCore.writeString(writer, 4, object.level);
return Isar.fastHash(object.id);
}
@@ -51,6 +63,7 @@ Category deserializeCategory(IsarReader reader) {
final object = Category();
object.id = IsarCore.readString(reader, 1) ?? '';
object.categoryName = IsarCore.readString(reader, 2) ?? '';
object.parentId = IsarCore.readString(reader, 3);
return object;
}
@@ -61,6 +74,10 @@ dynamic deserializeCategoryProp(IsarReader reader, int property) {
return IsarCore.readString(reader, 1) ?? '';
case 2:
return IsarCore.readString(reader, 2) ?? '';
case 3:
return IsarCore.readString(reader, 3);
case 4:
return IsarCore.readString(reader, 4) ?? '';
default:
throw ArgumentError('Unknown property: $property');
}
@@ -70,6 +87,8 @@ sealed class _CategoryUpdate {
bool call({
required String id,
String? categoryName,
String? parentId,
String? level,
});
}
@@ -82,12 +101,17 @@ class _CategoryUpdateImpl implements _CategoryUpdate {
bool call({
required String id,
Object? categoryName = ignore,
Object? parentId = ignore,
Object? level = ignore,
}) {
return collection.updateProperties([
id
], {
if (categoryName != ignore) 2: categoryName as String?,
}) >
return collection.updateProperties(
[id],
{
if (categoryName != ignore) 2: categoryName as String?,
if (parentId != ignore) 3: parentId as String?,
if (level != ignore) 4: level as String?,
},
) >
0;
}
}
@@ -96,6 +120,8 @@ sealed class _CategoryUpdateAll {
int call({
required List<String> id,
String? categoryName,
String? parentId,
String? level,
});
}
@@ -108,9 +134,13 @@ class _CategoryUpdateAllImpl implements _CategoryUpdateAll {
int call({
required List<String> id,
Object? categoryName = ignore,
Object? parentId = ignore,
Object? level = ignore,
}) {
return collection.updateProperties(id, {
if (categoryName != ignore) 2: categoryName as String?,
if (parentId != ignore) 3: parentId as String?,
if (level != ignore) 4: level as String?,
});
}
}
@@ -122,9 +152,7 @@ extension CategoryUpdate on IsarCollection<String, Category> {
}
sealed class _CategoryQueryUpdate {
int call({
String? categoryName,
});
int call({String? categoryName, String? parentId, String? level});
}
class _CategoryQueryUpdateImpl implements _CategoryQueryUpdate {
@@ -136,9 +164,13 @@ class _CategoryQueryUpdateImpl implements _CategoryQueryUpdate {
@override
int call({
Object? categoryName = ignore,
Object? parentId = ignore,
Object? level = ignore,
}) {
return query.updateProperties(limit: limit, {
if (categoryName != ignore) 2: categoryName as String?,
if (parentId != ignore) 3: parentId as String?,
if (level != ignore) 4: level as String?,
});
}
}
@@ -159,11 +191,15 @@ class _CategoryQueryBuilderUpdateImpl implements _CategoryQueryUpdate {
@override
int call({
Object? categoryName = ignore,
Object? parentId = ignore,
Object? level = ignore,
}) {
final q = query.build();
try {
return q.updateProperties(limit: limit, {
if (categoryName != ignore) 2: categoryName as String?,
if (parentId != ignore) 3: parentId as String?,
if (level != ignore) 4: level as String?,
});
} finally {
q.close();
@@ -187,11 +223,7 @@ extension CategoryQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 1,
value: value,
caseSensitive: caseSensitive,
),
EqualCondition(property: 1, value: value, caseSensitive: caseSensitive),
);
});
}
@@ -212,10 +244,7 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition>
idGreaterThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
idGreaterThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
@@ -233,11 +262,7 @@ extension CategoryQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 1,
value: value,
caseSensitive: caseSensitive,
),
LessCondition(property: 1, value: value, caseSensitive: caseSensitive),
);
});
}
@@ -305,8 +330,9 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition> idContains(
String value,
{bool caseSensitive = true}) {
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
@@ -319,8 +345,9 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition> idMatches(
String pattern,
{bool caseSensitive = true}) {
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
@@ -335,10 +362,7 @@ extension CategoryQueryFilter
QueryBuilder<Category, Category, QAfterFilterCondition> idIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(
property: 1,
value: '',
),
const EqualCondition(property: 1, value: ''),
);
});
}
@@ -346,10 +370,7 @@ extension CategoryQueryFilter
QueryBuilder<Category, Category, QAfterFilterCondition> idIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(
property: 1,
value: '',
),
const GreaterCondition(property: 1, value: ''),
);
});
}
@@ -360,20 +381,13 @@ extension CategoryQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 2,
value: value,
caseSensitive: caseSensitive,
),
EqualCondition(property: 2, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameGreaterThan(
String value, {
bool caseSensitive = true,
}) {
categoryNameGreaterThan(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
@@ -386,10 +400,7 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameGreaterThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
categoryNameGreaterThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
@@ -407,20 +418,13 @@ extension CategoryQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 2,
value: value,
caseSensitive: caseSensitive,
),
LessCondition(property: 2, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameLessThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
categoryNameLessThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
@@ -450,10 +454,7 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameStartsWith(
String value, {
bool caseSensitive = true,
}) {
categoryNameStartsWith(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
StartsWithCondition(
@@ -481,8 +482,9 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition> categoryNameContains(
String value,
{bool caseSensitive = true}) {
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
@@ -495,8 +497,9 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition> categoryNameMatches(
String pattern,
{bool caseSensitive = true}) {
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
@@ -509,116 +512,509 @@ extension CategoryQueryFilter
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameIsEmpty() {
categoryNameIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(
property: 2,
value: '',
const EqualCondition(property: 2, value: ''),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(property: 2, value: ''),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const IsNullCondition(property: 3));
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdIsNotNull() {
return QueryBuilder.apply(not(), (query) {
return query.addFilterCondition(const IsNullCondition(property: 3));
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(property: 3, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdGreaterThan(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
categoryNameIsNotEmpty() {
parentIdGreaterThanOrEqualTo(String? value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(
property: 2,
value: '',
GreaterOrEqualCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdLessThan(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(property: 3, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
parentIdLessThanOrEqualTo(String? value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdBetween(
String? lower,
String? upper, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
BetweenCondition(
property: 3,
lower: lower,
upper: upper,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
StartsWithCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EndsWithCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdContains(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdMatches(
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
property: 3,
wildcard: pattern,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(property: 3, value: ''),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> parentIdIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(property: 3, value: ''),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelEqualTo(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(property: 4, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelGreaterThan(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
property: 4,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
levelGreaterThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
property: 4,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelLessThan(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(property: 4, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition>
levelLessThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
property: 4,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelBetween(
String lower,
String upper, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
BetweenCondition(
property: 4,
lower: lower,
upper: upper,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
StartsWithCondition(
property: 4,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EndsWithCondition(
property: 4,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelContains(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
property: 4,
value: value,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelMatches(
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
property: 4,
wildcard: pattern,
caseSensitive: caseSensitive,
),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(property: 4, value: ''),
);
});
}
QueryBuilder<Category, Category, QAfterFilterCondition> levelIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(property: 4, value: ''),
);
});
}
}
extension CategoryQueryObject
on QueryBuilder<Category, Category, QFilterCondition> {}
extension CategoryQuerySortBy on QueryBuilder<Category, Category, QSortBy> {
QueryBuilder<Category, Category, QAfterSortBy> sortById(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> sortById({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
1,
caseSensitive: caseSensitive,
);
return query.addSortBy(1, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByIdDesc(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> sortByIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
1,
sort: Sort.desc,
caseSensitive: caseSensitive,
);
return query.addSortBy(1, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByCategoryName(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> sortByCategoryName({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
2,
caseSensitive: caseSensitive,
);
return query.addSortBy(2, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByCategoryNameDesc(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> sortByCategoryNameDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
2,
sort: Sort.desc,
caseSensitive: caseSensitive,
);
return query.addSortBy(2, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByParentId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(3, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByParentIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(3, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByLevel({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(4, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> sortByLevelDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(4, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
}
extension CategoryQuerySortThenBy
on QueryBuilder<Category, Category, QSortThenBy> {
QueryBuilder<Category, Category, QAfterSortBy> thenById(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> thenById({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(1, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByIdDesc(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> thenByIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(1, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByCategoryName(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> thenByCategoryName({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(2, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByCategoryNameDesc(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterSortBy> thenByCategoryNameDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(2, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByParentId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(3, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByParentIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(3, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByLevel({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(4, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterSortBy> thenByLevelDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(4, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
}
extension CategoryQueryWhereDistinct
on QueryBuilder<Category, Category, QDistinct> {
QueryBuilder<Category, Category, QAfterDistinct> distinctByCategoryName(
{bool caseSensitive = true}) {
QueryBuilder<Category, Category, QAfterDistinct> distinctByCategoryName({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(2, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterDistinct> distinctByParentId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(3, caseSensitive: caseSensitive);
});
}
QueryBuilder<Category, Category, QAfterDistinct> distinctByLevel({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(4, caseSensitive: caseSensitive);
});
}
}
extension CategoryQueryProperty1
@@ -634,6 +1030,18 @@ extension CategoryQueryProperty1
return query.addProperty(2);
});
}
QueryBuilder<Category, String?, QAfterProperty> parentIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(3);
});
}
QueryBuilder<Category, String, QAfterProperty> levelProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(4);
});
}
}
extension CategoryQueryProperty2<R>
@@ -649,6 +1057,18 @@ extension CategoryQueryProperty2<R>
return query.addProperty(2);
});
}
QueryBuilder<Category, (R, String?), QAfterProperty> parentIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(3);
});
}
QueryBuilder<Category, (R, String), QAfterProperty> levelProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(4);
});
}
}
extension CategoryQueryProperty3<R1, R2>
@@ -664,4 +1084,16 @@ extension CategoryQueryProperty3<R1, R2>
return query.addProperty(2);
});
}
QueryBuilder<Category, (R1, R2, String?), QOperations> parentIdProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(3);
});
}
QueryBuilder<Category, (R1, R2, String), QOperations> levelProperty() {
return QueryBuilder.apply(this, (query) {
return query.addProperty(4);
});
}
}

View File

@@ -67,6 +67,12 @@ class Diary {
// 位置信息
List<String> position = [];
// 关键词
List<String> keywords = [];
// 分词
List<String> tokenizer = [];
// 类型,富文本还是纯文本,不会为空,延迟加载
late String type;
@@ -97,6 +103,8 @@ class Diary {
const ListEquality().equals(videoName, other.videoName) &&
const ListEquality().equals(tags, other.tags) &&
const ListEquality().equals(position, other.position) &&
const ListEquality().equals(keywords, other.keywords) &&
const ListEquality().equals(tokenizer, other.tokenizer) &&
type == other.type &&
imageColor == other.imageColor &&
aspect == other.aspect;
@@ -118,6 +126,8 @@ class Diary {
const ListEquality().hash(videoName) ^
const ListEquality().hash(tags) ^
const ListEquality().hash(position) ^
const ListEquality().hash(keywords) ^
const ListEquality().hash(tokenizer) ^
type.hashCode ^
imageColor.hashCode ^
aspect.hashCode;
@@ -135,7 +145,8 @@ class Diary {
..contentText = contentText
..time = DateTime.fromMillisecondsSinceEpoch(time.millisecondsSinceEpoch)
..lastModified = DateTime.fromMillisecondsSinceEpoch(
lastModified.millisecondsSinceEpoch)
lastModified.millisecondsSinceEpoch,
)
..show = show
..mood = mood
..weather = List<String>.from(weather)
@@ -144,6 +155,8 @@ class Diary {
..videoName = List<String>.from(videoName)
..tags = List<String>.from(tags)
..position = List<String>.from(position)
..keywords = List<String>.from(keywords)
..tokenizer = List<String>.from(tokenizer)
..type = type
..imageColor = imageColor
..aspect = aspect;
@@ -167,6 +180,8 @@ class Diary {
'videoName': videoName,
'tags': tags,
'position': position,
'keywords': keywords,
'tokenizer': tokenizer,
'type': type,
'imageColor': imageColor,
'aspect': aspect,
@@ -190,6 +205,8 @@ class Diary {
..videoName = List<String>.from(json['videoName'] as List)
..tags = List<String>.from(json['tags'] as List)
..position = List<String>.from(json['position'] as List)
..keywords = List<String>.from(json['keywords'] as List)
..tokenizer = List<String>.from(json['tokenizer'] as List)
..type = json['type'] as String
..imageColor = json['imageColor'] as int?
..aspect = (json['aspect'] as num?)?.toDouble();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
import 'package:isar/isar.dart';
import 'package:path/path.dart';
part 'font.g.dart';
@collection
class Font {
@Id()
int get id => fastHash(fontFamily);
final String fontFileName;
String get fontFamily => basenameWithoutExtension(fontFileName);
String get fontType => extension(fontFileName);
final Map<String, dynamic> fontWghtAxisMap;
Font({required this.fontFileName, required this.fontWghtAxisMap});
}
int fastHash(String string) {
var hash = 0xcbf29ce484222325;
var i = 0;
while (i < string.length) {
final codeUnit = string.codeUnitAt(i++);
hash ^= codeUnit >> 8;
hash *= 0x100000001b3;
hash ^= codeUnit & 0xFF;
hash *= 0x100000001b3;
}
return hash;
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,22 +20,10 @@ const SyncRecordSchema = IsarGeneratedSchema(
idName: 'isarId',
embedded: false,
properties: [
IsarPropertySchema(
name: 'syncId',
type: IsarType.string,
),
IsarPropertySchema(
name: 'diaryId',
type: IsarType.string,
),
IsarPropertySchema(
name: 'diaryJson',
type: IsarType.string,
),
IsarPropertySchema(
name: 'time',
type: IsarType.dateTime,
),
IsarPropertySchema(name: 'syncId', type: IsarType.string),
IsarPropertySchema(name: 'diaryId', type: IsarType.string),
IsarPropertySchema(name: 'diaryJson', type: IsarType.string),
IsarPropertySchema(name: 'time', type: IsarType.dateTime),
IsarPropertySchema(
name: 'syncType',
type: IsarType.byte,
@@ -104,8 +92,10 @@ dynamic deserializeSyncRecordProp(IsarReader reader, int property) {
if (value == -9223372036854775808) {
return DateTime.fromMillisecondsSinceEpoch(0, isUtc: true).toLocal();
} else {
return DateTime.fromMicrosecondsSinceEpoch(value, isUtc: true)
.toLocal();
return DateTime.fromMicrosecondsSinceEpoch(
value,
isUtc: true,
).toLocal();
}
}
case 5:
@@ -149,15 +139,16 @@ class _SyncRecordUpdateImpl implements _SyncRecordUpdate {
Object? time = ignore,
Object? syncType = ignore,
}) {
return collection.updateProperties([
isarId
], {
if (syncId != ignore) 1: syncId as String?,
if (diaryId != ignore) 2: diaryId as String?,
if (diaryJson != ignore) 3: diaryJson as String?,
if (time != ignore) 4: time as DateTime?,
if (syncType != ignore) 5: syncType as SyncType?,
}) >
return collection.updateProperties(
[isarId],
{
if (syncId != ignore) 1: syncId as String?,
if (diaryId != ignore) 2: diaryId as String?,
if (diaryJson != ignore) 3: diaryJson as String?,
if (time != ignore) 4: time as DateTime?,
if (syncType != ignore) 5: syncType as SyncType?,
},
) >
0;
}
}
@@ -297,11 +288,7 @@ extension SyncRecordQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 1,
value: value,
caseSensitive: caseSensitive,
),
EqualCondition(property: 1, value: value, caseSensitive: caseSensitive),
);
});
}
@@ -322,10 +309,7 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
syncIdGreaterThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
syncIdGreaterThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
@@ -343,20 +327,13 @@ extension SyncRecordQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 1,
value: value,
caseSensitive: caseSensitive,
),
LessCondition(property: 1, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
syncIdLessThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
syncIdLessThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
@@ -416,8 +393,9 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> syncIdContains(
String value,
{bool caseSensitive = true}) {
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
@@ -430,8 +408,9 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> syncIdMatches(
String pattern,
{bool caseSensitive = true}) {
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
@@ -446,22 +425,16 @@ extension SyncRecordQueryFilter
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> syncIdIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(
property: 1,
value: '',
),
const EqualCondition(property: 1, value: ''),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
syncIdIsNotEmpty() {
syncIdIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(
property: 1,
value: '',
),
const GreaterCondition(property: 1, value: ''),
);
});
}
@@ -472,20 +445,13 @@ extension SyncRecordQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 2,
value: value,
caseSensitive: caseSensitive,
),
EqualCondition(property: 2, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryIdGreaterThan(
String value, {
bool caseSensitive = true,
}) {
diaryIdGreaterThan(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
@@ -498,10 +464,7 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryIdGreaterThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
diaryIdGreaterThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
@@ -519,20 +482,13 @@ extension SyncRecordQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 2,
value: value,
caseSensitive: caseSensitive,
),
LessCondition(property: 2, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryIdLessThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
diaryIdLessThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
@@ -592,8 +548,9 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> diaryIdContains(
String value,
{bool caseSensitive = true}) {
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
@@ -606,8 +563,9 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> diaryIdMatches(
String pattern,
{bool caseSensitive = true}) {
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
@@ -622,22 +580,16 @@ extension SyncRecordQueryFilter
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> diaryIdIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(
property: 2,
value: '',
),
const EqualCondition(property: 2, value: ''),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryIdIsNotEmpty() {
diaryIdIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(
property: 2,
value: '',
),
const GreaterCondition(property: 2, value: ''),
);
});
}
@@ -648,20 +600,13 @@ extension SyncRecordQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
EqualCondition(property: 3, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryJsonGreaterThan(
String value, {
bool caseSensitive = true,
}) {
diaryJsonGreaterThan(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
@@ -674,10 +619,7 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryJsonGreaterThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
diaryJsonGreaterThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
@@ -695,20 +637,13 @@ extension SyncRecordQueryFilter
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 3,
value: value,
caseSensitive: caseSensitive,
),
LessCondition(property: 3, value: value, caseSensitive: caseSensitive),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryJsonLessThanOrEqualTo(
String value, {
bool caseSensitive = true,
}) {
diaryJsonLessThanOrEqualTo(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
@@ -738,10 +673,7 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryJsonStartsWith(
String value, {
bool caseSensitive = true,
}) {
diaryJsonStartsWith(String value, {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
StartsWithCondition(
@@ -769,8 +701,9 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> diaryJsonContains(
String value,
{bool caseSensitive = true}) {
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
ContainsCondition(
@@ -783,8 +716,9 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition> diaryJsonMatches(
String pattern,
{bool caseSensitive = true}) {
String pattern, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
MatchesCondition(
@@ -797,25 +731,19 @@ extension SyncRecordQueryFilter
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryJsonIsEmpty() {
diaryJsonIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const EqualCondition(
property: 3,
value: '',
),
const EqualCondition(property: 3, value: ''),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
diaryJsonIsNotEmpty() {
diaryJsonIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
const GreaterCondition(
property: 3,
value: '',
),
const GreaterCondition(property: 3, value: ''),
);
});
}
@@ -825,10 +753,7 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 4,
value: value,
),
EqualCondition(property: 4, value: value),
);
});
}
@@ -838,24 +763,16 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
property: 4,
value: value,
),
GreaterCondition(property: 4, value: value),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
timeGreaterThanOrEqualTo(
DateTime value,
) {
timeGreaterThanOrEqualTo(DateTime value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
property: 4,
value: value,
),
GreaterOrEqualCondition(property: 4, value: value),
);
});
}
@@ -864,25 +781,15 @@ extension SyncRecordQueryFilter
DateTime value,
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 4,
value: value,
),
);
return query.addFilterCondition(LessCondition(property: 4, value: value));
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
timeLessThanOrEqualTo(
DateTime value,
) {
timeLessThanOrEqualTo(DateTime value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
property: 4,
value: value,
),
LessOrEqualCondition(property: 4, value: value),
);
});
}
@@ -893,11 +800,7 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
BetweenCondition(
property: 4,
lower: lower,
upper: upper,
),
BetweenCondition(property: 4, lower: lower, upper: upper),
);
});
}
@@ -907,38 +810,25 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 5,
value: value.index,
),
EqualCondition(property: 5, value: value.index),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
syncTypeGreaterThan(
SyncType value,
) {
syncTypeGreaterThan(SyncType value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
property: 5,
value: value.index,
),
GreaterCondition(property: 5, value: value.index),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
syncTypeGreaterThanOrEqualTo(
SyncType value,
) {
syncTypeGreaterThanOrEqualTo(SyncType value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
property: 5,
value: value.index,
),
GreaterOrEqualCondition(property: 5, value: value.index),
);
});
}
@@ -948,24 +838,16 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 5,
value: value.index,
),
LessCondition(property: 5, value: value.index),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
syncTypeLessThanOrEqualTo(
SyncType value,
) {
syncTypeLessThanOrEqualTo(SyncType value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
property: 5,
value: value.index,
),
LessOrEqualCondition(property: 5, value: value.index),
);
});
}
@@ -976,11 +858,7 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
BetweenCondition(
property: 5,
lower: lower.index,
upper: upper.index,
),
BetweenCondition(property: 5, lower: lower.index, upper: upper.index),
);
});
}
@@ -990,10 +868,7 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
EqualCondition(
property: 0,
value: value,
),
EqualCondition(property: 0, value: value),
);
});
}
@@ -1003,24 +878,16 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterCondition(
property: 0,
value: value,
),
GreaterCondition(property: 0, value: value),
);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
isarIdGreaterThanOrEqualTo(
int value,
) {
isarIdGreaterThanOrEqualTo(int value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
GreaterOrEqualCondition(
property: 0,
value: value,
),
GreaterOrEqualCondition(property: 0, value: value),
);
});
}
@@ -1029,25 +896,15 @@ extension SyncRecordQueryFilter
int value,
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessCondition(
property: 0,
value: value,
),
);
return query.addFilterCondition(LessCondition(property: 0, value: value));
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterFilterCondition>
isarIdLessThanOrEqualTo(
int value,
) {
isarIdLessThanOrEqualTo(int value) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
LessOrEqualCondition(
property: 0,
value: value,
),
LessOrEqualCondition(property: 0, value: value),
);
});
}
@@ -1058,11 +915,7 @@ extension SyncRecordQueryFilter
) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(
BetweenCondition(
property: 0,
lower: lower,
upper: upper,
),
BetweenCondition(property: 0, lower: lower, upper: upper),
);
});
}
@@ -1073,66 +926,51 @@ extension SyncRecordQueryObject
extension SyncRecordQuerySortBy
on QueryBuilder<SyncRecord, SyncRecord, QSortBy> {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortBySyncId(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortBySyncId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
1,
caseSensitive: caseSensitive,
);
return query.addSortBy(1, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortBySyncIdDesc(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortBySyncIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
1,
sort: Sort.desc,
caseSensitive: caseSensitive,
);
return query.addSortBy(1, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryId(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
2,
caseSensitive: caseSensitive,
);
return query.addSortBy(2, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryIdDesc(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
2,
sort: Sort.desc,
caseSensitive: caseSensitive,
);
return query.addSortBy(2, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryJson(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryJson({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
3,
caseSensitive: caseSensitive,
);
return query.addSortBy(3, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryJsonDesc(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> sortByDiaryJsonDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(
3,
sort: Sort.desc,
caseSensitive: caseSensitive,
);
return query.addSortBy(3, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
@@ -1175,43 +1013,49 @@ extension SyncRecordQuerySortBy
extension SyncRecordQuerySortThenBy
on QueryBuilder<SyncRecord, SyncRecord, QSortThenBy> {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenBySyncId(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenBySyncId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(1, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenBySyncIdDesc(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenBySyncIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(1, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryId(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(2, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryIdDesc(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryIdDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(2, sort: Sort.desc, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryJson(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryJson({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(3, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryJsonDesc(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterSortBy> thenByDiaryJsonDesc({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(3, sort: Sort.desc, caseSensitive: caseSensitive);
});
@@ -1256,22 +1100,25 @@ extension SyncRecordQuerySortThenBy
extension SyncRecordQueryWhereDistinct
on QueryBuilder<SyncRecord, SyncRecord, QDistinct> {
QueryBuilder<SyncRecord, SyncRecord, QAfterDistinct> distinctBySyncId(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterDistinct> distinctBySyncId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(1, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterDistinct> distinctByDiaryId(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterDistinct> distinctByDiaryId({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(2, caseSensitive: caseSensitive);
});
}
QueryBuilder<SyncRecord, SyncRecord, QAfterDistinct> distinctByDiaryJson(
{bool caseSensitive = true}) {
QueryBuilder<SyncRecord, SyncRecord, QAfterDistinct> distinctByDiaryJson({
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(3, caseSensitive: caseSensitive);
});

View File

@@ -16,31 +16,34 @@ class ShiplyResponse {
int? updateStrategy;
int? updateTime;
ShiplyResponse(
{this.apkBasicInfo,
this.clientInfo,
this.extra,
this.grayType,
this.newFeature,
this.popInterval,
this.popTimes,
this.publishTime,
this.receiveMoment,
this.remindType,
this.status,
this.tacticsId,
this.title,
this.undisturbedDuration,
this.updateStrategy,
this.updateTime});
ShiplyResponse({
this.apkBasicInfo,
this.clientInfo,
this.extra,
this.grayType,
this.newFeature,
this.popInterval,
this.popTimes,
this.publishTime,
this.receiveMoment,
this.remindType,
this.status,
this.tacticsId,
this.title,
this.undisturbedDuration,
this.updateStrategy,
this.updateTime,
});
ShiplyResponse.fromJson(Map<String, dynamic> json) {
apkBasicInfo = json["apkBasicInfo"] == null
? null
: ApkBasicInfo.fromJson(json["apkBasicInfo"]);
clientInfo = json["clientInfo"] == null
? null
: ClientInfo.fromJson(json["clientInfo"]);
apkBasicInfo =
json["apkBasicInfo"] == null
? null
: ApkBasicInfo.fromJson(json["apkBasicInfo"]);
clientInfo =
json["clientInfo"] == null
? null
: ClientInfo.fromJson(json["clientInfo"]);
extra = json["extra"] == null ? null : Extra.fromJson(json["extra"]);
grayType = json["grayType"];
newFeature = json["newFeature"];
@@ -129,15 +132,16 @@ class ApkBasicInfo {
int? versionCode;
String? version;
ApkBasicInfo(
{this.md5,
this.pkgSize,
this.buildNo,
this.bundleId,
this.downloadUrl,
this.pkgName,
this.versionCode,
this.version});
ApkBasicInfo({
this.md5,
this.pkgSize,
this.buildNo,
this.bundleId,
this.downloadUrl,
this.pkgName,
this.versionCode,
this.version,
});
ApkBasicInfo.fromJson(Map<String, dynamic> json) {
md5 = json["md5"];

View File

@@ -0,0 +1,8 @@
import 'package:moodiary/common/values/sync_status.dart';
class SyncResult<T> {
final SyncStatus status;
T? data;
SyncResult({required this.status, this.data});
}

View File

@@ -24,7 +24,7 @@ class SquadExample {
String toString() => _repr();
String _repr() {
var s = StringBuffer();
final s = StringBuffer();
s.write('qas_id: ${_printableText(qasId)}, ');
s.write('question_text: ${_printableText(questionText)}, ');
s.write('doc_tokens: [${docTokens.join(' ')}]');
@@ -104,9 +104,12 @@ class RawResult {
});
List<int> getAnswerIndices() {
int startIndex =
startLogits.indexOf(startLogits.reduce((a, b) => a > b ? a : b));
int endIndex = endLogits.indexOf(endLogits.reduce((a, b) => a > b ? a : b));
final int startIndex = startLogits.indexOf(
startLogits.reduce((a, b) => a > b ? a : b),
);
final int endIndex = endLogits.indexOf(
endLogits.reduce((a, b) => a > b ? a : b),
);
return [startIndex, endIndex];
}

View File

@@ -1,130 +1,48 @@
class WeatherResponse {
String? code;
String? updateTime;
String? fxLink;
Now? now;
Refer? refer;
import 'package:freezed_annotation/freezed_annotation.dart';
WeatherResponse(
{this.code, this.updateTime, this.fxLink, this.now, this.refer});
part 'weather.freezed.dart';
part 'weather.g.dart';
WeatherResponse.fromJson(Map<String, dynamic> json) {
code = json["code"];
updateTime = json["updateTime"];
fxLink = json["fxLink"];
now = json["now"] == null ? null : Now.fromJson(json["now"]);
refer = json["refer"] == null ? null : Refer.fromJson(json["refer"]);
}
@freezed
abstract class WeatherResponse with _$WeatherResponse {
const factory WeatherResponse({
String? code,
String? updateTime,
String? fxLink,
Now? now,
Refer? refer,
}) = _WeatherResponse;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["code"] = code;
data["updateTime"] = updateTime;
data["fxLink"] = fxLink;
if (now != null) {
data["now"] = now?.toJson();
}
if (refer != null) {
data["refer"] = refer?.toJson();
}
return data;
}
factory WeatherResponse.fromJson(Map<String, dynamic> json) =>
_$WeatherResponseFromJson(json);
}
class Refer {
List<String>? sources;
List<String>? license;
@freezed
abstract class Refer with _$Refer {
const factory Refer({List<String>? sources, List<String>? license}) = _Refer;
Refer({this.sources, this.license});
Refer.fromJson(Map<String, dynamic> json) {
sources =
json["sources"] == null ? null : List<String>.from(json["sources"]);
license =
json["license"] == null ? null : List<String>.from(json["license"]);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (sources != null) {
data["sources"] = sources;
}
if (license != null) {
data["license"] = license;
}
return data;
}
factory Refer.fromJson(Map<String, dynamic> json) => _$ReferFromJson(json);
}
class Now {
String? obsTime;
String? temp;
String? feelsLike;
String? icon;
String? text;
String? wind360;
String? windDir;
String? windScale;
String? windSpeed;
String? humidity;
String? precip;
String? pressure;
String? vis;
String? cloud;
String? dew;
@freezed
abstract class Now with _$Now {
const factory Now({
String? obsTime,
String? temp,
String? feelsLike,
String? icon,
String? text,
String? wind360,
String? windDir,
String? windScale,
String? windSpeed,
String? humidity,
String? precip,
String? pressure,
String? vis,
String? cloud,
String? dew,
}) = _Now;
Now(
{this.obsTime,
this.temp,
this.feelsLike,
this.icon,
this.text,
this.wind360,
this.windDir,
this.windScale,
this.windSpeed,
this.humidity,
this.precip,
this.pressure,
this.vis,
this.cloud,
this.dew});
Now.fromJson(Map<String, dynamic> json) {
obsTime = json["obsTime"];
temp = json["temp"];
feelsLike = json["feelsLike"];
icon = json["icon"];
text = json["text"];
wind360 = json["wind360"];
windDir = json["windDir"];
windScale = json["windScale"];
windSpeed = json["windSpeed"];
humidity = json["humidity"];
precip = json["precip"];
pressure = json["pressure"];
vis = json["vis"];
cloud = json["cloud"];
dew = json["dew"];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data["obsTime"] = obsTime;
data["temp"] = temp;
data["feelsLike"] = feelsLike;
data["icon"] = icon;
data["text"] = text;
data["wind360"] = wind360;
data["windDir"] = windDir;
data["windScale"] = windScale;
data["windSpeed"] = windSpeed;
data["humidity"] = humidity;
data["precip"] = precip;
data["pressure"] = pressure;
data["vis"] = vis;
data["cloud"] = cloud;
data["dew"] = dew;
return data;
}
factory Now.fromJson(Map<String, dynamic> json) => _$NowFromJson(json);
}

View File

@@ -0,0 +1,861 @@
// dart format width=80
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'weather.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$WeatherResponse {
String? get code;
String? get updateTime;
String? get fxLink;
Now? get now;
Refer? get refer;
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$WeatherResponseCopyWith<WeatherResponse> get copyWith =>
_$WeatherResponseCopyWithImpl<WeatherResponse>(
this as WeatherResponse, _$identity);
/// Serializes this WeatherResponse to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is WeatherResponse &&
(identical(other.code, code) || other.code == code) &&
(identical(other.updateTime, updateTime) ||
other.updateTime == updateTime) &&
(identical(other.fxLink, fxLink) || other.fxLink == fxLink) &&
(identical(other.now, now) || other.now == now) &&
(identical(other.refer, refer) || other.refer == refer));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, code, updateTime, fxLink, now, refer);
@override
String toString() {
return 'WeatherResponse(code: $code, updateTime: $updateTime, fxLink: $fxLink, now: $now, refer: $refer)';
}
}
/// @nodoc
abstract mixin class $WeatherResponseCopyWith<$Res> {
factory $WeatherResponseCopyWith(WeatherResponse value,
$Res Function(WeatherResponse) _then) = _$WeatherResponseCopyWithImpl;
@useResult
$Res call({
String? code, String? updateTime, String? fxLink, Now? now, Refer? refer
});
$NowCopyWith<$Res>? get now;
$ReferCopyWith<$Res>? get refer;
}
/// @nodoc
class _$WeatherResponseCopyWithImpl<$Res>
implements $WeatherResponseCopyWith<$Res> {
_$WeatherResponseCopyWithImpl(this._self, this._then);
final WeatherResponse _self;
final $Res Function(WeatherResponse) _then;
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? code = freezed, Object? updateTime = freezed, Object? fxLink = freezed, Object? now = freezed, Object? refer = freezed,}) {
return _then(_self.copyWith(
code: freezed == code
? _self.code
: code // ignore: cast_nullable_to_non_nullable
as String?,
updateTime: freezed == updateTime
? _self.updateTime
: updateTime // ignore: cast_nullable_to_non_nullable
as String?,
fxLink: freezed == fxLink
? _self.fxLink
: fxLink // ignore: cast_nullable_to_non_nullable
as String?,
now: freezed == now
? _self.now
: now // ignore: cast_nullable_to_non_nullable
as Now?,
refer: freezed == refer
? _self.refer
: refer // ignore: cast_nullable_to_non_nullable
as Refer?,
));
}
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$NowCopyWith<$Res>? get now {
if (_self.now == null) {
return null;
}
return $NowCopyWith<$Res>(_self.now!, (value) {
return _then(_self.copyWith(now: value));
});
}
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ReferCopyWith<$Res>? get refer {
if (_self.refer == null) {
return null;
}
return $ReferCopyWith<$Res>(_self.refer!, (value) {
return _then(_self.copyWith(refer: value));
});
}
}
/// @nodoc
@JsonSerializable()
class _WeatherResponse implements WeatherResponse {
const _WeatherResponse(
{this.code, this.updateTime, this.fxLink, this.now, this.refer});
factory _WeatherResponse.fromJson(Map<String, dynamic> json) =>
_$WeatherResponseFromJson(json);
@override final String? code;
@override final String? updateTime;
@override final String? fxLink;
@override final Now? now;
@override final Refer? refer;
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$WeatherResponseCopyWith<_WeatherResponse> get copyWith =>
__$WeatherResponseCopyWithImpl<_WeatherResponse>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$WeatherResponseToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _WeatherResponse &&
(identical(other.code, code) || other.code == code) &&
(identical(other.updateTime, updateTime) ||
other.updateTime == updateTime) &&
(identical(other.fxLink, fxLink) || other.fxLink == fxLink) &&
(identical(other.now, now) || other.now == now) &&
(identical(other.refer, refer) || other.refer == refer));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, code, updateTime, fxLink, now, refer);
@override
String toString() {
return 'WeatherResponse(code: $code, updateTime: $updateTime, fxLink: $fxLink, now: $now, refer: $refer)';
}
}
/// @nodoc
abstract mixin class _$WeatherResponseCopyWith<$Res>
implements $WeatherResponseCopyWith<$Res> {
factory _$WeatherResponseCopyWith(_WeatherResponse value,
$Res Function(_WeatherResponse) _then) = __$WeatherResponseCopyWithImpl;
@override
@useResult
$Res call({
String? code, String? updateTime, String? fxLink, Now? now, Refer? refer
});
@override $NowCopyWith<$Res>? get now;
@override $ReferCopyWith<$Res>? get refer;
}
/// @nodoc
class __$WeatherResponseCopyWithImpl<$Res>
implements _$WeatherResponseCopyWith<$Res> {
__$WeatherResponseCopyWithImpl(this._self, this._then);
final _WeatherResponse _self;
final $Res Function(_WeatherResponse) _then;
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? code = freezed, Object? updateTime = freezed, Object? fxLink = freezed, Object? now = freezed, Object? refer = freezed,}) {
return _then(_WeatherResponse(
code: freezed == code
? _self.code
: code // ignore: cast_nullable_to_non_nullable
as String?,
updateTime: freezed == updateTime
? _self.updateTime
: updateTime // ignore: cast_nullable_to_non_nullable
as String?,
fxLink: freezed == fxLink
? _self.fxLink
: fxLink // ignore: cast_nullable_to_non_nullable
as String?,
now: freezed == now
? _self.now
: now // ignore: cast_nullable_to_non_nullable
as Now?,
refer: freezed == refer
? _self.refer
: refer // ignore: cast_nullable_to_non_nullable
as Refer?,
));
}
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$NowCopyWith<$Res>? get now {
if (_self.now == null) {
return null;
}
return $NowCopyWith<$Res>(_self.now!, (value) {
return _then(_self.copyWith(now: value));
});
}
/// Create a copy of WeatherResponse
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ReferCopyWith<$Res>? get refer {
if (_self.refer == null) {
return null;
}
return $ReferCopyWith<$Res>(_self.refer!, (value) {
return _then(_self.copyWith(refer: value));
});
}
}
/// @nodoc
mixin _$Refer {
List<String>? get sources;
List<String>? get license;
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ReferCopyWith<Refer> get copyWith =>
_$ReferCopyWithImpl<Refer>(this as Refer, _$identity);
/// Serializes this Refer to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Refer &&
const DeepCollectionEquality().equals(other.sources, sources) &&
const DeepCollectionEquality().equals(other.license, license));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, const DeepCollectionEquality().hash(sources),
const DeepCollectionEquality().hash(license));
@override
String toString() {
return 'Refer(sources: $sources, license: $license)';
}
}
/// @nodoc
abstract mixin class $ReferCopyWith<$Res> {
factory $ReferCopyWith(Refer value,
$Res Function(Refer) _then) = _$ReferCopyWithImpl;
@useResult
$Res call({
List<String>? sources, List<String>? license
});
}
/// @nodoc
class _$ReferCopyWithImpl<$Res>
implements $ReferCopyWith<$Res> {
_$ReferCopyWithImpl(this._self, this._then);
final Refer _self;
final $Res Function(Refer) _then;
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? sources = freezed, Object? license = freezed,}) {
return _then(_self.copyWith(
sources: freezed == sources
? _self.sources
: sources // ignore: cast_nullable_to_non_nullable
as List<String>?,
license: freezed == license
? _self.license
: license // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}
}
/// @nodoc
@JsonSerializable()
class _Refer implements Refer {
const _Refer({final List<String>? sources, final List<String>? license})
: _sources = sources,
_license = license;
factory _Refer.fromJson(Map<String, dynamic> json) => _$ReferFromJson(json);
final List<String>? _sources;
@override List<String>? get sources {
final value = _sources;
if (value == null) return null;
if (_sources is EqualUnmodifiableListView) return _sources;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
final List<String>? _license;
@override List<String>? get license {
final value = _license;
if (value == null) return null;
if (_license is EqualUnmodifiableListView) return _license;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ReferCopyWith<_Refer> get copyWith =>
__$ReferCopyWithImpl<_Refer>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$ReferToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _Refer &&
const DeepCollectionEquality().equals(other._sources, _sources) &&
const DeepCollectionEquality().equals(other._license, _license));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType, const DeepCollectionEquality().hash(_sources),
const DeepCollectionEquality().hash(_license));
@override
String toString() {
return 'Refer(sources: $sources, license: $license)';
}
}
/// @nodoc
abstract mixin class _$ReferCopyWith<$Res> implements $ReferCopyWith<$Res> {
factory _$ReferCopyWith(_Refer value,
$Res Function(_Refer) _then) = __$ReferCopyWithImpl;
@override
@useResult
$Res call({
List<String>? sources, List<String>? license
});
}
/// @nodoc
class __$ReferCopyWithImpl<$Res>
implements _$ReferCopyWith<$Res> {
__$ReferCopyWithImpl(this._self, this._then);
final _Refer _self;
final $Res Function(_Refer) _then;
/// Create a copy of Refer
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call({Object? sources = freezed, Object? license = freezed,}) {
return _then(_Refer(
sources: freezed == sources
? _self._sources
: sources // ignore: cast_nullable_to_non_nullable
as List<String>?,
license: freezed == license
? _self._license
: license // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}
}
/// @nodoc
mixin _$Now {
String? get obsTime;
String? get temp;
String? get feelsLike;
String? get icon;
String? get text;
String? get wind360;
String? get windDir;
String? get windScale;
String? get windSpeed;
String? get humidity;
String? get precip;
String? get pressure;
String? get vis;
String? get cloud;
String? get dew;
/// Create a copy of Now
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$NowCopyWith<Now> get copyWith =>
_$NowCopyWithImpl<Now>(this as Now, _$identity);
/// Serializes this Now to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Now &&
(identical(other.obsTime, obsTime) || other.obsTime == obsTime) &&
(identical(other.temp, temp) || other.temp == temp) &&
(identical(other.feelsLike, feelsLike) ||
other.feelsLike == feelsLike) &&
(identical(other.icon, icon) || other.icon == icon) &&
(identical(other.text, text) || other.text == text) &&
(identical(other.wind360, wind360) || other.wind360 == wind360) &&
(identical(other.windDir, windDir) || other.windDir == windDir) &&
(identical(other.windScale, windScale) ||
other.windScale == windScale) &&
(identical(other.windSpeed, windSpeed) ||
other.windSpeed == windSpeed) &&
(identical(other.humidity, humidity) ||
other.humidity == humidity) &&
(identical(other.precip, precip) || other.precip == precip) &&
(identical(other.pressure, pressure) ||
other.pressure == pressure) &&
(identical(other.vis, vis) || other.vis == vis) &&
(identical(other.cloud, cloud) || other.cloud == cloud) &&
(identical(other.dew, dew) || other.dew == dew));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
obsTime,
temp,
feelsLike,
icon,
text,
wind360,
windDir,
windScale,
windSpeed,
humidity,
precip,
pressure,
vis,
cloud,
dew);
@override
String toString() {
return 'Now(obsTime: $obsTime, temp: $temp, feelsLike: $feelsLike, icon: $icon, text: $text, wind360: $wind360, windDir: $windDir, windScale: $windScale, windSpeed: $windSpeed, humidity: $humidity, precip: $precip, pressure: $pressure, vis: $vis, cloud: $cloud, dew: $dew)';
}
}
/// @nodoc
abstract mixin class $NowCopyWith<$Res> {
factory $NowCopyWith(Now value, $Res Function(Now) _then) = _$NowCopyWithImpl;
@useResult
$Res call({
String? obsTime, String? temp, String? feelsLike, String? icon, String? text, String? wind360, String? windDir, String? windScale, String? windSpeed, String? humidity, String? precip, String? pressure, String? vis, String? cloud, String? dew
});
}
/// @nodoc
class _$NowCopyWithImpl<$Res>
implements $NowCopyWith<$Res> {
_$NowCopyWithImpl(this._self, this._then);
final Now _self;
final $Res Function(Now) _then;
/// Create a copy of Now
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call(
{Object? obsTime = freezed, Object? temp = freezed, Object? feelsLike = freezed, Object? icon = freezed, Object? text = freezed, Object? wind360 = freezed, Object? windDir = freezed, Object? windScale = freezed, Object? windSpeed = freezed, Object? humidity = freezed, Object? precip = freezed, Object? pressure = freezed, Object? vis = freezed, Object? cloud = freezed, Object? dew = freezed,}) {
return _then(_self.copyWith(
obsTime: freezed == obsTime
? _self.obsTime
: obsTime // ignore: cast_nullable_to_non_nullable
as String?,
temp: freezed == temp
? _self.temp
: temp // ignore: cast_nullable_to_non_nullable
as String?,
feelsLike: freezed == feelsLike
? _self.feelsLike
: feelsLike // ignore: cast_nullable_to_non_nullable
as String?,
icon: freezed == icon
? _self.icon
: icon // ignore: cast_nullable_to_non_nullable
as String?,
text: freezed == text
? _self.text
: text // ignore: cast_nullable_to_non_nullable
as String?,
wind360: freezed == wind360
? _self.wind360
: wind360 // ignore: cast_nullable_to_non_nullable
as String?,
windDir: freezed == windDir
? _self.windDir
: windDir // ignore: cast_nullable_to_non_nullable
as String?,
windScale: freezed == windScale
? _self.windScale
: windScale // ignore: cast_nullable_to_non_nullable
as String?,
windSpeed: freezed == windSpeed
? _self.windSpeed
: windSpeed // ignore: cast_nullable_to_non_nullable
as String?,
humidity: freezed == humidity
? _self.humidity
: humidity // ignore: cast_nullable_to_non_nullable
as String?,
precip: freezed == precip
? _self.precip
: precip // ignore: cast_nullable_to_non_nullable
as String?,
pressure: freezed == pressure
? _self.pressure
: pressure // ignore: cast_nullable_to_non_nullable
as String?,
vis: freezed == vis
? _self.vis
: vis // ignore: cast_nullable_to_non_nullable
as String?,
cloud: freezed == cloud
? _self.cloud
: cloud // ignore: cast_nullable_to_non_nullable
as String?,
dew: freezed == dew
? _self.dew
: dew // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// @nodoc
@JsonSerializable()
class _Now implements Now {
const _Now(
{this.obsTime, this.temp, this.feelsLike, this.icon, this.text, this.wind360, this.windDir, this.windScale, this.windSpeed, this.humidity, this.precip, this.pressure, this.vis, this.cloud, this.dew});
factory _Now.fromJson(Map<String, dynamic> json) => _$NowFromJson(json);
@override final String? obsTime;
@override final String? temp;
@override final String? feelsLike;
@override final String? icon;
@override final String? text;
@override final String? wind360;
@override final String? windDir;
@override final String? windScale;
@override final String? windSpeed;
@override final String? humidity;
@override final String? precip;
@override final String? pressure;
@override final String? vis;
@override final String? cloud;
@override final String? dew;
/// Create a copy of Now
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$NowCopyWith<_Now> get copyWith =>
__$NowCopyWithImpl<_Now>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$NowToJson(this,);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _Now &&
(identical(other.obsTime, obsTime) || other.obsTime == obsTime) &&
(identical(other.temp, temp) || other.temp == temp) &&
(identical(other.feelsLike, feelsLike) ||
other.feelsLike == feelsLike) &&
(identical(other.icon, icon) || other.icon == icon) &&
(identical(other.text, text) || other.text == text) &&
(identical(other.wind360, wind360) || other.wind360 == wind360) &&
(identical(other.windDir, windDir) || other.windDir == windDir) &&
(identical(other.windScale, windScale) ||
other.windScale == windScale) &&
(identical(other.windSpeed, windSpeed) ||
other.windSpeed == windSpeed) &&
(identical(other.humidity, humidity) ||
other.humidity == humidity) &&
(identical(other.precip, precip) || other.precip == precip) &&
(identical(other.pressure, pressure) ||
other.pressure == pressure) &&
(identical(other.vis, vis) || other.vis == vis) &&
(identical(other.cloud, cloud) || other.cloud == cloud) &&
(identical(other.dew, dew) || other.dew == dew));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(
runtimeType,
obsTime,
temp,
feelsLike,
icon,
text,
wind360,
windDir,
windScale,
windSpeed,
humidity,
precip,
pressure,
vis,
cloud,
dew);
@override
String toString() {
return 'Now(obsTime: $obsTime, temp: $temp, feelsLike: $feelsLike, icon: $icon, text: $text, wind360: $wind360, windDir: $windDir, windScale: $windScale, windSpeed: $windSpeed, humidity: $humidity, precip: $precip, pressure: $pressure, vis: $vis, cloud: $cloud, dew: $dew)';
}
}
/// @nodoc
abstract mixin class _$NowCopyWith<$Res> implements $NowCopyWith<$Res> {
factory _$NowCopyWith(_Now value,
$Res Function(_Now) _then) = __$NowCopyWithImpl;
@override
@useResult
$Res call({
String? obsTime, String? temp, String? feelsLike, String? icon, String? text, String? wind360, String? windDir, String? windScale, String? windSpeed, String? humidity, String? precip, String? pressure, String? vis, String? cloud, String? dew
});
}
/// @nodoc
class __$NowCopyWithImpl<$Res>
implements _$NowCopyWith<$Res> {
__$NowCopyWithImpl(this._self, this._then);
final _Now _self;
final $Res Function(_Now) _then;
/// Create a copy of Now
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$Res call(
{Object? obsTime = freezed, Object? temp = freezed, Object? feelsLike = freezed, Object? icon = freezed, Object? text = freezed, Object? wind360 = freezed, Object? windDir = freezed, Object? windScale = freezed, Object? windSpeed = freezed, Object? humidity = freezed, Object? precip = freezed, Object? pressure = freezed, Object? vis = freezed, Object? cloud = freezed, Object? dew = freezed,}) {
return _then(_Now(
obsTime: freezed == obsTime
? _self.obsTime
: obsTime // ignore: cast_nullable_to_non_nullable
as String?,
temp: freezed == temp
? _self.temp
: temp // ignore: cast_nullable_to_non_nullable
as String?,
feelsLike: freezed == feelsLike
? _self.feelsLike
: feelsLike // ignore: cast_nullable_to_non_nullable
as String?,
icon: freezed == icon
? _self.icon
: icon // ignore: cast_nullable_to_non_nullable
as String?,
text: freezed == text
? _self.text
: text // ignore: cast_nullable_to_non_nullable
as String?,
wind360: freezed == wind360
? _self.wind360
: wind360 // ignore: cast_nullable_to_non_nullable
as String?,
windDir: freezed == windDir
? _self.windDir
: windDir // ignore: cast_nullable_to_non_nullable
as String?,
windScale: freezed == windScale
? _self.windScale
: windScale // ignore: cast_nullable_to_non_nullable
as String?,
windSpeed: freezed == windSpeed
? _self.windSpeed
: windSpeed // ignore: cast_nullable_to_non_nullable
as String?,
humidity: freezed == humidity
? _self.humidity
: humidity // ignore: cast_nullable_to_non_nullable
as String?,
precip: freezed == precip
? _self.precip
: precip // ignore: cast_nullable_to_non_nullable
as String?,
pressure: freezed == pressure
? _self.pressure
: pressure // ignore: cast_nullable_to_non_nullable
as String?,
vis: freezed == vis
? _self.vis
: vis // ignore: cast_nullable_to_non_nullable
as String?,
cloud: freezed == cloud
? _self.cloud
: cloud // ignore: cast_nullable_to_non_nullable
as String?,
dew: freezed == dew
? _self.dew
: dew // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on

View File

@@ -0,0 +1,79 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'weather.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_WeatherResponse _$WeatherResponseFromJson(Map<String, dynamic> json) =>
_WeatherResponse(
code: json['code'] as String?,
updateTime: json['updateTime'] as String?,
fxLink: json['fxLink'] as String?,
now:
json['now'] == null
? null
: Now.fromJson(json['now'] as Map<String, dynamic>),
refer:
json['refer'] == null
? null
: Refer.fromJson(json['refer'] as Map<String, dynamic>),
);
Map<String, dynamic> _$WeatherResponseToJson(_WeatherResponse instance) =>
<String, dynamic>{
if (instance.code case final value?) 'code': value,
if (instance.updateTime case final value?) 'updateTime': value,
if (instance.fxLink case final value?) 'fxLink': value,
if (instance.now case final value?) 'now': value,
if (instance.refer case final value?) 'refer': value,
};
_Refer _$ReferFromJson(Map<String, dynamic> json) => _Refer(
sources:
(json['sources'] as List<dynamic>?)?.map((e) => e as String).toList(),
license:
(json['license'] as List<dynamic>?)?.map((e) => e as String).toList(),
);
Map<String, dynamic> _$ReferToJson(_Refer instance) => <String, dynamic>{
if (instance.sources case final value?) 'sources': value,
if (instance.license case final value?) 'license': value,
};
_Now _$NowFromJson(Map<String, dynamic> json) => _Now(
obsTime: json['obsTime'] as String?,
temp: json['temp'] as String?,
feelsLike: json['feelsLike'] as String?,
icon: json['icon'] as String?,
text: json['text'] as String?,
wind360: json['wind360'] as String?,
windDir: json['windDir'] as String?,
windScale: json['windScale'] as String?,
windSpeed: json['windSpeed'] as String?,
humidity: json['humidity'] as String?,
precip: json['precip'] as String?,
pressure: json['pressure'] as String?,
vis: json['vis'] as String?,
cloud: json['cloud'] as String?,
dew: json['dew'] as String?,
);
Map<String, dynamic> _$NowToJson(_Now instance) => <String, dynamic>{
if (instance.obsTime case final value?) 'obsTime': value,
if (instance.temp case final value?) 'temp': value,
if (instance.feelsLike case final value?) 'feelsLike': value,
if (instance.icon case final value?) 'icon': value,
if (instance.text case final value?) 'text': value,
if (instance.wind360 case final value?) 'wind360': value,
if (instance.windDir case final value?) 'windDir': value,
if (instance.windScale case final value?) 'windScale': value,
if (instance.windSpeed case final value?) 'windSpeed': value,
if (instance.humidity case final value?) 'humidity': value,
if (instance.precip case final value?) 'precip': value,
if (instance.pressure case final value?) 'pressure': value,
if (instance.vis case final value?) 'vis': value,
if (instance.cloud case final value?) 'cloud': value,
if (instance.dew case final value?) 'dew': value,
};

View File

@@ -1,10 +1,13 @@
import 'package:flutter/cupertino.dart';
class AppBorderRadius {
static const BorderRadius smallBorderRadius =
BorderRadius.all(Radius.circular(8.0));
static const BorderRadius mediumBorderRadius =
BorderRadius.all(Radius.circular(12.0));
static const BorderRadius largeBorderRadius =
BorderRadius.all(Radius.circular(16.0));
static const BorderRadius smallBorderRadius = BorderRadius.all(
Radius.circular(8.0),
);
static const BorderRadius mediumBorderRadius = BorderRadius.all(
Radius.circular(12.0),
);
static const BorderRadius largeBorderRadius = BorderRadius.all(
Radius.circular(16.0),
);
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import '../../main.dart';
import 'package:moodiary/l10n/l10n.dart';
enum AppColorType {
common(0),
@@ -35,21 +34,33 @@ class AppColor {
// PANTONE 2008 Blue Iris
static Color answerColor = const Color(0xFF5A5B9F);
static String colorName(index) {
static String colorName(index, BuildContext context) {
return switch (index) {
0 => l10n.colorNameBaiCaoShuang,
1 => l10n.colorNameQunQin,
2 => l10n.colorNameQinDai,
3 => l10n.colorNameShuiZhuHua,
4 => l10n.colorNameJiHe,
5 => l10n.colorNameXiangYe,
9990 => l10n.specialColorNameMochaMousse,
_ => l10n.colorNameSystem
0 => context.l10n.colorNameBaiCaoShuang,
1 => context.l10n.colorNameQunQin,
2 => context.l10n.colorNameQinDai,
3 => context.l10n.colorNameShuiZhuHua,
4 => context.l10n.colorNameJiHe,
5 => context.l10n.colorNameXiangYe,
9990 => context.l10n.specialColorNameMochaMousse,
_ => context.l10n.colorNameSystem,
};
}
static List<Color> emoColorList = [
const Color(0xFFFA4659),
const Color(0xFF2EB872)
const Color(0xFF2EB872),
];
}
class ShareCardColor {
static List<Color> cardColorList = [
const Color(0xFFF8F3D4),
const Color(0xFFF5F5F5),
const Color(0xFFFFFFFF),
const Color(0xFF393e46),
const Color(0xFF252A34),
const Color(0xFF212121),
const Color(0xFF000000),
];
}

View File

@@ -1,3 +1,6 @@
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
enum DiaryType {
text('text'),
markdown('markdown'),
@@ -6,4 +9,21 @@ enum DiaryType {
final String value;
const DiaryType(this.value);
static DiaryType fromValue(String value) {
return DiaryType.values.firstWhere((e) => e.value == value);
}
}
extension DiaryTypeIcon on DiaryType {
IconData get icon {
switch (this) {
case DiaryType.text:
return FontAwesomeIcons.font;
case DiaryType.markdown:
return FontAwesomeIcons.markdown;
case DiaryType.richText:
return FontAwesomeIcons.feather;
}
}
}

View File

@@ -419,6 +419,6 @@ class WeatherIcon {
"2425": const IconData(62147, fontFamily: 'qweather'),
"2426": const IconData(62148, fontFamily: 'qweather'),
"9998": const IconData(61898, fontFamily: 'qweather'),
"9999": const IconData(61899, fontFamily: 'qweather')
"9999": const IconData(61899, fontFamily: 'qweather'),
};
}

View File

@@ -0,0 +1,25 @@
import 'package:flutter/cupertino.dart';
import 'package:moodiary/l10n/l10n.dart';
enum Language {
system('system'),
chinese('zh'),
english('en');
final String languageCode;
const Language(this.languageCode);
}
extension LanguageExtension on Language {
String l10nText(BuildContext context) {
switch (this) {
case Language.system:
return context.l10n.settingLanguageSystem;
case Language.chinese:
return context.l10n.settingLanguageSimpleChinese;
case Language.english:
return context.l10n.settingLanguageEnglish;
}
}
}

View File

@@ -1,145 +0,0 @@
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:latlong2/latlong.dart';
class ChinaRegions {
// 定义中国所有省份的瓦片区域
static final regions = {
'Beijing': RectangleRegion(
LatLngBounds(
const LatLng(39.4427, 115.4213), const LatLng(41.0599, 117.5143)),
),
'Tianjin': RectangleRegion(
LatLngBounds(
const LatLng(38.5547, 116.7257), const LatLng(40.2555, 118.0481)),
),
'Hebei': RectangleRegion(
LatLngBounds(
const LatLng(36.0513, 113.4620), const LatLng(42.6502, 119.8485)),
),
'Shanxi': RectangleRegion(
LatLngBounds(
const LatLng(34.4824, 110.1619), const LatLng(40.9982, 114.7482)),
),
'Inner Mongolia': RectangleRegion(
LatLngBounds(
const LatLng(37.4229, 97.1681), const LatLng(53.3318, 126.0386)),
),
'Liaoning': RectangleRegion(
LatLngBounds(
const LatLng(38.7216, 118.9151), const LatLng(43.4845, 125.5690)),
),
'Jilin': RectangleRegion(
LatLngBounds(
const LatLng(40.8624, 121.3644), const LatLng(46.6387, 131.2433)),
),
'Heilongjiang': RectangleRegion(
LatLngBounds(
const LatLng(43.4329, 121.1836), const LatLng(53.5581, 135.0865)),
),
'Shanghai': RectangleRegion(
LatLngBounds(
const LatLng(30.6647, 120.8526), const LatLng(31.8832, 122.1084)),
),
'Jiangsu': RectangleRegion(
LatLngBounds(
const LatLng(30.4505, 116.8286), const LatLng(35.1224, 121.1320)),
),
'Zhejiang': RectangleRegion(
LatLngBounds(
const LatLng(27.1934, 118.0219), const LatLng(31.0895, 122.2274)),
),
'Anhui': RectangleRegion(
LatLngBounds(
const LatLng(29.4119, 114.8926), const LatLng(34.6568, 119.6455)),
),
'Fujian': RectangleRegion(
LatLngBounds(
const LatLng(23.5636, 116.7407), const LatLng(28.4387, 120.8369)),
),
'Jiangxi': RectangleRegion(
LatLngBounds(
const LatLng(24.4124, 113.5877), const LatLng(30.0756, 118.6083)),
),
'Shandong': RectangleRegion(
LatLngBounds(
const LatLng(34.3944, 114.8054), const LatLng(38.3072, 122.6978)),
),
'Henan': RectangleRegion(
LatLngBounds(
const LatLng(31.3802, 110.3787), const LatLng(36.5819, 116.6942)),
),
'Hubei': RectangleRegion(
LatLngBounds(
const LatLng(29.0470, 108.4110), const LatLng(33.4238, 116.0797)),
),
'Hunan': RectangleRegion(
LatLngBounds(
const LatLng(24.6366, 108.7877), const LatLng(30.1238, 114.2341)),
),
'Guangdong': RectangleRegion(
LatLngBounds(
const LatLng(20.0268, 109.6642), const LatLng(25.5186, 117.2037)),
),
'Guangxi': RectangleRegion(
LatLngBounds(
const LatLng(20.5400, 104.4759), const LatLng(26.2504, 112.0472)),
),
'Hainan': RectangleRegion(
LatLngBounds(
const LatLng(18.1460, 108.5117), const LatLng(20.2706, 111.0257)),
),
'Chongqing': RectangleRegion(
LatLngBounds(
const LatLng(28.1124, 105.2875), const LatLng(32.1310, 110.4740)),
),
'Sichuan': RectangleRegion(
LatLngBounds(
const LatLng(26.0490, 97.3516), const LatLng(34.3143, 108.5740)),
),
'Guizhou': RectangleRegion(
LatLngBounds(
const LatLng(24.3965, 103.5901), const LatLng(29.2275, 109.2114)),
),
'Yunnan': RectangleRegion(
LatLngBounds(
const LatLng(21.1375, 97.6400), const LatLng(29.2227, 106.1124)),
),
'Tibet': RectangleRegion(
LatLngBounds(
const LatLng(26.0000, 78.3949), const LatLng(36.4538, 99.1089)),
),
'Shaanxi': RectangleRegion(
LatLngBounds(
const LatLng(31.7024, 105.4964), const LatLng(39.5957, 111.2605)),
),
'Gansu': RectangleRegion(
LatLngBounds(
const LatLng(32.1043, 92.1316), const LatLng(42.7932, 108.7309)),
),
'Qinghai': RectangleRegion(
LatLngBounds(
const LatLng(31.5875, 89.6198), const LatLng(39.7931, 101.8822)),
),
'Ningxia': RectangleRegion(
LatLngBounds(
const LatLng(35.3135, 104.1455), const LatLng(39.3749, 107.9047)),
),
'Xinjiang': RectangleRegion(
LatLngBounds(
const LatLng(34.2231, 73.4994), const LatLng(49.1720, 96.3877)),
),
'Hong Kong': RectangleRegion(
LatLngBounds(
const LatLng(22.1193, 113.8252), const LatLng(22.5645, 114.4462)),
),
'Macau': RectangleRegion(
LatLngBounds(
const LatLng(22.1092, 113.5286), const LatLng(22.2176, 113.6069)),
),
'Taiwan': RectangleRegion(
LatLngBounds(
const LatLng(20.5170, 119.4189), const LatLng(25.4152, 122.0184)),
),
};
}

View File

@@ -0,0 +1,32 @@
/// 同步状态枚举
enum SyncStatus {
/// 已分配任务但还未执行
pending,
/// 正在同步中
syncing,
/// 同步成功
success,
/// 同步失败
failure,
/// 验证失败
invalid,
/// 未知状态
unknown,
}
/// 连接状态枚举
enum ConnectivityStatus {
/// 未连接
disconnected,
/// 正在连接
connecting,
/// 已连接
connected,
}

View File

@@ -18,8 +18,4 @@ class WebDavOptions {
static const Color connectingColor = Color(0xFFFFC107);
}
enum WebDavConnectivityStatus {
connected,
unconnected,
connecting,
}
enum WebDavConnectivityStatus { connected, unconnected, connecting }

View File

@@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:mood_diary/common/values/keyboard_state.dart';
import 'package:mood_diary/utils/literunner.dart';
import 'package:mood_diary/utils/tokenization.dart';
import 'package:refreshed/refreshed.dart';
import 'package:get/get.dart';
import 'package:moodiary/common/values/keyboard_state.dart';
import 'package:moodiary/components/keyboard_listener/keyboard_listener.dart';
import 'package:moodiary/utils/literunner.dart';
import 'package:moodiary/utils/tokenization.dart';
import 'ask_question_state.dart';
class AskQuestionLogic extends GetxController with WidgetsBindingObserver {
class AskQuestionLogic extends GetxController {
final AskQuestionState state = AskQuestionState();
//输入框控制器
@@ -23,42 +24,31 @@ class AskQuestionLogic extends GetxController with WidgetsBindingObserver {
List<double> heightList = [];
late final KeyboardObserver keyboardObserver;
@override
void didChangeMetrics() {
WidgetsBinding.instance.addPostFrameCallback((_) {
var height = MediaQuery.viewInsetsOf(Get.context!).bottom;
if (heightList.isNotEmpty && height != heightList.last) {
if (height > heightList.last &&
state.keyboardState != KeyboardState.opening) {
state.keyboardState = KeyboardState.opening;
//正在打开
} else if (height < heightList.last &&
state.keyboardState != KeyboardState.closing) {
state.keyboardState = KeyboardState.closing;
//正在关闭
unFocus();
void onInit() {
keyboardObserver = KeyboardObserver(
onStateChanged: (state) {
switch (state) {
case KeyboardState.opening:
break;
case KeyboardState.closing:
unFocus();
break;
case KeyboardState.closed:
toBottom();
break;
case KeyboardState.unknown:
break;
}
}
// 只在高度变化时记录高度
if (heightList.isEmpty || height != heightList.last) {
heightList.add(height);
}
// 当高度为0且键盘经历了开启关闭过程时认为键盘已完全关闭
if (height == 0 && state.keyboardState != KeyboardState.closed) {
state.keyboardState = KeyboardState.closed;
heightList.clear();
toBottom();
//已经关闭
}
});
super.didChangeMetrics();
},
);
super.onInit();
}
@override
void onReady() async {
WidgetsBinding.instance.addObserver(this);
//await fullTokenizer.init();
//await liteRunner.initializeInterpreter(state.modelPath, fullTokenizer);
super.onReady();
@@ -66,11 +56,10 @@ class AskQuestionLogic extends GetxController with WidgetsBindingObserver {
@override
void onClose() {
WidgetsBinding.instance.removeObserver(this);
keyboardObserver.stop();
textEditingController.dispose();
scrollController.dispose();
focusNode.dispose();
// 销毁实例
liteRunner.close();
super.onClose();
}
@@ -85,7 +74,7 @@ class AskQuestionLogic extends GetxController with WidgetsBindingObserver {
// 添加提问
state.qaList.add(question);
var answer = await liteRunner.ask(content, question);
final answer = await liteRunner.ask(content, question);
// 添加回答
if (answer != null) {
state.qaList.add(answer);
@@ -98,12 +87,15 @@ class AskQuestionLogic extends GetxController with WidgetsBindingObserver {
}
Future<void> toBottom() async {
await scrollController.animateTo(scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 100), curve: Curves.linear);
await scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 100),
curve: Curves.linear,
);
}
Future<void> ask(String content) async {
var text = textEditingController.text;
final text = textEditingController.text;
if (text.isNotEmpty) {
//await getAnswer(content, text);
}

View File

@@ -1,5 +1,5 @@
import 'package:mood_diary/common/values/keyboard_state.dart';
import 'package:refreshed/refreshed.dart';
import 'package:get/get.dart';
import 'package:moodiary/common/values/keyboard_state.dart';
class AskQuestionState {
late String modelPath;

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:mood_diary/common/values/border.dart';
import 'package:refreshed/refreshed.dart';
import 'package:get/get.dart';
import 'package:moodiary/common/values/border.dart';
import 'ask_question_logic.dart';
@@ -13,7 +13,7 @@ class AskQuestionComponent extends StatelessWidget {
Widget build(BuildContext context) {
final logic = Get.put(AskQuestionLogic());
final state = Bind.find<AskQuestionLogic>().state;
final colorScheme = Theme.of(context).colorScheme;
Widget buildInput() {
return Container(
padding: const EdgeInsets.all(8.0),
@@ -21,25 +21,28 @@ class AskQuestionComponent extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextField(
focusNode: logic.focusNode,
controller: logic.textEditingController,
maxLines: 1,
decoration: InputDecoration(
fillColor: colorScheme.surfaceContainerHighest,
child: TextField(
focusNode: logic.focusNode,
controller: logic.textEditingController,
maxLines: 1,
decoration: InputDecoration(
fillColor: context.theme.colorScheme.surfaceContainerHighest,
filled: true,
isDense: true,
hintText: '提问',
border: const OutlineInputBorder(
borderRadius: AppBorderRadius.largeBorderRadius,
borderSide: BorderSide.none,
)),
)),
),
),
),
),
IconButton.filled(
onPressed: () async {
await logic.ask(content);
},
icon: const Icon(Icons.arrow_upward))
onPressed: () async {
await logic.ask(content);
},
icon: const Icon(Icons.arrow_upward),
),
],
),
);
@@ -52,12 +55,14 @@ class AskQuestionComponent extends StatelessWidget {
ConstrainedBox(
constraints: BoxConstraints(maxWidth: constraint.maxWidth * 0.618),
child: Card.filled(
color: colorScheme.surfaceContainerHigh,
color: context.theme.colorScheme.surfaceContainerHigh,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text,
style: TextStyle(color: colorScheme.onSecondaryContainer),
style: TextStyle(
color: context.theme.colorScheme.onSecondaryContainer,
),
),
),
),
@@ -72,12 +77,14 @@ class AskQuestionComponent extends StatelessWidget {
ConstrainedBox(
constraints: BoxConstraints(maxWidth: constraint.maxWidth * 0.618),
child: Card.filled(
color: colorScheme.primaryContainer,
color: context.theme.colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text,
style: TextStyle(color: colorScheme.onPrimaryContainer),
style: TextStyle(
color: context.theme.colorScheme.onPrimaryContainer,
),
),
),
),
@@ -106,10 +113,12 @@ class AskQuestionComponent extends StatelessWidget {
builder: (_) {
return Column(
children: [
Expanded(child: Obx(() {
return buildChat(constraint);
})),
buildInput()
Expanded(
child: Obx(() {
return buildChat(constraint);
}),
),
buildInput(),
],
);
},

View File

@@ -1,7 +1,7 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/animation.dart';
import 'package:mood_diary/pages/edit/edit_logic.dart';
import 'package:refreshed/refreshed.dart';
import 'package:get/get.dart';
import 'package:moodiary/pages/edit/edit_logic.dart';
import 'audio_player_state.dart';
@@ -11,7 +11,9 @@ class AudioPlayerLogic extends GetxController
final AudioPlayer audioPlayer = AudioPlayer();
late final AnimationController animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 100));
vsync: this,
duration: const Duration(milliseconds: 100),
);
late final EditLogic editLogic = Bind.find<EditLogic>();
@override
@@ -67,7 +69,7 @@ class AudioPlayerLogic extends GetxController
Future<void> changeValue(value) async {
state.handleChange.value = true;
state.currentDuration.value = Duration(
milliseconds:
(state.totalDuration.value.inMilliseconds * value).toInt());
milliseconds: (state.totalDuration.value.inMilliseconds * value).toInt(),
);
}
}

View File

@@ -1,4 +1,4 @@
import 'package:refreshed/refreshed.dart';
import 'package:get/get.dart';
class AudioPlayerState {
late String audioPath;

View File

@@ -1,6 +1,6 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:refreshed/refreshed.dart';
import 'package:get/get.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';
import 'audio_player_logic.dart';
@@ -27,31 +27,38 @@ class SmallThumbShape extends SfThumbShape {
}) {
// 使用原有逻辑绘制缩小的滑块
final double radius = getPreferredSize(themeData).width / 2;
final Paint thumbPaint = Paint()
..color = themeData.thumbColor ?? Colors.blue
..isAntiAlias = true;
final Paint thumbPaint =
Paint()
..color = themeData.thumbColor ?? Colors.blue
..isAntiAlias = true;
context.canvas.drawCircle(center, radius, thumbPaint);
if (child != null) {
context.paintChild(
child,
Offset(center.dx - child.size.width / 2,
center.dy - child.size.height / 2));
child,
Offset(
center.dx - child.size.width / 2,
center.dy - child.size.height / 2,
),
);
}
}
}
class NoOverlayShape extends SfOverlayShape {
@override
void paint(PaintingContext context, Offset center,
{required RenderBox parentBox,
required dynamic themeData,
SfRangeValues? currentValues,
currentValue,
required Paint? paint,
required Animation<double> animation,
required SfThumb? thumb}) {}
void paint(
PaintingContext context,
Offset center, {
required RenderBox parentBox,
required dynamic themeData,
SfRangeValues? currentValues,
currentValue,
required Paint? paint,
required Animation<double> animation,
required SfThumb? thumb,
}) {}
}
class AudioPlayerComponent extends StatelessWidget {
@@ -64,7 +71,6 @@ class AudioPlayerComponent extends StatelessWidget {
final logic = Get.put(AudioPlayerLogic(), tag: path);
final state = Bind.find<AudioPlayerLogic>(tag: path).state;
final colorScheme = Theme.of(context).colorScheme;
return GetBuilder<AudioPlayerLogic>(
tag: path,
initState: (_) async {
@@ -73,7 +79,8 @@ class AudioPlayerComponent extends StatelessWidget {
assignId: true,
builder: (_) {
return Card.filled(
color: colorScheme.secondaryContainer,
color: context.theme.colorScheme.secondaryContainer,
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
@@ -84,18 +91,26 @@ class AudioPlayerComponent extends StatelessWidget {
children: [
Obx(() {
return SfSlider(
value: state.totalDuration.value != Duration.zero
? ((state.currentDuration.value.inMilliseconds /
state.totalDuration.value.inMilliseconds)
.clamp(0, 1))
: 0,
value:
state.totalDuration.value != Duration.zero
? ((state
.currentDuration
.value
.inMilliseconds /
state
.totalDuration
.value
.inMilliseconds)
.clamp(0, 1))
: 0,
onChangeEnd: (value) {
logic.to(value);
},
onChanged: (value) {
logic.changeValue(value);
},
inactiveColor: colorScheme.surfaceContainer,
inactiveColor:
context.theme.colorScheme.surfaceContainer,
overlayShape: NoOverlayShape(),
thumbShape: SmallThumbShape(),
);
@@ -112,7 +127,12 @@ class AudioPlayerComponent extends StatelessWidget {
.split('.')[0]
.padLeft(8, '0'),
style: TextStyle(
color: colorScheme.onSecondaryContainer),
color:
context
.theme
.colorScheme
.onSecondaryContainer,
),
);
}),
IconButton.filled(
@@ -121,12 +141,9 @@ class AudioPlayerComponent extends StatelessWidget {
? logic.pause()
: logic.play(path);
},
style: const ButtonStyle(
tapTargetSize:
MaterialTapTargetSize.shrinkWrap),
icon: AnimatedIcon(
icon: AnimatedIcons.play_pause,
color: colorScheme.onPrimary,
color: context.theme.colorScheme.onPrimary,
progress: logic.animationController,
),
),
@@ -137,7 +154,12 @@ class AudioPlayerComponent extends StatelessWidget {
.split('.')[0]
.padLeft(8, '0'),
style: TextStyle(
color: colorScheme.onSecondaryContainer),
color:
context
.theme
.colorScheme
.onSecondaryContainer,
),
);
}),
],

View File

@@ -1,14 +1,20 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:moodiary/l10n/l10n.dart';
class FrostedGlassButton extends StatelessWidget {
final Widget child;
final double size;
final Function()? onPressed;
const FrostedGlassButton(
{super.key, required this.child, required this.size, this.onPressed});
const FrostedGlassButton({
super.key,
required this.child,
required this.size,
this.onPressed,
});
@override
Widget build(BuildContext context) {
@@ -29,10 +35,8 @@ class FrostedGlassButton extends StatelessWidget {
}
class MultiFabLayoutDelegate extends MultiChildLayoutDelegate {
MultiFabLayoutDelegate({
required this.controller,
required this.layoutIds,
}) : super(relayout: controller);
MultiFabLayoutDelegate({required this.controller, required this.layoutIds})
: super(relayout: controller);
final Animation<double> controller;
final List<int> layoutIds;
@@ -53,8 +57,10 @@ class MultiFabLayoutDelegate extends MultiChildLayoutDelegate {
);
positionChild(
0,
Offset(size.width - mainButtonSize.width,
size.height - mainButtonSize.height),
Offset(
size.width - mainButtonSize.width,
size.height - mainButtonSize.height,
),
);
}
@@ -64,18 +70,17 @@ class MultiFabLayoutDelegate extends MultiChildLayoutDelegate {
if (hasChild(layoutId)) {
final childButtonSize = layoutChild(
layoutId,
BoxConstraints(
maxWidth: size.width,
maxHeight: childButtonHeight,
),
BoxConstraints(maxWidth: size.width, maxHeight: childButtonHeight),
);
// 动态计算子按钮的垂直偏移量
final dyOffset = (mainButtonHeight + buttonSpacing) +
final dyOffset =
(mainButtonHeight + buttonSpacing) +
(i - 1) * (childButtonSize.height + buttonSpacing) * animationValue;
// 动态计算水平偏移量,使子按钮完全隐藏在主按钮底部时的水平位置对齐
final dxOffset = (mainButtonHeight - childButtonSize.width) /
final dxOffset =
(mainButtonHeight - childButtonSize.width) /
2 *
(1 - animationValue);
@@ -96,3 +101,23 @@ class MultiFabLayoutDelegate extends MultiChildLayoutDelegate {
oldDelegate.layoutIds != layoutIds;
}
}
class PageBackButton extends StatelessWidget {
final Function()? onBack;
final Color? color;
const PageBackButton({super.key, this.onBack, this.color});
@override
Widget build(BuildContext context) {
return Center(
child: IconButton(
onPressed: onBack ?? Get.back,
icon: const Icon(Icons.arrow_back_rounded),
color: color ?? context.theme.colorScheme.onSurface,
tooltip: context.l10n.back,
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/widgets.dart';
import 'package:moodiary/common/values/border.dart';
class TopRRectClipper extends CustomClipper<RRect> {
final double topOffset;
TopRRectClipper({this.topOffset = 0});
@override
RRect getClip(Size size) {
final Rect rect = Rect.fromLTWH(
0,
topOffset,
size.width,
size.height - topOffset,
);
const Radius radius = Radius.circular(12.0);
return RRect.fromRectAndRadius(rect, radius);
}
@override
bool shouldReclip(covariant TopRRectClipper oldClipper) {
return oldClipper.topOffset != topOffset;
}
}
class PageClipper extends StatelessWidget {
final Widget child;
final CustomClipper<RRect>? clipper;
const PageClipper({super.key, required this.child, this.clipper});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ClipRRect(
borderRadius: AppBorderRadius.mediumBorderRadius,
clipper: clipper,
child: child,
),
);
}
}

View File

@@ -0,0 +1,185 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
Color debugPaintExpandAreaColor = const Color(
0xFFFF0000,
).withValues(alpha: 0.03);
Color debugPaintClipAreaColor = const Color(0xFF0000FF).withValues(alpha: 0.02);
class ExpandTapWidget extends SingleChildRenderObjectWidget {
const ExpandTapWidget({
super.key,
super.child,
required this.onTap,
required this.tapPadding,
});
final VoidCallback onTap;
final EdgeInsets tapPadding;
@override
RenderObject createRenderObject(BuildContext context) =>
_ExpandTapRenderBox(onTap: onTap, tapPadding: tapPadding);
@override
void updateRenderObject(BuildContext context, RenderBox renderObject) {
renderObject as _ExpandTapRenderBox;
if (renderObject.tapPadding != tapPadding) {
renderObject.tapPadding = tapPadding;
}
if (renderObject.onTap != onTap) {
renderObject.onTap = onTap;
}
}
}
class _TmpGestureArenaMember extends GestureArenaMember {
_TmpGestureArenaMember({required this.onTap});
final VoidCallback onTap;
@override
void acceptGesture(int key) {
onTap();
}
@override
void rejectGesture(int key) {}
}
class _ExpandTapRenderBox extends RenderBox
with RenderObjectWithChildMixin<RenderBox> {
_ExpandTapRenderBox({
required VoidCallback onTap,
required EdgeInsets tapPadding,
}) : _onTap = onTap,
_tapPadding = tapPadding;
VoidCallback _onTap;
EdgeInsets _tapPadding;
set onTap(VoidCallback value) {
if (_onTap != value) {
_onTap = value;
}
}
set tapPadding(EdgeInsets value) {
if (_tapPadding == value) return;
_tapPadding = value;
markNeedsPaint();
}
EdgeInsets get tapPadding => _tapPadding;
VoidCallback get onTap => _onTap;
@override
void performLayout() {
child!.layout(constraints, parentUsesSize: true);
size = child!.size;
if (size.isEmpty) {
_tapPadding = EdgeInsets.zero;
}
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
final BoxParentData childParentData = child!.parentData! as BoxParentData;
context.paintChild(child!, childParentData.offset + offset);
}
assert(() {
debugPaintExpandArea(context, offset);
return true;
}());
}
void debugPaintExpandArea(PaintingContext context, Offset offset) {
if (size.isEmpty) return;
final RenderBox parentBox = parent as RenderBox;
Offset parentPosition = Offset.zero;
parentPosition = offset - localToGlobal(Offset.zero, ancestor: parentBox);
final Size parentSize = parentBox.size;
final Rect parentRect = Rect.fromLTWH(
parentPosition.dx,
parentPosition.dy,
parentSize.width,
parentSize.height,
);
final BoxParentData childParentData = child!.parentData! as BoxParentData;
final Offset paintOffset =
childParentData.offset + offset - tapPadding.topLeft;
final Rect paintRect = Rect.fromLTWH(
paintOffset.dx,
paintOffset.dy,
size.width + tapPadding.horizontal,
size.height + tapPadding.vertical,
);
final Paint paint =
Paint()
..style = PaintingStyle.fill
..strokeWidth = 1.0
..color = debugPaintExpandAreaColor;
final Paint paint2 =
Paint()
..style = PaintingStyle.fill
..strokeWidth = 1.0
..color = debugPaintClipAreaColor;
context.canvas.drawRect(paintRect, paint);
context.canvas.drawRect(paintRect.intersect(parentRect), paint2);
}
@override
void handleEvent(PointerEvent event, BoxHitTestEntry entry) {
if (event is PointerDownEvent) {
final _TmpGestureArenaMember member = _TmpGestureArenaMember(
onTap: onTap,
);
GestureBinding.instance.gestureArena.add(event.pointer, member);
} else if (event is PointerUpEvent) {
GestureBinding.instance.gestureArena.sweep(event.pointer);
}
}
@override
bool hitTestSelf(Offset position) => true;
@override
bool hitTestChildren(BoxHitTestResult result, {Offset? position}) {
visitChildren((child) {
if (child is RenderBox) {
final BoxParentData parentData = child.parentData! as BoxParentData;
if (child.hitTest(result, position: position! - parentData.offset)) {
return;
}
}
});
return false;
}
@override
bool hitTest(BoxHitTestResult result, {Offset? position}) {
final Rect expandRect = Rect.fromLTWH(
0 - tapPadding.left,
0 - tapPadding.top,
size.width + tapPadding.right + tapPadding.left,
size.height + tapPadding.top + tapPadding.bottom,
);
if (expandRect.contains(position!)) {
final bool hitTarget =
hitTestChildren(result, position: position) || hitTestSelf(position);
if (hitTarget) {
result.add(BoxHitTestEntry(this, position));
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,321 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:moodiary/utils/cache_util.dart';
final kTransparentImage = Uint8List.fromList(<int>[
0x89,
0x50,
0x4E,
0x47,
0x0D,
0x0A,
0x1A,
0x0A,
0x00,
0x00,
0x00,
0x0D,
0x49,
0x48,
0x44,
0x52,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x01,
0x08,
0x06,
0x00,
0x00,
0x00,
0x1F,
0x15,
0xC4,
0x89,
0x00,
0x00,
0x00,
0x0A,
0x49,
0x44,
0x41,
0x54,
0x78,
0x9C,
0x63,
0x00,
0x01,
0x00,
0x00,
0x05,
0x00,
0x01,
0x0D,
0x0A,
0x2D,
0xB4,
0x00,
0x00,
0x00,
0x00,
0x49,
0x45,
0x4E,
0x44,
0xAE,
0x42,
0x60,
0x82,
]);
enum _ImageLoadState { loading, error, success }
class _ImageState {
final int width;
final int height;
final String path;
final double aspectRatio;
_ImageState({
required this.width,
required this.height,
required this.path,
required this.aspectRatio,
});
}
class MoodiaryImage extends StatefulWidget {
final String imagePath;
final int size;
final BoxFit? fit;
final VoidCallback? onTap;
final String? heroTag;
final BorderRadius? borderRadius;
final bool showBorder;
final EdgeInsets? padding;
const MoodiaryImage({
super.key,
required this.imagePath,
required this.size,
this.fit,
this.onTap,
this.heroTag,
this.borderRadius,
this.showBorder = false,
this.padding,
});
@override
State<MoodiaryImage> createState() => _MoodiaryImageState();
}
class _MoodiaryImageState extends State<MoodiaryImage> {
final Rx<_ImageLoadState> _loadState = Rx<_ImageLoadState>(
_ImageLoadState.loading,
);
late _ImageState _imageState;
@override
void initState() {
super.initState();
_loadImage();
}
@override
void dispose() {
_loadState.close();
super.dispose();
}
@override
void didUpdateWidget(covariant MoodiaryImage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.imagePath != oldWidget.imagePath ||
widget.size != oldWidget.size) {
_loadImage();
}
}
void _loadImage() async {
_loadState.value = _ImageLoadState.loading;
try {
//logger.d('Image loaded from path: ${widget.imagePath}');
final imageAspect = await ImageCacheUtil().getImageAspectRatioWithCache(
imagePath: widget.imagePath,
);
final imageSize = widget.size;
final width =
imageAspect < 1.0 ? imageSize : (imageSize * imageAspect).ceil();
final height =
imageAspect >= 1.0 ? imageSize : (imageSize / imageAspect).ceil();
final path = await ImageCacheUtil().getLocalImagePathWithCache(
imagePath: widget.imagePath,
imageWidth: width * 2,
imageHeight: height * 2,
imageAspectRatio: imageAspect,
);
_imageState = _ImageState(
width: width,
height: height,
path: path,
aspectRatio: imageAspect,
);
_loadState.value = _ImageLoadState.success;
} catch (e) {
_loadState.value = _ImageLoadState.error;
}
}
BorderRadius _shrinkBorderRadius(BorderRadius radius, double amount) {
return BorderRadius.only(
topLeft: Radius.elliptical(
(radius.topLeft.x - amount).clamp(0, double.infinity),
(radius.topLeft.y - amount).clamp(0, double.infinity),
),
topRight: Radius.elliptical(
(radius.topRight.x - amount).clamp(0, double.infinity),
(radius.topRight.y - amount).clamp(0, double.infinity),
),
bottomLeft: Radius.elliptical(
(radius.bottomLeft.x - amount).clamp(0, double.infinity),
(radius.bottomLeft.y - amount).clamp(0, double.infinity),
),
bottomRight: Radius.elliptical(
(radius.bottomRight.x - amount).clamp(0, double.infinity),
(radius.bottomRight.y - amount).clamp(0, double.infinity),
),
);
}
@override
Widget build(BuildContext context) {
const borderWidth = 1.0;
final outerRadius = widget.borderRadius ?? BorderRadius.zero;
final innerRadius =
widget.showBorder
? _shrinkBorderRadius(outerRadius, borderWidth)
: outerRadius;
return Container(
decoration: BoxDecoration(
borderRadius: outerRadius,
border:
widget.showBorder
? Border.all(
color: context.theme.colorScheme.outline.withValues(
alpha: 0.6,
),
width: borderWidth,
)
: null,
),
margin: widget.padding,
child: ClipRRect(
borderRadius: innerRadius,
child: AnimatedSwitcher(
duration: Durations.short3,
switchInCurve: Curves.easeOut,
switchOutCurve: Curves.easeIn,
child: Obx(() {
switch (_loadState.value) {
case _ImageLoadState.loading:
return const _LoadingPlaceholder(key: ValueKey('loading'));
case _ImageLoadState.error:
return const _ErrorPlaceholder(key: ValueKey('error'));
case _ImageLoadState.success:
final imagePath = _imageState.path;
final width = _imageState.width;
final height = _imageState.height;
return GestureDetector(
key: const ValueKey('image'),
onTap:
widget.onTap != null
? () async {
if (widget.heroTag != null) {
await precacheImage(
FileImage(File(widget.imagePath)),
context,
);
}
widget.onTap?.call();
}
: null,
behavior: HitTestBehavior.translucent,
child: HeroMode(
enabled: widget.heroTag != null,
child: Hero(
tag: widget.heroTag ?? '',
child: FadeInImage(
key: ValueKey(imagePath),
image: FileImage(File(imagePath)),
placeholder: MemoryImage(kTransparentImage),
fadeInDuration: Durations.short2,
fadeOutDuration: Durations.short1,
fit: widget.fit ?? BoxFit.cover,
width: width.toDouble(),
height: height.toDouble(),
imageErrorBuilder: (_, __, ___) {
return const _ErrorPlaceholder(
key: ValueKey('image_error'),
);
},
),
),
),
);
}
}),
),
),
);
}
}
class _ErrorPlaceholder extends StatelessWidget {
const _ErrorPlaceholder({super.key});
@override
Widget build(BuildContext context) {
return ColoredBox(
color: context.theme.colorScheme.errorContainer,
child: Center(
child: Icon(
Icons.error_rounded,
color: context.theme.colorScheme.onErrorContainer,
),
),
);
}
}
class _LoadingPlaceholder extends StatelessWidget {
const _LoadingPlaceholder({super.key});
@override
Widget build(BuildContext context) {
return ColoredBox(
color: context.theme.colorScheme.surfaceContainer,
child: Center(
child: Icon(
Icons.image_search_rounded,
color: context.theme.colorScheme.onSurfaceVariant,
),
),
);
}
}

View File

@@ -0,0 +1,64 @@
import 'dart:math';
import 'package:flutter/material.dart';
class MoodiaryLoading extends StatelessWidget {
const MoodiaryLoading({super.key, this.size = 24, this.color});
final double size;
final Color? color;
@override
Widget build(BuildContext context) {
return Center(child: CircularProgressIndicator(color: color));
}
}
class MoodiarySyncing extends StatefulWidget {
const MoodiarySyncing({super.key});
@override
State<MoodiarySyncing> createState() => _MoodiarySyncingState();
}
class _MoodiarySyncingState extends State<MoodiarySyncing>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.elasticInOut,
);
}
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.rotate(
angle: _animation.value * 2 * pi,
child: child,
);
},
child: const Icon(Icons.sync_rounded),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@@ -0,0 +1,301 @@
import 'dart:async';
import 'package:flutter/material.dart';
class IntegralCurve extends Curve {
static double delta = 0.01;
const IntegralCurve._(this.original, this.integral, this._values);
final Curve original;
final double integral;
final Map<double, double> _values;
factory IntegralCurve(Curve original) {
double integral = 0.0;
final values = <double, double>{};
for (double t = 0.0; t <= 1.0; t += delta) {
integral += original.transform(t) * delta;
values[t] = integral;
}
values[1.0] = integral;
// Normalize.
for (final double t in values.keys) {
values[t] = values[t]! / integral;
}
return IntegralCurve._(original, integral, values);
}
@override
double transform(double t) {
if (t < 0) return 0.0;
for (final key in _values.keys) {
if (key > t) return _values[key]!;
}
return 1.0;
}
}
class Marquee extends StatefulWidget {
Marquee({
super.key,
required this.text,
this.style,
this.textScaler,
this.textDirection = TextDirection.ltr,
this.scrollAxis = Axis.horizontal,
this.crossAxisAlignment = CrossAxisAlignment.center,
this.blankSpace = 0.0,
this.velocity = 50.0,
this.startAfter = Duration.zero,
this.pauseAfterRound = Duration.zero,
this.fadingEdgeStartFraction = 0.0,
this.fadingEdgeEndFraction = 0.0,
this.numberOfRounds,
this.startPadding = 0.0,
this.accelerationDuration = Duration.zero,
Curve accelerationCurve = Curves.decelerate,
this.decelerationDuration = Duration.zero,
Curve decelerationCurve = Curves.decelerate,
this.onDone,
}) : assert(!blankSpace.isNaN),
assert(blankSpace >= 0, "The blankSpace needs to be positive or zero."),
assert(blankSpace.isFinite),
assert(!velocity.isNaN),
assert(velocity != 0.0, "The velocity cannot be zero."),
assert(velocity.isFinite),
assert(
pauseAfterRound >= Duration.zero,
"The pauseAfterRound cannot be negative as time travel isn't "
"invented yet.",
),
assert(
fadingEdgeStartFraction >= 0 && fadingEdgeStartFraction <= 1,
"The fadingEdgeGradientFractionOnStart value should be between 0 and "
"1, inclusive",
),
assert(
fadingEdgeEndFraction >= 0 && fadingEdgeEndFraction <= 1,
"The fadingEdgeGradientFractionOnEnd value should be between 0 and "
"1, inclusive",
),
assert(numberOfRounds == null || numberOfRounds > 0),
assert(
accelerationDuration >= Duration.zero,
"The accelerationDuration cannot be negative as time travel isn't "
"invented yet.",
),
assert(
decelerationDuration >= Duration.zero,
"The decelerationDuration must be positive or zero as time travel "
"isn't invented yet.",
),
accelerationCurve = IntegralCurve(accelerationCurve),
decelerationCurve = IntegralCurve(decelerationCurve);
final String text;
final TextStyle? style;
final TextScaler? textScaler;
final TextDirection textDirection;
final Axis scrollAxis;
final CrossAxisAlignment crossAxisAlignment;
final double blankSpace;
final double velocity;
final Duration startAfter;
final Duration pauseAfterRound;
final int? numberOfRounds;
final double fadingEdgeStartFraction;
final double fadingEdgeEndFraction;
final double startPadding;
final Duration accelerationDuration;
final IntegralCurve accelerationCurve;
final Duration decelerationDuration;
final IntegralCurve decelerationCurve;
final VoidCallback? onDone;
@override
State<StatefulWidget> createState() => _MarqueeState();
}
class _MarqueeState extends State<Marquee> with SingleTickerProviderStateMixin {
final ScrollController _controller = ScrollController();
late double _startPosition;
late double _accelerationTarget;
late double _linearTarget;
late double _decelerationTarget;
late Duration _totalDuration;
Duration get _accelerationDuration => widget.accelerationDuration;
Duration? _linearDuration;
Duration get _decelerationDuration => widget.decelerationDuration;
bool _running = false;
int _roundCounter = 0;
bool get isDone =>
widget.numberOfRounds == null
? false
: widget.numberOfRounds == _roundCounter;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!_running) {
_running = true;
if (_controller.hasClients) {
_controller.jumpTo(_startPosition);
await Future<void>.delayed(widget.startAfter);
Future.doWhile(_scroll);
}
}
});
}
Future<bool> _scroll() async {
await _makeRoundTrip();
if (isDone && widget.onDone != null) {
widget.onDone!();
}
return _running && !isDone && _controller.hasClients;
}
@override
void didUpdateWidget(Widget oldWidget) {
super.didUpdateWidget(oldWidget as Marquee);
}
@override
void dispose() {
_running = false;
_controller.dispose();
super.dispose();
}
void _initialize(BuildContext context) {
final totalLength = _getTextWidth(context) + widget.blankSpace;
final accelerationLength =
widget.accelerationCurve.integral *
widget.velocity *
_accelerationDuration.inMilliseconds /
1000.0;
final decelerationLength =
widget.decelerationCurve.integral *
widget.velocity *
_decelerationDuration.inMilliseconds /
1000.0;
final linearLength =
(totalLength - accelerationLength.abs() - decelerationLength.abs()) *
(widget.velocity > 0 ? 1 : -1);
_startPosition = 2 * totalLength - widget.startPadding;
_accelerationTarget = _startPosition + accelerationLength;
_linearTarget = _accelerationTarget + linearLength;
_decelerationTarget = _linearTarget + decelerationLength;
_totalDuration =
_accelerationDuration +
_decelerationDuration +
Duration(milliseconds: (linearLength / widget.velocity * 1000).toInt());
_linearDuration =
_totalDuration - _accelerationDuration - _decelerationDuration;
}
Future<void> _makeRoundTrip() async {
if (!_controller.hasClients) return;
_controller.jumpTo(_startPosition);
if (!_running) return;
await _accelerate();
if (!_running) return;
await _moveLinearly();
if (!_running) return;
await _decelerate();
_roundCounter++;
if (!_running || !mounted) return;
}
Future<void> _accelerate() async {
await _animateTo(
_accelerationTarget,
_accelerationDuration,
widget.accelerationCurve,
);
}
Future<void> _moveLinearly() async {
await _animateTo(_linearTarget, _linearDuration, Curves.linear);
}
Future<void> _decelerate() async {
await _animateTo(
_decelerationTarget,
_decelerationDuration,
widget.decelerationCurve.flipped,
);
}
Future<void> _animateTo(
double? target,
Duration? duration,
Curve curve,
) async {
if (!_controller.hasClients) return;
if (duration! > Duration.zero) {
await _controller.animateTo(target!, duration: duration, curve: curve);
} else {
_controller.jumpTo(target!);
}
}
double _getTextWidth(BuildContext context) {
final span = TextSpan(text: widget.text, style: widget.style);
const constraints = BoxConstraints(maxWidth: double.infinity);
final richTextWidget = Text.rich(span).build(context) as RichText;
final renderObject = richTextWidget.createRenderObject(context);
renderObject.layout(constraints);
final boxes = renderObject.getBoxesForSelection(
TextSelection(
baseOffset: 0,
extentOffset: TextSpan(text: widget.text).toPlainText().length,
),
);
return boxes.last.right;
}
@override
Widget build(BuildContext context) {
_initialize(context);
return ListView.builder(
controller: _controller,
scrollDirection: widget.scrollAxis,
reverse: widget.textDirection == TextDirection.rtl,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (_, i) {
return i.isEven
? Text(
widget.text,
style: widget.style,
textScaler: widget.textScaler,
)
: SizedBox(
width:
widget.scrollAxis == Axis.horizontal
? widget.blankSpace
: null,
height:
widget.scrollAxis == Axis.vertical ? widget.blankSpace : null,
);
},
);
}
}

View File

@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class Modal extends StatelessWidget {
final Animation<double> animation;
final Function() onTap;
const Modal({super.key, required this.animation, required this.onTap});
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Visibility(
visible: animation.value > 0,
child: ModalBarrier(
color: context.theme.colorScheme.surfaceContainer.withValues(
alpha: 0.6 * animation.value,
),
barrierSemanticsDismissible: false,
onDismiss: onTap,
),
);
},
);
}
}

View File

@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:moodiary/common/values/border.dart';
class _TrianglePainter extends CustomPainter {
final Color color;
final Size size;
_TrianglePainter({required this.color, required this.size});
@override
void paint(Canvas canvas, Size size) {
final paint =
Paint()
..color = color
..style = PaintingStyle.fill;
final path =
Path()
..moveTo(0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width / 2, 0)
..close();
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant _TrianglePainter oldDelegate) => false;
}
void showPopupWidget({
required BuildContext targetContext,
required Widget child,
}) {
SmartDialog.showAttach(
targetContext: targetContext,
maskColor: Colors.transparent,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomPaint(
size: const Size(12, 6),
painter: _TrianglePainter(
color: context.theme.colorScheme.surfaceContainer,
size: const Size(12, 6),
),
),
Container(
padding: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
color: context.theme.colorScheme.surfaceContainer,
borderRadius: AppBorderRadius.mediumBorderRadius,
),
child: child,
),
],
);
},
);
}

View File

@@ -0,0 +1,134 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:moodiary/utils/aes_util.dart';
import 'package:qr_flutter/qr_flutter.dart';
class EncryptQrCode extends StatefulWidget {
final String data;
final double size;
final Duration validDuration;
final String? prefix;
const EncryptQrCode({
super.key,
required this.data,
this.size = 64,
this.validDuration = const Duration(minutes: 2),
this.prefix,
});
@override
State<EncryptQrCode> createState() => _EncryptQrCodeState();
}
class _EncryptQrCodeState extends State<EncryptQrCode> {
Uint8List? encryptedData;
late int expireAt; // Unix 时间戳
bool isExpired = false;
@override
void initState() {
super.initState();
_encryptData();
}
Future<void> _encryptData() async {
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
expireAt = now + widget.validDuration.inSeconds;
final aesData = await AesUtil.encryptWithTimeWindow(
data: '${widget.prefix}${widget.data}',
validDuration: widget.validDuration,
);
setState(() {
isExpired = false;
encryptedData = aesData;
});
_startExpirationChecker();
}
void _startExpirationChecker() {
Timer.periodic(const Duration(seconds: 1), (timer) {
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
if (now >= expireAt) {
setState(() {
isExpired = true;
});
timer.cancel();
}
});
}
Widget _buildQrChild() {
if (encryptedData == null) {
return Center(
key: const ValueKey('loading'),
child: CircularProgressIndicator(
color: context.theme.colorScheme.onSurfaceVariant,
),
);
}
if (isExpired) {
return GestureDetector(
key: const ValueKey('expired'),
onTap: _encryptData,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_rounded,
color: context.theme.colorScheme.onSurfaceVariant,
),
const SizedBox(height: 8),
Text(
'已过期',
style: context.textTheme.labelMedium?.copyWith(
color: context.theme.colorScheme.onSurfaceVariant,
),
),
],
),
),
);
}
return QrImageView(
key: const ValueKey('qr'),
data: base64Encode(encryptedData!),
size: widget.size,
backgroundColor: Colors.transparent,
dataModuleStyle: QrDataModuleStyle(
color: context.theme.colorScheme.onSurface,
dataModuleShape: QrDataModuleShape.circle,
),
eyeStyle: QrEyeStyle(
color: context.theme.colorScheme.onSurface,
eyeShape: QrEyeShape.circle,
),
gapless: false,
padding: EdgeInsets.zero,
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.size,
height: widget.size,
child: AnimatedSwitcher(
duration: Durations.medium2,
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
child: _buildQrChild(),
),
);
}
}

View File

@@ -0,0 +1,334 @@
import 'dart:async';
import 'dart:convert';
import 'package:dartx/dartx.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:moodiary/l10n/l10n.dart';
import 'package:moodiary/router/app_pages.dart';
import 'package:moodiary/utils/aes_util.dart';
import 'package:moodiary/utils/log_util.dart';
import 'package:moodiary/utils/notice_util.dart';
import 'package:throttling/throttling.dart';
Future<String?> showQrScanner({
required BuildContext context,
Duration? validDuration,
String? prefix,
}) async {
return Navigator.push<String?>(
context,
MoodiaryFadeInPageRoute(
builder: (context) {
return QrScanner(validDuration: validDuration, prefix: prefix);
},
),
);
}
class QrScanner extends StatefulWidget {
final Duration? validDuration;
final String? prefix;
const QrScanner({super.key, this.validDuration, this.prefix});
@override
State<QrScanner> createState() => _QrScannerState();
}
class _QrScannerState extends State<QrScanner>
with SingleTickerProviderStateMixin {
late final AnimationController _animationController = AnimationController(
vsync: this,
duration: Durations.long4,
)..repeat(reverse: true);
late final Animation<double> _curvedAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
);
late final MobileScannerController _scannerController =
MobileScannerController(
invertImage: true,
autoStart: false,
cameraResolution: const Size.square(640),
formats: [BarcodeFormat.qrCode],
);
late final Throttling _throttling = Throttling();
late final AppLifecycleListener _appLifecycleListener;
StreamSubscription<Object?>? _subscription;
late final ValueNotifier<TorchState> _torchState = ValueNotifier(
TorchState.unavailable,
);
@override
void initState() {
_subscription = _scannerController.barcodes.listen(_handleBarcode);
_appLifecycleListener = AppLifecycleListener(
onStateChange: (state) {
if (!_scannerController.value.hasCameraPermission) {
return;
}
switch (state) {
case AppLifecycleState.detached:
case AppLifecycleState.hidden:
case AppLifecycleState.paused:
return;
case AppLifecycleState.resumed:
_subscription = _scannerController.barcodes.listen(_handleBarcode);
unawaited(_scannerController.start());
case AppLifecycleState.inactive:
unawaited(_subscription?.cancel());
_subscription = null;
unawaited(_scannerController.stop());
}
},
);
_scannerController.addListener(() {
if (_scannerController.value.torchState != _torchState.value) {
_torchState.value = _scannerController.value.torchState;
}
});
unawaited(_scannerController.start());
super.initState();
}
@override
void dispose() async {
unawaited(_subscription?.cancel());
_subscription = null;
_animationController.dispose();
_throttling.close();
_appLifecycleListener.dispose();
super.dispose();
await _scannerController.dispose();
}
void _handleBarcode(BarcodeCapture value) {
_throttling.throttle(() async {
final raw = value.barcodes.firstOrNull?.rawValue;
if (raw == null) return;
try {
if (widget.validDuration != null) {
final resBytes = base64Decode(raw);
final decrypted = await AesUtil.decryptWithTimeWindow(
encryptedData: resBytes,
validDuration: widget.validDuration!,
);
if (decrypted.isNullOrBlank) {
_handleInvalidQr();
return;
}
if (widget.prefix.isNotNullOrBlank) {
if (!decrypted!.startsWith(widget.prefix!)) {
_handleInvalidQr();
return;
}
final realData = decrypted.substring(widget.prefix!.length);
if (mounted) Navigator.pop(context, realData);
} else {
if (mounted) Navigator.pop(context, decrypted);
}
} else {
if (raw.isNullOrBlank) {
_handleInvalidQr();
} else {
if (mounted) Navigator.pop(context, raw);
}
}
} catch (e) {
logger.d(e);
_handleInvalidQr();
}
});
}
void _handleInvalidQr() {
if (mounted) {
toast.info(message: context.l10n.qrCodeInvalid);
}
}
@override
Widget build(BuildContext context) {
final scanWindow = Rect.fromCenter(
center: MediaQuery.sizeOf(context).center(Offset.zero),
width: 200,
height: 200,
);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
leading: BackButton(
color: Colors.white,
onPressed: () {
Navigator.pop(context);
},
),
),
extendBodyBehindAppBar: true,
body: Stack(
alignment: Alignment.center,
children: [
MobileScanner(
scanWindow: scanWindow,
controller: _scannerController,
overlayBuilder: (context, constraints) {
return AnimatedBuilder(
animation: _curvedAnimation,
builder: (context, child) {
return Stack(
children: [
CustomPaint(
size: constraints.biggest,
painter: _ScannerOverlayPainter(
centerSize: _curvedAnimation.value * 20 + 200,
),
),
Center(
child: CustomPaint(
size: Size.square(_curvedAnimation.value * 20 + 200),
painter: _CornerBorderPainter(
color: Colors.white,
strokeWidth: 4,
cornerLength: _curvedAnimation.value * 2 + 20,
radius: 12,
),
),
),
],
);
},
);
},
),
Positioned(
bottom: MediaQuery.sizeOf(context).height / 2 - 300,
child: ValueListenableBuilder(
valueListenable: _torchState,
builder: (context, value, child) {
if (value == TorchState.unavailable) {
return const SizedBox.shrink();
}
return IconButton(
onPressed: () async {
await _scannerController.toggleTorch();
},
iconSize: 56,
icon: switch (_torchState.value) {
TorchState.auto => const Icon(
Icons.flash_auto_rounded,
color: Colors.white,
),
TorchState.off => const Icon(
Icons.flashlight_off_rounded,
color: Colors.white54,
),
TorchState.on => const Icon(
Icons.flashlight_on_rounded,
color: Colors.white,
),
TorchState.unavailable => throw UnimplementedError(),
},
);
},
),
),
],
),
);
}
}
class _CornerBorderPainter extends CustomPainter {
final Color color;
final double strokeWidth;
final double cornerLength;
final double radius;
_CornerBorderPainter({
required this.color,
this.strokeWidth = 2,
this.cornerLength = 20,
this.radius = 0,
});
@override
void paint(Canvas canvas, Size size) {
final paint =
Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
final path = Path();
final double w = size.width;
final double h = size.height;
final r = radius;
path.moveTo(0, r + cornerLength);
path.lineTo(0, r);
path.quadraticBezierTo(0, 0, r, 0);
path.lineTo(r + cornerLength, 0);
path.moveTo(w - r - cornerLength, 0);
path.lineTo(w - r, 0);
path.quadraticBezierTo(w, 0, w, r);
path.lineTo(w, r + cornerLength);
path.moveTo(w, h - r - cornerLength);
path.lineTo(w, h - r);
path.quadraticBezierTo(w, h, w - r, h);
path.lineTo(w - r - cornerLength, h);
path.moveTo(r + cornerLength, h);
path.lineTo(r, h);
path.quadraticBezierTo(0, h, 0, h - r);
path.lineTo(0, h - r - cornerLength);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
class _ScannerOverlayPainter extends CustomPainter {
final double centerSize;
_ScannerOverlayPainter({required this.centerSize});
@override
void paint(Canvas canvas, Size size) {
final paint =
Paint()
..color = Colors.black54
..style = PaintingStyle.fill;
final outer = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
final scanSize = centerSize;
final left = (size.width - scanSize) / 2;
final top = (size.height - scanSize) / 2;
final scanRect = Rect.fromLTWH(left, top, scanSize, scanSize);
final hole = Path()..addRRect(RRect.fromRectXY(scanRect, 12, 12));
final overlay = Path.combine(PathOperation.difference, outer, hole);
canvas.drawPath(overlay, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -1,33 +1,40 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:mood_diary/common/values/border.dart';
import 'package:moodiary/common/values/border.dart';
class FloatingModal extends StatelessWidget {
final Widget child;
final bool isScrollControlled;
const FloatingModal(
{super.key, required this.child, required this.isScrollControlled});
const FloatingModal({
super.key,
required this.child,
required this.isScrollControlled,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final padding = MediaQuery.viewPaddingOf(context);
final padding = MediaQuery.viewInsetsOf(context);
final size = MediaQuery.sizeOf(context);
final maxHeight = isScrollControlled
? size.height - (padding.top - padding.bottom)
: size.height * 9.0 / 16.0;
final maxHeight =
isScrollControlled
? size.height - (padding.top - padding.bottom)
: size.height * 9.0 / 16.0;
return Align(
alignment: Alignment.bottomCenter,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: maxHeight, maxWidth: 640),
constraints: BoxConstraints(maxHeight: maxHeight+padding.bottom, maxWidth: 640),
child: Padding(
padding: EdgeInsets.only(
left: 16, right: 16, bottom: max(padding.bottom, 16)),
left: 16,
right: 16,
bottom: max(padding.bottom + 16, 16),
),
child: Material(
color: colorScheme.surfaceContainerHigh,
color: context.theme.colorScheme.surfaceContainerHigh,
clipBehavior: Clip.antiAlias,
borderRadius: AppBorderRadius.largeBorderRadius,
child: child,
@@ -44,14 +51,14 @@ Future<T> showFloatingModalBottomSheet<T>({
bool isScrollControlled = false,
}) async {
final result = await showCustomModalBottomSheet(
context: context,
builder: builder,
enableDrag: isScrollControlled,
containerWidget: (_, animation, child) => FloatingModal(
isScrollControlled: isScrollControlled,
child: child,
),
expand: false);
context: context,
builder: builder,
enableDrag: isScrollControlled,
containerWidget:
(_, animation, child) =>
FloatingModal(isScrollControlled: isScrollControlled, child: child),
expand: false,
);
return result;
}

View File

@@ -41,8 +41,8 @@ class Tab extends StatelessWidget implements PreferredSizeWidget {
this.iconMargin,
this.height,
this.child,
}) : assert(text != null || child != null || icon != null),
assert(text == null || child == null);
}) : assert(text != null || child != null || icon != null),
assert(text == null || child == null);
/// The text to display as the tab's label.
///
@@ -93,17 +93,15 @@ class Tab extends StatelessWidget implements PreferredSizeWidget {
label = icon!;
} else {
calculatedHeight = _kTextAndIconTabHeight;
final EdgeInsetsGeometry effectiveIconMargin = iconMargin ??
final EdgeInsetsGeometry effectiveIconMargin =
iconMargin ??
(Theme.of(context).useMaterial3
? _TabsPrimaryDefaultsM3.iconMargin
: _TabsDefaultsM2.iconMargin);
label = Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: effectiveIconMargin,
child: icon,
),
Padding(padding: effectiveIconMargin, child: icon),
_buildLabelText(),
],
);
@@ -111,10 +109,7 @@ class Tab extends StatelessWidget implements PreferredSizeWidget {
return SizedBox(
height: height ?? calculatedHeight,
child: Center(
widthFactor: 1.0,
child: label,
),
child: Center(widthFactor: 1.0, child: label),
);
}
@@ -166,7 +161,8 @@ class _TabStyle extends AnimatedWidget {
// labelStyle.color (and tabBarTheme.labelStyle.color) is not considered
// as it'll be a breaking change without a possible migration plan. for
// details: https://github.com/flutter/flutter/pull/109541#issuecomment-1294241417
Color selectedColor = labelColor ??
Color selectedColor =
labelColor ??
tabBarTheme.labelColor ??
labelStyle?.color ??
tabBarTheme.labelStyle?.color ??
@@ -176,12 +172,14 @@ class _TabStyle extends AnimatedWidget {
if (selectedColor is WidgetStateColor) {
unselectedColor = selectedColor.resolve(const <WidgetState>{});
selectedColor =
selectedColor.resolve(const <WidgetState>{WidgetState.selected});
selectedColor = selectedColor.resolve(const <WidgetState>{
WidgetState.selected,
});
} else {
// unselectedLabelColor and tabBarTheme.unselectedLabelColor are ignored
// when labelColor is a WidgetStateColor.
unselectedColor = unselectedLabelColor ??
unselectedColor =
unselectedLabelColor ??
tabBarTheme.unselectedLabelColor ??
unselectedLabelStyle?.color ??
tabBarTheme.unselectedLabelStyle?.color ??
@@ -207,31 +205,40 @@ class _TabStyle extends AnimatedWidget {
final TabBarThemeData tabBarTheme = TabBarTheme.of(context);
final Animation<double> animation = listenable as Animation<double>;
final Set<WidgetState> states = isSelected
? const <WidgetState>{WidgetState.selected}
: const <WidgetState>{};
final Set<WidgetState> states =
isSelected
? const <WidgetState>{WidgetState.selected}
: const <WidgetState>{};
// Ensure both styles have inherit=true for proper lerp behavior.
final TextStyle selectedStyle =
(labelStyle ?? tabBarTheme.labelStyle ?? defaults.labelStyle!)
.copyWith(inherit: true);
final TextStyle selectedStyle = (labelStyle ??
tabBarTheme.labelStyle ??
defaults.labelStyle!)
.copyWith(inherit: true);
final TextStyle unselectedStyle = (unselectedLabelStyle ??
tabBarTheme.unselectedLabelStyle ??
labelStyle ??
defaults.unselectedLabelStyle!)
.copyWith(inherit: true);
final TextStyle textStyle = isSelected
? TextStyle.lerp(selectedStyle, unselectedStyle, animation.value)!
: TextStyle.lerp(unselectedStyle, selectedStyle, animation.value)!;
final TextStyle textStyle =
isSelected
? TextStyle.lerp(selectedStyle, unselectedStyle, animation.value)!
: TextStyle.lerp(unselectedStyle, selectedStyle, animation.value)!;
final Color color = _resolveWithLabelColor(context).resolve(states);
// Calculate adjusted height based on isSelected state.
double adjustedHeight(
double fontSize, double? currentHeight, bool isSelected) {
double effectiveHeight =
double fontSize,
double? currentHeight,
bool isSelected,
) {
final double effectiveHeight =
currentHeight ?? 1.0; // Default height is 1.0 if null.
double actualLineHeight = fixPrecision(fontSize * effectiveHeight, 6);
final double actualLineHeight = fixPrecision(
fontSize * effectiveHeight,
6,
);
double adjustedLineHeight;
if (isSelected) {
// Use ceil for selected state to round up.
@@ -259,18 +266,19 @@ class _TabStyle extends AnimatedWidget {
height: adjustedHeightValue, // Use adjusted height.
),
child: IconTheme.merge(
data: IconThemeData(
size: 24.0,
color: color,
),
data: IconThemeData(size: 24.0, color: color),
child: child,
),
);
}
}
typedef _LayoutCallback = void Function(
List<double> xOffsets, TextDirection textDirection, double width);
typedef _LayoutCallback =
void Function(
List<double> xOffsets,
TextDirection textDirection,
double width,
);
class _TabLabelBarRenderer extends RenderFlex {
_TabLabelBarRenderer({
@@ -321,11 +329,11 @@ class _TabLabelBar extends Flex {
required this.onPerformLayout,
required super.mainAxisSize,
}) : super(
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
verticalDirection: VerticalDirection.down,
);
direction: Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
verticalDirection: VerticalDirection.down,
);
final _LayoutCallback onPerformLayout;
@@ -344,7 +352,9 @@ class _TabLabelBar extends Flex {
@override
void updateRenderObject(
BuildContext context, _TabLabelBarRenderer renderObject) {
BuildContext context,
_TabLabelBarRenderer renderObject,
) {
super.updateRenderObject(context, renderObject);
renderObject.onPerformLayout = onPerformLayout;
}
@@ -367,10 +377,7 @@ double _indexChangeProgress(TabController controller) {
}
class _DividerPainter extends CustomPainter {
_DividerPainter({
required this.dividerColor,
required this.dividerHeight,
});
_DividerPainter({required this.dividerColor, required this.dividerHeight});
final Color dividerColor;
final double dividerHeight;
@@ -381,9 +388,10 @@ class _DividerPainter extends CustomPainter {
return;
}
final Paint paint = Paint()
..color = dividerColor
..strokeWidth = dividerHeight;
final Paint paint =
Paint()
..color = dividerColor
..strokeWidth = dividerHeight;
canvas.drawLine(
Offset(0, size.height - (paint.strokeWidth / 2)),
@@ -488,13 +496,13 @@ class _IndicatorPainter extends CustomPainter {
double tabLeft, tabRight;
(tabLeft, tabRight) = switch (_currentTextDirection!) {
TextDirection.rtl => (
_currentTabOffsets![tabIndex + 1],
_currentTabOffsets![tabIndex]
),
_currentTabOffsets![tabIndex + 1],
_currentTabOffsets![tabIndex],
),
TextDirection.ltr => (
_currentTabOffsets![tabIndex],
_currentTabOffsets![tabIndex + 1]
),
_currentTabOffsets![tabIndex],
_currentTabOffsets![tabIndex + 1],
),
};
if (indicatorSize == TabBarIndicatorSize.label) {
@@ -508,8 +516,12 @@ class _IndicatorPainter extends CustomPainter {
}
final EdgeInsets insets = indicatorPadding.resolve(_currentTextDirection);
final Rect rect =
Rect.fromLTWH(tabLeft, 0.0, tabRight - tabLeft, tabBarSize.height);
final Rect rect = Rect.fromLTWH(
tabLeft,
0.0,
tabRight - tabLeft,
tabBarSize.height,
);
if (!(rect.size >= insets.collapsedSize)) {
throw FlutterError(
@@ -536,8 +548,10 @@ class _IndicatorPainter extends CustomPainter {
_currentRect = switch (indicatorAnimation) {
TabIndicatorAnimation.linear => _currentRect,
TabIndicatorAnimation.elastic =>
_applyElasticEffect(_currentRect!, fromRect),
TabIndicatorAnimation.elastic => _applyElasticEffect(
_currentRect!,
fromRect,
),
};
assert(_currentRect != null);
@@ -548,13 +562,18 @@ class _IndicatorPainter extends CustomPainter {
devicePixelRatio: devicePixelRatio,
);
if (showDivider && dividerHeight! > 0) {
final Paint dividerPaint = Paint()
..color = dividerColor!
..strokeWidth = dividerHeight!;
final Offset dividerP1 =
Offset(0, size.height - (dividerPaint.strokeWidth / 2));
final Offset dividerP2 =
Offset(size.width, size.height - (dividerPaint.strokeWidth / 2));
final Paint dividerPaint =
Paint()
..color = dividerColor!
..strokeWidth = dividerHeight!;
final Offset dividerP1 = Offset(
0,
size.height - (dividerPaint.strokeWidth / 2),
);
final Offset dividerP2 = Offset(
size.width,
size.height - (dividerPaint.strokeWidth / 2),
);
canvas.drawLine(dividerP1, dividerP2, dividerPaint);
}
_painter!.paint(canvas, _currentRect!.topLeft, configuration);
@@ -593,8 +612,11 @@ class _IndicatorPainter extends CustomPainter {
false => decelerateInterpolation(tabChangeProgress),
};
final Rect stretchedRect =
_inflateRectHorizontally(rect, targetRect, fraction);
final Rect stretchedRect = _inflateRectHorizontally(
rect,
targetRect,
fraction,
);
return stretchedRect;
}
@@ -674,8 +696,11 @@ class _DragAnimation extends Animation<double>
double get value {
assert(!controller.indexIsChanging);
final double controllerMaxValue = (controller.length - 1).toDouble();
final double controllerValue =
clampDouble(controller.animation!.value, 0.0, controllerMaxValue);
final double controllerValue = clampDouble(
controller.animation!.value,
0.0,
controllerMaxValue,
);
return clampDouble((controllerValue - index.toDouble()).abs(), 0.0, 1.0);
}
}
@@ -690,9 +715,7 @@ class _TabBarScrollPosition extends ScrollPositionWithSingleContext {
required super.context,
required super.oldPosition,
required this.tabBar,
}) : super(
initialPixels: null,
);
}) : super(initialPixels: null);
final _TabBarState tabBar;
@@ -715,8 +738,13 @@ class _TabBarScrollPosition extends ScrollPositionWithSingleContext {
// guard because the super call below would start a ballistic scroll activity.
if (!_viewportDimensionWasNonZero || _needsPixelsCorrection) {
_needsPixelsCorrection = false;
correctPixels(tabBar._initialScrollOffset(
viewportDimension, minScrollExtent, maxScrollExtent));
correctPixels(
tabBar._initialScrollOffset(
viewportDimension,
minScrollExtent,
maxScrollExtent,
),
);
result = false;
}
return super.applyContentDimensions(minScrollExtent, maxScrollExtent) &&
@@ -736,8 +764,11 @@ class _TabBarScrollController extends ScrollController {
final _TabBarState tabBar;
@override
ScrollPosition createScrollPosition(ScrollPhysics physics,
ScrollContext context, ScrollPosition? oldPosition) {
ScrollPosition createScrollPosition(
ScrollPhysics physics,
ScrollContext context,
ScrollPosition? oldPosition,
) {
return _TabBarScrollPosition(
physics: physics,
context: context,
@@ -841,8 +872,8 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.tabAlignment,
this.textScaler,
this.indicatorAnimation,
}) : _isPrimary = true,
assert(indicator != null || (indicatorWeight > 0.0));
}) : _isPrimary = true,
assert(indicator != null || (indicatorWeight > 0.0));
/// Creates a Material Design secondary tab bar.
///
@@ -894,8 +925,8 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.tabAlignment,
this.textScaler,
this.indicatorAnimation,
}) : _isPrimary = false,
assert(indicator != null || (indicatorWeight > 0.0));
}) : _isPrimary = false,
assert(indicator != null || (indicatorWeight > 0.0));
/// Typically a list of two or more [Tab] widgets.
///
@@ -1315,8 +1346,10 @@ class _TabBarState extends State<TabBar> {
// the width of tab widget i. See _IndicatorPainter.indicatorRect().
_tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();
_labelPaddings = List<EdgeInsetsGeometry>.filled(
widget.tabs.length, EdgeInsets.zero,
growable: true);
widget.tabs.length,
EdgeInsets.zero,
growable: true,
);
}
TabBarThemeData get _defaults {
@@ -1340,7 +1373,8 @@ class _TabBarState extends State<TabBar> {
return tabBarTheme.indicator!;
}
Color color = widget.indicatorColor ??
Color color =
widget.indicatorColor ??
tabBarTheme.indicatorColor ??
_defaults.indicatorColor!;
// ThemeData tries to avoid this by having indicatorColor avoid being the
@@ -1362,15 +1396,13 @@ class _TabBarState extends State<TabBar> {
color = Colors.white;
}
final double effectiveIndicatorWeight = theme.useMaterial3
? math.max(
widget.indicatorWeight,
switch (widget._isPrimary) {
final double effectiveIndicatorWeight =
theme.useMaterial3
? math.max(widget.indicatorWeight, switch (widget._isPrimary) {
true => _TabsPrimaryDefaultsM3.indicatorWeight(indicatorSize),
false => _TabsSecondaryDefaultsM3.indicatorWeight,
},
)
: widget.indicatorWeight;
})
: widget.indicatorWeight;
// Only Material 3 primary TabBar with label indicatorSize should be rounded.
final bool primaryWithLabelIndicator = switch (indicatorSize) {
TabBarIndicatorSize.label => widget._isPrimary,
@@ -1379,9 +1411,9 @@ class _TabBarState extends State<TabBar> {
final BorderRadius? effectiveBorderRadius =
theme.useMaterial3 && primaryWithLabelIndicator
? BorderRadius.only(
topLeft: Radius.circular(effectiveIndicatorWeight),
topRight: Radius.circular(effectiveIndicatorWeight),
)
topLeft: Radius.circular(effectiveIndicatorWeight),
topRight: Radius.circular(effectiveIndicatorWeight),
)
: null;
return UnderlineTabIndicator(
borderRadius: effectiveBorderRadius,
@@ -1436,7 +1468,8 @@ class _TabBarState extends State<TabBar> {
void _initIndicatorPainter() {
final ThemeData theme = Theme.of(context);
final TabBarThemeData tabBarTheme = TabBarTheme.of(context);
final TabBarIndicatorSize indicatorSize = widget.indicatorSize ??
final TabBarIndicatorSize indicatorSize =
widget.indicatorSize ??
tabBarTheme.indicatorSize ??
_defaults.indicatorSize!;
@@ -1444,33 +1477,37 @@ class _TabBarState extends State<TabBar> {
final TabIndicatorAnimation defaultTabIndicatorAnimation =
switch (indicatorSize) {
TabBarIndicatorSize.label => TabIndicatorAnimation.elastic,
TabBarIndicatorSize.tab => TabIndicatorAnimation.linear,
};
TabBarIndicatorSize.label => TabIndicatorAnimation.elastic,
TabBarIndicatorSize.tab => TabIndicatorAnimation.linear,
};
_indicatorPainter = !_controllerIsValid
? null
: _IndicatorPainter(
controller: _controller!,
indicator: _getIndicator(indicatorSize),
indicatorSize: indicatorSize,
indicatorPadding: widget.indicatorPadding,
tabKeys: _tabKeys,
// Passing old painter so that the constructor can copy some values from it.
old: oldPainter,
labelPaddings: _labelPaddings,
dividerColor: widget.dividerColor ??
tabBarTheme.dividerColor ??
_defaults.dividerColor,
dividerHeight: widget.dividerHeight ??
tabBarTheme.dividerHeight ??
_defaults.dividerHeight,
showDivider: theme.useMaterial3 && !widget.isScrollable,
devicePixelRatio: MediaQuery.devicePixelRatioOf(context),
indicatorAnimation: widget.indicatorAnimation ??
tabBarTheme.indicatorAnimation ??
defaultTabIndicatorAnimation,
);
_indicatorPainter =
!_controllerIsValid
? null
: _IndicatorPainter(
controller: _controller!,
indicator: _getIndicator(indicatorSize),
indicatorSize: indicatorSize,
indicatorPadding: widget.indicatorPadding,
tabKeys: _tabKeys,
// Passing old painter so that the constructor can copy some values from it.
old: oldPainter,
labelPaddings: _labelPaddings,
dividerColor:
widget.dividerColor ??
tabBarTheme.dividerColor ??
_defaults.dividerColor,
dividerHeight:
widget.dividerHeight ??
tabBarTheme.dividerHeight ??
_defaults.dividerHeight,
showDivider: theme.useMaterial3 && !widget.isScrollable,
devicePixelRatio: MediaQuery.devicePixelRatioOf(context),
indicatorAnimation:
widget.indicatorAnimation ??
tabBarTheme.indicatorAnimation ??
defaultTabIndicatorAnimation,
);
oldPainter?.dispose();
}
@@ -1510,8 +1547,9 @@ class _TabBarState extends State<TabBar> {
if (widget.tabs.length > _tabKeys.length) {
final int delta = widget.tabs.length - _tabKeys.length;
_tabKeys.addAll(List<GlobalKey>.generate(delta, (int n) => GlobalKey()));
_labelPaddings
.addAll(List<EdgeInsetsGeometry>.filled(delta, EdgeInsets.zero));
_labelPaddings.addAll(
List<EdgeInsetsGeometry>.filled(delta, EdgeInsets.zero),
);
} else if (widget.tabs.length < _tabKeys.length) {
_tabKeys.removeRange(widget.tabs.length, _tabKeys.length);
_labelPaddings.removeRange(widget.tabs.length, _tabKeys.length);
@@ -1534,7 +1572,11 @@ class _TabBarState extends State<TabBar> {
int get maxTabIndex => _indicatorPainter!.maxTabIndex;
double _tabScrollOffset(
int index, double viewportWidth, double minExtent, double maxExtent) {
int index,
double viewportWidth,
double minExtent,
double maxExtent,
) {
if (!widget.isScrollable) {
return 0.0;
}
@@ -1549,35 +1591,54 @@ class _TabBarState extends State<TabBar> {
}
return clampDouble(
tabCenter + paddingStart - viewportWidth / 2.0, minExtent, maxExtent);
tabCenter + paddingStart - viewportWidth / 2.0,
minExtent,
maxExtent,
);
}
double _tabCenteredScrollOffset(int index) {
final ScrollPosition position = _scrollController!.position;
return _tabScrollOffset(index, position.viewportDimension,
position.minScrollExtent, position.maxScrollExtent);
return _tabScrollOffset(
index,
position.viewportDimension,
position.minScrollExtent,
position.maxScrollExtent,
);
}
double _initialScrollOffset(
double viewportWidth, double minExtent, double maxExtent) {
double viewportWidth,
double minExtent,
double maxExtent,
) {
return _tabScrollOffset(
_currentIndex!, viewportWidth, minExtent, maxExtent);
_currentIndex!,
viewportWidth,
minExtent,
maxExtent,
);
}
void _scrollToCurrentIndex() {
final double offset = _tabCenteredScrollOffset(_currentIndex!);
_scrollController!
.animateTo(offset, duration: kTabScrollDuration, curve: Curves.ease);
_scrollController!.animateTo(
offset,
duration: kTabScrollDuration,
curve: Curves.ease,
);
}
void _scrollToControllerValue() {
final double? leadingPosition = _currentIndex! > 0
? _tabCenteredScrollOffset(_currentIndex! - 1)
: null;
final double? leadingPosition =
_currentIndex! > 0
? _tabCenteredScrollOffset(_currentIndex! - 1)
: null;
final double middlePosition = _tabCenteredScrollOffset(_currentIndex!);
final double? trailingPosition = _currentIndex! < maxTabIndex
? _tabCenteredScrollOffset(_currentIndex! + 1)
: null;
final double? trailingPosition =
_currentIndex! < maxTabIndex
? _tabCenteredScrollOffset(_currentIndex! + 1)
: null;
final double index = _controller!.index.toDouble();
final double value = _controller!.animation!.value;
@@ -1585,12 +1646,14 @@ class _TabBarState extends State<TabBar> {
-1.0 => leadingPosition ?? middlePosition,
1.0 => trailingPosition ?? middlePosition,
0 => middlePosition,
< 0 => leadingPosition == null
? middlePosition
: lerpDouble(middlePosition, leadingPosition, index - value)!,
_ => trailingPosition == null
? middlePosition
: lerpDouble(middlePosition, trailingPosition, value - index)!,
< 0 =>
leadingPosition == null
? middlePosition
: lerpDouble(middlePosition, leadingPosition, index - value)!,
_ =>
trailingPosition == null
? middlePosition
: lerpDouble(middlePosition, trailingPosition, value - index)!,
};
_scrollController!.jumpTo(offset);
@@ -1620,7 +1683,10 @@ class _TabBarState extends State<TabBar> {
// Called each time layout completes.
void _saveTabOffsets(
List<double> tabOffsets, TextDirection textDirection, double width) {
List<double> tabOffsets,
TextDirection textDirection,
double width,
) {
_tabStripWidth = width;
_indicatorPainter?.saveTabOffsets(tabOffsets, textDirection);
}
@@ -1631,8 +1697,12 @@ class _TabBarState extends State<TabBar> {
widget.onTap?.call(index);
}
Widget _buildStyledTab(Widget child, bool isSelected,
Animation<double> animation, TabBarThemeData defaults) {
Widget _buildStyledTab(
Widget child,
bool isSelected,
Animation<double> animation,
TabBarThemeData defaults,
) {
return _TabStyle(
animation: animation,
isSelected: isSelected,
@@ -1694,24 +1764,28 @@ class _TabBarState extends State<TabBar> {
assert(_debugScheduleCheckHasValidTabsCount());
final ThemeData theme = Theme.of(context);
final TabBarThemeData tabBarTheme = TabBarTheme.of(context);
final TabAlignment effectiveTabAlignment = widget.tabAlignment ??
final TabAlignment effectiveTabAlignment =
widget.tabAlignment ??
tabBarTheme.tabAlignment ??
_defaults.tabAlignment!;
assert(_debugTabAlignmentIsValid(effectiveTabAlignment));
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final MaterialLocalizations localizations = MaterialLocalizations.of(
context,
);
if (_controller!.length == 0) {
return LimitedBox(
maxWidth: 0.0,
child: SizedBox(
width: double.infinity,
height: _kTabHeight + widget.indicatorWeight),
width: double.infinity,
height: _kTabHeight + widget.indicatorWeight,
),
);
}
final List<Widget> wrappedTabs =
List<Widget>.generate(widget.tabs.length, (int index) {
final List<Widget> wrappedTabs = List<Widget>.generate(widget.tabs.length, (
int index,
) {
EdgeInsetsGeometry padding =
widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding;
const double verticalAdjustment =
@@ -1721,8 +1795,9 @@ class _TabBarState extends State<TabBar> {
if (tab is PreferredSizeWidget &&
tab.preferredSize.height == _kTabHeight &&
widget.tabHasTextAndIcon) {
padding = padding
.add(const EdgeInsets.symmetric(vertical: verticalAdjustment));
padding = padding.add(
const EdgeInsets.symmetric(vertical: verticalAdjustment),
);
}
_labelPaddings[index] = padding;
@@ -1730,10 +1805,7 @@ class _TabBarState extends State<TabBar> {
heightFactor: 1.0,
child: Padding(
padding: _labelPaddings[index],
child: KeyedSubtree(
key: _tabKeys[index],
child: widget.tabs[index],
),
child: KeyedSubtree(key: _tabKeys[index], child: widget.tabs[index]),
),
);
});
@@ -1764,8 +1836,10 @@ class _TabBarState extends State<TabBar> {
// The user is dragging the TabBarView's PageView left or right.
if (_currentIndex! < widget.tabs.length) {
final int tabIndex = _currentIndex!;
final Animation<double> centerAnimation =
_DragAnimation(_controller!, tabIndex);
final Animation<double> centerAnimation = _DragAnimation(
_controller!,
tabIndex,
);
wrappedTabs[tabIndex] = _buildStyledTab(
wrappedTabs[tabIndex],
true,
@@ -1775,8 +1849,9 @@ class _TabBarState extends State<TabBar> {
}
if (_currentIndex! > 0) {
final int tabIndex = _currentIndex! - 1;
final Animation<double> previousAnimation =
ReverseAnimation(_DragAnimation(_controller!, tabIndex));
final Animation<double> previousAnimation = ReverseAnimation(
_DragAnimation(_controller!, tabIndex),
);
wrappedTabs[tabIndex] = _buildStyledTab(
wrappedTabs[tabIndex],
false,
@@ -1786,8 +1861,9 @@ class _TabBarState extends State<TabBar> {
}
if (_currentIndex! < widget.tabs.length - 1) {
final int tabIndex = _currentIndex! + 1;
final Animation<double> nextAnimation =
ReverseAnimation(_DragAnimation(_controller!, tabIndex));
final Animation<double> nextAnimation = ReverseAnimation(
_DragAnimation(_controller!, tabIndex),
);
wrappedTabs[tabIndex] = _buildStyledTab(
wrappedTabs[tabIndex],
false,
@@ -1809,18 +1885,18 @@ class _TabBarState extends State<TabBar> {
final MouseCursor effectiveMouseCursor =
WidgetStateProperty.resolveAs<MouseCursor?>(
widget.mouseCursor, selectedState) ??
tabBarTheme.mouseCursor?.resolve(selectedState) ??
WidgetStateMouseCursor.clickable.resolve(selectedState);
widget.mouseCursor,
selectedState,
) ??
tabBarTheme.mouseCursor?.resolve(selectedState) ??
WidgetStateMouseCursor.clickable.resolve(selectedState);
final WidgetStateProperty<Color?> defaultOverlay =
WidgetStateProperty.resolveWith<Color?>(
(Set<WidgetState> states) {
final Set<WidgetState> effectiveStates = selectedState
..addAll(states);
return _defaults.overlayColor?.resolve(effectiveStates);
},
);
WidgetStateProperty.resolveWith<Color?>((Set<WidgetState> states) {
final Set<WidgetState> effectiveStates =
selectedState..addAll(states);
return _defaults.overlayColor?.resolve(effectiveStates);
});
wrappedTabs[index] = InkWell(
mouseCursor: effectiveMouseCursor,
onTap: () {
@@ -1829,7 +1905,8 @@ class _TabBarState extends State<TabBar> {
enableFeedback: widget.enableFeedback ?? true,
overlayColor:
widget.overlayColor ?? tabBarTheme.overlayColor ?? defaultOverlay,
splashFactory: widget.splashFactory ??
splashFactory:
widget.splashFactory ??
tabBarTheme.splashFactory ??
_defaults.splashFactory,
borderRadius: widget.splashBorderRadius,
@@ -1841,7 +1918,9 @@ class _TabBarState extends State<TabBar> {
Semantics(
selected: index == _currentIndex,
label: localizations.tabLabel(
tabIndex: index + 1, tabCount: tabCount),
tabIndex: index + 1,
tabCount: tabCount,
),
),
],
),
@@ -1865,9 +1944,10 @@ class _TabBarState extends State<TabBar> {
defaults: _defaults,
child: _TabLabelBar(
onPerformLayout: _saveTabOffsets,
mainAxisSize: effectiveTabAlignment == TabAlignment.fill
? MainAxisSize.max
: MainAxisSize.min,
mainAxisSize:
effectiveTabAlignment == TabAlignment.fill
? MainAxisSize.max
: MainAxisSize.min,
children: wrappedTabs,
),
),
@@ -1876,8 +1956,9 @@ class _TabBarState extends State<TabBar> {
if (widget.isScrollable) {
final EdgeInsetsGeometry? effectivePadding =
effectiveTabAlignment == TabAlignment.startOffset
? const EdgeInsetsDirectional.only(start: _kStartOffset)
.add(widget.padding ?? EdgeInsets.zero)
? const EdgeInsetsDirectional.only(
start: _kStartOffset,
).add(widget.padding ?? EdgeInsets.zero)
: widget.padding;
_scrollController ??= _TabBarScrollController(this);
tabBar = ScrollConfiguration(
@@ -1895,17 +1976,18 @@ class _TabBarState extends State<TabBar> {
if (theme.useMaterial3) {
final AlignmentGeometry effectiveAlignment =
switch (effectiveTabAlignment) {
TabAlignment.center => Alignment.center,
TabAlignment.start ||
TabAlignment.startOffset ||
TabAlignment.fill =>
AlignmentDirectional.centerStart,
};
TabAlignment.center => Alignment.center,
TabAlignment.start ||
TabAlignment.startOffset ||
TabAlignment.fill => AlignmentDirectional.centerStart,
};
final Color dividerColor = widget.dividerColor ??
final Color dividerColor =
widget.dividerColor ??
tabBarTheme.dividerColor ??
_defaults.dividerColor!;
final double dividerHeight = widget.dividerHeight ??
final double dividerHeight =
widget.dividerHeight ??
tabBarTheme.dividerHeight ??
_defaults.dividerHeight!;
@@ -1927,15 +2009,13 @@ class _TabBarState extends State<TabBar> {
}
}
} else if (widget.padding != null) {
tabBar = Padding(
padding: widget.padding!,
child: tabBar,
);
tabBar = Padding(padding: widget.padding!, child: tabBar);
}
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(textScaler: widget.textScaler ?? tabBarTheme.textScaler),
data: MediaQuery.of(
context,
).copyWith(textScaler: widget.textScaler ?? tabBarTheme.textScaler),
child: tabBar,
);
}
@@ -2062,8 +2142,11 @@ class _TabBarViewState extends State<TabBarView> {
required Curve curve,
}) async {
_warpUnderwayCount += 1;
await _pageController!
.animateToPage(page, duration: duration, curve: curve);
await _pageController!.animateToPage(
page,
duration: duration,
curve: curve,
);
_warpUnderwayCount -= 1;
}
@@ -2154,8 +2237,11 @@ class _TabBarViewState extends State<TabBarView> {
if (duration == Duration.zero) {
_jumpToPage(_currentIndex!);
} else {
await _animateToPage(_currentIndex!,
duration: duration, curve: Curves.ease);
await _animateToPage(
_currentIndex!,
duration: duration,
curve: Curves.ease,
);
}
if (mounted) {
setState(() {
@@ -2171,9 +2257,10 @@ class _TabBarViewState extends State<TabBarView> {
// initialPage defines which page is shown when starting the animation.
// This page is adjacent to the destination page.
final int initialPage = _currentIndex! > previousIndex
? _currentIndex! - 1
: _currentIndex! + 1;
final int initialPage =
_currentIndex! > previousIndex
? _currentIndex! - 1
: _currentIndex! + 1;
setState(() {
// Needed for `RenderSliverMultiBoxAdaptor.move` and kept alive children.
@@ -2192,8 +2279,11 @@ class _TabBarViewState extends State<TabBarView> {
if (duration == Duration.zero) {
_jumpToPage(_currentIndex!);
} else {
await _animateToPage(_currentIndex!,
duration: duration, curve: Curves.ease);
await _animateToPage(
_currentIndex!,
duration: duration,
curve: Curves.ease,
);
}
if (mounted) {
@@ -2204,8 +2294,11 @@ class _TabBarViewState extends State<TabBarView> {
}
void _syncControllerOffset() {
_controller!.offset =
clampDouble(_pageController!.page! - _controller!.index, -1.0, 1.0);
_controller!.offset = clampDouble(
_pageController!.page! - _controller!.index,
-1.0,
1.0,
);
}
// Called when the PageView scrolls
@@ -2277,9 +2370,12 @@ class _TabBarViewState extends State<TabBarView> {
dragStartBehavior: widget.dragStartBehavior,
clipBehavior: widget.clipBehavior,
controller: _pageController,
physics: widget.physics == null
? const PageScrollPhysics().applyTo(const ClampingScrollPhysics())
: const PageScrollPhysics().applyTo(widget.physics),
physics:
widget.physics == null
? const PageScrollPhysics().applyTo(
const ClampingScrollPhysics(),
)
: const PageScrollPhysics().applyTo(widget.physics),
children: _childrenWithKey,
),
);
@@ -2484,26 +2580,36 @@ class _TabPageSelectorState extends State<TabPageSelector> {
final Color fixColor = widget.color ?? Colors.transparent;
final Color fixSelectedColor =
widget.selectedColor ?? Theme.of(context).colorScheme.secondary;
final ColorTween selectedColorTween =
ColorTween(begin: fixColor, end: fixSelectedColor);
final ColorTween previousColorTween =
ColorTween(begin: fixSelectedColor, end: fixColor);
final MaterialLocalizations localizations =
MaterialLocalizations.of(context);
final ColorTween selectedColorTween = ColorTween(
begin: fixColor,
end: fixSelectedColor,
);
final ColorTween previousColorTween = ColorTween(
begin: fixSelectedColor,
end: fixColor,
);
final MaterialLocalizations localizations = MaterialLocalizations.of(
context,
);
return AnimatedBuilder(
animation: _animation!,
builder: (BuildContext context, Widget? child) {
return Semantics(
label: localizations.tabLabel(
tabIndex: _tabController.index + 1,
tabCount: _tabController.length),
tabIndex: _tabController.index + 1,
tabCount: _tabController.length,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children:
List<Widget>.generate(_tabController.length, (int tabIndex) {
return _buildTabIndicator(tabIndex, _tabController,
selectedColorTween, previousColorTween);
}).toList(),
return _buildTabIndicator(
tabIndex,
_tabController,
selectedColorTween,
previousColorTween,
);
}).toList(),
),
);
},
@@ -2514,7 +2620,7 @@ class _TabPageSelectorState extends State<TabPageSelector> {
// Hand coded defaults based on Material Design 2.
class _TabsDefaultsM2 extends TabBarThemeData {
const _TabsDefaultsM2(this.context, this.isScrollable)
: super(indicatorSize: TabBarIndicatorSize.tab);
: super(indicatorSize: TabBarIndicatorSize.tab);
final BuildContext context;
final bool isScrollable;
@@ -2552,7 +2658,7 @@ class _TabsDefaultsM2 extends TabBarThemeData {
class _TabsPrimaryDefaultsM3 extends TabBarThemeData {
_TabsPrimaryDefaultsM3(this.context, this.isScrollable)
: super(indicatorSize: TabBarIndicatorSize.label);
: super(indicatorSize: TabBarIndicatorSize.label);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@@ -2633,7 +2739,7 @@ class _TabsPrimaryDefaultsM3 extends TabBarThemeData {
class _TabsSecondaryDefaultsM3 extends TabBarThemeData {
_TabsSecondaryDefaultsM3(this.context, this.isScrollable)
: super(indicatorSize: TabBarIndicatorSize.tab);
: super(indicatorSize: TabBarIndicatorSize.tab);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;

View File

@@ -0,0 +1,283 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:moodiary/components/base/marquee.dart';
import 'package:moodiary/utils/lru.dart';
class AdaptiveText extends StatelessWidget {
final String text;
final TextStyle? style;
final double? maxWidth;
final bool? isTileTitle;
final bool? isTileSubtitle;
final bool? isPrimaryTitle;
final bool? isTitle;
const AdaptiveText(
this.text, {
super.key,
this.style,
this.maxWidth,
this.isTileTitle,
this.isTileSubtitle,
this.isPrimaryTitle,
this.isTitle,
});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
final textScaler = MediaQuery.textScalerOf(context);
var textStyle = style;
if (isTileTitle == true) {
textStyle = textTheme.bodyLarge?.copyWith(
color: context.theme.colorScheme.onSurface,
);
}
if (isTileSubtitle == true) {
textStyle = textTheme.bodyMedium?.copyWith(
color: context.theme.colorScheme.onSurfaceVariant,
);
}
if (isTitle == true) {
textStyle = textTheme.titleLarge;
}
if (isPrimaryTitle == true) {
textStyle = textTheme.titleLarge?.copyWith(
color: context.theme.colorScheme.primary,
);
}
return LayoutBuilder(
builder: (context, constraints) {
final textPainter = TextPainter(
text: TextSpan(text: text, style: textStyle),
textDirection: TextDirection.ltr,
maxLines: 1,
textScaler: textScaler,
)..layout(maxWidth: maxWidth ?? constraints.maxWidth);
return textPainter.didExceedMaxLines
? SizedBox(
height: textPainter.height,
width: maxWidth ?? constraints.maxWidth,
child: Marquee(
text: text,
velocity: 20,
blankSpace: 20,
textScaler: textScaler,
pauseAfterRound: const Duration(seconds: 1),
accelerationDuration: const Duration(seconds: 1),
accelerationCurve: Curves.linear,
decelerationDuration: const Duration(milliseconds: 300),
decelerationCurve: Curves.easeOut,
style: textStyle,
),
)
: Text(
text,
style: textStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
},
);
}
}
class EllipsisText extends StatelessWidget {
final String text;
final TextStyle? style;
final String ellipsis;
final int? maxLines;
static final _cache = LRUCache<int, String>(maxSize: 1000);
/// 当字体相关属性发生变化时,需要清空缓存
static void clearCache() {
_cache.clear();
}
const EllipsisText(
this.text, {
super.key,
this.style,
this.ellipsis = '...',
this.maxLines,
});
int _getCacheKey(
String text,
String ellipsis,
double maxWidth,
int? maxLines,
TextStyle? style,
TextScaler textScaler,
) {
return Object.hash(
text,
ellipsis,
maxWidth.toStringAsFixed(2),
maxLines,
style,
textScaler,
);
}
double _calculateTextWidth(String text, TextScaler textScaler) {
final span = TextSpan(text: text.fixAutoLines(), style: style);
final tp = TextPainter(
text: span,
maxLines: 1,
textDirection: TextDirection.ltr,
textScaler: textScaler,
)..layout();
return tp.width;
}
TextPainter _createTextPainter(
String displayText,
double maxWidth,
TextScaler textScaler,
) {
return TextPainter(
text: TextSpan(text: displayText.fixAutoLines(), style: style),
maxLines: maxLines,
textDirection: TextDirection.ltr,
textScaler: textScaler,
)..layout(maxWidth: maxWidth);
}
@override
Widget build(BuildContext context) {
final textScaler = MediaQuery.textScalerOf(context);
return LayoutBuilder(
builder: (context, constraints) {
final cacheKey = _getCacheKey(
text,
ellipsis,
constraints.maxWidth,
maxLines,
style,
textScaler,
);
if (text.isEmpty) {
return const SizedBox.shrink();
}
final cachedResult = _cache.get(cacheKey);
if (cachedResult != null) {
return Text(
cachedResult,
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
style: style,
textScaler: textScaler,
);
}
if (!_createTextPainter(
text,
constraints.maxWidth,
textScaler,
).didExceedMaxLines) {
_cache.put(cacheKey, text.fixAutoLines());
return Text(
text.fixAutoLines(),
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
style: style,
textScaler: textScaler,
);
}
int leftIndex = 0;
int rightIndex = text.characters.length;
double leftWidth = 0;
double rightWidth = 0;
int lastValidLeftIndex = 0;
int lastValidRightIndex = text.characters.length;
while (leftIndex < rightIndex) {
final nextLeftWidth =
_calculateTextWidth(
text.characters.elementAt(leftIndex),
textScaler,
) +
leftWidth;
final nextRightWidth =
_calculateTextWidth(
text.characters.elementAt(rightIndex - 1),
textScaler,
) +
rightWidth;
final currentText =
'${text.charSubstring(0, leftIndex)}$ellipsis${text.charSubstring(rightIndex)}';
if (_createTextPainter(
currentText,
constraints.maxWidth,
textScaler,
).didExceedMaxLines) {
break;
} else {
lastValidLeftIndex = leftIndex;
lastValidRightIndex = rightIndex;
if (leftWidth <= rightWidth) {
leftWidth = nextLeftWidth;
leftIndex++;
} else {
rightWidth = nextRightWidth;
rightIndex--;
}
}
}
final truncatedText =
'${text.charSubstring(0, lastValidLeftIndex)}$ellipsis${text.charSubstring(lastValidRightIndex)}';
_cache.put(cacheKey, truncatedText.fixAutoLines());
return Text(
truncatedText.fixAutoLines(),
maxLines: maxLines,
style: style,
textScaler: textScaler,
);
},
);
}
}
extension StringExt on String {
String fixAutoLines() => characters.join('\u{200B}');
String charSubstring(int start, [int? end]) =>
characters.getRange(start, end).join();
String removeLineBreaks() => replaceAll(RegExp(r'[\r\n]+'), '');
}
class AnimatedText extends StatelessWidget {
const AnimatedText(
this.text, {
super.key,
required this.style,
required this.isFetching,
this.placeholder = '...',
});
final String placeholder;
final String text;
final TextStyle? style;
final bool isFetching;
@override
Widget build(BuildContext context) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child:
isFetching
? Text(placeholder, key: const ValueKey('empty'), style: style)
: Text(text, key: const ValueKey('text'), style: style),
);
}
}

View File

@@ -0,0 +1,169 @@
import 'package:adaptive_dialog/adaptive_dialog.dart';
import 'package:dartx/dartx.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:moodiary/common/values/border.dart';
import 'package:moodiary/components/base/popup.dart';
import 'package:moodiary/components/base/qr/qr_code.dart';
import 'package:moodiary/components/base/qr/qr_scanner.dart';
import 'package:moodiary/components/base/tile/setting_tile.dart';
import 'package:moodiary/l10n/l10n.dart';
import 'package:moodiary/utils/notice_util.dart';
class QrInputTile extends StatelessWidget {
final String title;
final String? subtitle;
final String? prefix;
final String value;
final bool withStyle;
final void Function(String)? onValue;
final Widget? leading;
final VoidCallback? onScan;
final VoidCallback? onInput;
const QrInputTile({
super.key,
required this.title,
this.subtitle,
required this.value,
this.onValue,
this.withStyle = true,
this.onScan,
this.onInput,
this.leading,
this.prefix,
});
@override
Widget build(BuildContext context) {
const validDuration = Duration(minutes: 2);
return AdaptiveListTile(
title: Text(title),
leading: leading,
subtitle:
subtitle ??
(value.isNotNullOrBlank
? Text(context.l10n.hasOption)
: Text(context.l10n.noOption)),
tileColor:
withStyle ? context.theme.colorScheme.surfaceContainerLow : null,
shape:
withStyle
? const RoundedRectangleBorder(
borderRadius: AppBorderRadius.mediumBorderRadius,
)
: null,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Builder(
builder: (context) {
return IconButton.filled(
tooltip: context.l10n.genQrCodeTooltip,
onPressed: () {
if (value.isBlank) {
toast.info(message: context.l10n.genQrCodeError1(title));
return;
}
showPopupWidget(
targetContext: context,
child: EncryptQrCode(
data: value,
size: 96,
prefix: prefix,
validDuration: validDuration,
),
);
},
icon: Icon(
Icons.qr_code_rounded,
color: context.theme.colorScheme.onPrimary,
),
);
},
),
IconButton.filled(
tooltip: context.l10n.inputTooltip,
onPressed: () async {
final choice = await showDialog<String?>(
context: context,
builder: (context) {
return SimpleDialog(
title: Text(context.l10n.inputMethodTitle),
children: [
SimpleDialogOption(
child: Row(
spacing: 8.0,
children: [
const Icon(Icons.qr_code_scanner_rounded),
Text(context.l10n.inputMethodScanQrCode),
],
),
onPressed: () {
Navigator.pop(context, 'qr');
},
),
SimpleDialogOption(
child: Row(
spacing: 8.0,
children: [
const Icon(Icons.keyboard_rounded),
Text(context.l10n.inputMethodHandelInput),
],
),
onPressed: () {
Navigator.pop(context, 'input');
},
),
],
);
},
);
if (choice == null) return;
if (choice == 'qr' && context.mounted) {
if (onScan != null) {
onScan?.call();
return;
}
final res = await showQrScanner(
context: context,
validDuration: validDuration,
prefix: prefix,
);
if (res != null) {
onValue?.call(res);
}
}
if (choice == 'input' && context.mounted) {
if (onInput != null) {
onInput?.call();
return;
}
final res = await showTextInputDialog(
context: context,
textFields: [DialogTextField(initialText: value)],
title: title,
message: context.l10n.getKeyFromConsole,
style: AdaptiveStyle.material,
);
if (res != null && res.isNotEmpty) {
final value = res[0];
onValue?.call(value);
}
}
},
icon: Icon(
Icons.input_rounded,
color: context.theme.colorScheme.onPrimary,
),
),
],
),
);
}
}

View File

@@ -0,0 +1,183 @@
import 'package:flutter/material.dart';
import 'package:moodiary/components/base/text.dart';
class AdaptiveTitleTile extends StatelessWidget {
const AdaptiveTitleTile({super.key, required this.title, this.subtitle});
final dynamic title;
final dynamic subtitle;
@override
Widget build(BuildContext context) {
assert(
title is String || title is Widget,
'title must be a String or a Widget',
);
assert(
subtitle == null || subtitle is String || subtitle is Widget,
'subtitle must be a String or a Widget',
);
return ListTile(
title:
(title is String) ? AdaptiveText(title, isPrimaryTitle: true) : title,
subtitle:
(subtitle is String)
? AdaptiveText(subtitle!, isTileSubtitle: true)
: subtitle,
);
}
}
class AdaptiveListTile extends StatelessWidget {
const AdaptiveListTile({
super.key,
required this.title,
this.subtitle,
this.trailing,
this.onTap,
this.leading,
this.isFirst,
this.isLast,
this.contentPadding,
this.tileColor,
this.shape,
});
final dynamic title;
final dynamic subtitle;
final Widget? trailing;
final Widget? leading;
final VoidCallback? onTap;
final bool? isFirst;
final bool? isLast;
final EdgeInsets? contentPadding;
final Color? tileColor;
final ShapeBorder? shape;
@override
Widget build(BuildContext context) {
assert(
title is String || title is Widget,
'title must be a String or a Widget',
);
assert(
subtitle == null || subtitle is String || subtitle is Widget,
'subtitle must be a String or a Widget',
);
var realTitle = title;
var realSubtitle = subtitle;
if (title is Text) {
realTitle = (title as Text).data;
}
if (subtitle is Text) {
realSubtitle = (subtitle as Text).data;
}
return ListTile(
tileColor: tileColor,
title:
(realTitle is String)
? AdaptiveText(realTitle, isTileTitle: true)
: realTitle,
shape:
shape ??
RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: isFirst == true ? const Radius.circular(12) : Radius.zero,
bottom: isLast == true ? const Radius.circular(12) : Radius.zero,
),
),
contentPadding: contentPadding,
subtitle:
(realSubtitle is String)
? AdaptiveText(realSubtitle, isTileSubtitle: true)
: realSubtitle,
trailing: trailing,
leading: leading,
onTap: onTap,
);
}
}
class AdaptiveSwitchListTile extends StatelessWidget {
const AdaptiveSwitchListTile({
super.key,
required this.title,
required this.value,
required this.onChanged,
this.subtitle,
this.isFirst,
this.isLast,
this.secondary,
this.isSingle,
});
final dynamic title;
final dynamic subtitle;
final bool value;
final ValueChanged<bool>? onChanged;
final Widget? secondary;
final bool? isFirst;
final bool? isLast;
final bool? isSingle;
@override
Widget build(BuildContext context) {
assert(
title is String || title is Widget,
'title must be a String or a Widget',
);
assert(
subtitle == null || subtitle is String || subtitle is Widget,
'subtitle must be a String or a Widget',
);
var realTitle = title;
var realSubtitle = subtitle;
if (title is Text) {
realTitle = (title as Text).data;
}
if (subtitle is Text) {
realSubtitle = (subtitle as Text).data;
}
return SwitchListTile(
title:
(realTitle is String)
? AdaptiveText(realTitle, isTileTitle: true)
: realTitle,
secondary: secondary,
shape: RoundedRectangleBorder(
borderRadius:
isSingle == true
? BorderRadius.circular(12)
: BorderRadius.vertical(
top:
isFirst == true ? const Radius.circular(12) : Radius.zero,
bottom:
isLast == true ? const Radius.circular(12) : Radius.zero,
),
),
subtitle:
(realSubtitle is String)
? AdaptiveText(realSubtitle, isTileSubtitle: true)
: realSubtitle,
value: value,
onChanged: onChanged,
);
}
}

View File

@@ -21,10 +21,7 @@ class Bubble extends StatelessWidget {
),
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: child,
),
child: Padding(padding: const EdgeInsets.all(4.0), child: child),
),
);
}
@@ -38,9 +35,10 @@ class BubblePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
final paint =
Paint()
..color = color
..style = PaintingStyle.fill;
const arrowWidth = 16.0;
const arrowHeight = 8.0;
final rectWidth = size.width;
@@ -56,11 +54,12 @@ class BubblePainter extends CustomPainter {
);
// 创建路径
final path = Path()
..addRRect(rrect) // 添加圆角矩形
..moveTo((rectWidth - arrowWidth) / 2, rectHeight) // 箭头左侧
..lineTo(rectWidth / 2, rectHeight + arrowHeight) // 箭头尖端
..lineTo((rectWidth + arrowWidth) / 2, rectHeight); // 箭头右侧
final path =
Path()
..addRRect(rrect) // 添加圆角矩形
..moveTo((rectWidth - arrowWidth) / 2, rectHeight) // 箭头左侧
..lineTo(rectWidth / 2, rectHeight + arrowHeight) // 箭头尖端
..lineTo((rectWidth + arrowWidth) / 2, rectHeight); // 箭头右侧
canvas.drawPath(path, paint);
}

Some files were not shown because too many files have changed in this diff Show More