├── .all-contributorsrc
├── .gitignore
├── .gitlab-ci.yml
├── LICENSE
├── README.md
├── android
├── .gradle
│ ├── 4.10.2
│ │ ├── fileChanges
│ │ │ └── last-build.bin
│ │ ├── fileContent
│ │ │ └── fileContent.lock
│ │ └── gc.properties
│ └── vcs-1
│ │ └── gc.properties
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── yu
│ │ │ │ └── MainActivity.java
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── logo.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── logo.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── logo.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── logo.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── logo.png
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── flutter.jks
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── yu_android.iml
├── dique.iml
├── flrs
├── animation_test.flr
├── aura.flr
├── bg_head.flr
├── loading_bird.flr
└── main_logo.flr
├── images
├── bird.png
├── explain.png
├── explain_2.png
├── splash_screen.png
└── token_introduce.png
├── ios
├── Flutter
│ ├── AppFrameworkInfo.plist
│ └── Debug.xcconfig
├── Podfile
├── Pods
│ ├── Headers
│ │ ├── Private
│ │ │ ├── FMDB
│ │ │ │ ├── FMDB.h
│ │ │ │ ├── FMDatabase.h
│ │ │ │ ├── FMDatabaseAdditions.h
│ │ │ │ ├── FMDatabasePool.h
│ │ │ │ ├── FMDatabaseQueue.h
│ │ │ │ └── FMResultSet.h
│ │ │ ├── JPush
│ │ │ │ └── JPUSHService.h
│ │ │ ├── path_provider
│ │ │ │ └── PathProviderPlugin.h
│ │ │ ├── shared_preferences
│ │ │ │ └── SharedPreferencesPlugin.h
│ │ │ └── sqflite
│ │ │ │ ├── SqfliteOperation.h
│ │ │ │ └── SqflitePlugin.h
│ │ └── Public
│ │ │ ├── FMDB
│ │ │ ├── FMDB.h
│ │ │ ├── FMDatabase.h
│ │ │ ├── FMDatabaseAdditions.h
│ │ │ ├── FMDatabasePool.h
│ │ │ ├── FMDatabaseQueue.h
│ │ │ └── FMResultSet.h
│ │ │ ├── JPush
│ │ │ └── JPUSHService.h
│ │ │ ├── path_provider
│ │ │ └── PathProviderPlugin.h
│ │ │ ├── shared_preferences
│ │ │ └── SharedPreferencesPlugin.h
│ │ │ └── sqflite
│ │ │ ├── SqfliteOperation.h
│ │ │ └── SqflitePlugin.h
│ ├── Local Podspecs
│ │ ├── flutter_webview_plugin.podspec.json
│ │ ├── jpush_flutter.podspec.json
│ │ ├── path_provider.podspec.json
│ │ ├── shared_preferences.podspec.json
│ │ └── sqflite.podspec.json
│ ├── Pods.xcodeproj
│ │ └── xcuserdata
│ │ │ └── yuan.xcuserdatad
│ │ │ └── xcschemes
│ │ │ ├── FMDB.xcscheme
│ │ │ ├── flutter_webview_plugin.xcscheme
│ │ │ ├── jpush_flutter.xcscheme
│ │ │ └── path_provider.xcscheme
│ └── Target Support Files
│ │ ├── FMDB
│ │ ├── FMDB-dummy.m
│ │ └── FMDB-prefix.pch
│ │ ├── Pods-Runner
│ │ └── Pods-Runner-dummy.m
│ │ ├── path_provider
│ │ ├── path_provider-dummy.m
│ │ └── path_provider-prefix.pch
│ │ ├── shared_preferences
│ │ ├── shared_preferences-dummy.m
│ │ └── shared_preferences-prefix.pch
│ │ └── sqflite
│ │ ├── sqflite-dummy.m
│ │ └── sqflite-prefix.pch
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
├── Runner
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── icon_dique_1024x1024.png
│ │ │ ├── icon_dique_29x29@1x.png
│ │ │ ├── icon_dique_29x29@2x.png
│ │ │ ├── icon_dique_40x40@2x-1.png
│ │ │ ├── icon_dique_60x60-1.png
│ │ │ ├── icon_dique_60x60@2x-1.png
│ │ │ ├── icon_dique_60x60@2x.png
│ │ │ ├── icon_dique_60x60@3x.png
│ │ │ ├── icon_dique_80x80@2x.png
│ │ │ └── icon_dique_87x87@3x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── GeneratedPluginRegistrant.h
│ ├── GeneratedPluginRegistrant.m
│ ├── Info.plist
│ ├── Runner.entitlements
│ ├── main.m
│ └── splash_screen.png
└── ServiceDefinitions.json
├── lib
├── json
│ ├── article_detail_bean.dart
│ ├── article_list_bean.dart
│ ├── check_update_bean.dart
│ ├── group_repository_bean.dart
│ ├── jpush_bean.dart
│ ├── local_repolist_bean.dart
│ ├── repo_detail_bean.dart
│ ├── repo_directory_bean.dart
│ ├── user_groups_bean.dart
│ └── user_info_bean.dart
├── logic
│ ├── all_logic.dart
│ ├── article_detail_page_logic.dart
│ ├── article_page_logic.dart
│ ├── login_page_logic.dart
│ ├── main_page_logic.dart
│ └── repository_page_logic.dart
├── main.dart
├── model
│ ├── all_model.dart
│ ├── article_detail_page_event.dart
│ ├── article_page_event.dart
│ ├── login_page_event.dart
│ ├── main_page_event.dart
│ └── repository_page_event.dart
├── pages
│ ├── about_page.dart
│ ├── all_pages.dart
│ ├── article_detail_page.dart
│ ├── article_page.dart
│ ├── explain_page.dart
│ ├── feedback_page.dart
│ ├── image_page.dart
│ ├── login_page.dart
│ ├── main_page.dart
│ ├── provider_pages.dart
│ ├── repository_page.dart
│ ├── version_page.dart
│ └── webview_page.dart
├── public
│ ├── HttpService.dart
│ ├── NetManager.dart
│ ├── api_service.dart
│ ├── public_header.dart
│ └── theme.dart
├── utils
│ ├── check_update_util.dart
│ ├── keys.dart
│ ├── shared_util.dart
│ └── toast_util.dart
└── widget
│ ├── custom_book.dart
│ ├── explosion_widget.dart
│ ├── hide_anim_widget.dart
│ ├── loading_dialog.dart
│ ├── loading_widget.dart
│ ├── nav_head.dart
│ ├── rotate_anim_widget.dart
│ ├── top_show_widget.dart
│ └── update_dialog.dart
├── pubspec.yaml
└── test
└── widget_test.dart
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "dique",
3 | "projectOwner": "FEMessage",
4 | "repoType": "github",
5 | "repoHost": "https://github.com",
6 | "files": [
7 | "README.md"
8 | ],
9 | "imageSize": 100,
10 | "commit": false,
11 | "commitConvention": "angular",
12 | "contributors": [
13 | {
14 | "login": "asjqkkkk",
15 | "name": "android bro",
16 | "avatar_url": "https://avatars3.githubusercontent.com/u/30992818?v=4",
17 | "profile": "https://github.com/asjqkkkk",
18 | "contributions": [
19 | "code",
20 | "doc",
21 | "design",
22 | "infra"
23 | ]
24 | },
25 | {
26 | "login": "kira2015",
27 | "name": "wu_zy",
28 | "avatar_url": "https://avatars2.githubusercontent.com/u/14231117?v=4",
29 | "profile": "https://github.com/kira2015",
30 | "contributions": [
31 | "code",
32 | "doc",
33 | "content",
34 | "infra"
35 | ]
36 | }
37 | ],
38 | "contributorsPerLine": 7
39 | }
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/encodings.xml
2 | .idea/modules.xml
3 | .idea/vcs.xml
4 | .idea/workspace.xml
5 | .idea/yuque-app.iml
6 | .idea/libraries/Dart_Packages.xml
7 | .idea/libraries/Dart_SDK.xml
8 | .idea/libraries/Flutter_Plugins.xml
9 | android/.gradle/4.10.2/fileHashes/fileHashes.bin
10 | android/.gradle/4.10.2/fileHashes/fileHashes.lock
11 | android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin
12 | android/.gradle/4.10.2/javaCompile/classAnalysis.bin
13 | android/.gradle/4.10.2/javaCompile/jarAnalysis.bin
14 | android/.gradle/4.10.2/javaCompile/javaCompile.lock
15 | android/.gradle/4.10.2/javaCompile/taskHistory.bin
16 | android/.gradle/4.10.2/taskHistory/taskHistory.bin
17 | android/.gradle/4.10.2/taskHistory/taskHistory.lock
18 | android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
19 | android/.gradle/buildOutputCleanup/outputFiles.bin
20 | android/android.iml
21 | android/.idea/encodings.xml
22 | android/.idea/gradle.xml
23 | android/.idea/misc.xml
24 | android/.idea/modules.xml
25 | android/.idea/runConfigurations.xml
26 | android/.idea/vcs.xml
27 | android/.idea/workspace.xml
28 | android/.idea/caches/build_file_checksums.ser
29 | android/.idea/caches/gradle_models.ser
30 | android/.idea/libraries/Gradle____local_aars____Users_lichen_flutter_bin_cache_artifacts_engine_android_arm_flutter_jar_unspecified_jar.xml
31 | android/.idea/libraries/Gradle____local_aars____Users_lichen_flutter_projects_yuque_app_build_app_intermediates_flutter_flutter_x86_jar_unspecified_jar.xml
32 | android/.idea/libraries/Gradle__androidx_annotation_annotation_1_0_0_jar.xml
33 | android/.idea/libraries/Gradle__androidx_appcompat_appcompat_1_0_0_aar.xml
34 | android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_0_0_jar.xml
35 | android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml
36 | android/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml
37 | android/.idea/libraries/Gradle__androidx_collection_collection_1_0_0_jar.xml
38 | android/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_0_0_aar.xml
39 | android/.idea/libraries/Gradle__androidx_core_core_1_0_0_aar.xml
40 | android/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml
41 | android/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml
42 | android/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml
43 | android/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml
44 | android/.idea/libraries/Gradle__androidx_fragment_fragment_1_0_0_aar.xml
45 | android/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml
46 | android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml
47 | android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml
48 | android/.idea/libraries/Gradle__androidx_legacy_legacy_support_v4_1_0_0_aar.xml
49 | android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_0_0_jar.xml
50 | android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml
51 | android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml
52 | android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_0_0_aar.xml
53 | android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_0_0_aar.xml
54 | android/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml
55 | android/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml
56 | android/.idea/libraries/Gradle__androidx_media_media_1_0_0_aar.xml
57 | android/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml
58 | android/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml
59 | android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml
60 | android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_1_0_0_aar.xml
61 | android/.idea/libraries/Gradle__androidx_vectordrawable_vectordrawable_animated_1_0_0_aar.xml
62 | android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_0_0_aar.xml
63 | android/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml
64 | android/.idea/libraries/Gradle__cn_jiguang_sdk_jcore_1_2_5_aar.xml
65 | android/.idea/libraries/Gradle__cn_jiguang_sdk_jpush_3_1_6_aar.xml
66 | android/.idea/libraries/Gradle__com_android_support_support_annotations_27_1_1_jar.xml
67 | android/.idea/libraries/Gradle__com_android_support_test_espresso_espresso_core_3_0_2_aar.xml
68 | android/.idea/libraries/Gradle__com_android_support_test_espresso_espresso_idling_resource_3_0_2_aar.xml
69 | android/.idea/libraries/Gradle__com_android_support_test_monitor_1_0_2_aar.xml
70 | android/.idea/libraries/Gradle__com_android_support_test_runner_1_0_2_aar.xml
71 | android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1_jar.xml
72 | android/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1_jar.xml
73 | android/.idea/libraries/Gradle__com_tencent_bugly_crashreport_upgrade_1_4_0_aar.xml
74 | android/.idea/libraries/Gradle__com_tencent_bugly_nativecrashreport_3_7_1_aar.xml
75 | android/.idea/libraries/Gradle__javax_inject_javax_inject_1_jar.xml
76 | android/.idea/libraries/Gradle__junit_junit_4_12_jar.xml
77 | android/.idea/libraries/Gradle__net_sf_kxml_kxml2_2_3_0_jar.xml
78 | android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3_jar.xml
79 | android/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3_jar.xml
80 | android/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3_jar.xml
81 | android/app/app.iml
82 | ios/.symlinks/plugins/flutter_bugly
83 | ios/.symlinks/plugins/path_provider
84 | ios/.symlinks/plugins/shared_preferences
85 | ios/.symlinks/plugins/sqflite
86 | key.properties
87 | GeneratedPluginRegistrant.java
88 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: openjdk:8-jdk
2 |
3 | variables:
4 | ANDROID_COMPILE_SDK: "28"
5 | ANDROID_BUILD_TOOLS: "28.0.3"
6 | ANDROID_SDK_TOOLS: "4333796"
7 |
8 | #cache:
9 | # key: flutter_sdk_cache
10 | # paths:
11 | # - .gradle/wrapper
12 | # - .gradle/caches
13 |
14 |
15 |
16 | before_script:
17 | # android
18 | - apt-get --quiet update --yes
19 | - apt-get --quiet install --yes wget tar unzip lib32stdc++6 lib32z1
20 | - wget --quiet --output-document=android-sdk.zip https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_TOOLS}.zip
21 | - unzip -d android-sdk-linux android-sdk.zip
22 | - echo y | android-sdk-linux/tools/bin/sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" >/dev/null
23 | - echo y | android-sdk-linux/tools/bin/sdkmanager "platform-tools" >/dev/null
24 | - echo y | android-sdk-linux/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}" >/dev/null
25 | - export ANDROID_HOME=$PWD/android-sdk-linux
26 | - export PATH=$PATH:$PWD/android-sdk-linux/platform-tools/
27 |
28 | # 暂时禁用检查错误,并使用yes接受所有许可证
29 | - set +o pipefail
30 | - yes | android-sdk-linux/tools/bin/sdkmanager --licenses
31 | - set -o pipefail
32 |
33 |
34 | # flutter
35 | - wget --output-document=flutter-sdk.tar.xz https://storage.googleapis.com/flutter_infra/releases/stable/linux/flutter_linux_v1.7.8+hotfix.4-stable.tar.xz
36 | - tar -xf flutter-sdk.tar.xz
37 | - export PATH=$PATH:$PWD/flutter/bin
38 | - export PUB_HOSTED_URL=https://pub.flutter-io.cn
39 | - export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
40 | - echo flutter.sdk=$PWD/flutter > android/local.properties
41 | - cd android
42 | - rm -rf android/.gradle
43 |
44 | stages:
45 | - android-build
46 |
47 |
48 | assembleRelease:
49 | stage: android-build
50 | # tags:
51 | # - build
52 | only:
53 | - dev
54 | script:
55 | - cd ..
56 | # - flutter doctor
57 | # - ifconfig
58 | - flutter packages get
59 | - flutter build apk --target-platform android-arm --split-per-abi
60 | - mv build/app/outputs/apk/release/app-armeabi-v7a-release.apk build
61 | - ls
62 | # - curl http://127.0.0.1:8080/file/upload -F "file=@/build/app/outputs/apk/release/app-release.apk"
63 | artifacts:
64 | paths:
65 | - build/app-armeabi-v7a-release.apk
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 deepexi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | [](https://github.com/FEMessage/dique)
5 | [](http://levy.ren/)
6 |
7 | # 介绍
8 |
9 | 【滴雀】app面向所有语雀用户。
10 |
11 | 它完全使用flutter编写,同时所有内容提供来源接口皆由语雀提供:
12 | [**语雀开发者**](https://www.yuque.com/yuque/developer)
13 |
14 | 使用只需要提供你的语雀账号token即可!
15 |
16 |
17 | 登录页| 主页 | 文章详情
18 | ---|---|---
19 |
|
|
20 |
21 |
22 | # 项目结构
23 |
24 | 下面是项目文件结构
25 |
26 |
27 |
28 |
29 |
30 | - flr:存放flare动画文件
31 | - images:存放图片文件
32 | - json:存放网络请求json文件
33 | - logic:逻辑操作
34 | - model:数据存放
35 | - pages:所有页面
36 | - public:一些配置类
37 | - utils:工具类
38 | - widgets:自定义Widget
39 |
40 |
41 |
42 | # 第三方库
43 |
44 | 下面是项目中使用到的第三方库说明
45 |
46 | 控件 | 说明
47 | ---|---
48 | [dio](https://pub.flutter-io.cn/packages/dio) | 网络请求
49 | [shared_preferences](https://pub.flutter-io.cn/packages/shared_preferences) | 本地存储
50 | [provider](https://pub.flutter-io.cn/packages/provider) | 状态管理
51 | [test](https://pub.flutter-io.cn/packages/test) | 单元测试
52 | [cached_network_image](https://pub.flutter-io.cn/packages/cached_network_image) | 图片缓存
53 | [path_provider](https://pub.flutter-io.cn/packages/path_provider) | 路径获取
54 | [package_info](https://pub.flutter-io.cn/packages/package_info) | 获取package信息
55 | [flutter_webview_plugin](https://pub.flutter-io.cn/packages/flutter_webview_plugin) | 网页
56 | [pull_to_refresh](https://pub.flutter-io.cn/packages/pull_to_refresh) | 上拉加载
57 | [photo_view](https://pub.flutter-io.cn/packages/photo_view) | 图片展示
58 | [font_awesome_flutter](https://pub.flutter-io.cn/packages/font_awesome_flutter) | 各种矢量图标
59 | [open_file](https://pub.flutter-io.cn/packages/open_file) | 打开文件,android更新下载安装包用
60 | [flare_flutter](https://pub.flutter-io.cn/packages/flare_flutter) | flare动画
61 | [flutter_html](https://pub.flutter-io.cn/packages/flutter_html) | 解析html
62 | [jpush_flutter](https://pub.flutter-io.cn/packages/jpush_flutter) | 极光推送
63 |
64 |
65 | # 构建配置
66 |
67 | 为了避免类似android打包key存放在云端,我们使用的是譬如gitlab的variables功能来通过环境变量提供所需参数:
68 |
69 | 
70 |
71 | 如上面中所配置的这样,项目中有五个参数需要在使用的时候进行配置,可以查看android/app/build.gradle文件:
72 |
73 |
74 | ```
75 | def appKeyPassword = System.getenv('KEY_PASSWORD')
76 | def appStorePassword = System.getenv('STORE_PASSWORD')
77 | def appKeyAlias = System.getenv('KEY_ALIAS')
78 | def appStoreFile = System.getenv('STORE_FILE')
79 | def jPushKey = System.getenv('JPUSH_APP_KEY')
80 | ```
81 | 前四个参数用于打包android apk,最后的参数是使用极光推送所需要的appkey
82 |
83 | ## Contributors
84 |
85 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
--------------------------------------------------------------------------------
/android/.gradle/4.10.2/fileChanges/last-build.bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/android/.gradle/4.10.2/fileContent/fileContent.lock:
--------------------------------------------------------------------------------
1 | |a㽒_ش
--------------------------------------------------------------------------------
/android/.gradle/4.10.2/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/.gradle/4.10.2/gc.properties
--------------------------------------------------------------------------------
/android/.gradle/vcs-1/gc.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/.gradle/vcs-1/gc.properties
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | def appKeyPassword = System.getenv('KEY_PASSWORD')
28 | def appStorePassword = System.getenv('STORE_PASSWORD')
29 | def appKeyAlias = System.getenv('KEY_ALIAS')
30 | def appStoreFile = System.getenv('STORE_FILE')
31 | def jPushKey = System.getenv('JPUSH_APP_KEY')
32 |
33 |
34 | android {
35 | compileSdkVersion 28
36 |
37 | lintOptions {
38 | disable 'InvalidPackage'
39 | }
40 |
41 | defaultConfig {
42 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
43 | applicationId "com.example.yu"
44 | minSdkVersion 16
45 | targetSdkVersion 28
46 | versionCode flutterVersionCode.toInteger()
47 | versionName flutterVersionName
48 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
49 |
50 | // ndk {
51 | // //选择要添加的对应 cpu 类型的 .so 库。
52 | // abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64', 'mips', 'mips64' ,'arm64-v8a'
53 | //// abiFilters 'armeabi-v7a'
54 | // }
55 |
56 | if(System.getenv('STORE_FILE') == null){
57 | def keystorePropertiesFile = rootProject.file("key.properties")
58 | def keystoreProperties = new Properties()
59 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
60 | manifestPlaceholders = [
61 | JPUSH_PKGNAME : applicationId,
62 | JPUSH_APPKEY : keystoreProperties['jPushAPPKEY'], // NOTE: JPush 上注册的包名对应的 Appkey.
63 | JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
64 | ]
65 | } else {
66 | manifestPlaceholders = [
67 | JPUSH_PKGNAME : applicationId,
68 | JPUSH_APPKEY : jPushKey, // NOTE: JPush 上注册的包名对应的 Appkey.
69 | JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
70 | ]
71 | }
72 |
73 |
74 |
75 | }
76 |
77 | signingConfigs {
78 | if(System.getenv('STORE_FILE') == null){
79 | def keystorePropertiesFile = rootProject.file("key.properties")
80 | def keystoreProperties = new Properties()
81 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
82 | release {
83 | keyAlias keystoreProperties['keyAlias']
84 | keyPassword keystoreProperties['keyPassword']
85 | storeFile file(keystoreProperties['storeFile'])
86 | storePassword keystoreProperties['storePassword']
87 | }
88 | } else{
89 | release {
90 | keyAlias appKeyAlias
91 | keyPassword appKeyPassword
92 | storeFile file(appStoreFile)
93 | storePassword appStorePassword
94 | }
95 | }
96 | }
97 |
98 | buildTypes {
99 | release {
100 | // TODO: Add your own signing config for the release build.
101 | // Signing with the debug keys for now, so `flutter run --release` works.
102 | signingConfig signingConfigs.release
103 | }
104 | }
105 | }
106 |
107 | flutter {
108 | source '../..'
109 | }
110 |
111 | dependencies {
112 | testImplementation 'junit:junit:4.12'
113 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
114 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
115 | }
116 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
22 |
29 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/yu/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.yu;
2 |
3 | import android.os.Bundle;
4 | import io.flutter.app.FlutterActivity;
5 | import io.flutter.plugins.GeneratedPluginRegistrant;
6 |
7 | public class MainActivity extends FlutterActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | GeneratedPluginRegistrant.registerWith(this);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-hdpi/logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-mdpi/logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-xhdpi/logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-xxhdpi/logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/app/src/main/res/mipmap-xxxhdpi/logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.2.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/android/flutter.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/flutter.jks
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableJetifier=true
3 | android.useAndroidX=true
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/android/yu_android.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/dique.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/flrs/animation_test.flr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/flrs/animation_test.flr
--------------------------------------------------------------------------------
/flrs/aura.flr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/flrs/aura.flr
--------------------------------------------------------------------------------
/flrs/bg_head.flr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/flrs/bg_head.flr
--------------------------------------------------------------------------------
/flrs/loading_bird.flr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/flrs/loading_bird.flr
--------------------------------------------------------------------------------
/flrs/main_logo.flr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/flrs/main_logo.flr
--------------------------------------------------------------------------------
/images/bird.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/images/bird.png
--------------------------------------------------------------------------------
/images/explain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/images/explain.png
--------------------------------------------------------------------------------
/images/explain_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/images/explain_2.png
--------------------------------------------------------------------------------
/images/splash_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/images/splash_screen.png
--------------------------------------------------------------------------------
/images/token_introduce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/images/token_introduce.png
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def parse_KV_file(file, separator='=')
14 | file_abs_path = File.expand_path(file)
15 | if !File.exists? file_abs_path
16 | return [];
17 | end
18 | pods_ary = []
19 | skip_line_start_symbols = ["#", "/"]
20 | File.foreach(file_abs_path) { |line|
21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
22 | plugin = line.split(pattern=separator)
23 | if plugin.length == 2
24 | podname = plugin[0].strip()
25 | path = plugin[1].strip()
26 | podpath = File.expand_path("#{path}", file_abs_path)
27 | pods_ary.push({:name => podname, :path => podpath});
28 | else
29 | puts "Invalid plugin specification: #{line}"
30 | end
31 | }
32 | return pods_ary
33 | end
34 |
35 | target 'Runner' do
36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
37 | # referring to absolute paths on developers' machines.
38 | system('rm -rf .symlinks')
39 | system('mkdir -p .symlinks/plugins')
40 |
41 | # Flutter Pods
42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
43 | if generated_xcode_build_settings.empty?
44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
45 | end
46 | generated_xcode_build_settings.map { |p|
47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
48 | symlink = File.join('.symlinks', 'flutter')
49 | File.symlink(File.dirname(p[:path]), symlink)
50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path]))
51 | end
52 | }
53 |
54 | # Plugin Pods
55 | plugin_pods = parse_KV_file('../.flutter-plugins')
56 | plugin_pods.map { |p|
57 | symlink = File.join('.symlinks', 'plugins', p[:name])
58 | File.symlink(p[:path], symlink)
59 | pod p[:name], :path => File.join(symlink, 'ios')
60 | }
61 | end
62 |
63 | post_install do |installer|
64 | installer.pods_project.targets.each do |target|
65 | target.build_configurations.each do |config|
66 | config.build_settings['ENABLE_BITCODE'] = 'NO'
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/FMDB/FMDB.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDB.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/FMDB/FMDatabase.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabase.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/FMDB/FMDatabaseAdditions.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabaseAdditions.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/FMDB/FMDatabasePool.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabasePool.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/FMDB/FMDatabaseQueue.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabaseQueue.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/FMDB/FMResultSet.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMResultSet.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/JPush/JPUSHService.h:
--------------------------------------------------------------------------------
1 | ../../../JPush/JPUSHService.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/path_provider/PathProviderPlugin.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/path_provider/ios/Classes/PathProviderPlugin.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/shared_preferences/SharedPreferencesPlugin.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/shared_preferences/ios/Classes/SharedPreferencesPlugin.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/sqflite/SqfliteOperation.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/sqflite/ios/Classes/SqfliteOperation.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Private/sqflite/SqflitePlugin.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/sqflite/ios/Classes/SqflitePlugin.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/FMDB/FMDB.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDB.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/FMDB/FMDatabase.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabase.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/FMDB/FMDatabaseAdditions.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabaseAdditions.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/FMDB/FMDatabasePool.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabasePool.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/FMDB/FMDatabaseQueue.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMDatabaseQueue.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/FMDB/FMResultSet.h:
--------------------------------------------------------------------------------
1 | ../../../FMDB/src/fmdb/FMResultSet.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/JPush/JPUSHService.h:
--------------------------------------------------------------------------------
1 | ../../../JPush/JPUSHService.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/path_provider/PathProviderPlugin.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/path_provider/ios/Classes/PathProviderPlugin.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/shared_preferences/SharedPreferencesPlugin.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/shared_preferences/ios/Classes/SharedPreferencesPlugin.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/sqflite/SqfliteOperation.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/sqflite/ios/Classes/SqfliteOperation.h
--------------------------------------------------------------------------------
/ios/Pods/Headers/Public/sqflite/SqflitePlugin.h:
--------------------------------------------------------------------------------
1 | ../../../../.symlinks/plugins/sqflite/ios/Classes/SqflitePlugin.h
--------------------------------------------------------------------------------
/ios/Pods/Local Podspecs/flutter_webview_plugin.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "flutter_webview_plugin",
3 | "version": "0.0.1",
4 | "summary": "A new flutter plugin project.",
5 | "description": "A new flutter plugin project.",
6 | "homepage": "https://github.com/dart-flitter/flutter_webview_plugin",
7 | "license": {
8 | "file": "../LICENSE"
9 | },
10 | "authors": {
11 | "Your Company": "email@example.com"
12 | },
13 | "source": {
14 | "path": "."
15 | },
16 | "source_files": "Classes/**/*",
17 | "public_header_files": "Classes/**/*.h",
18 | "dependencies": {
19 | "Flutter": [
20 |
21 | ]
22 | },
23 | "platforms": {
24 | "ios": "8.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ios/Pods/Local Podspecs/jpush_flutter.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jpush_flutter",
3 | "version": "0.0.2",
4 | "summary": "A new flutter plugin project.",
5 | "description": "A new flutter plugin project.",
6 | "homepage": "http://example.com",
7 | "license": {
8 | "file": "../LICENSE"
9 | },
10 | "authors": {
11 | "huminios": "380108184@qq.com"
12 | },
13 | "source": {
14 | "path": "."
15 | },
16 | "source_files": "Classes/**/*",
17 | "public_header_files": "Classes/**/*.h",
18 | "dependencies": {
19 | "Flutter": [
20 |
21 | ],
22 | "JPush": [
23 |
24 | ]
25 | },
26 | "platforms": {
27 | "ios": "8.0"
28 | },
29 | "static_framework": true
30 | }
31 |
--------------------------------------------------------------------------------
/ios/Pods/Local Podspecs/path_provider.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "path_provider",
3 | "version": "0.0.1",
4 | "summary": "A Flutter plugin for getting commonly used locations on the filesystem.",
5 | "description": "A Flutter plugin for getting commonly used locations on the filesystem.",
6 | "homepage": "https://github.com/flutter/plugins/tree/master/packages/path_provider",
7 | "license": {
8 | "file": "../LICENSE"
9 | },
10 | "authors": {
11 | "Flutter Team": "flutter-dev@googlegroups.com"
12 | },
13 | "source": {
14 | "path": "."
15 | },
16 | "source_files": "Classes/**/*",
17 | "public_header_files": "Classes/**/*.h",
18 | "dependencies": {
19 | "Flutter": [
20 |
21 | ]
22 | },
23 | "platforms": {
24 | "ios": "8.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ios/Pods/Local Podspecs/shared_preferences.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shared_preferences",
3 | "version": "0.0.1",
4 | "summary": "A Flutter plugin for reading and writing simple key-value pairs.",
5 | "description": "A Flutter plugin for reading and writing simple key-value pairs.",
6 | "homepage": "https://github.com/flutter/plugins/tree/master/packages/shared_preferences",
7 | "license": {
8 | "file": "../LICENSE"
9 | },
10 | "authors": {
11 | "Flutter Team": "flutter-dev@googlegroups.com"
12 | },
13 | "source": {
14 | "path": "."
15 | },
16 | "source_files": "Classes/**/*",
17 | "public_header_files": "Classes/**/*.h",
18 | "dependencies": {
19 | "Flutter": [
20 |
21 | ]
22 | },
23 | "platforms": {
24 | "ios": "8.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ios/Pods/Local Podspecs/sqflite.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sqflite",
3 | "version": "0.0.1",
4 | "summary": "A new flutter plugin project.",
5 | "description": "A new flutter plugin project.",
6 | "homepage": "http://example.com",
7 | "license": {
8 | "file": "../LICENSE"
9 | },
10 | "authors": {
11 | "Your Company": "email@example.com"
12 | },
13 | "source": {
14 | "path": "."
15 | },
16 | "source_files": "Classes/**/*",
17 | "public_header_files": "Classes/**/*.h",
18 | "dependencies": {
19 | "Flutter": [
20 |
21 | ],
22 | "FMDB": [
23 | "~> 2.7.2"
24 | ]
25 | },
26 | "platforms": {
27 | "ios": "8.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ios/Pods/Pods.xcodeproj/xcuserdata/yuan.xcuserdatad/xcschemes/FMDB.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/ios/Pods/Pods.xcodeproj/xcuserdata/yuan.xcuserdatad/xcschemes/flutter_webview_plugin.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/ios/Pods/Pods.xcodeproj/xcuserdata/yuan.xcuserdatad/xcschemes/jpush_flutter.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/ios/Pods/Pods.xcodeproj/xcuserdata/yuan.xcuserdatad/xcschemes/path_provider.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/FMDB/FMDB-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_FMDB : NSObject
3 | @end
4 | @implementation PodsDummy_FMDB
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/FMDB/FMDB-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/Pods-Runner/Pods-Runner-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_Runner : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_Runner
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/path_provider/path_provider-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_path_provider : NSObject
3 | @end
4 | @implementation PodsDummy_path_provider
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/path_provider/path_provider-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/shared_preferences/shared_preferences-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_shared_preferences : NSObject
3 | @end
4 | @implementation PodsDummy_shared_preferences
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/shared_preferences/shared_preferences-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/sqflite/sqflite-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_sqflite : NSObject
3 | @end
4 | @implementation PodsDummy_sqflite
5 | @end
6 |
--------------------------------------------------------------------------------
/ios/Pods/Target Support Files/sqflite/sqflite-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #else
4 | #ifndef FOUNDATION_EXPORT
5 | #if defined(__cplusplus)
6 | #define FOUNDATION_EXPORT extern "C"
7 | #else
8 | #define FOUNDATION_EXPORT extern
9 | #endif
10 | #endif
11 | #endif
12 |
13 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.m:
--------------------------------------------------------------------------------
1 | #include "AppDelegate.h"
2 | #include "GeneratedPluginRegistrant.h"
3 |
4 | @implementation AppDelegate
5 |
6 | - (BOOL)application:(UIApplication *)application
7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
8 | [GeneratedPluginRegistrant registerWithRegistry:self];
9 | // Override point for customization after application launch.
10 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
11 | }
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "icon_dique_40x40@2x-1.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "icon_dique_60x60-1.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon_dique_29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "icon_dique_29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "icon_dique_87x87@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "icon_dique_80x80@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "icon_dique_60x60@2x-1.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "icon_dique_60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "icon_dique_60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "idiom" : "ipad",
59 | "size" : "20x20",
60 | "scale" : "1x"
61 | },
62 | {
63 | "idiom" : "ipad",
64 | "size" : "20x20",
65 | "scale" : "2x"
66 | },
67 | {
68 | "idiom" : "ipad",
69 | "size" : "29x29",
70 | "scale" : "1x"
71 | },
72 | {
73 | "idiom" : "ipad",
74 | "size" : "29x29",
75 | "scale" : "2x"
76 | },
77 | {
78 | "idiom" : "ipad",
79 | "size" : "40x40",
80 | "scale" : "1x"
81 | },
82 | {
83 | "idiom" : "ipad",
84 | "size" : "40x40",
85 | "scale" : "2x"
86 | },
87 | {
88 | "idiom" : "ipad",
89 | "size" : "76x76",
90 | "scale" : "1x"
91 | },
92 | {
93 | "idiom" : "ipad",
94 | "size" : "76x76",
95 | "scale" : "2x"
96 | },
97 | {
98 | "idiom" : "ipad",
99 | "size" : "83.5x83.5",
100 | "scale" : "2x"
101 | },
102 | {
103 | "size" : "1024x1024",
104 | "idiom" : "ios-marketing",
105 | "filename" : "icon_dique_1024x1024.png",
106 | "scale" : "1x"
107 | }
108 | ],
109 | "info" : {
110 | "version" : 1,
111 | "author" : "xcode"
112 | }
113 | }
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_1024x1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_40x40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_40x40@2x-1.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60-1.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60@2x-1.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_80x80@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_80x80@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_87x87@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon_dique_87x87@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/GeneratedPluginRegistrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | #ifndef GeneratedPluginRegistrant_h
6 | #define GeneratedPluginRegistrant_h
7 |
8 | #import
9 |
10 | @interface GeneratedPluginRegistrant : NSObject
11 | + (void)registerWithRegistry:(NSObject*)registry;
12 | @end
13 |
14 | #endif /* GeneratedPluginRegistrant_h */
15 |
--------------------------------------------------------------------------------
/ios/Runner/GeneratedPluginRegistrant.m:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | #import "GeneratedPluginRegistrant.h"
6 | #import
7 | #import
8 | #import
9 | #import
10 | #import
11 | #import
12 | #import
13 | #import
14 | #import
15 |
16 | @implementation GeneratedPluginRegistrant
17 |
18 | + (void)registerWithRegistry:(NSObject*)registry {
19 | [FlutterWebviewPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterWebviewPlugin"]];
20 | [JPushPlugin registerWithRegistrar:[registry registrarForPlugin:@"JPushPlugin"]];
21 | [OpenFilePlugin registerWithRegistrar:[registry registrarForPlugin:@"OpenFilePlugin"]];
22 | [FLTPackageInfoPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPackageInfoPlugin"]];
23 | [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]];
24 | [FLTSharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharedPreferencesPlugin"]];
25 | [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]];
26 | [FLTUrlLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTUrlLauncherPlugin"]];
27 | [FLTWebViewFlutterPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTWebViewFlutterPlugin"]];
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleAllowMixedLocalizations
6 |
7 | ITSAppUsesNonExemptEncryption
8 |
9 | CFBundleLocalizations
10 |
11 | zh_CN
12 |
13 | CFBundleDevelopmentRegion
14 | en
15 | CFBundleExecutable
16 | $(EXECUTABLE_NAME)
17 | CFBundleIdentifier
18 | $(PRODUCT_BUNDLE_IDENTIFIER)
19 | CFBundleInfoDictionaryVersion
20 | 6.0
21 | CFBundleName
22 | 滴雀
23 | CFBundlePackageType
24 | APPL
25 | CFBundleShortVersionString
26 | 1.0.1
27 | CFBundleSignature
28 | ????
29 | CFBundleVersion
30 | 1
31 | LSRequiresIPhoneOS
32 |
33 | NSAppTransportSecurity
34 |
35 | NSAllowsArbitraryLoads
36 |
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIMainStoryboardFile
41 | Main
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 | UIViewControllerBasedStatusBarAppearance
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/ios/Runner/Runner.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/main.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char* argv[]) {
6 | @autoreleasepool {
7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ios/Runner/splash_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FEMessage/dique/8c810e922efc9aa3c139e95bfc589282b4e8e9b4/ios/Runner/splash_screen.png
--------------------------------------------------------------------------------
/ios/ServiceDefinitions.json:
--------------------------------------------------------------------------------
1 | {"services":[]}
--------------------------------------------------------------------------------
/lib/json/check_update_bean.dart:
--------------------------------------------------------------------------------
1 | class CheckUpdateBean {
2 |
3 | /**
4 | * android : {"version":"1.0.0","content":"1.新增xxx\n2.优化xxx","isForceUpdate":0,"updateTime":"2019-04-28","downloadLink":"https://work-1256696029.cos.ap-guangzhou.myqcloud.com/apk_download/dique.apk"}
5 | * ios : {"version":"1.0.0","content":"1.新增xxx\n2.优化xxx","updateTime":"2019-06-12","isForceUpdate":0,"downloadLink":"https://apps.apple.com/cn/app/%E6%BB%B4%E9%9B%80/id1466759938"}
6 | */
7 |
8 | AndroidBean android;
9 | IosBean ios;
10 |
11 | static CheckUpdateBean fromMap(Map map) {
12 | CheckUpdateBean check_update_bean = new CheckUpdateBean();
13 | check_update_bean.android = AndroidBean.fromMap(map['android']);
14 | check_update_bean.ios = IosBean.fromMap(map['ios']);
15 | return check_update_bean;
16 | }
17 |
18 | static List fromMapList(dynamic mapList) {
19 | List list = new List(mapList.length);
20 | for (int i = 0; i < mapList.length; i++) {
21 | list[i] = fromMap(mapList[i]);
22 | }
23 | return list;
24 | }
25 |
26 | }
27 |
28 | class AndroidBean {
29 |
30 | /**
31 | * version : "1.0.0"
32 | * content : "1.新增xxx\n2.优化xxx"
33 | * updateTime : "2019-04-28"
34 | * downloadLink : "https://work-1256696029.cos.ap-guangzhou.myqcloud.com/apk_download/dique.apk"
35 | * isForceUpdate : 0
36 | */
37 |
38 | String version;
39 | String content;
40 | String updateTime;
41 | String downloadLink;
42 | int isForceUpdate;
43 |
44 | static AndroidBean fromMap(Map map) {
45 | AndroidBean androidBean = new AndroidBean();
46 | androidBean.version = map['version'];
47 | androidBean.content = map['content'];
48 | androidBean.updateTime = map['updateTime'];
49 | androidBean.downloadLink = map['downloadLink'];
50 | androidBean.isForceUpdate = map['isForceUpdate'];
51 | return androidBean;
52 | }
53 |
54 | static List fromMapList(dynamic mapList) {
55 | List list = new List(mapList.length);
56 | for (int i = 0; i < mapList.length; i++) {
57 | list[i] = fromMap(mapList[i]);
58 | }
59 | return list;
60 | }
61 | }
62 |
63 | class IosBean {
64 |
65 | /**
66 | * version : "1.0.0"
67 | * content : "1.新增xxx\n2.优化xxx"
68 | * updateTime : "2019-06-12"
69 | * downloadLink : "https://apps.apple.com/cn/app/%E6%BB%B4%E9%9B%80/id1466759938"
70 | * isForceUpdate : 0
71 | */
72 |
73 | String version;
74 | String content;
75 | String updateTime;
76 | String downloadLink;
77 | int isForceUpdate;
78 |
79 | static IosBean fromMap(Map map) {
80 | IosBean iosBean = new IosBean();
81 | iosBean.version = map['version'];
82 | iosBean.content = map['content'];
83 | iosBean.updateTime = map['updateTime'];
84 | iosBean.downloadLink = map['downloadLink'];
85 | iosBean.isForceUpdate = map['isForceUpdate'];
86 | return iosBean;
87 | }
88 |
89 | static List fromMapList(dynamic mapList) {
90 | List list = new List(mapList.length);
91 | for (int i = 0; i < mapList.length; i++) {
92 | list[i] = fromMap(mapList[i]);
93 | }
94 | return list;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/lib/json/jpush_bean.dart:
--------------------------------------------------------------------------------
1 | class JPushBean {
2 |
3 | /**
4 | * alert : "推送内容"
5 | * title : "文章标题"
6 | * extras : {"cn.jpush.android.ALERT_TYPE":-1,"cn.jpush.android.NOTIFICATION_ID":536281999,"cn.jpush.android.MSG_ID":58546804879130989,"cn.jpush.android.ALERT":"推送内容","cn.jpush.android.EXTRA":{"nameSpace":"deepexi-serverless/dev-doc","slug":"engxg1"}}
7 | */
8 |
9 | String alert;
10 | String title;
11 | var extras;
12 | EXTRABean extraBean;
13 |
14 | static JPushBean fromMap(Map map) {
15 | JPushBean jpush_bean = new JPushBean();
16 | jpush_bean.alert = map['alert'];
17 | jpush_bean.title = map['title'];
18 | jpush_bean.extras = map['extras'];
19 | var data = jpush_bean.extras['cn.jpush.android.EXTRA'];
20 | jpush_bean.extraBean = EXTRABean.fromMap(data);
21 | return jpush_bean;
22 | }
23 |
24 | static List fromMapList(dynamic mapList) {
25 | List list = new List(mapList.length);
26 | for (int i = 0; i < mapList.length; i++) {
27 | list[i] = fromMap(mapList[i]);
28 | }
29 | return list;
30 | }
31 |
32 | }
33 |
34 | class ExtrasBean {
35 |
36 | /**
37 | * cn.jpush.android.ALERT : "推送内容"
38 | * cn.jpush.android.ALERT_TYPE : -1
39 | * cn.jpush.android.NOTIFICATION_ID : 536281999
40 | * cn.jpush.android.MSG_ID : 58546804879130989
41 | * cn.jpush.android.EXTRA : {"nameSpace":"deepexi-serverless/dev-doc","slug":"engxg1"}
42 | */
43 |
44 | var extra;
45 |
46 | static ExtrasBean fromMap(Map map) {
47 | ExtrasBean extrasBean = new ExtrasBean();
48 |
49 | extrasBean.extra = EXTRABean.fromMap(map['cn.jpush.android.EXTRA']);
50 | return extrasBean;
51 | }
52 |
53 | static List fromMapList(dynamic mapList) {
54 | List list = new List(mapList.length);
55 | for (int i = 0; i < mapList.length; i++) {
56 | list[i] = fromMap(mapList[i]);
57 | }
58 | return list;
59 | }
60 | }
61 |
62 | class EXTRABean {
63 |
64 | /**
65 | * nameSpace : "deepexi-serverless/dev-doc"
66 | * slug : "engxg1"
67 | */
68 |
69 | String nameSpace;
70 | String slug;
71 |
72 | static EXTRABean fromMap(Map map) {
73 | EXTRABean bean = new EXTRABean();
74 | bean.nameSpace = map['nameSpace'];
75 | bean.slug = map['slug'];
76 | return bean;
77 | }
78 |
79 | }
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/lib/json/local_repolist_bean.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | class LocalRepoListBean {
4 |
5 | /**
6 | * teamName : "teamName"
7 | * repoName : "repoName"
8 | * groupLoginId : "groupLoginId"
9 | * groupAvatarUrl : "groupAvatarUrl"
10 | */
11 |
12 | String teamName;
13 | String repoName;
14 | String groupLoginId;
15 | String groupAvatarUrl;
16 | String nameSpace;
17 | List articleBeans;
18 |
19 | static LocalRepoListBean fromMap(Map map) {
20 | LocalRepoListBean local_repolist_bean = new LocalRepoListBean();
21 | local_repolist_bean.teamName = map['teamName'];
22 | local_repolist_bean.repoName = map['repoName'];
23 | local_repolist_bean.groupLoginId = map['groupLoginId'];
24 | local_repolist_bean.groupAvatarUrl = map['groupAvatarUrl'];
25 | local_repolist_bean.nameSpace = map['nameSpace'];
26 | local_repolist_bean.articleBeans = ArticleBean.fromMapList(map['articles']);
27 | return local_repolist_bean;
28 | }
29 |
30 | static List fromMapList(dynamic mapList) {
31 | List list = new List(mapList.length);
32 | for (int i = 0; i < mapList.length; i++) {
33 | list[i] = fromMap(mapList[i]);
34 | }
35 | return list;
36 | }
37 |
38 | LocalRepoListBean({this.teamName, this.repoName, this.groupLoginId,
39 | this.groupAvatarUrl, this.nameSpace, this.articleBeans});
40 |
41 | static List fromStringList(List repoList){
42 | List list = new List(repoList.length);
43 | List newList = List.from(list);
44 | for (int i = 0; i < repoList.length; i++) {
45 | var data = jsonDecode(repoList[i]);
46 | newList[i] = fromMap(data);
47 | }
48 | return newList;
49 | }
50 |
51 | static List fromBeanList(List beans){
52 | List list = new List(beans.length);
53 | List newList = List.from(list);
54 | for (int i = 0; i < beans.length; i++) {
55 | var data = toJson(beans[i]);
56 | newList[i] = jsonEncode(data);
57 | }
58 | return newList;
59 | }
60 |
61 | static bool containsRepoByNameSpace(LocalRepoListBean bean, List beans){
62 | bool isContain = false;
63 | for(LocalRepoListBean repo in beans){
64 | if(bean.nameSpace == repo.nameSpace){
65 | isContain = true;
66 | }
67 | }
68 | return isContain;
69 | }
70 |
71 | // static bool containsRepoByName(LocalRepoListBean bean, List beans){
72 | // bool isContain = false;
73 | // for(LocalRepoListBean repo in beans){
74 | // print("仓库名字:${bean.nameSpace} ____ 列表${repo.nameSpace}");
75 | // if(bean.repoName == repo.repoName){
76 | // isContain = true;
77 | // }
78 | // }
79 | // return isContain;
80 | // }
81 |
82 | static bool containArticle(ArticleBean bean, List beans){
83 | bool isContain = false;
84 | for(ArticleBean article in beans){
85 | if(bean.articleSlug == article.articleSlug){
86 | isContain = true;
87 | }
88 | }
89 | return isContain;
90 | }
91 |
92 |
93 | static Map toJson(LocalRepoListBean bean) =>{
94 | 'teamName': bean.teamName,
95 | 'repoName': bean.repoName,
96 | 'groupLoginId': bean.groupLoginId,
97 | 'groupAvatarUrl': bean.groupAvatarUrl,
98 | 'nameSpace': bean.nameSpace,
99 | 'articles': ArticleBean.toJsonList(bean.articleBeans),
100 | };
101 |
102 |
103 | }
104 |
105 |
106 | class ArticleBean{
107 | String articleTitle;
108 | String articleSlug;
109 | String nameSpace;
110 |
111 |
112 | ArticleBean({this.articleTitle, this.articleSlug, this.nameSpace});
113 |
114 | static ArticleBean fromMap(Map map) {
115 | ArticleBean articleBean = new ArticleBean();
116 | articleBean.articleTitle = map['articleTitle'];
117 | articleBean.articleSlug = map['articleSlug'];
118 | articleBean.nameSpace = map['nameSpace'];
119 | return articleBean;
120 | }
121 |
122 |
123 | static List fromMapList(dynamic mapList) {
124 | List list = new List(mapList.length);
125 | for (int i = 0; i < mapList.length; i++) {
126 | list[i] = fromMap(mapList[i]);
127 | }
128 | return list;
129 | }
130 |
131 | static Map toJson(ArticleBean bean) =>{
132 | 'articleTitle': bean.articleTitle,
133 | 'articleSlug': bean.articleSlug,
134 | 'nameSpace': bean.nameSpace,
135 | };
136 |
137 | static List toJsonList(List beans){
138 | List list = new List(beans.length);
139 | for (int i = 0; i < list.length; i++) {
140 | list[i] = toJson(beans[i]);
141 | }
142 | return list;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/lib/json/repo_directory_bean.dart:
--------------------------------------------------------------------------------
1 | class RepoDirectoryBean {
2 |
3 | /**
4 | * data : [{"title":"目标方向","slug":"cs0drm","depth":1},{"title":"分工协作","slug":"kqdgoa","depth":1},{"title":"小程序","slug":"#","depth":1},{"title":"握手计划一期开发总结","slug":"gmtoym","depth":2},{"title":"踩坑记录","slug":"aqr6fb","depth":2},{"title":"app","slug":"#","depth":1},{"title":"移动端入职培训","slug":"zwnmw8","depth":2},{"title":"新手入门(flutter)","slug":"xbuua2","depth":2},{"title":"状态管理之Stream","slug":"uqtp1e","depth":2},{"title":"使用Flare创建2D矢量动画","slug":"engxg1","depth":2},{"title":"核心技术点","slug":"sygy2g","depth":2},{"title":"Flutter开发记录","slug":"lw5wkh","depth":2},{"title":"移动端-推送","slug":"dc74gz","depth":2},{"title":"测试排版","slug":"nsnun2","depth":2},{"title":"Web","slug":"#","depth":1},{"title":"Mobile Safari 下 input 引起的问题总结","slug":"fe4iqc","depth":2},{"title":"我们的Github Workflow","slug":"qcg9x0","depth":1}]
5 | */
6 |
7 | List data;
8 |
9 | static RepoDirectoryBean fromMap(Map map) {
10 | RepoDirectoryBean repo_directory_bean = new RepoDirectoryBean();
11 | repo_directory_bean.data = RepoDirListBean.fromMapList(map['data']);
12 | return repo_directory_bean;
13 | }
14 |
15 | static List fromMapList(dynamic mapList) {
16 | List list = new List(mapList.length);
17 | for (int i = 0; i < mapList.length; i++) {
18 | list[i] = fromMap(mapList[i]);
19 | }
20 | return list;
21 | }
22 |
23 | }
24 |
25 | class RepoDirListBean {
26 |
27 | /**
28 | * title : "目标方向"
29 | * slug : "cs0drm"
30 | * depth : 1
31 | */
32 |
33 | String title;
34 | String slug;
35 | int depth;
36 |
37 | static RepoDirListBean fromMap(Map map) {
38 | RepoDirListBean dataListBean = new RepoDirListBean();
39 | dataListBean.title = map['title'];
40 | dataListBean.slug = map['slug'];
41 | dataListBean.depth = map['depth'];
42 | return dataListBean;
43 | }
44 |
45 | static List fromMapList(dynamic mapList) {
46 | List list = new List(mapList.length);
47 | for (int i = 0; i < mapList.length; i++) {
48 | list[i] = fromMap(mapList[i]);
49 | }
50 | return list;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/json/user_groups_bean.dart:
--------------------------------------------------------------------------------
1 | class UserGroupsBean {
2 |
3 | /**
4 | * data : [{"id":210644,"login":"deepexi-company","name":"滴普科技","avatar_url":"https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png","large_avatar_url":"https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png?x-oss-process=image/resize,m_fill,w_320,h_320","medium_avatar_url":"https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png?x-oss-process=image/resize,m_fill,w_160,h_160","small_avatar_url":"https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png?x-oss-process=image/resize,m_fill,w_80,h_80","books_count":26,"public_books_count":0,"topics_count":0,"public_topics_count":0,"members_count":315,"public":0,"description":"公司全员","created_at":"2018-11-20T03:14:33.000Z","updated_at":"2019-05-14T06:39:45.000Z","_serializer":"v2.group"},{"id":313927,"login":"deepexi-serverless","name":"DEEPEXI Serverless","avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/160590/1556430655844-avatar/51841d71-b69f-46fd-9a42-3a71d717922f.png","large_avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/160590/1556430655844-avatar/51841d71-b69f-46fd-9a42-3a71d717922f.png?x-oss-process=image/resize,m_fill,w_320,h_320","medium_avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/160590/1556430655844-avatar/51841d71-b69f-46fd-9a42-3a71d717922f.png?x-oss-process=image/resize,m_fill,w_160,h_160","small_avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/160590/1556430655844-avatar/51841d71-b69f-46fd-9a42-3a71d717922f.png?x-oss-process=image/resize,m_fill,w_80,h_80","books_count":4,"public_books_count":0,"topics_count":0,"public_topics_count":0,"members_count":10,"public":0,"description":null,"created_at":"2019-04-10T12:11:58.000Z","updated_at":"2019-05-09T02:10:57.000Z","_serializer":"v2.group"}]
5 | */
6 |
7 | List data;
8 |
9 | static UserGroupsBean fromMap(Map map) {
10 | UserGroupsBean user_groups_bean = new UserGroupsBean();
11 | user_groups_bean.data = UserGroupListBean.fromMapList(map['data']);
12 | return user_groups_bean;
13 | }
14 |
15 | static List fromMapList(dynamic mapList) {
16 | List list = new List(mapList.length);
17 | for (int i = 0; i < mapList.length; i++) {
18 | list[i] = fromMap(mapList[i]);
19 | }
20 | return list;
21 | }
22 |
23 | }
24 |
25 | class UserGroupListBean {
26 |
27 | /**
28 | * login : "deepexi-company"
29 | * name : "滴普科技"
30 | * avatar_url : "https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png"
31 | * large_avatar_url : "https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png?x-oss-process=image/resize,m_fill,w_320,h_320"
32 | * medium_avatar_url : "https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png?x-oss-process=image/resize,m_fill,w_160,h_160"
33 | * small_avatar_url : "https://cdn.nlark.com/yuque/0/2018/png/210635/1542683667591-aae0cf37-d163-439c-b1f4-289231a4df7c.png?x-oss-process=image/resize,m_fill,w_80,h_80"
34 | * description : "公司全员"
35 | * created_at : "2018-11-20T03:14:33.000Z"
36 | * updated_at : "2019-05-14T06:39:45.000Z"
37 | * _serializer : "v2.group"
38 | * id : 210644
39 | * books_count : 26
40 | * public_books_count : 0
41 | * topics_count : 0
42 | * public_topics_count : 0
43 | * members_count : 315
44 | * public : 0
45 | */
46 |
47 | String login;
48 | String name;
49 | String avatar_url;
50 | String large_avatar_url;
51 | String medium_avatar_url;
52 | String small_avatar_url;
53 | String description;
54 | String created_at;
55 | String updated_at;
56 | String _serializer;
57 | int id;
58 | int books_count;
59 | int public_books_count;
60 | int topics_count;
61 | int public_topics_count;
62 | int members_count;
63 | int public;
64 |
65 | static UserGroupListBean fromMap(Map map) {
66 | UserGroupListBean dataListBean = new UserGroupListBean();
67 | dataListBean.login = map['login'];
68 | dataListBean.name = map['name'];
69 | dataListBean.avatar_url = map['avatar_url'];
70 | dataListBean.large_avatar_url = map['large_avatar_url'];
71 | dataListBean.medium_avatar_url = map['medium_avatar_url'];
72 | dataListBean.small_avatar_url = map['small_avatar_url'];
73 | dataListBean.description = map['description'];
74 | dataListBean.created_at = map['created_at'];
75 | dataListBean.updated_at = map['updated_at'];
76 | dataListBean._serializer = map['_serializer'];
77 | dataListBean.id = map['id'];
78 | dataListBean.books_count = map['books_count'];
79 | dataListBean.public_books_count = map['public_books_count'];
80 | dataListBean.topics_count = map['topics_count'];
81 | dataListBean.public_topics_count = map['public_topics_count'];
82 | dataListBean.members_count = map['members_count'];
83 | dataListBean.public = map['public'];
84 | return dataListBean;
85 | }
86 |
87 | static List fromMapList(dynamic mapList) {
88 | List list = new List(mapList.length);
89 | for (int i = 0; i < mapList.length; i++) {
90 | list[i] = fromMap(mapList[i]);
91 | }
92 | return list;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/lib/json/user_info_bean.dart:
--------------------------------------------------------------------------------
1 | class UserInfoBean {
2 |
3 | /**
4 | * data : {"id":274963,"type":"User","space_id":0,"account_id":136961,"login":"newfish","name":"李子晨(安卓)","avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png","large_avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png?x-oss-process=image/resize,m_fill,w_320,h_320","medium_avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png?x-oss-process=image/resize,m_fill,w_160,h_160","small_avatar_url":"https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png?x-oss-process=image/resize,m_fill,w_80,h_80","books_count":0,"public_books_count":0,"followers_count":0,"following_count":0,"public":1,"description":null,"created_at":"2019-02-25T14:48:11.000Z","updated_at":"2019-05-08T08:04:47.000Z","_serializer":"v2.user_detail"}
5 | */
6 |
7 | DataBean data;
8 |
9 | static UserInfoBean fromMap(Map map) {
10 | UserInfoBean user_info_bean = new UserInfoBean();
11 | user_info_bean.data = DataBean.fromMap(map['data']);
12 | return user_info_bean;
13 | }
14 |
15 | static List fromMapList(dynamic mapList) {
16 | List list = new List(mapList.length);
17 | for (int i = 0; i < mapList.length; i++) {
18 | list[i] = fromMap(mapList[i]);
19 | }
20 | return list;
21 | }
22 |
23 | }
24 |
25 | class DataBean {
26 |
27 | /**
28 | * type : "User"
29 | * login : "newfish"
30 | * name : "李子晨(安卓)"
31 | * avatar_url : "https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png"
32 | * large_avatar_url : "https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png?x-oss-process=image/resize,m_fill,w_320,h_320"
33 | * medium_avatar_url : "https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png?x-oss-process=image/resize,m_fill,w_160,h_160"
34 | * small_avatar_url : "https://cdn.nlark.com/yuque/0/2019/png/274963/1557302684519-avatar/d4247287-5306-455d-8162-9b9411416870.png?x-oss-process=image/resize,m_fill,w_80,h_80"
35 | * created_at : "2019-02-25T14:48:11.000Z"
36 | * updated_at : "2019-05-08T08:04:47.000Z"
37 | * _serializer : "v2.user_detail"
38 | * id : 274963
39 | * space_id : 0
40 | * account_id : 136961
41 | * books_count : 0
42 | * public_books_count : 0
43 | * followers_count : 0
44 | * following_count : 0
45 | * public : 1
46 | */
47 |
48 | String type;
49 | String login;
50 | String name;
51 | String avatar_url;
52 | String large_avatar_url;
53 | String medium_avatar_url;
54 | String small_avatar_url;
55 | String created_at;
56 | String updated_at;
57 | String _serializer;
58 | int id;
59 | int space_id;
60 | int account_id;
61 | int books_count;
62 | int public_books_count;
63 | int followers_count;
64 | int following_count;
65 | int public;
66 |
67 | static DataBean fromMap(Map map) {
68 | DataBean dataBean = new DataBean();
69 | dataBean.type = map['type'];
70 | dataBean.login = map['login'];
71 | dataBean.name = map['name'];
72 | dataBean.avatar_url = map['avatar_url'];
73 | dataBean.large_avatar_url = map['large_avatar_url'];
74 | dataBean.medium_avatar_url = map['medium_avatar_url'];
75 | dataBean.small_avatar_url = map['small_avatar_url'];
76 | dataBean.created_at = map['created_at'];
77 | dataBean.updated_at = map['updated_at'];
78 | dataBean._serializer = map['_serializer'];
79 | dataBean.id = map['id'];
80 | dataBean.space_id = map['space_id'];
81 | dataBean.account_id = map['account_id'];
82 | dataBean.books_count = map['books_count'];
83 | dataBean.public_books_count = map['public_books_count'];
84 | dataBean.followers_count = map['followers_count'];
85 | dataBean.following_count = map['following_count'];
86 | dataBean.public = map['public'];
87 | return dataBean;
88 | }
89 |
90 | static List fromMapList(dynamic mapList) {
91 | List list = new List(mapList.length);
92 | for (int i = 0; i < mapList.length; i++) {
93 | list[i] = fromMap(mapList[i]);
94 | }
95 | return list;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/lib/logic/all_logic.dart:
--------------------------------------------------------------------------------
1 | export 'login_page_logic.dart';
2 | export 'main_page_logic.dart';
3 | export 'repository_page_logic.dart';
4 | export 'article_page_logic.dart';
5 | export 'article_detail_page_logic.dart';
6 |
--------------------------------------------------------------------------------
/lib/logic/article_page_logic.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 | import 'package:yuque/model/article_page_event.dart';
4 | import 'package:yuque/json/article_list_bean.dart';
5 | import 'package:yuque/json/local_repolist_bean.dart';
6 | import 'package:yuque/json/repo_directory_bean.dart';
7 | import 'package:yuque/public/api_service.dart';
8 | import 'package:yuque/utils/shared_util.dart';
9 | import 'package:yuque/widget/loading_widget.dart';
10 |
11 | class ArticlePageLogic{
12 |
13 | final ArticlePageModel _model;
14 |
15 | ArticlePageLogic(this._model);
16 |
17 | void getArticleList() async{
18 | _model.setLoadingFlag(LoadingFlag.loading);
19 | String token = await SharedUtil.instance.getString(Keys.xToken);
20 | ApiService.getInstance().getRepoArticles(token, _model.nameSpace, (data){
21 | ArticleListBean bean = ArticleListBean.fromMap(data);
22 | _model.articleList.clear();
23 | _model.articleList.addAll(bean.data);
24 | _model.loadingFlag = LoadingFlag.success;
25 | _model.refresh();
26 | }, (msg){
27 | _model?.setLoadingFlag(LoadingFlag.error);
28 | _model?.scaffoldKey?.currentState?.showSnackBar(SnackBar(
29 | backgroundColor: Colors.redAccent,
30 | content: Text(
31 | "出错了 -.-",
32 | style: TextStyle(color: Colors.white),
33 | textAlign: TextAlign.center,
34 | )));
35 | });
36 | }
37 |
38 | void getRepoDirList() async{
39 | _model.setLoadingFlag(LoadingFlag.loading);
40 | String token = await SharedUtil.instance.getString(Keys.xToken);
41 | ApiService.getInstance().getRepoDirectory(token, _model.nameSpace, (data){
42 | RepoDirectoryBean bean = RepoDirectoryBean.fromMap(data);
43 | _model.repoDirList.clear();
44 | _model.repoDirList.addAll(bean.data);
45 | _model.loadingFlag = LoadingFlag.success;
46 | _model.refresh();
47 | }, (msg){
48 | _model?.setLoadingFlag(LoadingFlag.error);
49 | _model?.scaffoldKey?.currentState?.showSnackBar(SnackBar(
50 | backgroundColor: Colors.redAccent,
51 | content: Text(
52 | "出错了 -.-",
53 | style: TextStyle(color: Colors.white),
54 | textAlign: TextAlign.center,
55 | )));
56 | });
57 | }
58 |
59 |
60 | void articleListPushAndSave(String title,String slug,String nameSpace,{int number}) async{
61 | List stringList = await SharedUtil().getListWithToken(Keys.recentRepo)??[];
62 | List repoLists = LocalRepoListBean.fromStringList(stringList);
63 | for (var i = 0; i < repoLists.length; i++) {
64 | if(repoLists[i].nameSpace == nameSpace){
65 | List articleBeans = List.from(repoLists[i].articleBeans);
66 | var articleBean = ArticleBean(articleTitle: title,articleSlug: slug,nameSpace: nameSpace);
67 | if(!LocalRepoListBean.containArticle(articleBean, articleBeans)){
68 | articleBeans.insert(0, articleBean);
69 | }
70 | if(articleBeans.length > number) articleBeans.removeRange(number - 1, articleBeans.length - 1);
71 | repoLists[i].articleBeans = articleBeans;
72 | }
73 | }
74 |
75 | List newStringList = LocalRepoListBean.fromBeanList(repoLists);
76 | SharedUtil().saveListWithToken(Keys.recentRepo, newStringList);
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/lib/logic/login_page_logic.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:yuque/model/login_page_event.dart';
4 | import 'package:yuque/pages/provider_pages.dart';
5 | import 'package:yuque/public/api_service.dart';
6 | import 'package:yuque/json/user_info_bean.dart';
7 | import 'package:yuque/utils/shared_util.dart';
8 |
9 |
10 | class LoginPageLogic {
11 | final LoginPageModel _model;
12 |
13 | LoginPageLogic(this._model);
14 |
15 | void onAnimationButtonTap() {
16 | debugPrint("点击登陆按钮");
17 | if (_model.currentAnimation == "normal") {
18 | _model.setCurrentAnimation("loading");
19 | doLogin(_model.context);
20 | }
21 | }
22 |
23 | void animationCallBack(String currentAnimation, BuildContext context) {
24 | switch (currentAnimation) {
25 | case "success":
26 | _model.setCurrentAnimation("normal");
27 | SharedUtil().saveBoolean(Keys.hasLogged, true);
28 | Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(builder: (context){
29 | return ProviderPages.getInstance().getMainPage();
30 | }), (router) => router == null);
31 | break;
32 | case "fail":
33 | _model.setCurrentAnimation("normal");
34 | break;
35 | case "loading":
36 | // _event.setCurrentAnimation("loading");
37 | // doLogin(context);
38 | break;
39 | }
40 | }
41 |
42 | void doLogin(BuildContext context) {
43 | ApiService.getInstance().getUserInfo(_model.token, (data) {
44 | UserInfoBean userInfoBean = UserInfoBean.fromMap(data);
45 | SharedUtil.instance.saveString(Keys.xToken, _model.token);
46 | SharedUtil.instance.saveString(Keys.username, userInfoBean.data.name);
47 | SharedUtil.instance.saveString(Keys.userId, userInfoBean.data.login);
48 | SharedUtil.instance.saveString(
49 | Keys.userAvatarUrl, userInfoBean.data.medium_avatar_url);
50 | _model.setCurrentAnimation("success");
51 | }, (msg) {
52 | print("网络请求错误:${msg}");
53 | _model?.setCurrentAnimation("fail");
54 | _model?.scaffoldKey?.currentState?.showSnackBar(SnackBar(
55 | backgroundColor: Theme
56 | .of(context)
57 | .primaryColor,
58 | content: Text(
59 | "出错了 -.-",
60 | style: TextStyle(color: Colors.white),
61 | textAlign: TextAlign.center,
62 | )));
63 | });
64 | }
65 |
66 | double isTextOpacity() {
67 | return _model.currentAnimation == "normal" ? 1 : 0;
68 | }
69 |
70 | String invalidToken(String token) {
71 | if (token.isEmpty) {
72 | return 'token不能为空';
73 | } else if (token.length > 50) {
74 | return 'token应该没有这么长吧';
75 | } else {
76 | _model.token = token;
77 | return null;
78 | }
79 | }
80 |
81 | getClipboard() async{
82 | ClipboardData tt = await Clipboard.getData(Clipboard.kTextPlain);
83 | debugPrint("粘贴的内容:${tt?.text}");
84 | _model.textControll.text = tt?.text??"";
85 | _model.token = tt?.text??"";
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/logic/repository_page_logic.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:yuque/model/repository_page_event.dart';
3 | import 'package:yuque/json/group_repository_bean.dart';
4 | import 'package:yuque/json/local_repolist_bean.dart';
5 | import 'package:yuque/public/api_service.dart';
6 | import 'package:yuque/utils/shared_util.dart';
7 | import 'package:yuque/widget/loading_widget.dart';
8 |
9 | class RepositoryPageLogic{
10 |
11 | final RepositoryPageModel _model;
12 |
13 | RepositoryPageLogic(this._model);
14 |
15 | void getRepoList() async{
16 | _model.setLoadingFlag(LoadingFlag.loading);
17 | String token = await SharedUtil.instance.getString(Keys.xToken);
18 | ApiService.getInstance().getGroupRepo(token, _model.groupId, (data){
19 | GroupRepositoryBean bean = GroupRepositoryBean.fromMap(data);
20 | _model.dataList.clear();
21 | _model.dataList.addAll(bean.data);
22 | _model.loadingFlag = LoadingFlag.success;
23 | _model.refresh();
24 | }, (msg){
25 | _model?.setLoadingFlag(LoadingFlag.error);
26 | _model?.scaffoldKey?.currentState?.showSnackBar(SnackBar(
27 | backgroundColor: Colors.redAccent,
28 | content: Text(
29 | "出错了 -.-",
30 | style: TextStyle(color: Colors.white),
31 | textAlign: TextAlign.center,
32 | )));
33 | });
34 | }
35 |
36 |
37 | void repoListReadAndSave(String teamName, String repositoryName, String nameSpace, String groupLoginId, String groupAvatar) async{
38 | LocalRepoListBean repoListBean = LocalRepoListBean(
39 | teamName: teamName,
40 | repoName: repositoryName,
41 | nameSpace: nameSpace,
42 | groupLoginId: groupLoginId,
43 | groupAvatarUrl: groupAvatar,
44 | articleBeans: [],
45 | );
46 | List stringList = await SharedUtil().getListWithToken(Keys.recentRepo)??[];
47 | List repoLists = LocalRepoListBean.fromStringList(stringList);
48 |
49 | if(!LocalRepoListBean.containsRepoByNameSpace(repoListBean, repoLists)){
50 | repoLists.add(repoListBean);
51 | }
52 | List newStringList = LocalRepoListBean.fromBeanList(repoLists);
53 | SharedUtil().saveListWithToken(Keys.recentRepo, newStringList);
54 | }
55 |
56 |
57 |
58 | }
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:jpush_flutter/jpush_flutter.dart';
6 | import 'package:yuque/utils/shared_util.dart';
7 | import 'pages/provider_pages.dart';
8 | //import 'package:flutter_bugly/flutter_bugly.dart';
9 |
10 | //void main()=>FlutterBugly.postCatchedException((){
11 | // // 强制竖屏
12 | // SystemChrome.setPreferredOrientations([
13 | // DeviceOrientation.portraitUp,
14 | // DeviceOrientation.portraitDown
15 | // ]);
16 | //
17 | // runApp(MyApp());
18 | //}, useLog: false);
19 |
20 | void main(){
21 | SystemChrome.setPreferredOrientations([
22 | DeviceOrientation.portraitUp,
23 | DeviceOrientation.portraitDown
24 | ]);
25 | runApp(MyApp());
26 | }
27 |
28 | class MyApp extends StatefulWidget {
29 | // This widget is the root of your application.
30 | @override
31 | _MyAppState createState() => _MyAppState();
32 | }
33 |
34 | class _MyAppState extends State {
35 | @override
36 | void initState() {
37 | super.initState();
38 | print('极光开启');
39 | JPush().applyPushAuthority(
40 | new NotificationSettingsIOS(sound: true, alert: true, badge: true),
41 | );
42 |
43 |
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | return MaterialApp(
49 | title: '滴雀',
50 | theme: ThemeData(
51 | primaryColor: Color.fromRGBO(106 , 180, 255, 1),
52 | primaryTextTheme: TextTheme(
53 | title: TextStyle(color: Colors.white),
54 |
55 | ),
56 | appBarTheme: AppBarTheme(iconTheme: IconThemeData(
57 | color: Colors.white,
58 | )),
59 | iconTheme: IconThemeData(
60 | color: Colors.lightBlue,
61 | )
62 | ),
63 | home: FutureBuilder(
64 | future: SharedUtil.instance.getBoolean(Keys.hasLogged),
65 | builder: (context, snapshot) {
66 | bool hasLogged = snapshot?.data ?? false;
67 | return hasLogged
68 | ? ProviderPages.getInstance().getMainPage()
69 | : ProviderPages.getInstance().getLoginPage();
70 | }),
71 | );
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/lib/model/all_model.dart:
--------------------------------------------------------------------------------
1 | export 'login_page_event.dart';
2 | export 'main_page_event.dart';
3 | export 'repository_page_event.dart';
4 | export 'article_page_event.dart';
5 | export 'article_detail_page_event.dart';
6 |
--------------------------------------------------------------------------------
/lib/model/article_detail_page_event.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:yuque/json/article_detail_bean.dart';
3 | import 'package:yuque/logic/article_detail_page_logic.dart';
4 | import 'package:yuque/widget/loading_widget.dart';
5 | class ArticleDetailPageModel extends ChangeNotifier{
6 |
7 | ArticleDetailPageLogic logic;
8 | BuildContext context;
9 | LoadingFlag loadingFlag = LoadingFlag.loading;
10 | final scaffoldKey = GlobalKey();
11 | String nameSpace;
12 | String articleSlug;
13 | ArticleDetailBean bean;
14 |
15 |
16 | ArticleDetailPageModel(){
17 | logic = ArticleDetailPageLogic(this);
18 | }
19 |
20 | void setContext(BuildContext context){
21 | if(this.context == null){
22 | this.context = context;
23 | logic.getArticleDetail();
24 | }
25 | }
26 |
27 | @override
28 | void dispose(){
29 | super.dispose();
30 | scaffoldKey?.currentState?.dispose();
31 | debugPrint("ArticleDetailPageEvent销毁了");
32 | }
33 |
34 | void refresh(){
35 | notifyListeners();
36 | }
37 |
38 | void setInitialData(String nameSpace, String articleSlug) {
39 | this.nameSpace = nameSpace;
40 | this.articleSlug = articleSlug;
41 | }
42 |
43 | void setArticleDetailBean(ArticleDetailBean bean){
44 | if(this.bean == null){
45 | this.bean = bean;
46 | refresh();
47 | }
48 | }
49 |
50 | void setLoadingFlag(LoadingFlag flag) {
51 | if (this.loadingFlag != flag) {
52 | this.loadingFlag = flag;
53 | notifyListeners();
54 | }
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/lib/model/article_page_event.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:yuque/json/article_list_bean.dart';
3 | import 'package:yuque/json/repo_directory_bean.dart';
4 | import 'package:yuque/logic/article_page_logic.dart';
5 | import 'package:yuque/public/NetManager.dart';
6 | import 'package:yuque/widget/loading_widget.dart';
7 |
8 |
9 | class ArticlePageModel extends ChangeNotifier{
10 |
11 | ArticlePageLogic logic;
12 | BuildContext context;
13 | String nameSpace;
14 | LoadingFlag loadingFlag = LoadingFlag.loading;
15 | final scaffoldKey = GlobalKey();
16 | List articleList = [];
17 | List repoDirList = [];
18 |
19 | ArticlePageModel(){
20 | logic = ArticlePageLogic(this);
21 | }
22 |
23 | void setContext(BuildContext context){
24 | if(this.context == null){
25 | this.context = context;
26 | logic.getRepoDirList();
27 | }
28 | }
29 |
30 | @override
31 | void dispose(){
32 | super.dispose();
33 | scaffoldKey?.currentState?.dispose();
34 | debugPrint("ArticlePageEvent销毁了");
35 | }
36 |
37 | void refresh(){
38 | notifyListeners();
39 | }
40 |
41 | void setNameSpace(String nameSpace) {this.nameSpace = nameSpace;}
42 |
43 | void setLoadingFlag(LoadingFlag flag) {
44 | if (this.loadingFlag != flag) {
45 | this.loadingFlag = flag;
46 | notifyListeners();
47 | }
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/lib/model/login_page_event.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:yuque/logic/login_page_logic.dart';
3 | import 'package:yuque/utils/shared_util.dart';
4 |
5 | class LoginPageModel extends ChangeNotifier{
6 |
7 | String currentAnimation = "normal";
8 | String token = "";
9 | final scaffoldKey = GlobalKey();
10 | final textControll = TextEditingController();
11 | LoginPageLogic logic;
12 | BuildContext context;
13 |
14 |
15 | LoginPageModel(){
16 | logic = LoginPageLogic(this);
17 | debugPrint("进入到结构类");
18 | }
19 |
20 | @override
21 | void dispose(){
22 | super.dispose();
23 | scaffoldKey?.currentState?.dispose();
24 | textControll?.dispose();
25 | debugPrint("LoginPageEvent销毁了");
26 | }
27 |
28 | void refresh(){
29 | notifyListeners();
30 | }
31 |
32 | void setCurrentAnimation(String animation){
33 | if(currentAnimation != animation){
34 | currentAnimation = animation;
35 | notifyListeners();
36 | }
37 | }
38 |
39 | void setContext(BuildContext context){
40 | if(this.context == null)
41 | this.context = context;
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/lib/model/main_page_event.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter/rendering.dart';
7 | import 'package:jpush_flutter/jpush_flutter.dart';
8 | import 'package:pull_to_refresh/pull_to_refresh.dart';
9 | import 'package:yuque/json/group_repository_bean.dart';
10 | import 'package:yuque/json/local_repolist_bean.dart';
11 | import 'package:yuque/json/user_groups_bean.dart';
12 | import 'package:yuque/logic/main_page_logic.dart';
13 | import 'package:yuque/pages/provider_pages.dart';
14 | import 'package:yuque/utils/check_update_util.dart';
15 | import 'package:yuque/widget/loading_widget.dart';
16 | //import 'package:flutter_bugly/flutter_bugly.dart';
17 |
18 | class MainPageModel extends ChangeNotifier {
19 | MainPageLogic logic;
20 | BuildContext context;
21 | LoadingFlag groupLoadingFlag = LoadingFlag.loading;
22 | LoadingFlag personRepoLoadingFlag = LoadingFlag.loading;
23 | bool isEditing = false;
24 | int currentExplosionWidget = -99;
25 | List groupsBeans = [];
26 | List personRepoBeans = [];
27 | List deleteChooseList = [];
28 | List deleteCheckList = [];
29 | final scaffoldKey = GlobalKey();
30 | final refreshController = RefreshController();
31 |
32 | MainPageModel() {
33 | logic = MainPageLogic(this);
34 | }
35 |
36 | @override
37 | void dispose() {
38 | super.dispose();
39 | // refreshController.dispose();
40 | scaffoldKey?.currentState?.dispose();
41 | debugPrint("MainPageEvent销毁了");
42 | }
43 |
44 | void notifyMessagePush(notify) {
45 | var extras;
46 | if (Platform.isAndroid) {
47 | var extra = notify['extras'];
48 | var result = extra['cn.jpush.android.EXTRA'];
49 | extras = jsonDecode(result);
50 | } else {
51 | extras = notify['extras'];
52 | }
53 | String title = extras['title'];
54 | String slug = extras['slug'];
55 | String nameSpace = extras['nameSpace'];
56 | print("分别是:$title\n$slug\n$nameSpace");
57 | Navigator.of(context).push(
58 | new CupertinoPageRoute(
59 | builder: (context) {
60 | return ProviderPages.getInstance()
61 | .getArticleDetailPage(title, slug, nameSpace);
62 | },
63 | ),
64 | );
65 | }
66 |
67 | void refresh() {
68 | notifyListeners();
69 | }
70 |
71 | void setContext(BuildContext context) {
72 | if (this.context == null) {
73 | //未启动App,点击推送
74 | JPush().getLaunchAppNotification().then((notify) {
75 | if (notify['extras'] != null) {
76 | print('首次打开==$notify');
77 | notifyMessagePush(notify);
78 | }
79 | });
80 |
81 | logic.getGroups();
82 | logic.getPersonRepos();
83 | CheckUpdateUtil().checkUpdate(context);
84 | JPush().addEventHandler(onReceiveMessage: (notify) {
85 | debugPrint("收到信息:$notify");
86 | // notifyMessagePush(notify);
87 | }, onReceiveNotification: (notify) {
88 | debugPrint("收到通知内容:${notify}");
89 | if (Platform.isIOS) {
90 | var content = notify['aps']['alert'];
91 | var extras = notify['extras'];
92 | String title = extras['title'];
93 | String slug = extras['slug'];
94 | String nameSpace = extras['nameSpace'];
95 |
96 | scaffoldKey.currentState.showSnackBar(SnackBar(
97 | backgroundColor: Theme.of(context).primaryColor,
98 | // duration: Duration(seconds: 2),
99 | content: CupertinoButton(
100 | child: Text(
101 | content,
102 | style: TextStyle(color: Colors.white),
103 | ),
104 | onPressed: () {
105 | Navigator.of(context).push(
106 | new CupertinoPageRoute(
107 | builder: (context) {
108 | return ProviderPages.getInstance()
109 | .getArticleDetailPage(title, slug, nameSpace);
110 | },
111 | ),
112 | );
113 | },
114 | ),
115 | ));
116 | }
117 |
118 | // notifyMessagePush(notify);
119 | }, onOpenNotification: (notify) {
120 | print('点击推送--$notify');
121 | notifyMessagePush(notify);
122 | });
123 |
124 | this.context = context;
125 | }
126 | }
127 |
128 | void setGroupLoadingFlag(LoadingFlag flag) {
129 | if (this.groupLoadingFlag != flag) {
130 | this.groupLoadingFlag = flag;
131 | notifyListeners();
132 | }
133 | }
134 |
135 | void setPersonRepoLoadingFlag(LoadingFlag flag) {
136 | if (this.personRepoLoadingFlag != flag) {
137 | this.personRepoLoadingFlag = flag;
138 | notifyListeners();
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/lib/model/repository_page_event.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:yuque/json/group_repository_bean.dart';
3 | import 'package:yuque/logic/repository_page_logic.dart';
4 | import 'package:yuque/widget/loading_widget.dart';
5 |
6 | class RepositoryPageModel extends ChangeNotifier {
7 |
8 | RepositoryPageLogic logic;
9 | BuildContext context;
10 | String groupId;
11 | final scaffoldKey = GlobalKey();
12 | List dataList = [];
13 | LoadingFlag loadingFlag = LoadingFlag.loading;
14 |
15 |
16 | RepositoryPageModel() {
17 | logic = RepositoryPageLogic(this);
18 | }
19 |
20 | void setContext(BuildContext context) {
21 | if (this.context == null) {
22 | this.context = context;
23 | logic.getRepoList();
24 | }
25 | }
26 |
27 | @override
28 | void dispose() {
29 | super.dispose();
30 | scaffoldKey?.currentState?.dispose();
31 | debugPrint("RepositoryEvent销毁了");
32 | }
33 |
34 | void refresh() {
35 | notifyListeners();
36 | }
37 |
38 | void setGroupId(String groupId) {
39 | this.groupId = groupId;
40 | }
41 |
42 | void setLoadingFlag(LoadingFlag flag) {
43 | if (this.loadingFlag != flag) {
44 | this.loadingFlag = flag;
45 | notifyListeners();
46 | }
47 | }
48 |
49 |
50 | }
--------------------------------------------------------------------------------
/lib/pages/about_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:yuque/pages/all_pages.dart';
4 | import 'dart:io';
5 | import 'package:font_awesome_flutter/font_awesome_flutter.dart';
6 |
7 | class AboutPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: AppBar(title: Text("关于"),),
12 | body: Container(
13 | child: ListView(
14 | scrollDirection: Axis.vertical,
15 | children: [
16 | ListTile(
17 | leading: Icon(Icons.supervised_user_circle, color: Theme.of(context).primaryColor,),
18 | title: Text("关于我们"),
19 | trailing: Icon(Icons.keyboard_arrow_right,),
20 | onTap: (){
21 | Navigator.of(context).push(new CupertinoPageRoute(builder: (ctx){
22 | return WebViewPage("https://femessage.github.io/blog/", title: "关于我们",);
23 | }));
24 | },
25 | ),
26 | ListTile(
27 | leading: Icon(Platform.isAndroid?Icons.android:FontAwesomeIcons.apple, color: Theme.of(context).primaryColor,),
28 | title: Text("版本信息"),
29 | trailing: Icon(Icons.keyboard_arrow_right,),
30 | onTap: (){
31 | Navigator.of(context).push(new CupertinoPageRoute(builder: (ctx){
32 | return VersionPage();
33 | }));
34 | },
35 | ),
36 | ],
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/pages/all_pages.dart:
--------------------------------------------------------------------------------
1 | export 'login_page.dart';
2 | export 'main_page.dart';
3 | export 'repository_page.dart';
4 | export 'article_detail_page.dart';
5 | export 'article_page.dart';
6 | export 'feedback_page.dart';
7 | export 'image_page.dart';
8 | export 'webview_page.dart';
9 | export 'version_page.dart';
10 |
--------------------------------------------------------------------------------
/lib/pages/article_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:yuque/model/article_page_event.dart';
6 | import 'package:yuque/pages/provider_pages.dart';
7 | import 'package:yuque/pages/webview_page.dart';
8 | import 'package:yuque/widget/loading_widget.dart';
9 |
10 | class ArticlePage extends StatelessWidget {
11 | final String repositoryName;
12 | final String nameSpace;
13 |
14 | ArticlePage(this.repositoryName, this.nameSpace);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | final model = Provider.of(context);
19 | model.setNameSpace(nameSpace);
20 | model.setContext(context);
21 | return Scaffold(
22 | key: model.scaffoldKey,
23 | appBar: AppBar(
24 | title: Text(repositoryName),
25 | ),
26 | body: Container(
27 | child: model.loadingFlag != LoadingFlag.success
28 | ? LoadingWidget(
29 | text: "文章列表加载中...",
30 | errorCallBack: model.logic.getRepoDirList,
31 | flag: model.loadingFlag,
32 | )
33 | : ListView.builder(
34 | padding: EdgeInsets.all(10),
35 | itemCount: model.repoDirList.length,
36 | itemBuilder: (context, index) {
37 | final data = model.repoDirList[index];
38 | final depth = data.depth.toInt();
39 | // debugPrint("深度:$depth");
40 |
41 | return Container(
42 | child: Row(
43 | children: [
44 | Expanded(
45 | child: InkWell(
46 | onTap: data.slug == "#"
47 | ? null
48 | : () {
49 | if(data.slug.contains("http")){
50 | Navigator.of(context).push(new CupertinoPageRoute(builder: (context) {
51 | return WebViewPage(data.slug);
52 | }));
53 | return;
54 | }
55 | double screenWidth =
56 | MediaQuery.of(context).size.width;
57 | int num = (screenWidth / 2 / 40).toInt();
58 | Navigator.of(context).push(
59 | new CupertinoPageRoute(
60 | builder: (context) {
61 | model.logic.articleListPushAndSave(
62 | model.repoDirList[index].title,
63 | model.repoDirList[index].slug,
64 | model.nameSpace,
65 | number: num);
66 | return ProviderPages.getInstance()
67 | .getArticleDetailPage(
68 | model.repoDirList[index].title,
69 | model.repoDirList[index].slug,
70 | model.nameSpace);
71 | }));
72 | },
73 | child: Container(
74 | margin: EdgeInsets.only(
75 | left: (20 * (depth - 1).toDouble() + 10), right: 10, top: 10,bottom: 10),
76 | child: Text(
77 | data.title,
78 | textAlign: TextAlign.left,
79 | style: data.slug == "#"
80 | ? TextStyle(
81 | fontSize: 16, color: Colors.black54)
82 | : TextStyle(
83 | fontSize: 16,
84 | ),
85 | overflow: TextOverflow.ellipsis,
86 | ),
87 | ),
88 | ),
89 | flex: 9,
90 | ),
91 | Expanded(
92 | flex: 1,
93 | child: data.slug == "#"
94 | ? SizedBox()
95 | : Icon(Icons.keyboard_arrow_right))
96 | ],
97 | ),
98 | );
99 | },
100 | ),
101 | ),
102 | );
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/lib/pages/explain_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:yuque/pages/image_page.dart';
3 |
4 | class ExplainHome extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | Widget _titleWidget(text) {
8 | return Container(
9 | padding: EdgeInsets.only(bottom: 5.0),
10 | child: Text(
11 | text,
12 | style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
13 | ),
14 | );
15 | }
16 |
17 | Widget _describeWidget(text) {
18 | return Container(
19 | padding: EdgeInsets.only(bottom: 5.0),
20 | child: Text(
21 | text,
22 | style: TextStyle(fontSize: 13, fontWeight: FontWeight.normal),
23 | ),
24 | );
25 | }
26 |
27 | return Scaffold(
28 | appBar: AppBar(
29 | title: Text('滴雀'),
30 | ),
31 | body: SingleChildScrollView(
32 | padding: EdgeInsets.fromLTRB(15, 30, 15, 0),
33 | child: Column(
34 | crossAxisAlignment: CrossAxisAlignment.start,
35 | children: [
36 | // _titleWidget('什么是Token?'),
37 | // _describeWidget('Token是语雀平台用于客户端/ API访问用户自己的私密数据!'),
38 | // SizedBox(
39 | // height: 25,
40 | // ),
41 | // _titleWidget('Token的作用?'),
42 | // _describeWidget(
43 | // 'Token是语雀平台用于客户端/ API访问用户自己的私密数据,不需要账户密码即可授权访问,信息更安全!'),
44 | // SizedBox(
45 | // height: 25,
46 | // ),
47 | _titleWidget('如何获取Token?'),
48 | _describeWidget('按照以下流程即可。(仅限拥有 语雀平台使用账号情况下)'),
49 | _describeWidget('1)打开浏览器登录语雀账号'),
50 | _describeWidget('2)点击右上角头像,进入“设置”页面'),
51 | _describeWidget('3)选择“Token”并点击“新建”'),
52 | _describeWidget('4)填写好“用途”(推荐写法:App使用-Token)'),
53 | _describeWidget('5)选择授权范围(推荐全部勾选以免使用时出错,App不会记录任何用户信息,也不侵犯用户隐私)'),
54 | _describeWidget('6)完成操作,即可复制Token用于登录App啦'),
55 | GestureDetector(onTap:(){
56 | showDialog(
57 | context: context,
58 | builder: (context) {
59 | return ImagePage(
60 | 'images/explain_2.png',
61 | isNetwork: false,
62 | );
63 | });
64 | },child: Container(height: 200,child: Image.asset('images/explain_2.png'))),
65 | GestureDetector(onTap:(){
66 | showDialog(
67 | context: context,
68 | builder: (context) {
69 | return ImagePage(
70 | 'images/explain.png',
71 | isNetwork: false,
72 | );
73 | });
74 | },child: Image.asset('images/explain.png')),
75 | SizedBox(height: 20,),
76 | Text("注意:请不要忘了给Token添加权限哦!", style: TextStyle(fontWeight: FontWeight.bold),),
77 | GestureDetector(onTap:(){
78 | showDialog(
79 | context: context,
80 | builder: (context) {
81 | return ImagePage(
82 | 'images/token_introduce.png',
83 | isNetwork: false,
84 | );
85 | });
86 | },child: Image.asset('images/token_introduce.png')),
87 | Container(
88 | alignment: Alignment(0, 0),
89 | child: Text(
90 | '- 到底了 -',
91 | style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 13.0),
92 | ),
93 | )
94 | ],
95 | ),
96 | ),
97 | );
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/lib/pages/image_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:photo_view/photo_view.dart';
3 | import 'package:cached_network_image/cached_network_image.dart';
4 | import 'dart:math';
5 |
6 | class ImagePage extends StatelessWidget {
7 |
8 | final String imageUrl;
9 | final bool isNetwork;
10 |
11 |
12 | ImagePage(this.imageUrl, {this.isNetwork = true});
13 |
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Scaffold(
18 | backgroundColor: Colors.transparent,
19 | body: Stack(
20 | children: [
21 | PhotoView(
22 | onTapUp: (ctx,details,value){
23 | debugPrint("up:${details}");
24 | Navigator.of(context).pop();
25 | },
26 | imageProvider: isNetwork? CachedNetworkImageProvider(imageUrl) : AssetImage(imageUrl),
27 | ),
28 | Align(
29 | alignment: Alignment(0,0.8),
30 | child: IconButton(icon: Icon(Icons.cancel, color: Colors.white70,size: 35,), onPressed: (){
31 | Navigator.of(context).pop();
32 | }),
33 | )
34 | ],
35 | )
36 | );
37 | }
38 | }
39 |
40 |
41 | class StfImagePage extends StatefulWidget {
42 |
43 | final String imageUrl;
44 |
45 |
46 | StfImagePage(this.imageUrl);
47 |
48 | @override
49 | _StfImagePageState createState() => _StfImagePageState();
50 | }
51 |
52 | class _StfImagePageState extends State {
53 |
54 | PhotoViewScaleState _state = PhotoViewScaleState.initial;
55 | bool isDragging = false;
56 | PhotoViewController controller;
57 |
58 |
59 | @override
60 | void initState() {
61 | super.initState();
62 | controller = PhotoViewController();
63 | controller.addIgnorableListener((){
64 | debugPrint("ignore");
65 | });
66 | controller.outputStateStream.listen((value){
67 | debugPrint("value:${value}");
68 | });
69 | }
70 |
71 | @override
72 | Widget build(BuildContext context) {
73 | return Scaffold(
74 | backgroundColor: Colors.transparent,
75 | body: Stack(
76 | children: [
77 | Container(
78 | width: MediaQuery.of(context).size.width,
79 | height: MediaQuery.of(context).size.height,
80 | child: PhotoView(
81 | controller: controller,
82 | scaleStateChangedCallback: (state){
83 | debugPrint("当前state:${state.index}");
84 | setState(() {
85 | _state = state;
86 | });
87 | },
88 | imageProvider: CachedNetworkImageProvider(widget.imageUrl),
89 | ),
90 | ),
91 | Align(
92 | alignment: Alignment(0,0.8),
93 | child: IconButton(icon: Icon(Icons.cancel, color: Colors.white70,size: 35,), onPressed: (){
94 | Navigator.of(context).pop();
95 | }),
96 | )
97 | ],
98 | )
99 | // Draggable(
100 | // onDragEnd: (detail){
101 | // if(detail.offset.dx.abs() > 100 || detail.offset.dy.abs() > 100){
102 | // Navigator.of(context).pop();
103 | // isDragging = false;
104 | // } else{
105 | // isDragging = false;
106 | // setState(() {
107 | //
108 | // });
109 | // }
110 | // debugPrint("结束x:${detail.offset.dx} y:${detail.offset.dy}");
111 | // },
112 | // onDragStarted: (){
113 | // isDragging = true;
114 | // setState(() {
115 | //
116 | // });
117 | // debugPrint("开始");
118 | // },
119 | // maxSimultaneousDrags: getDragFlag(),
120 | // feedback: Container(
121 | // width: MediaQuery.of(context).size.width,
122 | // height: MediaQuery.of(context).size.height,
123 | // alignment: Alignment.center,
124 | // child: CachedNetworkImage(imageUrl: widget.imageUrl,),
125 | // ),
126 | // childWhenDragging: Container(),
127 | // child: !isDragging? Stack(
128 | // children: [
129 | // Container(
130 | // width: MediaQuery.of(context).size.width,
131 | // height: MediaQuery.of(context).size.height,
132 | // child: PhotoView(
133 | // scaleStateChangedCallback: (state){
134 | // setState(() {
135 | // _state = state;
136 | // });
137 | // },
138 | // imageProvider: CachedNetworkImageProvider(widget.imageUrl),
139 | // ),
140 | // ),
141 | // Align(
142 | // alignment: Alignment(0,0.8),
143 | // child: IconButton(icon: Icon(Icons.cancel, color: Colors.white70,size: 35,), onPressed: (){
144 | // Navigator.of(context).pop();
145 | // }),
146 | // )
147 | // ],
148 | // ):Container()
149 | // ),
150 | );
151 | }
152 |
153 | int getDragFlag(){
154 | int flag = _state == PhotoViewScaleState.initial?1:0;
155 | debugPrint("flag是${flag}");
156 | return flag;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/lib/pages/login_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flare_flutter/flare_actor.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:yuque/model/login_page_event.dart';
6 | import 'package:yuque/pages/explain_page.dart';
7 |
8 | class LoginPage extends StatelessWidget {
9 | @override
10 | Widget build(BuildContext context) {
11 | final model = Provider.of(context);
12 | model.setContext(context);
13 | return Scaffold(
14 | key: model.scaffoldKey,
15 | body: Container(
16 | alignment: Alignment.center,
17 | child: SingleChildScrollView(
18 | scrollDirection: Axis.vertical,
19 | child: Column(
20 | mainAxisAlignment: MainAxisAlignment.center,
21 | crossAxisAlignment: CrossAxisAlignment.center,
22 | children: [
23 | Container(
24 | width: 300,
25 | height: 320,
26 | child: FlareActor(
27 | "flrs/main_logo.flr",
28 | animation: "rain",
29 | fit: BoxFit.contain,
30 | ),
31 | ),
32 | Container(
33 | margin: EdgeInsets.all(5),
34 | child: Text(
35 | "滴•雀",
36 | style: TextStyle(fontSize: 15, color: Colors.black38),
37 | )
38 | // Text.rich(TextSpan(
39 | // style: TextStyle(color: Colors.blueAccent, fontSize: 15), text: "滴", children: [
40 | // TextSpan(style: TextStyle(color: Colors.black), text: "•"),
41 | // TextSpan(style: TextStyle(color: Colors.green), text: "雀"),
42 | // ]))
43 | ),
44 | Container(
45 | margin: EdgeInsets.only(left: 40, right: 40),
46 | child: Form(
47 | autovalidate: true,
48 | child: TextFormField(
49 | controller: model.textControll,
50 | keyboardType: TextInputType.text,
51 | textDirection: TextDirection.ltr,
52 | validator: (token) => model.logic.invalidToken(token),
53 | decoration: InputDecoration(
54 | suffixIcon: FlatButton(
55 | onPressed: model.logic.getClipboard,
56 | child: Text(
57 | "粘贴",
58 | style: TextStyle(
59 | color: Theme.of(context).primaryColor),
60 | )),
61 | hintText: "输入你的token",
62 | labelText: "Token",
63 | ),
64 | ),
65 | ),
66 | ),
67 | SizedBox(
68 | height: 10,
69 | ),
70 | Stack(
71 | alignment: Alignment.center,
72 | children: [
73 | InkWell(
74 | onTap: model.logic.onAnimationButtonTap,
75 | child: Container(
76 | height: 50,
77 | margin: EdgeInsets.only(left: 100, right: 100),
78 | decoration: BoxDecoration(
79 | shape: BoxShape.rectangle,
80 | borderRadius: BorderRadius.circular(20),
81 | gradient: RadialGradient(//背景径向渐变
82 | colors: [
83 | Colors.white,
84 | Theme.of(context).primaryColor.withOpacity(0.8)
85 | ], center: Alignment.topLeft, radius: .99),
86 | ),
87 | child: Center(
88 | child: model.logic.isTextOpacity() == 1? Text(
89 | "登 录",
90 | style: TextStyle(
91 | fontSize: 20,
92 | color: Colors.white,
93 | fontWeight: FontWeight.bold),
94 | ):SizedBox(),
95 | ),
96 | ),
97 | ),
98 | model.logic.isTextOpacity() == 1 ? SizedBox() : Container(
99 | height: 60,
100 | width: 130,
101 | child: FlareActor(
102 | "flrs/animation_test.flr",
103 | animation: model.currentAnimation,
104 | fit: BoxFit.contain,
105 | callback: (animationName) => model.logic
106 | .animationCallBack(animationName, context),
107 | ),
108 | ),
109 |
110 | ],
111 | ),
112 | FlatButton(
113 | onPressed: () {
114 | Navigator.push(context,
115 | CupertinoPageRoute(builder: (context) {
116 | return ExplainHome();
117 | }));
118 | },
119 | child: Text(
120 | "如何获取Token?",
121 | style: TextStyle(color: Theme.of(context).primaryColor),
122 | ))
123 | ],
124 | ),
125 | ),
126 | ),
127 | );
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/lib/pages/provider_pages.dart:
--------------------------------------------------------------------------------
1 | import 'package:provider/provider.dart';
2 | import 'package:yuque/model/all_model.dart';
3 | import 'all_pages.dart';
4 |
5 | class ProviderPages{
6 | static ProviderPages _instance;
7 |
8 | static ProviderPages getInstance(){
9 | if(_instance == null){
10 | _instance = ProviderPages._internal();
11 | }
12 | return _instance;
13 | }
14 |
15 | ProviderPages._internal();
16 |
17 | ChangeNotifierProvider getLoginPage(){
18 | return ChangeNotifierProvider(
19 | builder:(context) => LoginPageModel(),
20 | child: LoginPage(),
21 | );
22 | }
23 |
24 | ChangeNotifierProvider getMainPage(){
25 | return ChangeNotifierProvider(
26 | builder:(context) => MainPageModel(),
27 | child: MainPage(),
28 | );
29 | }
30 |
31 | ChangeNotifierProvider getRepositoryPage(String name, String id,String avatarUrl){
32 | return ChangeNotifierProvider(
33 | builder:(context) => RepositoryPageModel(),
34 | child: RepositoryPage(name, id,avatarUrl),
35 | );
36 | }
37 |
38 | ChangeNotifierProvider getArticlePage(String repoName, String nameSpace){
39 | return ChangeNotifierProvider(
40 | builder:(context) => ArticlePageModel(),
41 | child: ArticlePage(repoName, nameSpace),
42 | );
43 | }
44 |
45 | ChangeNotifierProvider getArticleDetailPage(String articleName, String articleSlug, String nameSpace,{bool isFromMain}){
46 | return ChangeNotifierProvider(
47 | builder:(context) => ArticleDetailPageModel(),
48 | child: ArticleDetailPage(articleName, articleSlug, nameSpace,isFromMain: isFromMain??false,),
49 | );
50 | }
51 | }
--------------------------------------------------------------------------------
/lib/pages/repository_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:yuque/model/repository_page_event.dart';
6 | import 'package:yuque/pages/provider_pages.dart';
7 | import 'package:yuque/widget/loading_widget.dart';
8 | import 'package:yuque/widget/custom_book.dart';
9 |
10 | class RepositoryPage extends StatelessWidget {
11 | final String teamName;
12 | final String groupLoginId;
13 | final String groupAvatar;
14 |
15 | RepositoryPage(this.teamName, this.groupLoginId, this.groupAvatar);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | final model = Provider.of(context);
20 | model.setGroupId(groupLoginId);
21 | model.setContext(context);
22 | return Scaffold(
23 | key: model.scaffoldKey,
24 | appBar: AppBar(
25 | elevation: 0,
26 | title: SingleChildScrollView(
27 | child: Text(
28 | teamName,
29 | textAlign: TextAlign.end,
30 | ),
31 | ),
32 | ),
33 | body: Container(
34 | child: model.loadingFlag != LoadingFlag.success
35 | ? LoadingWidget(
36 | text: "仓库列表加载中...",
37 | errorCallBack: model.logic.getRepoList,
38 | flag: model.loadingFlag,
39 | )
40 | : ListView.builder(
41 | padding: EdgeInsets.all(10),
42 | itemCount: model.dataList.length,
43 | itemBuilder: (context, index) {
44 | return GestureDetector(
45 | onTap: () {
46 | Navigator.of(context)
47 | .push(new CupertinoPageRoute(builder: (context) {
48 | model.logic.repoListReadAndSave(
49 | teamName,
50 | model.dataList[index].name,
51 | model.dataList[index].namespace,
52 | groupLoginId,
53 | groupAvatar);
54 | return ProviderPages.getInstance().getArticlePage(
55 | model.dataList[index].name,
56 | model.dataList[index].namespace);
57 | }));
58 | },
59 | child: Card(
60 | elevation: 5,
61 | child: Container(
62 | height: 120,
63 | child: Column(
64 | mainAxisAlignment: MainAxisAlignment.start,
65 | crossAxisAlignment: CrossAxisAlignment.start,
66 | children: [
67 | SizedBox(
68 | height: 10,
69 | ),
70 | Row(
71 | children: [
72 | Expanded(
73 | flex: 1,
74 | child: Icon(
75 | Icons.home,
76 | color:
77 | Theme.of(context).primaryColorLight,
78 | )),
79 | Expanded(
80 | flex: 5,
81 | child: Text(
82 | "${model.dataList[index].name}",
83 | overflow: TextOverflow.ellipsis,
84 | style: TextStyle(
85 | color: Colors.black,
86 | fontWeight: FontWeight.bold,
87 | fontSize: 18,
88 | ),
89 | ),
90 | )
91 | ],
92 | ),
93 | SizedBox(
94 | height: 10,
95 | ),
96 | Container(
97 | margin: EdgeInsets.only(left: 18,right: 18),
98 | child: Text(
99 | model.dataList[index].description ,
100 | maxLines: 2,
101 | style: TextStyle(fontSize: 15),
102 | overflow: TextOverflow.ellipsis,
103 | )),
104 |
105 | ],
106 | ),
107 | ),
108 | ),
109 | );
110 | },
111 | ),
112 | ),
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/pages/version_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:package_info/package_info.dart';
5 | import 'package:yuque/utils/check_update_util.dart';
6 |
7 | class VersionPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 |
12 | appBar: AppBar(
13 | title: Text("版本信息"),
14 | ),
15 | body: Container(
16 | child: ListView(
17 | children: [
18 | Container(
19 | height: 100,
20 | width: 100,
21 | margin: EdgeInsets.only(top: 100),
22 | child: ClipRRect(
23 | borderRadius: BorderRadius.all(Radius.circular(50)),
24 | child: Image.asset(
25 | "images/bird.png",
26 | )),
27 | ),
28 | SizedBox(height: 20,),
29 | FutureBuilder(
30 | future: PackageInfo.fromPlatform(),
31 | builder: (context, snapshot) {
32 | PackageInfo info = snapshot.data;
33 | return Column(
34 | children: [
35 | Text(
36 | "${info?.appName ?? "滴雀"}",
37 | style: TextStyle(
38 | fontSize: 20,
39 | fontWeight: FontWeight.bold,),
40 | ),
41 | SizedBox(height: 5,),
42 | Text(
43 | "${info?.version ?? "1.0.0"}",
44 | style: TextStyle(fontSize: 12,),
45 | ),
46 | ],
47 | );
48 | }),
49 | Container(
50 | margin: EdgeInsets.only(left: 30,right: 30,top: 5),
51 | height: 1,
52 | child: Divider(),
53 | ),
54 | Platform.isAndroid ? Container(
55 | margin: EdgeInsets.only(left: 30,right: 30),
56 | child: ListTile(
57 | leading: Icon(Icons.cloud_upload,color: Theme.of(context).primaryColor,),
58 | title: Text("检查更新"),
59 | trailing: Icon(Icons.keyboard_arrow_right),
60 | onTap: (){
61 | CheckUpdateUtil().checkUpdate(context, isManual: true);
62 | },
63 | ),
64 | ):SizedBox(),
65 | Container(
66 | margin: EdgeInsets.only(left: 30,right: 30,),
67 | height: 1,
68 | child: Divider(),
69 | ),
70 | ],
71 | ),
72 | ),
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/pages/webview_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flare_flutter/flare_actor.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
4 | import 'package:yuque/widget/loading_widget.dart';
5 | import 'dart:io';
6 |
7 | class WebViewPage extends StatefulWidget {
8 | final String url;
9 | final String title;
10 |
11 | WebViewPage(this.url, {this.title});
12 |
13 | @override
14 | _WebViewPageState createState() => _WebViewPageState();
15 | }
16 |
17 | class _WebViewPageState extends State {
18 | FlutterWebviewPlugin flutterWebviewPlugin;
19 |
20 |
21 | @override
22 | void initState() {
23 | super.initState();
24 | flutterWebviewPlugin = new FlutterWebviewPlugin();
25 |
26 | }
27 |
28 |
29 | @override
30 | void dispose() {
31 | super.dispose();
32 | flutterWebviewPlugin.dispose();
33 |
34 | }
35 |
36 | @override
37 | Widget build(BuildContext context) {
38 | flutterWebviewPlugin.onProgressChanged.listen((value){
39 | if (value==1) {
40 | flutterWebviewPlugin.show();
41 | }
42 | });
43 | return WillPopScope(
44 | onWillPop: (){
45 | flutterWebviewPlugin.close().then((a){
46 | Navigator.of(context).pop();
47 | });
48 | },
49 | child: Scaffold(
50 | appBar: AppBar(title: Text(widget.title??widget.url, overflow: TextOverflow.ellipsis,),
51 | leading: IconButton(icon: Platform.isIOS?Icon(Icons.arrow_back_ios):Icon(Icons.arrow_back), onPressed: (){
52 | flutterWebviewPlugin.close().then((a){
53 | Navigator.of(context).pop();
54 | });
55 | }),
56 | ),
57 | body: WebviewScaffold(
58 | url: widget.url,
59 | hidden: true,
60 | initialChild: Center(
61 | child: LoadingWidget(
62 | text: widget.title == null?"网页加载中...":"请稍后...",
63 | errorCallBack: (){
64 | flutterWebviewPlugin.reload();
65 | },
66 | ),
67 | ),
68 | ),
69 | ),
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/public/HttpService.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:dio/dio.dart';
4 | import 'package:flutter/material.dart';
5 | import 'NetManager.dart';
6 |
7 | const String CONTENT_TYPE_JSON = "application";
8 | const String CONTENT_TYPE_FORM = "x-www-form-urlencoded";
9 | const String CONTENT_CHART_SET = 'utf-8';
10 |
11 | enum Method {
12 | GET,
13 | POST,
14 | UPLOAD,
15 | DOWNLOAD,
16 | }
17 |
18 | class ResultData {
19 | var data;
20 |
21 | /// true 请求成功 false 请求失败,show data
22 | bool result;
23 |
24 | ResultData(this.data, this.result) {
25 | print('最后数据:$result--$data');
26 | }
27 | }
28 |
29 | class HttpService {
30 | final Map headers;
31 | final String basicUrl;
32 | final BaseOptions dioOptions;
33 |
34 | HttpService({this.headers, this.basicUrl, this.dioOptions});
35 |
36 | /// get请求
37 | get(String url,
38 | {Map params,
39 | @required Function onSuccess,
40 | @required Function onError}) async {
41 | return await request(url,
42 | method: Method.GET,
43 | params: params,
44 | onSuccess: onSuccess,
45 | onError: onError);
46 | }
47 |
48 | /// post请求
49 | post(String url,
50 | {Map params,
51 | @required Function onSuccess,
52 | @required Function onError}) async {
53 | return await request(url,
54 | method: Method.POST,
55 | params: params,
56 | onSuccess: onSuccess,
57 | onError: onError);
58 | }
59 |
60 | /// 附件上传
61 | upLoad(var file, String fileName, String url,
62 | {Map params,
63 | @required Function onSuccess,
64 | @required Function onError}) async {
65 | return await request(url,
66 | method: Method.UPLOAD,
67 | params: params,
68 | file: file,
69 | fileName: fileName,
70 | onSuccess: onSuccess,
71 | onError: onError);
72 | }
73 |
74 | /// 附件下载
75 | download(String url, String savePath, @required Function onSuccess,
76 | @required Function onError) async {
77 | return await request(url,
78 | method: Method.DOWNLOAD,
79 | fileSavePath: savePath,
80 | onSuccess: onSuccess,
81 | onError: onError);
82 | }
83 |
84 | /// 请求部分
85 | request(String url,
86 | {Method method,
87 | Map params,
88 | var file,
89 | String fileName,
90 | String fileSavePath,
91 | Function onSuccess,
92 | Function onError}) async {
93 | try {
94 | Response response;
95 |
96 | NetManager manager = NetManager();
97 | manager.options = BaseOptions(
98 | // 15s 超时时间
99 | connectTimeout:15000,
100 | receiveTimeout:15000,
101 | responseType: ResponseType.json,
102 | contentType: ContentType(CONTENT_TYPE_JSON, CONTENT_TYPE_FORM,charset: CONTENT_CHART_SET),
103 | baseUrl: 'https://www.yuque.com/api/v2/',
104 |
105 | );
106 |
107 | if (this.headers != null) {
108 | manager.options.headers = headers;
109 | }
110 | if (this.basicUrl != null) {
111 | manager.options.baseUrl = basicUrl;
112 | }
113 |
114 | if (this.dioOptions != null) {
115 | manager.options = dioOptions;
116 | }
117 |
118 | print('请求' +
119 | '$method : ' +
120 | manager.options.baseUrl +
121 | url +
122 | '\nHead:${this.headers}' +
123 | '\nParams:$params');
124 | switch (method) {
125 | case Method.GET:
126 | response = await manager.get(url, queryParameters: params);
127 | break;
128 | case Method.POST:
129 | response = await manager.post(url, data: params);
130 | break;
131 | case Method.UPLOAD:
132 | {
133 | FormData formData = new FormData();
134 | if (params != null) {
135 | formData = FormData.from(params);
136 | }
137 | formData.add(
138 | "files", UploadFileInfo.fromBytes(file, fileName + '.png'));
139 | response = await manager.post(url, data: formData);
140 | break;
141 | }
142 | case Method.DOWNLOAD:
143 | response = await manager.download(url, fileSavePath);
144 | break;
145 | }
146 | return await handleDataSource(response, method, onSuccess, onError);
147 | // await handleDataSource(response, method,callback);
148 | } catch (exception) {
149 | print('请求异常了');
150 | // return ResultData(exception.toString(), false);
151 | return onError(exception.toString());
152 | }
153 | }
154 |
155 | /// 数据处理
156 | static handleDataSource(
157 | Response response, Method method, Function onSuccess, Function onError) {
158 | print('数据在处理中....');
159 |
160 | String errorMsg = "";
161 | int statusCode;
162 | statusCode = response.statusCode;
163 | if (method == Method.DOWNLOAD) {
164 | if (statusCode == 200) {
165 | /// 下载成功
166 | // return ResultData('下载成功', true);
167 |
168 | } else {
169 | /// 下载失败
170 | // return ResultData('下载失败', false);
171 | }
172 | }
173 | //处理错误部分
174 | if (statusCode < 0) {
175 | errorMsg = "网络请求错误,状态码:" + statusCode.toString();
176 | // return ResultData(errorMsg, false);
177 | return onError(errorMsg);
178 | }
179 | try {
180 | return onSuccess(response.data);
181 | // return ResultData(response.data, true);
182 |
183 | /* Map data = json.decode(response.data);
184 | if (data['code'] == 0 ) {
185 | try {
186 | return ResultData(data['data'], true);
187 | }catch (exception){
188 | return ResultData('暂无数据', false);
189 | }
190 | }else{
191 | return ResultData(data['msg'], false);
192 | } */
193 | } catch (exception) {
194 | /* List data = json.decode(response.data);
195 | return ResultData(data, true); */
196 |
197 | // return ResultData('数据解析异常', false);
198 | return onError(exception.toString());
199 | }
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/lib/public/NetManager.dart:
--------------------------------------------------------------------------------
1 | import 'package:dio/dio.dart';
2 |
3 |
4 | class NetManager extends Dio {
5 |
6 | // static const String CONTENT_TYPE_JSON = "application";
7 | // static const String CONTENT_TYPE_FORM = "x-www-form-urlencoded";
8 | // static const String CONTENT_CHART_SET = 'utf-8';
9 |
10 | // 工厂模式
11 | factory NetManager() =>_getInstance();
12 | static NetManager get instance => _getInstance();
13 | static NetManager _instance;
14 | NetManager._internal() {
15 | // 初始化
16 | }
17 |
18 | static NetManager _getInstance() {
19 | if (_instance == null) {
20 | _instance = NetManager._internal();
21 | }
22 | return _instance;
23 | }
24 | }
--------------------------------------------------------------------------------
/lib/public/api_service.dart:
--------------------------------------------------------------------------------
1 | import 'package:crypto/crypto.dart';
2 | import 'dart:convert';
3 |
4 | import 'public_header.dart';
5 |
6 | class ApiService {
7 | static ApiService _instance;
8 |
9 | static ApiService getInstance() {
10 | if (_instance == null) {
11 | _instance = ApiService._internal();
12 | }
13 | return _instance;
14 | }
15 |
16 | ApiService._internal();
17 |
18 | //获取用户信息
19 | void getUserInfo(String token, Function success, Function error) {
20 | HttpService(headers: {"X-Auth-Token": token})
21 | .get("user", onSuccess: success, onError: error);
22 | }
23 |
24 | //获取用户所在的组列表
25 | void getUserGroups(
26 | String token, String userId, Function success, Function error) {
27 | HttpService(headers: {"X-Auth-Token": token})
28 | .get("users/${userId}/groups", onSuccess: success, onError: error);
29 | }
30 |
31 | //获取某个组的仓库列表
32 | void getGroupRepo(
33 | String token, String groupId, Function success, Function error) {
34 | HttpService(headers: {"X-Auth-Token": token})
35 | .get("groups/${groupId}/repos", onSuccess: success, onError: error);
36 | }
37 |
38 | //获取某个用户的仓库列表
39 | void getPersonRepo(
40 | String token, String loginId, Function success, Function error) {
41 | HttpService(headers: {"X-Auth-Token": token})
42 | .get("users/${loginId}/repos", onSuccess: success, onError: error);
43 | }
44 |
45 | //获取某个仓库中的文章列表
46 | void getRepoArticles(
47 | String token, String nameSpace, Function success, Function error) {
48 | HttpService(headers: {"X-Auth-Token": token})
49 | .get("repos/${nameSpace}/docs", onSuccess: success, onError: error);
50 | }
51 |
52 | //获取某个仓库的目录结构
53 | void getRepoDirectory(
54 | String token, String nameSpace, Function success, Function error) {
55 | HttpService(headers: {"X-Auth-Token": token})
56 | .get("repos/${nameSpace}/toc", onSuccess: success, onError: error);
57 | }
58 |
59 | //获取某篇文章的详细信息
60 | void getArticleDetail(String token, String nameSpace, String articleSlug,
61 | Function success, Function error) {
62 | HttpService(headers: {"X-Auth-Token": token}).get(
63 | "repos/${nameSpace}/docs/${articleSlug}",
64 | onSuccess: success,
65 | onError: error);
66 | }
67 |
68 | //意见反馈
69 | void postFeedback(
70 | String message,
71 | String connectWay,
72 | Function success,
73 | Function error,
74 | ) {
75 | HttpService(basicUrl: "https://fd39c609.ap.ngrok.io/feedback").post("",
76 | onSuccess: success,
77 | onError: error,
78 | params: {"message": message, "email": connectWay});
79 | }
80 |
81 | //检测更新接口
82 | void getCheckUpdate(String token, Function success, Function error){
83 | HttpService(basicUrl: "https://www.easy-mock.com/mock/5ce268ea90fc7e1f09bce004/serverless/dique-update").get(
84 | "",
85 | params: {"_":md5.convert(Utf8Encoder().convert(token))},
86 | onSuccess: success,
87 | onError: error);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/public/public_header.dart:
--------------------------------------------------------------------------------
1 | export 'package:yuque/public/HttpService.dart';
2 | export 'package:yuque/public/theme.dart';
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/lib/public/theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 |
4 | class AppTheme {
5 | static Color greyColor = Color(0xFF666666);
6 | static Color blackColor = Color(0xFF333333);
7 | static Color lineColor = Color(0xFFEEEEEE);
8 | static Color redColor = Color(0xFFC53439);
9 | static Color whiteColor = Color(0xFFFFFFFF);
10 | static Color backgroundColor = Color(0xFFF6F8FA);
11 |
12 | static ThemeData themData = ThemeData(
13 |
14 | textTheme: TextTheme(
15 | body1: TextStyle(
16 | fontSize: 15.0
17 | ),
18 | display1: TextStyle(
19 | color: blackColor,
20 | fontSize: 13.0
21 | ),
22 | display2: TextStyle(
23 | color: blackColor,
24 | // fontWeight: FontWeight.bold,
25 | fontSize: 15.0
26 | ),
27 | display3: TextStyle(
28 | color: blackColor,
29 | // fontWeight: FontWeight.bold,
30 | fontSize: 17.0
31 | ),
32 | display4: TextStyle(
33 | color: blackColor,
34 | // fontWeight: FontWeight.bold,
35 | fontSize: 20.0
36 | ),
37 | subtitle: TextStyle(
38 | color: greyColor,
39 | fontWeight: FontWeight.normal,
40 | fontSize: 14.0
41 | ),
42 | // title: TextStyle(
43 | // color: Colors.yellow
44 | // )
45 |
46 | ),
47 | //platform: TargetPlatform.iOS,
48 | iconTheme: IconThemeData(
49 | size: 30,
50 | color: blackColor,
51 | opacity: 0.85,
52 | ),
53 |
54 | // primaryIconTheme 导航栏按钮颜色
55 | primaryIconTheme: IconThemeData(
56 | color: blackColor,
57 | ),
58 | accentColor: Colors.green, //强调颜色
59 | primarySwatch: Colors.red, //调色板,主题色但会被覆盖
60 | // primaryColorBrightness: Brightness.light,
61 | // primaryColor: Color(redColor), // appbar和tabbar:Tint的颜色
62 | appBarTheme: AppBarTheme(
63 | brightness: Brightness.light,
64 | color: whiteColor,
65 | textTheme: TextTheme(
66 |
67 | title: TextStyle(
68 | color: Colors.black,
69 | fontSize: 17,
70 | fontWeight: FontWeight.normal
71 |
72 | ),
73 |
74 | )
75 | ),
76 |
77 | scaffoldBackgroundColor: backgroundColor, // 整体的scaffold背景颜色
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/lib/utils/check_update_util.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:package_info/package_info.dart';
5 | import 'package:yuque/json/check_update_bean.dart';
6 | import 'package:yuque/public/api_service.dart';
7 | import 'package:yuque/utils/shared_util.dart';
8 | import 'package:yuque/widget/loading_dialog.dart';
9 | import 'package:yuque/widget/update_dialog.dart';
10 |
11 | class CheckUpdateUtil {
12 |
13 |
14 | Future checkUpdate(BuildContext context,{bool isManual = false}) async {
15 |
16 |
17 | if(isManual){
18 | showDialog(context: context, builder: (ctx){
19 | return LoadingDialog();
20 | });
21 | } else{
22 | String token = await SharedUtil.instance.getString(Keys.xToken);
23 | ApiService.getInstance().getCheckUpdate(token,(data) async {
24 | CheckUpdateBean bean = CheckUpdateBean.fromMap(data);
25 | print('看看更新的数据-->$data');
26 |
27 | int cloudAndroidVersion = int.parse((bean?.android?.version??"0").replaceAll(".", ""));
28 | PackageInfo packageInfo = await PackageInfo.fromPlatform();
29 | int localVersion = int.parse(packageInfo.version.replaceAll(".", ""));
30 | if(cloudAndroidVersion > localVersion){
31 | if(Platform.isAndroid){
32 | _showUpdateDialog(bean?.android?.version??"", bean.android.content, bean.android.downloadLink, bean.android.isForceUpdate == 1, context);
33 | } else if(Platform.isIOS){
34 | _showUpdateDialog(bean?.ios?.version??"", bean.ios.content, bean.ios.downloadLink, bean.ios.isForceUpdate == 1, context);
35 | }
36 | }
37 | }, (msg){
38 | debugPrint("${msg}");
39 | });
40 | }
41 |
42 | }
43 |
44 |
45 | _showUpdateDialog(String version, String updateInfo, String url,
46 | bool isForceUpgrade, BuildContext context) {
47 | showDialog(
48 | context: context,
49 | builder: (context) {
50 | return UpdateDialog(
51 | version: version,
52 | updateInfo: updateInfo,
53 | updateUrl: url,
54 | isForce: isForceUpgrade,
55 | );
56 | });
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/utils/keys.dart:
--------------------------------------------------------------------------------
1 | class Keys{
2 | static final String xToken = "x_token";
3 | static final String username = "username";
4 | static final String userId = "user_id";
5 | static final String userAvatarUrl = "user_avatar_url";
6 | static final String hasLogged = "has_logged";
7 | static final String hideRepository = "hide_repository";
8 | static final String hideGroups = "hide_groups";
9 | static final String recentRepo = "recent_repo"; //最近浏览的仓库
10 | }
--------------------------------------------------------------------------------
/lib/utils/shared_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 | export 'keys.dart';
3 | import 'keys.dart';
4 |
5 | class SharedUtil{
6 |
7 | factory SharedUtil() => _getInstance();
8 |
9 | static SharedUtil get instance => _getInstance();
10 | static SharedUtil _instance;
11 |
12 |
13 | SharedUtil._internal() {
14 | //初始化
15 | }
16 |
17 | static SharedUtil _getInstance() {
18 | if (_instance == null) {
19 | _instance = new SharedUtil._internal();
20 | }
21 | return _instance;
22 | }
23 |
24 |
25 | Future saveString (String key, String value) async{
26 | SharedPreferences prefs = await SharedPreferences.getInstance();
27 | await prefs.setString(key, value);
28 | }
29 |
30 | Future saveInt (String key, int value) async{
31 | SharedPreferences prefs = await SharedPreferences.getInstance();
32 | await prefs.setInt(key, value);
33 | }
34 |
35 | Future saveDouble (String key, double value) async{
36 | SharedPreferences prefs = await SharedPreferences.getInstance();
37 | await prefs.setDouble(key, value);
38 | }
39 |
40 | Future saveBoolean (String key, bool value) async{
41 | SharedPreferences prefs = await SharedPreferences.getInstance();
42 | await prefs.setBool(key, value);
43 | }
44 |
45 | Future saveStringList (String key, List list) async{
46 | SharedPreferences prefs = await SharedPreferences.getInstance();
47 | await prefs.setStringList(key, list);
48 | }
49 |
50 | Future saveListWithToken (String key, List list) async{
51 | SharedPreferences prefs = await SharedPreferences.getInstance();
52 | String token = await prefs.getString(Keys.xToken);
53 | await prefs.setStringList(key+token, list);
54 | }
55 |
56 |
57 |
58 |
59 | //-----------------------------------------------------get----------------------------------------------------
60 |
61 |
62 | Future getString (String key) async{
63 | SharedPreferences prefs = await SharedPreferences.getInstance();
64 | return prefs.getString(key);
65 | }
66 |
67 | Future getInt (String key) async{
68 | SharedPreferences prefs = await SharedPreferences.getInstance();
69 | return prefs.getInt(key);
70 | }
71 |
72 | Future getDouble (String key) async{
73 | SharedPreferences prefs = await SharedPreferences.getInstance();
74 | return prefs.getDouble(key);
75 | }
76 |
77 | Future getBoolean (String key) async{
78 | SharedPreferences prefs = await SharedPreferences.getInstance();
79 | return prefs.getBool(key)??false;
80 | }
81 |
82 | Future> getStringList(String key) async{
83 | SharedPreferences prefs = await SharedPreferences.getInstance();
84 | return prefs.getStringList(key);
85 | }
86 |
87 | Future> getListWithToken(String key) async{
88 | SharedPreferences prefs = await SharedPreferences.getInstance();
89 | String token = await prefs.getString(Keys.xToken);
90 | return prefs.getStringList(key+token);
91 | }
92 |
93 | }
--------------------------------------------------------------------------------
/lib/utils/toast_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'dart:async';
3 |
4 | class ToastUtil{
5 | static ToastUtil _instance;
6 |
7 | static ToastUtil getInstance(){
8 | if(_instance == null){
9 | _instance = ToastUtil._internal();
10 | }
11 | return _instance;
12 | }
13 |
14 | ToastUtil._internal();
15 |
16 | OverlayEntry _overlayEntry;
17 | Timer timeTask;
18 |
19 |
20 |
21 | void show(BuildContext context, {Widget showWidget, String text = "默认显示内容",Duration duration }){
22 | if(_overlayEntry == null){
23 | _showEntry(showWidget, context,text,duration);
24 | } else{
25 | _overlayEntry.remove();
26 | _overlayEntry = null;
27 | _showEntry(showWidget, context,text,duration);
28 | }
29 | }
30 |
31 | void _showEntry(Widget showWidget, BuildContext context, String text, Duration duration) {
32 | _overlayEntry = OverlayEntry(builder: (ctx){
33 | return showWidget??_defaultShow(text);
34 | });
35 | Overlay.of(context).insert(_overlayEntry);
36 | timeTask?.cancel();
37 | timeTask = Timer(duration??Duration(seconds: 3), (){
38 | if(_overlayEntry != null){
39 | _overlayEntry.remove();
40 | _overlayEntry = null;
41 | }
42 | });
43 | }
44 |
45 | Widget _defaultShow(String text){
46 | return Container(
47 | alignment: Alignment.bottomCenter,
48 | margin: EdgeInsets.only(bottom: 50),
49 |
50 | child: Material(
51 | borderRadius: BorderRadius.all(Radius.circular(20)),
52 | color: Colors.grey.withOpacity(0.5),
53 | child: Container(
54 | margin: EdgeInsets.fromLTRB(10, 5, 10, 5),
55 | child: Text(
56 | text,
57 | style: TextStyle(fontSize: 16, color: Colors.white),
58 | ),
59 | ),
60 | ),
61 | );
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/lib/widget/custom_book.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 |
4 | class CustomBook extends StatelessWidget {
5 |
6 | final Widget child;
7 | final Color bookLeftColor;
8 | final Color bookDownColor;
9 |
10 |
11 | CustomBook({this.child, this.bookLeftColor = Colors.green, this.bookDownColor = Colors.lightGreen});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Center(
16 | child: CustomPaint(
17 | child: child,
18 | painter: MyPainter(bookLeftColor, bookDownColor),
19 | ),
20 | );
21 | }
22 | }
23 |
24 |
25 | class MyPainter extends CustomPainter {
26 |
27 | final Color bookLeftColor;
28 | final Color bookDownColor;
29 |
30 | MyPainter(this.bookLeftColor, this.bookDownColor);
31 |
32 |
33 | @override
34 | void paint(Canvas canvas, Size size) {
35 |
36 | double distanceX = size.width / 15;
37 | double distanceY = size.height / 15;
38 |
39 | //书本最左侧
40 | var paint = Paint()
41 | ..color = bookLeftColor
42 | ..strokeWidth = 3
43 | ..style = PaintingStyle.fill;
44 |
45 | Path pathLeft = Path()
46 | ..moveTo(0, 0)
47 | ..lineTo(-distanceX, distanceY)
48 | ..lineTo(-distanceX, size.height + distanceY)
49 | ..lineTo(0, size.height);
50 | canvas.drawPath(pathLeft, paint);
51 |
52 |
53 | //书本下侧
54 | paint.color = bookDownColor;
55 |
56 | Path pathDown = Path()
57 | ..moveTo(0, size.height)
58 | ..lineTo(size.width, size.height)
59 | ..lineTo(size.width - distanceX, size.height + distanceY)
60 | ..lineTo(-distanceX, size.height + distanceY);
61 |
62 |
63 |
64 | canvas.drawPath(pathDown, paint);
65 |
66 |
67 | // canvas.drawLine(Offset(0, 0), Offset(100, 100), paint);
68 | }
69 |
70 | @override
71 | bool shouldRepaint(CustomPainter oldDelegate) {
72 | return false;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/lib/widget/hide_anim_widget.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class HideAnimWidget extends StatefulWidget {
6 |
7 | final Widget child;
8 | final bool start;
9 | final VoidCallback onComplete;
10 | final String tag;
11 |
12 | HideAnimWidget({this.child, this.start, this.onComplete, this.tag});
13 |
14 | @override
15 | _HideAnimWidgetState createState() => _HideAnimWidgetState();
16 | }
17 |
18 | class _HideAnimWidgetState extends State with SingleTickerProviderStateMixin {
19 | AnimationController controller;
20 | Animation animation;
21 |
22 | @override
23 | void initState() {
24 | super.initState();
25 | controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
26 | animation = new Tween(begin: 0.0, end: 1.0).animate(controller);
27 | controller.addStatusListener((status){
28 | debugPrint("当前动画状态:${status}");
29 | if (status == AnimationStatus.completed) {
30 | //动画执行结束时反向执行动画
31 | widget.onComplete();
32 | }
33 | });
34 | }
35 |
36 | @override
37 | void dispose() {
38 | controller.dispose();
39 | debugPrint("动画销毁 ${widget.tag}");
40 | super.dispose();
41 | }
42 |
43 | @override
44 | Widget build(BuildContext context) {
45 |
46 | return AnimatedBuilder(
47 | animation: animation,
48 | child: widget.child,
49 | builder: (BuildContext ctx, Widget child) {
50 | if(widget.start){
51 | controller.forward();
52 | } else {
53 | controller.reverse();
54 | }
55 | // debugPrint("value${animation.value} + tag:${widget.tag} start:${widget.start}");
56 | return Transform.scale(
57 | scale: (1 - (animation.value)),
58 | child: new Transform.rotate(
59 | angle: (animation.value) * pi * 4,
60 | child: child,
61 | ),
62 | );
63 | },
64 | );
65 | }
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/lib/widget/loading_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:package_info/package_info.dart';
5 | import 'package:yuque/json/check_update_bean.dart';
6 | import 'package:yuque/public/api_service.dart';
7 | import 'package:yuque/utils/shared_util.dart';
8 | import 'package:yuque/widget/update_dialog.dart';
9 | import 'loading_widget.dart';
10 |
11 | class LoadingDialog extends StatefulWidget {
12 | @override
13 | _LoadingDialogState createState() => _LoadingDialogState();
14 | }
15 |
16 | class _LoadingDialogState extends State {
17 |
18 | LoadingFlag checkUpdateFlag = LoadingFlag.loading;
19 | bool isLatest = false;
20 |
21 |
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | _checkUpdate();
27 | }
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | final width = MediaQuery.of(context).size.width;
32 | final height = MediaQuery.of(context).size.height;
33 | return Container(
34 | margin: EdgeInsets.fromLTRB(width/5, height/4, width/5, height/4),
35 | child: Card(
36 | child: !isLatest? LoadingWidget(
37 | text: "更新检测中...",
38 | errorText: "重新检测",
39 | flag: checkUpdateFlag,
40 | errorCallBack: (){
41 | _checkUpdate();
42 | },
43 | ) : Center(
44 | child: Text("已是最新版本"),
45 | )
46 | )
47 | );
48 | }
49 |
50 | Future _checkUpdate() async {
51 | setState(() {
52 | checkUpdateFlag = LoadingFlag.loading;
53 | });
54 | PackageInfo packageInfo = await PackageInfo.fromPlatform();
55 | debugPrint("当前版本:${packageInfo.version}");
56 | String token = await SharedUtil.instance.getString(Keys.xToken);
57 | ApiService.getInstance().getCheckUpdate(token, (data) async {
58 | CheckUpdateBean bean = CheckUpdateBean.fromMap(data);
59 | int cloudAndroidVersion = int.parse((bean?.android?.version??"0").replaceAll(".", ""));
60 | int localVersion = int.parse(packageInfo.version.replaceAll(".", ""));
61 | if(cloudAndroidVersion > localVersion){
62 | Navigator.of(context).pop();
63 | if(Platform.isAndroid){
64 | _showUpdateDialog(bean?.android?.version??"", bean.android.content, bean.android.downloadLink, bean.android.isForceUpdate == 0, context);
65 | } else if(Platform.isIOS){
66 | _showUpdateDialog(bean?.ios?.version??"", bean.ios.content, bean.ios.downloadLink, bean.ios.isForceUpdate == 0, context);
67 | }
68 | } else {
69 | setState(() {
70 | isLatest = true;
71 | });
72 | Future.delayed(Duration(milliseconds: 500), (){
73 | if(context != null){
74 | Navigator.of(context).pop();
75 | }
76 | });
77 | }
78 | }, (msg){
79 | debugPrint("${msg}");
80 | setState(() {
81 | checkUpdateFlag = LoadingFlag.error;
82 | });
83 | });
84 | }
85 |
86 | _showUpdateDialog(String version, String updateInfo, String url,
87 | bool isForceUpgrade, BuildContext context) {
88 | showDialog(
89 | context: context,
90 | barrierDismissible: false,
91 | builder: (context) {
92 | return UpdateDialog(
93 | version: version,
94 | updateInfo: updateInfo,
95 | updateUrl: url,
96 | isForce: isForceUpgrade,
97 | );
98 | });
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/widget/loading_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flare_flutter/flare_actor.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class LoadingWidget extends StatelessWidget {
5 | final Color progressColor;
6 | final Color textColor;
7 | final double textSize;
8 | final String text;
9 | final String emptyText;
10 | final String errorText;
11 | final LoadingFlag flag;
12 | final VoidCallback errorCallBack;
13 |
14 | LoadingWidget(
15 | {this.progressColor,
16 | this.textColor,
17 | this.textSize = 16,
18 | this.text = "加载中...",
19 | this.flag = LoadingFlag.loading,
20 | this.errorCallBack, this.emptyText, this.errorText})
21 | : assert(errorCallBack != null),
22 | assert(flag != null);
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | switch (flag) {
27 | case LoadingFlag.loading:
28 | return Center(
29 | child: Column(
30 | mainAxisAlignment: MainAxisAlignment.center,
31 | children: [
32 | Container(
33 | height: 70,
34 | width: 70,
35 | child: FlareActor(
36 | "flrs/aura.flr",
37 | animation: "Aura",
38 | fit: BoxFit.cover,
39 | )),
40 | SizedBox(
41 | height: 5,
42 | ),
43 | Text(
44 | text,
45 | style: TextStyle(
46 | fontSize: textSize, color: progressColor ?? Colors.black),
47 | )
48 | ],
49 | ),
50 | );
51 | break;
52 | case LoadingFlag.error:
53 | return Center(
54 | child: Column(
55 | mainAxisAlignment: MainAxisAlignment.center,
56 | children: [
57 | Icon(
58 | Icons.error,
59 | color: Colors.redAccent,
60 | size: 30,
61 | ),
62 | SizedBox(
63 | height: 5,
64 | ),
65 | FlatButton(
66 | onPressed: errorCallBack,
67 | child: Text(
68 | errorText??"重新加载",
69 | style: TextStyle(fontSize: 20, color: Colors.redAccent),
70 | )),
71 | ],
72 | ),
73 | );
74 | break;
75 | case LoadingFlag.success:
76 | return SizedBox();
77 | break;
78 | case LoadingFlag.empty:
79 | return Center(
80 | child: Text(
81 | emptyText??"空空如也",
82 | style: TextStyle(fontSize: 20, color: Theme.of(context).primaryColor),
83 | ),
84 | );
85 | break;
86 | }
87 | }
88 | }
89 |
90 | enum LoadingFlag { loading, error, success, empty }
91 |
--------------------------------------------------------------------------------
/lib/widget/nav_head.dart:
--------------------------------------------------------------------------------
1 | //navigation的头
2 | import 'dart:io';
3 |
4 | import 'package:cached_network_image/cached_network_image.dart';
5 | import 'package:flare_flutter/flare_actor.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter/cupertino.dart';
8 | import 'package:font_awesome_flutter/font_awesome_flutter.dart';
9 | import 'package:yuque/pages/image_page.dart';
10 | import 'package:yuque/pages/version_page.dart';
11 | import 'package:yuque/pages/webview_page.dart';
12 | import 'package:yuque/utils/check_update_util.dart';
13 | import 'package:yuque/utils/shared_util.dart';
14 | //import 'package:flutter_bugly/flutter_bugly.dart';
15 |
16 | class NavHeader extends StatelessWidget {
17 | final Widget exitWidget;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 |
22 | return Stack(children: [
23 | new Container(
24 | width: 500,
25 | height: MediaQuery.of(context).size.height,
26 | child: new FlareActor(
27 | "flrs/bg_head.flr",
28 | animation: "move",
29 | fit: BoxFit.cover,
30 | ),
31 | ),
32 | ListView(
33 | children: [
34 | UserAccountsDrawerHeader(
35 | decoration: BoxDecoration(color: Colors.transparent),
36 | accountName: FutureBuilder(
37 | future: SharedUtil.instance.getString(Keys.username),
38 | builder: (context, snapshot) {
39 | return Text(
40 | snapshot.data ?? "...",
41 | style: TextStyle(fontSize: 10, color: Colors.black54),
42 | );
43 | }),
44 | accountEmail: FutureBuilder(
45 | future: SharedUtil.instance.getString(Keys.userId),
46 | builder: (context, snapshot) {
47 | return Text(
48 | snapshot.data ?? "...",
49 | style: TextStyle(fontSize: 10, color: Colors.black54),
50 | );
51 | }),
52 | currentAccountPicture: ClipRRect(
53 | borderRadius: BorderRadius.circular(40.0),
54 | child: FutureBuilder(
55 | future: SharedUtil.instance.getString(Keys.userAvatarUrl),
56 | builder: (context, snapshot) {
57 | return snapshot.hasData
58 | ? GestureDetector(
59 | onTap: (){
60 | Navigator.of(context).push(new MaterialPageRoute(builder: (ctx){
61 | return ImagePage(snapshot.data);
62 | }));
63 | },
64 | child: CachedNetworkImage(
65 | imageUrl: snapshot.data,
66 | placeholder: (context, url) =>
67 | new CircularProgressIndicator(
68 | valueColor: new AlwaysStoppedAnimation(
69 | Theme.of(context).primaryColor),
70 | ),
71 | errorWidget: (context, url, error) => new Icon(
72 | Icons.error,
73 | color: Colors.redAccent,
74 | )),
75 | )
76 | : CircularProgressIndicator(
77 | valueColor: new AlwaysStoppedAnimation(
78 | Theme.of(context).primaryColor),
79 | );
80 | }),
81 | ),
82 |
83 | ),
84 | // ListTile(
85 | // leading: Icon(Icons.feedback, color: Theme.of(context).primaryColorLight,),
86 | // title: Text("意见反馈",),
87 | // trailing: Icon(Icons.keyboard_arrow_right),
88 | // onTap: (){
89 | // Navigator.of(context).push( new CupertinoPageRoute(builder: (context){
90 | // return FeedbackPage();
91 | // }));
92 | // },
93 | // ),
94 | ListTile(
95 | leading: Icon(Icons.supervised_user_circle, color: Theme.of(context).primaryColor,),
96 | title: Text("关于我们"),
97 | trailing: Icon(Icons.keyboard_arrow_right,),
98 | onTap: (){
99 | Navigator.of(context).push(new CupertinoPageRoute(builder: (ctx){
100 | return WebViewPage("https://femessage.github.io/blog/", title: "关于我们",);
101 | }));
102 | },
103 | ),
104 | ListTile(
105 | leading: Icon(Platform.isAndroid?Icons.android:FontAwesomeIcons.apple, color: Theme.of(context).primaryColor,),
106 | title: Text("版本信息"),
107 | trailing: Icon(Icons.keyboard_arrow_right,),
108 | onTap: (){
109 | Navigator.of(context).push(new CupertinoPageRoute(builder: (ctx){
110 | return VersionPage();
111 | }));
112 | },
113 | ),
114 |
115 | ],
116 | ),
117 | Positioned(
118 | right: 16,
119 | bottom: 16,
120 | child: exitWidget,
121 | ),
122 | ]);
123 | }
124 |
125 | NavHeader(this.exitWidget);
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/lib/widget/rotate_anim_widget.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class RotateAnimWidget extends StatefulWidget {
6 |
7 | final Widget child;
8 | final bool stop;
9 |
10 | RotateAnimWidget({this.child, this.stop});
11 |
12 | @override
13 | _RotateAnimWidgetState createState() => _RotateAnimWidgetState();
14 | }
15 |
16 | class _RotateAnimWidgetState extends State with SingleTickerProviderStateMixin {
17 | AnimationController controller;
18 | Animation animation;
19 |
20 | @override
21 | void initState() {
22 | super.initState();
23 | controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
24 | animation = new Tween(begin: -1.0, end: 1.0).animate(controller);
25 | animation.addStatusListener((status){
26 | if (status == AnimationStatus.completed) {
27 | //动画执行结束时反向执行动画
28 | controller.reverse();
29 | } else if (status == AnimationStatus.dismissed) {
30 | //动画恢复到初始状态时执行动画(正向)
31 | controller.forward();
32 | }
33 | });
34 | controller.forward();
35 | }
36 |
37 | @override
38 | void dispose() {
39 | controller.dispose();
40 | debugPrint("动画销毁");
41 | super.dispose();
42 | }
43 |
44 | @override
45 | Widget build(BuildContext context) {
46 | return AnimatedBuilder(
47 | animation: animation,
48 | child: widget.child,
49 | builder: (BuildContext ctx, Widget child) {
50 | if(widget.stop){
51 | controller.reset();
52 | };
53 | return new Transform.rotate(
54 | angle: widget.stop?0:animation.value * pi / 30,
55 | child: child,
56 | );
57 | },
58 | );
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/lib/widget/top_show_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TopAnimationShowWidget extends StatefulWidget {
4 | final Widget child;
5 | final Duration duration;
6 | final double distanceY;
7 |
8 | TopAnimationShowWidget({this.child, this.duration, this.distanceY = 0})
9 | : assert(child != null);
10 |
11 | @override
12 | _TopAnimationShowWidgetState createState() => _TopAnimationShowWidgetState();
13 | }
14 |
15 | class _TopAnimationShowWidgetState extends State
16 | with SingleTickerProviderStateMixin {
17 | AnimationController _controller;
18 | Animation _animation;
19 |
20 | @override
21 | void initState() {
22 | _controller = AnimationController(
23 | vsync: this, duration: widget.duration ?? Duration(seconds: 1));
24 | _animation = new Tween(begin: 0.0, end: 1.0)
25 | .animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutBack));
26 | _controller.forward();
27 | super.initState();
28 | }
29 |
30 | @override
31 | void dispose() {
32 | _controller.dispose();
33 | debugPrint("top_show销毁");
34 | super.dispose();
35 | }
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 |
40 | return AnimatedBuilder(
41 | animation: _animation,
42 | child: Container(child: widget.child),
43 | builder: (ctx, child) {
44 | return Transform.translate(
45 | offset: Offset(0, (_animation.value - 1) * (widget.distanceY)),
46 | child: child,
47 | );
48 | },
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/widget/update_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:dio/dio.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter/services.dart';
6 | import 'package:path_provider/path_provider.dart';
7 | import 'package:url_launcher/url_launcher.dart';
8 | import 'package:yuque/public/NetManager.dart';
9 | import 'package:open_file/open_file.dart';
10 |
11 | class UpdateDialog extends StatefulWidget {
12 | final String version;
13 | final String updateInfo;
14 | final String updateUrl;
15 | final bool isForce;
16 |
17 | UpdateDialog({
18 | this.version,
19 | this.updateInfo,
20 | this.updateUrl,
21 | this.isForce = false,
22 | });
23 |
24 | @override
25 | State createState() => new UpdateDialogState();
26 | }
27 |
28 | class UpdateDialogState extends State {
29 | int _downloadProgress = 0;
30 | NetManager manager;
31 | CancelToken token;
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | return WillPopScope(
36 | onWillPop: () async => false,
37 | child: Container(
38 | margin: EdgeInsets.only(left: 50, right: 50, top: 200, bottom: 200),
39 | decoration: BoxDecoration(
40 | shape: BoxShape.rectangle,
41 | borderRadius: BorderRadius.circular(10),
42 | color: Theme.of(context).primaryColor,
43 | ),
44 | child: Column(
45 | children: [
46 | Expanded(
47 | flex: 2,
48 | child: Container(
49 | margin: EdgeInsets.fromLTRB(5, 30, 5, 5),
50 | child: Material(
51 | child: Text(
52 | "新版本来啦!",
53 | style: TextStyle(color: Colors.white, fontSize: 20),
54 | ),
55 | color: Colors.transparent,
56 | )),
57 | ),
58 | Expanded(
59 | flex: 5,
60 | child: Container(
61 | margin: EdgeInsets.all(5),
62 | alignment: Alignment.center,
63 | child: Material(
64 | color: Colors.transparent,
65 | child: SingleChildScrollView(
66 | scrollDirection: Axis.vertical,
67 | child: Text(
68 | widget.updateInfo,
69 | style: TextStyle(color: Colors.white),
70 | ),
71 | ),
72 | ))),
73 | _downloadProgress != 0
74 | ? Expanded(
75 | child: Container(
76 | child: LinearProgressIndicator(
77 | valueColor:
78 | new AlwaysStoppedAnimation(Colors.orange),
79 | backgroundColor: Colors.grey[300],
80 | value: _downloadProgress / 100, //精确模式,进度20%
81 | ),
82 | ),
83 | flex: 1,
84 | )
85 | : SizedBox(),
86 | Expanded(
87 | flex: 2,
88 | child: Container(
89 | decoration: BoxDecoration(
90 | shape: BoxShape.rectangle,
91 | borderRadius: BorderRadius.only(
92 | bottomLeft: Radius.circular(10),
93 | bottomRight: Radius.circular(10)),
94 | color: Colors.white,
95 | ),
96 | child: Row(
97 | children: [
98 | !widget.isForce
99 | ? Expanded(
100 | flex: 1,
101 | child: FlatButton(
102 | onPressed: () {
103 | Navigator.of(context).pop();
104 | },
105 | child: Text(
106 | "取消",
107 | style: TextStyle(color: Colors.grey,fontSize: 16),
108 | )),
109 | )
110 | : SizedBox(),
111 | !widget.isForce
112 | ? Container(
113 | width: 1,
114 | color: Colors.grey[100],
115 | )
116 | : SizedBox(),
117 | Expanded(
118 | flex: 1,
119 | child: FlatButton(
120 | onPressed: () async {
121 | if (Platform.isAndroid) {
122 | _androidUpdate();
123 | } else if (Platform.isIOS) {
124 | _iosUpdate();
125 | }
126 | },
127 | child: Text(
128 | "升级",
129 | style: TextStyle(color: Colors.black,fontSize: 16),
130 | )),
131 | ),
132 | ],
133 | ),
134 | ),
135 | )
136 | ],
137 | ),
138 | ),
139 | );
140 | }
141 |
142 | void _androidUpdate() async {
143 | String path = (await getExternalStorageDirectory()).path;
144 | debugPrint("获取的目录:${path}");
145 | manager.download(widget.updateUrl, path + "/Download/" + "release.apk",
146 | cancelToken: token, onReceiveProgress: (int count, int total) {
147 | setState(() {
148 | _downloadProgress = ((count / total) * 100).toInt();
149 | if (_downloadProgress == 100) {
150 | debugPrint("读取的目录:${path}");
151 | try {
152 | OpenFile.open(path + "/Download/" + "release.apk");
153 | } catch (e) {}
154 | Navigator.of(context).pop();
155 | }
156 | });
157 | });
158 | }
159 |
160 | void _iosUpdate(){
161 | launch(widget.updateUrl);
162 | }
163 |
164 |
165 |
166 | @override
167 | void initState() {
168 | super.initState();
169 | manager = NetManager();
170 | token = new CancelToken();
171 | }
172 |
173 | @override
174 | void dispose() {
175 | super.dispose();
176 | token?.cancel();
177 | manager?.clear();
178 | debugPrint("升级销毁");
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: yuque
2 | description: A new Flutter project.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.1.0+1
15 |
16 | environment:
17 | sdk: ">=2.1.0 <3.0.0"
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 |
23 | #网络请求
24 | dio: ^2.1.2
25 | #本地存储
26 | shared_preferences: ^0.5.2
27 | #Flare插件
28 | flare_flutter: ^1.5.5
29 | #状态管理
30 | provider: ^2.0.1
31 | #html展示
32 | flutter_html: ^0.10.1+hotfix.1
33 | #图片缓存
34 | cached_network_image: ^1.1.1
35 | #拖动图片
36 | photo_view: ^0.4.2
37 | #webview
38 | flutter_webview_plugin: ^0.3.5
39 | #极光推送
40 | jpush_flutter:
41 | git:
42 | url: https://github.com/CaiJingLong/jpush-flutter-plugin.git
43 | ref: 4d847f9b2150dc4fd1f8a7620d414d60f464fab7
44 | #刷新
45 | pull_to_refresh: ^1.4.5
46 | #获取app相关信息
47 | package_info: ^0.4.0+4
48 | #app目录
49 | path_provider: ^0.5.0+1
50 | #打开文件
51 | open_file: ^2.0.3
52 | #各种图标
53 | font_awesome_flutter: ^8.4.0
54 | #单元测试
55 | test: ^1.6.0
56 |
57 |
58 | #markdown
59 | # flutter_markdown: ^0.2.0
60 | #html样式
61 | flutter_widget_from_html_core: ^0.2.2
62 | flutter_widget_from_html: ^0.2.2+1
63 | # html2md: ^0.3.1
64 |
65 |
66 | # The following adds the Cupertino Icons font to your application.
67 | # Use with the CupertinoIcons class for iOS style icons.
68 | cupertino_icons: ^0.1.2
69 |
70 | dev_dependencies:
71 | flutter_test:
72 | sdk: flutter
73 |
74 | device_info: ^0.4.0+2
75 |
76 |
77 | # For information on the generic Dart part of this file, see the
78 | # following page: https://www.dartlang.org/tools/pub/pubspec
79 |
80 | # The following section is specific to Flutter.
81 | flutter:
82 | #导入下面目录下的各种资源
83 | assets:
84 | - flrs/
85 | - images/
86 |
87 | # The following line ensures that the Material Icons font is
88 | # included with your application, so that you can use the icons in
89 | # the material Icons class.
90 | uses-material-design: true
91 |
92 | # To add assets to your application, add an assets section, like this:
93 | # assets:
94 | # - images/a_dot_burr.jpeg
95 | # - images/a_dot_ham.jpeg
96 |
97 | # An image asset can refer to one or more resolution-specific "variants", see
98 | # https://flutter.dev/assets-and-images/#resolution-aware.
99 |
100 | # For details regarding adding assets from package dependencies, see
101 | # https://flutter.dev/assets-and-images/#from-packages
102 |
103 | # To add custom fonts to your application, add a fonts section here,
104 | # in this "flutter" section. Each entry in this list should have a
105 | # "family" key with the font family name, and a "fonts" key with a
106 | # list giving the asset and other descriptors for the font. For
107 | # example:
108 | # fonts:
109 | # - family: Schyler
110 | # fonts:
111 | # - asset: fonts/Schyler-Regular.ttf
112 | # - asset: fonts/Schyler-Italic.ttf
113 | # style: italic
114 | # - family: Trajan Pro
115 | # fonts:
116 | # - asset: fonts/TrajanPro.ttf
117 | # - asset: fonts/TrajanPro_Bold.ttf
118 | # weight: 700
119 | #
120 | # For details regarding fonts from package dependencies,
121 | # see https://flutter.dev/custom-fonts/#from-packages
122 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:yuque/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------