├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .metadata ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── LICENSE ├── README.md ├── README_EN.md ├── RECORD.md ├── VERSION.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── exported.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ └── com │ │ │ └── shuyu │ │ │ └── gsygithub │ │ │ └── gsygithubappflutter │ │ │ ├── MainActivity.kt │ │ │ └── UpdateAlbumPlugin.kt │ │ └── res │ │ ├── drawable │ │ ├── launch_background.xml │ │ └── normal_background.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── launch_image.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── launch_image.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── launch_image.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── launch_image.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── launch_image.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── gsygithubapp-debug.jks └── settings.gradle ├── devtools_options.yaml ├── download.png ├── folder.png ├── framework2.png ├── googleplay.png ├── ios.gif ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── 120.jpg │ │ ├── 152.jpg │ │ ├── 167.jpg │ │ ├── 180.png │ │ ├── 40.png │ │ ├── 58.png │ │ ├── 60.jpg │ │ ├── 76.png │ │ ├── 80.jpg │ │ ├── 87.jpg │ │ ├── Contents.json │ │ ├── logo-4.png │ │ └── logo2x-3.jpg │ ├── Contents.json │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── Default@2x.png │ │ ├── Default@3x-1.png │ │ ├── Default@3x-2.png │ │ ├── Default@3x-3.png │ │ ├── Default@3x-4.png │ │ └── Default@3x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── ios_wait.png ├── l10n.yaml ├── lib ├── app.dart ├── common │ ├── config │ │ └── config.dart │ ├── event │ │ ├── event_bus.dart │ │ ├── http_error_event.dart │ │ └── index.dart │ ├── local │ │ └── local_storage.dart │ ├── localization │ │ ├── extension.dart │ │ └── l10n │ │ │ ├── app_en.arb │ │ │ ├── app_ja.arb │ │ │ ├── app_ko.arb │ │ │ ├── app_localizations.dart │ │ │ ├── app_localizations_en.dart │ │ │ ├── app_localizations_ja.dart │ │ │ ├── app_localizations_ko.dart │ │ │ ├── app_localizations_zh.dart │ │ │ └── app_zh.arb │ ├── logger.dart │ ├── net │ │ ├── address.dart │ │ ├── api.dart │ │ ├── code.dart │ │ ├── graphql │ │ │ ├── client.dart │ │ │ ├── repositories.dart │ │ │ └── users.dart │ │ ├── interceptors │ │ │ ├── error_interceptor.dart │ │ │ ├── header_interceptor.dart │ │ │ ├── log_interceptor.dart │ │ │ ├── response_interceptor.dart │ │ │ └── token_interceptor.dart │ │ ├── result_data.dart │ │ ├── transformer.dart │ │ ├── transformer.g.dart │ │ └── trending │ │ │ └── github_trending.dart │ ├── repositories │ │ ├── data_result.dart │ │ ├── event_repository.dart │ │ ├── issue_repository.dart │ │ ├── repos_repository.dart │ │ └── user_repository.dart │ ├── router │ │ └── anima_route.dart │ ├── style │ │ └── gsy_style.dart │ ├── toast.dart │ └── utils │ │ ├── code_utils.dart │ │ ├── common_utils.dart │ │ ├── event_utils.dart │ │ ├── html_utils.dart │ │ └── navigator_utils.dart ├── db │ ├── provider │ │ ├── event │ │ │ ├── received_event_db_provider.dart │ │ │ └── user_event_db_provider.dart │ │ ├── issue │ │ │ ├── issue_comment_db_provider.dart │ │ │ └── issue_detail_db_provider.dart │ │ ├── repos │ │ │ ├── read_history_db_provider.dart │ │ │ ├── repository_branch_db_provider.dart │ │ │ ├── repository_commitInfo_detail_db_provider.dart │ │ │ ├── repository_commits_db_provider.dart │ │ │ ├── repository_detail_db_provider.dart │ │ │ ├── repository_detail_readme_db_provider.dart │ │ │ ├── repository_event_db_provider.dart │ │ │ ├── repository_fork_db_provider.dart │ │ │ ├── repository_issue_db_provider.dart │ │ │ ├── repository_pulse_db_provider.dart │ │ │ ├── repository_star_db_provider.dart │ │ │ ├── repository_watcher_db_provider.dart │ │ │ └── trend_repository_db_provider.dart │ │ └── user │ │ │ ├── org_member_db_provider.dart │ │ │ ├── user_followed_db_provider.dart │ │ │ ├── user_follower_db_provider.dart │ │ │ ├── user_orgs_db_provider.dart │ │ │ ├── user_repos_db_provider.dart │ │ │ ├── user_stared_db_provider.dart │ │ │ └── userinfo_db_provider.dart │ ├── sql_manager.dart │ └── sql_provider.dart ├── env │ ├── config_wrapper.dart │ ├── dev.dart │ ├── dev.g.dart │ ├── env_config.dart │ ├── env_config.g.dart │ ├── env_json_dev.json │ ├── env_json_prod.json │ ├── prod.dart │ └── prod.g.dart ├── main.dart ├── main_prod.dart ├── model │ ├── branch.dart │ ├── branch.g.dart │ ├── commitFile.dart │ ├── commitFile.g.dart │ ├── commit_comment.dart │ ├── commit_comment.g.dart │ ├── commit_git_info.dart │ ├── commit_git_info.g.dart │ ├── commit_git_user.dart │ ├── commit_git_user.g.dart │ ├── commit_stats.dart │ ├── commit_stats.g.dart │ ├── commits_comparison.dart │ ├── commits_comparison.g.dart │ ├── common_list_datatype.dart │ ├── download_source.dart │ ├── download_source.g.dart │ ├── event.dart │ ├── event.g.dart │ ├── event_payload.dart │ ├── event_payload.g.dart │ ├── file_model.dart │ ├── file_model.g.dart │ ├── issue.dart │ ├── issue.g.dart │ ├── issue_event.dart │ ├── issue_event.g.dart │ ├── license.dart │ ├── license.g.dart │ ├── notification.dart │ ├── notification.g.dart │ ├── notification_subject.dart │ ├── notification_subject.g.dart │ ├── push_commit.dart │ ├── push_commit.g.dart │ ├── push_event_commit.dart │ ├── push_event_commit.g.dart │ ├── release.dart │ ├── release.g.dart │ ├── release_asset.dart │ ├── release_asset.g.dart │ ├── repo_commit.dart │ ├── repo_commit.g.dart │ ├── repository.dart │ ├── repository.g.dart │ ├── repository_permissions.dart │ ├── repository_permissions.g.dart │ ├── repository_ql.dart │ ├── search_user_ql.dart │ ├── template.dart │ ├── template.g.dart │ ├── trending_repo_model.dart │ ├── trending_repo_model.g.dart │ ├── user.dart │ ├── user.g.dart │ ├── user_org.dart │ └── user_org.g.dart ├── page │ ├── code_detail_page_web.dart │ ├── common_list_page.dart │ ├── debug │ │ ├── debug_data_page.dart │ │ └── debug_label.dart │ ├── dynamic │ │ ├── dynamic_bloc.dart │ │ └── dynamic_page.dart │ ├── error_page.dart │ ├── gsy_webview.dart │ ├── home │ │ ├── home_page.dart │ │ └── widget │ │ │ └── home_drawer.dart │ ├── honor_list_page.dart │ ├── issue │ │ ├── issue_detail_page.dart │ │ ├── issue_edit_dIalog.dart │ │ └── widget │ │ │ ├── issue_header_item.dart │ │ │ └── issue_item.dart │ ├── login │ │ ├── login_page.dart │ │ └── login_webview.dart │ ├── my_page.dart │ ├── notify │ │ └── notify_page.dart │ ├── photoview_page.dart │ ├── push │ │ ├── push_detail_page.dart │ │ └── widget │ │ │ ├── push_coed_item.dart │ │ │ └── push_header.dart │ ├── release │ │ ├── release_page.dart │ │ └── widget │ │ │ └── release_item.dart │ ├── repos │ │ ├── provider │ │ │ ├── repos_detail_provider.dart │ │ │ └── repos_network_provider.dart │ │ ├── repository_detail_issue_list_page.dart │ │ ├── repository_detail_page.dart │ │ ├── repository_detail_readme_page.dart │ │ ├── repository_file_list_page.dart │ │ ├── repostory_detail_info_page.dart │ │ └── widget │ │ │ ├── repos_header_item.dart │ │ │ └── repos_item.dart │ ├── search │ │ ├── search_bloc.dart │ │ ├── search_page.dart │ │ └── widget │ │ │ ├── gsy_search_drawer.dart │ │ │ └── gsy_search_input_widget.dart │ ├── trend │ │ ├── trend_page.dart │ │ ├── trend_provider.dart │ │ ├── trend_user_page.dart │ │ ├── trend_user_provider.dart │ │ └── trend_user_provider.g.dart │ ├── user │ │ ├── base_person_provider.dart │ │ ├── base_person_provider.g.dart │ │ ├── base_person_state.dart │ │ ├── person_page.dart │ │ └── widget │ │ │ ├── user_header.dart │ │ │ └── user_item.dart │ ├── user_profile_page.dart │ └── welcome_page.dart ├── provider │ ├── app_state_provider.dart │ └── app_state_provider.g.dart ├── redux │ ├── gsy_state.dart │ ├── login_redux.dart │ ├── middleware │ │ ├── combine_epics.dart │ │ ├── epic.dart │ │ ├── epic_middleware.dart │ │ └── epic_store.dart │ └── user_redux.dart ├── test │ ├── demo_app.dart │ ├── demo_appbar.dart │ ├── demo_bloc_page.dart │ ├── demo_db.dart │ ├── demo_item.dart │ ├── demo_mixins.dart │ ├── demo_page.dart │ ├── demo_tab_page.dart │ ├── demo_text_field_page.dart │ ├── demo_user_store.dart │ └── demo_widget.dart └── widget │ ├── anima │ └── curves_bezier.dart │ ├── animated_background.dart │ ├── diff_scale_text.dart │ ├── flutter_json_widget.dart │ ├── gsy_bottom_action_bar.dart │ ├── gsy_card_item.dart │ ├── gsy_common_option_widget.dart │ ├── gsy_event_item.dart │ ├── gsy_flex_button.dart │ ├── gsy_icon_text.dart │ ├── gsy_input_widget.dart │ ├── gsy_select_item_widget.dart │ ├── gsy_tabbar_widget.dart │ ├── gsy_tabs.dart │ ├── gsy_title_bar.dart │ ├── gsy_user_icon_widget.dart │ ├── markdown │ ├── gsy_markdown_widget.dart │ └── syntax_high_lighter.dart │ ├── menu │ ├── flutter_radial_menu.dart │ └── src │ │ ├── arc_progress_indicator.dart │ │ ├── radial_menu.dart │ │ ├── radial_menu_button.dart │ │ ├── radial_menu_center_button.dart │ │ └── radial_menu_item.dart │ ├── mole_widget.dart │ ├── never_overscroll_indicator.dart │ ├── only_share_widget.dart │ ├── particle │ ├── particle_model.dart │ ├── particle_painter.dart │ └── particle_widget.dart │ ├── pull │ ├── custom_bouncing_scroll_physics.dart │ ├── gsy_flare_mutli_pull_controller.dart │ ├── gsy_flare_pull_controller.dart │ ├── gsy_pull_load_widget.dart │ ├── gsy_pull_new_load_widget.dart │ ├── gsy_refresh_sliver.dart │ └── nested │ │ ├── gsy_nested_pull_load_widget.dart │ │ ├── gsy_sliver_header_delegate.dart │ │ └── nested_refresh.dart │ └── state │ └── gsy_list_state.dart ├── logo.png ├── p1.png ├── p2.png ├── p3.png ├── p4.png ├── pubspec.lock ├── pubspec.yaml ├── register0.png ├── register1.jpg ├── static ├── file │ ├── Space-Demo.flr │ ├── flare_flutter_logo_.flr │ ├── launch.riv │ ├── loading_world_now.flr │ ├── rejection.json │ ├── rejection2.json │ ├── search.json │ └── user.json ├── font │ ├── akronim.ttf │ ├── demo.css │ ├── demo_fontclass.html │ ├── demo_symbol.html │ ├── demo_unicode.html │ ├── google_kavivanar.ttf │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.svg │ ├── iconfont.ttf │ └── iconfont.woff └── images │ ├── default_img.png │ ├── logo.png │ └── welcome.png ├── thanks.jpg └── theme.gif /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: http://img.cdn.guoshuyu.cn/thanks.jpg 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | .flutter-plugins-dependencies 11 | 12 | .gradle/ 13 | 14 | ignoreConfig.dart 15 | 16 | flutter_export_environment.sh 17 | 18 | # Miscellaneous 19 | *.class 20 | *.log 21 | *.pyc 22 | *.swp 23 | .atom/ 24 | .buildlog/ 25 | .history 26 | .svn/ 27 | 28 | # IntelliJ related 29 | *.iml 30 | *.ipr 31 | *.iws 32 | .idea/ 33 | 34 | # The .vscode folder contains launch configuration and tasks you configure in 35 | # VS Code which you may wish to be included in version control, so this line 36 | # is commented out by default. 37 | #.vscode/ 38 | 39 | # Flutter/Dart/Pub related 40 | **/doc/api/ 41 | **/ios/Flutter/.last_build_id 42 | 43 | .pub-cache/ 44 | /build/ 45 | 46 | # Web related 47 | lib/generated_plugin_registrant.dart 48 | 49 | # Symbolication related 50 | app.*.symbols 51 | 52 | # Obfuscation related 53 | app.*.map.json 54 | 55 | # Exceptions to above rules. 56 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 18cd7a3601bcffb36fdf2f679f763b5e827c2e8e 8 | channel: unknown 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/1.jpg -------------------------------------------------------------------------------- /2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/2.jpg -------------------------------------------------------------------------------- /3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/3.jpg -------------------------------------------------------------------------------- /VERSION.md: -------------------------------------------------------------------------------- 1 | 2 | ### 请直接看 github release 3 | 4 | ### 1.1.9 5 | 6 | * 修正弹出键盘的时候被挤压问题 7 | * 修复切换主题导致长按输入框弹出异常 8 | * 修复其他小问题。 9 | 10 | 11 | ### 1.1.8 12 | 13 | * 修复反馈输入框遮挡问题。 14 | * 更新部分插件,更新了 sdk 到 1.1.9 15 | 16 | ### 1.1.7 17 | 18 | * 更新flutter SDK 到 1.1.3 版本,修复TargetSDK 28以上在9.0键盘无法弹出问题。 19 | 20 | 21 | 22 | ### 1.1.6 23 | * flutter升级正式版1.0 24 | 25 | 26 | ### 1.1.5 27 | 28 | * Android 代码详情使用 AndroidView 实现WebView 29 | * 升级flutter Sdk 30 | * 升级第三方包 31 | 32 | ### 1.1.3 33 | * 修复详情tab切换问题。 34 | 35 | ### 1.1.2 36 | * 增加滑动返回。 37 | * 修复主页抽屉小屏幕无法滚动。 38 | * 增加部分代码高亮。 39 | * 修复搜索排序按键问题。 40 | * 更新flutter SDK 41 | 42 | 43 | ### 1.1.1 44 | * 更新flutter SDK 0.5.8。 45 | * 修复一些仓库下的readme问题。 46 | 47 | ### 1.1.0 48 | * 切换用户切换数据库。 49 | * 多语言。 50 | 51 | ### 1.0.9 52 | * 切换主题支持 53 | * 问题修复 54 | 55 | ### 1.0.8 56 | * readme图片解析优化 57 | * readme图片增加点击查看 58 | * 组织账号不显示活跃记录Item 59 | * 增加用户组织显示 60 | 61 | 62 | 63 | ### 1.0.7 64 | * 增加图片预览 65 | * 修复未读的通知打开提示其他异常 66 | * 增加fork仓库跳转到原仓库 67 | * 增加仓库点击展示 issue 状态信息 68 | * 增加个人状态信息可跳转 69 | * 增加仓库Topic显示 70 | * 通知中心增加侧滑点击已读 71 | 72 | ### 1.0.6 73 | * trend修改为redux 74 | * 增加本地阅读历史 75 | * drawer 状态栏样式处理 76 | * 增加个人动态提交表。 77 | 78 | ### 1.0.5 79 | * 增加本地数据库 80 | * 修复分享问题。 81 | * 修改用户页面样式 82 | * 增加用户加入github时间显示 83 | 84 | ### 1.0.4 85 | 86 | * 修复启动页变形问题。 87 | * 调整个人页面字体动态大小。 88 | * 增加趋势语言,搜索语言dart选项。 89 | * 增加前后台切换刷新动态。 90 | * 增加触摸隐藏键盘。 91 | * 增加点击检测版本。 92 | * 增加 issue 使用markdown解析 93 | * 增加 issue 输入框的快速输入按键。 94 | * 增加 issue 关闭操作信息。 95 | * 返回文件列表的返回键处理逻辑。 96 | * 修复详情中存在的model转化问题。 97 | 98 | 99 | ### 1.0.1 (已发布) 100 | 101 | * 修复loading弹出框黄线问题。 102 | * 调整部分ui。 103 | * 增加Release列表。 104 | * 增加版检测。 105 | * Issue详情页显示问题。 106 | * 返回按键退出问题。 107 | 108 | 109 | 110 | ### 1.0.0 111 | 112 | * 第一版完成 -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | analyzer: 4 | errors: 5 | mixin_inherits_from_not_object: ignore 6 | plugins: 7 | - custom_lint 8 | 9 | linter: 10 | rules: 11 | non_constant_identifier_names: false 12 | file_names: false 13 | constant_identifier_names: false 14 | library_private_types_in_public_api: false 15 | library_prefixes: false -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | .cxx/ 12 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/shuyu/gsygithub/gsygithubappflutter/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.shuyu.gsygithub.gsygithubappflutter 2 | 3 | import UpdateAlbumPlugin 4 | import androidx.annotation.NonNull; 5 | import io.flutter.embedding.android.FlutterActivity 6 | import io.flutter.embedding.engine.FlutterEngine 7 | import io.flutter.plugins.GeneratedPluginRegistrant 8 | 9 | class MainActivity: FlutterActivity() { 10 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 11 | GeneratedPluginRegistrant.registerWith(flutterEngine) 12 | flutterEngine.plugins.add(UpdateAlbumPlugin()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/shuyu/gsygithub/gsygithubappflutter/UpdateAlbumPlugin.kt: -------------------------------------------------------------------------------- 1 | import android.content.Context 2 | import android.content.Intent 3 | import android.net.Uri 4 | import android.provider.MediaStore 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin 6 | import io.flutter.plugin.common.MethodCall 7 | import io.flutter.plugin.common.MethodChannel 8 | 9 | class UpdateAlbumPlugin : FlutterPlugin, MethodChannel.MethodCallHandler { 10 | 11 | /** Channel名称 **/ 12 | 13 | private var channel: MethodChannel? = null 14 | private var context: Context? = null 15 | 16 | companion object { 17 | 18 | private val sChannelName = "com.shuyu.gsygithub.gsygithubflutter/UpdateAlbumPlugin" 19 | } 20 | 21 | override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { 22 | channel = MethodChannel( 23 | binding.binaryMessenger, sChannelName) 24 | context = binding.applicationContext 25 | channel!!.setMethodCallHandler(this) 26 | } 27 | 28 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { 29 | channel?.setMethodCallHandler(null) 30 | channel = null 31 | } 32 | 33 | override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) { 34 | when (methodCall.method) { 35 | "updateAlbum" -> { 36 | val path: String? = methodCall.argument("path") 37 | val name: String? = methodCall.argument("name") 38 | try { 39 | MediaStore.Images.Media.insertImage(context?.contentResolver, path, name, null) 40 | } catch (e: Exception) { 41 | e.printStackTrace() 42 | } 43 | // 最后通知图库更新 44 | context?.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://$path"))) 45 | } 46 | } 47 | result.success(null) 48 | } 49 | } -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/normal_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-hdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-mdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-xhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-xxhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/app/src/main/res/mipmap-xxxhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | } 5 | } 6 | 7 | rootProject.buildDir = '../build' 8 | subprojects { 9 | project.buildDir = "${rootProject.buildDir}/${project.name}" 10 | } 11 | subprojects { 12 | project.evaluationDependsOn(':app') 13 | } 14 | 15 | tasks.register("clean", Delete) { 16 | delete rootProject.buildDir 17 | } 18 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M -Dfile.encoding=UTF-8 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | #systemProp.http.proxyHost=127.0.0.1 6 | #systemProp.http.proxyPort=7890 7 | #systemProp.https.proxyHost=127.0.0.1 8 | #systemProp.https.proxyPort=7890 -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /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/gsygithubapp-debug.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/android/gsygithubapp-debug.jks -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.6.1" apply false 22 | id "org.jetbrains.kotlin.android" version "1.8.10" apply false 23 | } 24 | 25 | include ":app" -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/download.png -------------------------------------------------------------------------------- /folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/folder.png -------------------------------------------------------------------------------- /framework2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/framework2.png -------------------------------------------------------------------------------- /googleplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/googleplay.png -------------------------------------------------------------------------------- /ios.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios.gif -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /Flutter/Flutter.podspec 43 | /ServiceDefinitions.json 44 | 45 | Pods/ 46 | .symlinks/ 47 | -------------------------------------------------------------------------------- /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 | 12.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/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.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 flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /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.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/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/120.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/152.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/167.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/60.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/80.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/87.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "40.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "60.jpg", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "58.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "87.jpg", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "80.jpg", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "logo-4.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "120.jpg", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "180.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "1024x1024", 53 | "idiom" : "ios-marketing", 54 | "filename" : "logo2x-3.jpg", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "152x152", 59 | "idiom" : "iphone", 60 | "filename" : "152.jpg", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "167x167", 65 | "idiom" : "iphone", 66 | "filename" : "167.jpg", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "76x76", 71 | "idiom" : "iphone", 72 | "filename" : "76.png", 73 | "scale" : "1x" 74 | } 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | } 80 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/logo-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/logo-4.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/logo2x-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/AppIcon.appiconset/logo2x-3.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "iphone", 9 | "filename" : "Default@3x-1.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "iphone", 14 | "filename" : "Default@3x.png", 15 | "scale" : "3x" 16 | }, 17 | { 18 | "idiom" : "iphone", 19 | "subtype" : "retina4", 20 | "scale" : "1x" 21 | }, 22 | { 23 | "idiom" : "iphone", 24 | "filename" : "Default@3x-2.png", 25 | "subtype" : "retina4", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "filename" : "Default@3x-4.png", 31 | "subtype" : "retina4", 32 | "scale" : "3x" 33 | }, 34 | { 35 | "idiom" : "iphone", 36 | "subtype" : "736h", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "idiom" : "iphone", 41 | "filename" : "Default@2x.png", 42 | "subtype" : "667h", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "idiom" : "iphone", 47 | "filename" : "Default@3x-3.png", 48 | "subtype" : "2436h", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "version" : 1, 54 | "author" : "xcode" 55 | } 56 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-1.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-2.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-3.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x-4.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios/Runner/Assets.xcassets/LaunchImage.imageset/Default@3x.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios_wait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/ios_wait.png -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/common/localization/l10n 2 | template-arb-file: app_en.arb 3 | output-localization-file: app_localizations.dart 4 | synthetic-package: false -------------------------------------------------------------------------------- /lib/common/config/config.dart: -------------------------------------------------------------------------------- 1 | class Config { 2 | static bool? DEBUG = true; 3 | 4 | 5 | 6 | static const PAGE_SIZE = 20; 7 | 8 | /// //////////////////////////////////////常量////////////////////////////////////// /// 9 | static const API_TOKEN = "4d65e2a5626103f92a71867d7b49fea0"; 10 | static const TOKEN_KEY = "token"; 11 | static const USER_NAME_KEY = "user-name"; 12 | static const PW_KEY = "user-pw"; 13 | static const USER_BASIC_CODE = "user-basic-code"; 14 | static const USER_INFO = "user-info"; 15 | static const LANGUAGE_SELECT = "language-select"; 16 | static const LANGUAGE_SELECT_NAME = "language-select-name"; 17 | static const REFRESH_LANGUAGE = "refreshLanguageApp"; 18 | static const THEME_COLOR = "theme-color"; 19 | static const LOCALE = "locale"; 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/event/event_bus.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | /// Dispatches events to listeners using the Dart [Stream] API. The [EventBus] 4 | /// enables decoupled applications. It allows objects to interact without 5 | /// requiring to explicitly define listeners and keeping track of them. 6 | /// 7 | /// Not all events should be broadcasted through the [EventBus] but only those of 8 | /// general interest. 9 | /// 10 | /// Events are normal Dart objects. By specifying a class, listeners can 11 | /// filter events. 12 | /// 13 | class EventBus { 14 | final StreamController _streamController; 15 | 16 | /// Controller for the event bus stream. 17 | StreamController get streamController => _streamController; 18 | 19 | /// Creates an [EventBus]. 20 | /// 21 | /// If [sync] is true, events are passed directly to the stream's listeners 22 | /// during a [fire] call. If false (the default), the event will be passed to 23 | /// the listeners at a later time, after the code creating the event has 24 | /// completed. 25 | EventBus({bool sync = false}) 26 | : _streamController = StreamController.broadcast(sync: sync); 27 | 28 | /// Instead of using the default [StreamController] you can use this constructor 29 | /// to pass your own controller. 30 | /// 31 | /// An example would be to use an RxDart Subject as the controller. 32 | EventBus.customController(StreamController controller) 33 | : _streamController = controller; 34 | 35 | /// Listens for events of Type [T] and its subtypes. 36 | /// 37 | /// The method is called like this: myEventBus.on(); 38 | /// 39 | /// If the method is called without a type parameter, the [Stream] contains every 40 | /// event of this [EventBus]. 41 | /// 42 | /// The returned [Stream] is a broadcast stream so multiple subscriptions are 43 | /// allowed. 44 | /// 45 | /// Each listener is handled independently, and if they pause, only the pausing 46 | /// listener is affected. A paused listener will buffer events internally until 47 | /// unpaused or canceled. So it's usually better to just cancel and later 48 | /// subscribe again (avoids memory leak). 49 | /// 50 | Stream on() { 51 | if (T == dynamic) { 52 | return streamController.stream as Stream ; 53 | } else { 54 | return streamController.stream.where((event) => event is T).cast(); 55 | } 56 | } 57 | 58 | /// Fires a new event on the event bus with the specified [event]. 59 | /// 60 | void fire(event) { 61 | streamController.add(event); 62 | } 63 | 64 | /// Destroy this [EventBus]. This is generally only in a testing context. 65 | /// 66 | void destroy() { 67 | _streamController.close(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/common/event/http_error_event.dart: -------------------------------------------------------------------------------- 1 | /// Created by guoshuyu 2 | /// Date: 2018-08-16 3 | library; 4 | 5 | class HttpErrorEvent { 6 | final int? code; 7 | 8 | final String message; 9 | 10 | HttpErrorEvent(this.code, this.message); 11 | } 12 | -------------------------------------------------------------------------------- /lib/common/event/index.dart: -------------------------------------------------------------------------------- 1 | import 'event_bus.dart'; 2 | 3 | EventBus eventBus = EventBus(); -------------------------------------------------------------------------------- /lib/common/local/local_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | ///SharedPreferences 本地存储 4 | class LocalStorage { 5 | 6 | static save(String key, value) async { 7 | SharedPreferences prefs = await SharedPreferences.getInstance(); 8 | prefs.setString(key, value); 9 | } 10 | 11 | static get(String key) async { 12 | SharedPreferences prefs = await SharedPreferences.getInstance(); 13 | return prefs.get(key); 14 | } 15 | 16 | static remove(String key) async { 17 | SharedPreferences prefs = await SharedPreferences.getInstance(); 18 | prefs.remove(key); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/common/localization/extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/common/localization/l10n/app_localizations.dart'; 3 | 4 | 5 | 6 | 7 | extension LocalizationExtension on BuildContext { 8 | AppLocalizations get l10n => AppLocalizations.of(this)!; 9 | } -------------------------------------------------------------------------------- /lib/common/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:talker_flutter/talker_flutter.dart'; 3 | 4 | final talker = TalkerFlutter.init( 5 | settings: TalkerSettings( 6 | /// You can enable/disable all talker processes with this field 7 | enabled: true, 8 | 9 | /// You can enable/disable saving logs data in history 10 | useHistory: true, 11 | 12 | /// Length of history that saving logs data 13 | maxHistoryItems: 100, 14 | 15 | /// You can enable/disable console logs 16 | useConsoleLogs: true, 17 | ), 18 | ); 19 | 20 | printLog(Object msg) { 21 | if (msg is Error) { 22 | talker.error("Catch Running Error:", msg); 23 | } else if (msg is Exception) { 24 | talker.error("Catch Running Exception:", msg); 25 | } 26 | if (kDebugMode) { 27 | print(msg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/common/net/code.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/common/event/http_error_event.dart'; 2 | import 'package:gsy_github_app_flutter/common/event/index.dart'; 3 | 4 | ///错误编码 5 | class Code { 6 | ///网络错误 7 | static const NETWORK_ERROR = -1; 8 | 9 | ///网络超时 10 | static const NETWORK_TIMEOUT = -2; 11 | 12 | ///网络返回数据格式化一次 13 | static const NETWORK_JSON_EXCEPTION = -3; 14 | 15 | ///Github APi Connection refused 16 | static const GITHUB_API_REFUSED = -4; 17 | 18 | static const SUCCESS = 200; 19 | 20 | static errorHandleFunction(code, message, noTip) { 21 | if (noTip) { 22 | return message; 23 | } 24 | if(message != null && message is String && (message.contains("Connection refused") || message.contains("Connection reset"))) { 25 | code = GITHUB_API_REFUSED; 26 | } 27 | eventBus.fire(HttpErrorEvent(code, message)); 28 | return message; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/common/net/graphql/client.dart: -------------------------------------------------------------------------------- 1 | import 'package:graphql/client.dart'; 2 | import 'package:gsy_github_app_flutter/common/net/graphql/repositories.dart'; 3 | import 'package:gsy_github_app_flutter/common/net/graphql/users.dart'; 4 | import 'package:gsy_github_app_flutter/common/utils/common_utils.dart'; 5 | 6 | Future _client(token) async { 7 | final HttpLink httpLink = HttpLink( 8 | 'https://api.github.com/graphql', 9 | ); 10 | 11 | final AuthLink authLink = AuthLink( 12 | getToken: () => '$token', 13 | ); 14 | 15 | final Link link = authLink.concat(httpLink); 16 | var path = await CommonUtils.getApplicationDocumentsPath(); 17 | final store = await HiveStore.open(path: path); 18 | return GraphQLClient( 19 | cache: GraphQLCache(store: store), 20 | link: link, 21 | ); 22 | } 23 | 24 | GraphQLClient? _innerClient; 25 | 26 | initClient(token) async { 27 | _innerClient ??= await _client(token); 28 | } 29 | 30 | releaseClient() { 31 | _innerClient = null; 32 | } 33 | 34 | Future? getRepository(String owner, String? name) async { 35 | final QueryOptions options = QueryOptions( 36 | document: gql(readRepository), 37 | variables: { 38 | 'owner': owner, 39 | 'name': name, 40 | }, 41 | fetchPolicy: FetchPolicy.noCache); 42 | return await _innerClient!.query(options); 43 | } 44 | 45 | Future? getTrendUser(String location, {String? cursor}) async { 46 | var variables = cursor == null 47 | ? { 48 | 'location': "location:$location sort:followers", 49 | } 50 | : { 51 | 'location': "location:$location sort:followers", 52 | 'after': cursor, 53 | }; 54 | final QueryOptions options = QueryOptions( 55 | document: gql(cursor == null ? readTrendUser : readTrendUserByCursor), 56 | variables: variables, 57 | fetchPolicy: FetchPolicy.noCache); 58 | return await _innerClient!.query(options); 59 | } 60 | -------------------------------------------------------------------------------- /lib/common/net/graphql/repositories.dart: -------------------------------------------------------------------------------- 1 | const String readRepository = r''' 2 | query getRepositoryDetail($owner:String!, $name:String!){ 3 | repository(name: $name, owner: $owner) { 4 | ...comparisonFields 5 | parent { 6 | ...comparisonFields 7 | } 8 | } 9 | } 10 | 11 | fragment comparisonFields on Repository { 12 | issuesClosed: issues(states : CLOSED) { 13 | totalCount 14 | } 15 | issuesOpen: issues(states : OPEN) { 16 | totalCount 17 | } 18 | issues { 19 | totalCount 20 | } 21 | nameWithOwner, 22 | id, 23 | name, 24 | owner { 25 | login, 26 | url, 27 | avatarUrl, 28 | }, 29 | licenseInfo { 30 | name 31 | } 32 | forkCount, 33 | stargazers{ 34 | totalCount 35 | } 36 | hasIssuesEnabled, 37 | viewerHasStarred, 38 | viewerSubscription, 39 | hasIssuesEnabled, 40 | defaultBranchRef { 41 | name 42 | }, 43 | watchers { 44 | totalCount, 45 | } 46 | isFork 47 | languages(first:100) { 48 | totalSize, 49 | nodes { 50 | name, 51 | } 52 | }, 53 | createdAt, 54 | pushedAt, 55 | pushedAt, 56 | sshUrl, 57 | url, 58 | shortDescriptionHTML, 59 | repositoryTopics(first: 100) { 60 | totalCount, 61 | nodes { 62 | topic { 63 | name, 64 | } 65 | } 66 | } 67 | } 68 | '''; 69 | -------------------------------------------------------------------------------- /lib/common/net/graphql/users.dart: -------------------------------------------------------------------------------- 1 | const String readTrendUser = r''' 2 | query getTrendUser($location: String!){ 3 | search(type: USER, query: $location, first: 100) { 4 | pageInfo { 5 | endCursor 6 | } 7 | user: edges { 8 | user: node { 9 | ... on User { 10 | name, 11 | avatarUrl, 12 | followers { 13 | totalCount 14 | }, 15 | bio, 16 | login, 17 | lang: repositories(orderBy: {field: STARGAZERS, direction: DESC}, first:1) { 18 | nodes{ 19 | name 20 | languages(first:1) { 21 | nodes { 22 | name 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | '''; 33 | 34 | 35 | const String readTrendUserByCursor = r''' 36 | query getTrendUser($location: String!, $after: String!){ 37 | search(type: USER, query: $location, first: 100, after: $after) { 38 | pageInfo { 39 | endCursor 40 | } 41 | user: edges { 42 | user: node { 43 | ... on User { 44 | name, 45 | avatarUrl, 46 | followers { 47 | totalCount 48 | }, 49 | bio, 50 | login, 51 | lang: repositories(orderBy: {field: STARGAZERS, direction: DESC}, first:1) { 52 | nodes{ 53 | name 54 | languages(first:1) { 55 | nodes { 56 | name 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | '''; 67 | -------------------------------------------------------------------------------- /lib/common/net/interceptors/error_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:connectivity_plus/connectivity_plus.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:gsy_github_app_flutter/common/net/code.dart'; 4 | import 'package:gsy_github_app_flutter/common/net/result_data.dart'; 5 | 6 | ///是否需要弹提示 7 | const NOT_TIP_KEY = "noTip"; 8 | 9 | /// 错误拦截 10 | /// Created by guoshuyu 11 | /// on 2019/3/23. 12 | class ErrorInterceptors extends InterceptorsWrapper { 13 | @override 14 | onRequest(RequestOptions options, handler) async { 15 | //没有网络 16 | var connectivityResult = await (Connectivity().checkConnectivity()); 17 | if (connectivityResult.isEmpty || 18 | connectivityResult[0] == ConnectivityResult.none) { 19 | return handler.reject(DioException( 20 | requestOptions: options, 21 | type: DioExceptionType.unknown, 22 | response: Response( 23 | requestOptions: options, 24 | data: ResultData( 25 | Code.errorHandleFunction(Code.NETWORK_ERROR, "", false), 26 | false, 27 | Code.NETWORK_ERROR)))); 28 | } 29 | return super.onRequest(options, handler); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/common/net/interceptors/header_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | /// header拦截器 4 | /// Created by guoshuyu 5 | /// on 2019/3/23. 6 | class HeaderInterceptors extends InterceptorsWrapper { 7 | @override 8 | onRequest(RequestOptions options, handler) async { 9 | ///超时 10 | options.connectTimeout = const Duration(seconds: 30); 11 | options.receiveTimeout = const Duration(seconds: 30); 12 | 13 | return super.onRequest(options, handler); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/common/net/interceptors/response_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:gsy_github_app_flutter/common/logger.dart'; 3 | import 'package:gsy_github_app_flutter/common/net/code.dart'; 4 | import 'package:gsy_github_app_flutter/common/net/result_data.dart'; 5 | 6 | /// Token拦截器 7 | /// Created by guoshuyu 8 | /// on 2019/3/23. 9 | class ResponseInterceptors extends InterceptorsWrapper { 10 | @override 11 | onResponse(Response response, handler) async { 12 | RequestOptions option = response.requestOptions; 13 | dynamic value; 14 | try { 15 | var header = response.headers[Headers.contentTypeHeader]; 16 | if ((header != null && header.toString().contains("text"))) { 17 | value = ResultData(response.data, true, Code.SUCCESS); 18 | } else if (response.statusCode! >= 200 && response.statusCode! < 300) { 19 | value = ResultData(response.data, true, Code.SUCCESS, 20 | headers: response.headers); 21 | } 22 | } catch (e) { 23 | printLog(e.toString() + option.path); 24 | value = ResultData(response.data, false, response.statusCode, 25 | headers: response.headers); 26 | } 27 | response.data = value; 28 | return handler.next(response); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/common/net/interceptors/token_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:gsy_github_app_flutter/common/config/config.dart'; 3 | import 'package:gsy_github_app_flutter/common/local/local_storage.dart'; 4 | import 'package:gsy_github_app_flutter/common/logger.dart'; 5 | import 'package:gsy_github_app_flutter/common/net/graphql/client.dart'; 6 | 7 | /// Token拦截器 8 | /// Created by guoshuyu 9 | /// on 2019/3/23. 10 | class TokenInterceptors extends InterceptorsWrapper { 11 | String? _token; 12 | 13 | @override 14 | onRequest(RequestOptions options, handler) async { 15 | //授权码 16 | if (_token == null) { 17 | var authorizationCode = await getAuthorization(); 18 | if (authorizationCode != null) { 19 | _token = authorizationCode; 20 | await initClient(_token); 21 | } 22 | } 23 | if(_token != null) { 24 | options.headers["Authorization"] = _token; 25 | } 26 | return super.onRequest(options, handler); 27 | } 28 | 29 | @override 30 | onResponse(Response response, handler) async { 31 | try { 32 | var responseJson = response.data; 33 | if (response.statusCode == 201 && responseJson["token"] != null) { 34 | _token = 'token ${responseJson["token"]}'; 35 | await LocalStorage.save(Config.TOKEN_KEY, _token); 36 | } 37 | } catch (e) { 38 | printLog(e); 39 | } 40 | return super.onResponse(response, handler); 41 | } 42 | 43 | ///清除授权 44 | clearAuthorization() { 45 | _token = null; 46 | LocalStorage.remove(Config.TOKEN_KEY); 47 | releaseClient(); 48 | } 49 | 50 | ///获取授权token 51 | getAuthorization() async { 52 | String? token = await LocalStorage.get(Config.TOKEN_KEY); 53 | if (token == null) { 54 | String? basic = await LocalStorage.get(Config.USER_BASIC_CODE); 55 | if (basic == null) { 56 | //提示输入账号密码 57 | } else { 58 | //通过 basic 去获取token,获取到设置,返回token 59 | return "Basic $basic"; 60 | } 61 | } else { 62 | _token = token; 63 | return token; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/common/net/result_data.dart: -------------------------------------------------------------------------------- 1 | /// 网络结果数据 2 | /// Created by guoshuyu 3 | /// Date: 2018-07-16 4 | class ResultData { 5 | dynamic data; 6 | bool result; 7 | int? code; 8 | dynamic headers; 9 | 10 | ResultData(this.data, this.result, this.code, {this.headers}); 11 | } 12 | -------------------------------------------------------------------------------- /lib/common/net/transformer.dart: -------------------------------------------------------------------------------- 1 | import 'package:built_value/iso_8601_date_time_serializer.dart'; 2 | import 'package:built_value/serializer.dart'; 3 | import 'package:built_value/standard_json_plugin.dart'; 4 | import 'package:gsy_github_app_flutter/model/branch.dart'; 5 | 6 | 7 | part 'transformer.g.dart'; 8 | 9 | @SerializersFor([ 10 | Branch, 11 | ]) 12 | final Serializers serializers = (_$serializers.toBuilder() 13 | ..addPlugin(StandardJsonPlugin()) 14 | ..add(Iso8601DateTimeSerializer()) 15 | ).build(); 16 | -------------------------------------------------------------------------------- /lib/common/net/transformer.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'transformer.dart'; 4 | 5 | // ************************************************************************** 6 | // BuiltValueGenerator 7 | // ************************************************************************** 8 | 9 | Serializers _$serializers = 10 | (new Serializers().toBuilder()..add(Branch.serializer)).build(); 11 | 12 | // ignore_for_file: deprecated_member_use_from_same_package,type=lint 13 | -------------------------------------------------------------------------------- /lib/common/repositories/data_result.dart: -------------------------------------------------------------------------------- 1 | class DataResult { 2 | Object? data; 3 | bool result; 4 | Function? next; 5 | 6 | DataResult(this.data, this.result, {this.next}); 7 | } 8 | -------------------------------------------------------------------------------- /lib/common/router/anima_route.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | ///动画大小变化打开的路由 4 | class SizeRoute extends PageRouteBuilder { 5 | final Widget? widget; 6 | 7 | SizeRoute({this.widget}) 8 | : super( 9 | pageBuilder: ( 10 | BuildContext context, 11 | Animation animation, 12 | Animation secondaryAnimation, 13 | ) => 14 | widget!, 15 | transitionsBuilder: ( 16 | BuildContext context, 17 | Animation animation, 18 | Animation secondaryAnimation, 19 | Widget child, 20 | ) { 21 | var begin = 0.0; 22 | var end = 1.0; 23 | var curve = Curves.ease; 24 | 25 | var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve)); 26 | 27 | return Align( 28 | child: SizeTransition( 29 | sizeFactor: animation.drive(tween), 30 | child: child, 31 | ), 32 | ); 33 | }, 34 | ); 35 | } 36 | 37 | class NoAnimationRoute extends PageRouteBuilder { 38 | final Widget? widget; 39 | 40 | NoAnimationRoute({this.widget}) 41 | : super( 42 | pageBuilder: ( 43 | BuildContext context, 44 | Animation animation, 45 | Animation secondaryAnimation, 46 | ) => 47 | widget!, 48 | transitionsBuilder: ( 49 | BuildContext context, 50 | Animation animation, 51 | Animation secondaryAnimation, 52 | Widget child, 53 | ) => 54 | SlideTransition( 55 | position: Tween( 56 | begin: const Offset(0.0, 0.0), 57 | end: const Offset(0.0, 0.0), 58 | ).animate(animation), 59 | child: child, 60 | ), 61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /lib/common/toast.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:fluttertoast/fluttertoast.dart'; 3 | 4 | showToast(String message) { 5 | Fluttertoast.showToast( 6 | msg: message, 7 | gravity: ToastGravity.CENTER, 8 | toastLength: Toast.LENGTH_LONG); 9 | } -------------------------------------------------------------------------------- /lib/common/utils/code_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | ///isolate 的 compute 需要静态方法 4 | class CodeUtils { 5 | static List decodeListResult(String? data) { 6 | return json.decode(data!); 7 | } 8 | 9 | static Map decodeMapResult(String? data) { 10 | return json.decode(data!); 11 | } 12 | 13 | static String encodeToString(String data) { 14 | return json.encode(data); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/db/provider/event/received_event_db_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:gsy_github_app_flutter/common/utils/code_utils.dart'; 4 | import 'package:gsy_github_app_flutter/db/sql_provider.dart'; 5 | import 'package:gsy_github_app_flutter/model/event.dart'; 6 | import 'package:sqflite/sqflite.dart'; 7 | 8 | /// 用户接受事件表 9 | /// Created by guoshuyu 10 | /// Date: 2018-08-07 11 | 12 | class ReceivedEventDbProvider extends BaseDbProvider { 13 | final String name = 'ReceivedEvent'; 14 | 15 | final String columnId = "_id"; 16 | final String columnData = "data"; 17 | 18 | int? id; 19 | String? data; 20 | 21 | ReceivedEventDbProvider(); 22 | 23 | Map toMap(String eventMapString) { 24 | Map map = {columnData: eventMapString}; 25 | if (id != null) { 26 | map[columnId] = id; 27 | } 28 | return map; 29 | } 30 | 31 | ReceivedEventDbProvider.fromMap(Map map) { 32 | id = map[columnId]; 33 | data = map[columnData]; 34 | } 35 | 36 | @override 37 | tableSqlString() { 38 | return tableBaseString(name, columnId) + 39 | ''' 40 | $columnData text not null) 41 | '''; 42 | } 43 | 44 | @override 45 | tableName() { 46 | return name; 47 | } 48 | 49 | ///插入到数据库 50 | Future insert(String eventMapString) async { 51 | Database db = await getDataBase(); 52 | 53 | ///清空后再插入,因为只保存第一页面 54 | db.execute("delete from $name"); 55 | return await db.insert(name, toMap(eventMapString)); 56 | } 57 | 58 | ///获取事件数据 59 | Future>? getEvents() async { 60 | Database db = await getDataBase(); 61 | List maps = await db.query(name, columns: [columnId, columnData]); 62 | List list = []; 63 | if (maps.isNotEmpty) { 64 | ReceivedEventDbProvider provider = 65 | ReceivedEventDbProvider.fromMap(maps.first); 66 | 67 | ///使用 compute 的 Isolate 优化 json decode 68 | List eventMap = 69 | await compute(CodeUtils.decodeListResult, provider.data); 70 | 71 | list = await compute(decodeMapToObject, eventMap); 72 | } 73 | return list; 74 | } 75 | 76 | static List decodeMapToObject(List mapList) { 77 | List list = []; 78 | for (var item in mapList) { 79 | list.add(Event.fromJson(item)); 80 | } 81 | return list; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/db/provider/repos/repository_branch_db_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/db/sql_provider.dart'; 2 | 3 | /// 仓库分支表 4 | /// Created by guoshuyu 5 | /// Date: 2018-08-07 6 | 7 | class RepositoryBranchDbProvider extends BaseDbProvider { 8 | final String name = 'RepositoryPulse'; 9 | final String columnId = "_id"; 10 | final String columnFullName = "fullName"; 11 | final String columnData = "data"; 12 | 13 | int? id; 14 | String? fullName; 15 | String? data; 16 | 17 | Map toMap() { 18 | Map map = {columnFullName: fullName, columnData: data}; 19 | if (id != null) { 20 | map[columnId] = id; 21 | } 22 | return map; 23 | } 24 | 25 | RepositoryBranchDbProvider.fromMap(Map map) { 26 | id = map[columnId]; 27 | fullName = map[columnFullName]; 28 | data = map[columnData]; 29 | } 30 | 31 | @override 32 | tableSqlString() {} 33 | 34 | @override 35 | tableName() { 36 | return name; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/db/provider/repos/repository_commitInfo_detail_db_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/db/sql_provider.dart'; 2 | 3 | /// 仓库提交信息详情表 4 | /// Created by guoshuyu 5 | /// Date: 2018-08-07 6 | 7 | class RepositoryCommitInfoDetailDbProvider extends BaseDbProvider { 8 | final String name = 'RepositoryCommitInfoDetail'; 9 | int? id; 10 | String? fullName; 11 | String? data; 12 | String? sha; 13 | 14 | final String columnId = "_id"; 15 | final String columnFullName = "fullName"; 16 | final String columnSha = "sha"; 17 | final String columnData = "data"; 18 | 19 | Map toMap() { 20 | Map map = { 21 | columnFullName: fullName, 22 | columnSha: sha, 23 | columnData: data 24 | }; 25 | if (id != null) { 26 | map[columnId] = id; 27 | } 28 | return map; 29 | } 30 | 31 | RepositoryCommitInfoDetailDbProvider.fromMap(Map map) { 32 | id = map[columnId]; 33 | fullName = map[columnFullName]; 34 | sha = map[columnSha]; 35 | data = map[columnData]; 36 | } 37 | 38 | @override 39 | tableSqlString() {} 40 | 41 | @override 42 | tableName() { 43 | return name; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/db/provider/repos/repository_pulse_db_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/db/sql_provider.dart'; 2 | 3 | /// 仓库pulse表 4 | /// Created by guoshuyu 5 | /// Date: 2018-08-07 6 | 7 | class RepositoryPulseDbProvider extends BaseDbProvider { 8 | final String name = 'RepositoryPulse'; 9 | final String columnId = "_id"; 10 | final String columnFullName = "fullName"; 11 | final String columnData = "data"; 12 | 13 | int? id; 14 | String? fullName; 15 | String? data; 16 | 17 | Map toMap() { 18 | Map map = {columnFullName: fullName, columnData: data}; 19 | if (id != null) { 20 | map[columnId] = id; 21 | } 22 | return map; 23 | } 24 | 25 | RepositoryPulseDbProvider.fromMap(Map map) { 26 | id = map[columnId]; 27 | fullName = map[columnFullName]; 28 | data = map[columnData]; 29 | } 30 | 31 | @override 32 | tableSqlString() {} 33 | 34 | @override 35 | tableName() { 36 | return name; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/db/provider/user/org_member_db_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/db/sql_provider.dart'; 2 | 3 | /// 用户关注表 4 | /// 5 | /// Created by guoshuyu 6 | /// Date: 2018-08-07 7 | 8 | class OrgMemberDbProvider extends BaseDbProvider { 9 | final String name = 'OrgMember'; 10 | 11 | final String columnId = "_id"; 12 | final String columnOrg = "org"; 13 | final String columnData = "data"; 14 | 15 | int? id; 16 | String? org; 17 | String? data; 18 | 19 | Map toMap() { 20 | Map map = {columnOrg: org, columnData: data}; 21 | if (id != null) { 22 | map[columnId] = id; 23 | } 24 | return map; 25 | } 26 | 27 | OrgMemberDbProvider.fromMap(Map map) { 28 | id = map[columnId]; 29 | org = map[columnOrg]; 30 | data = map[columnData]; 31 | } 32 | 33 | @override 34 | tableSqlString() {} 35 | 36 | @override 37 | tableName() { 38 | return name; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/db/sql_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:gsy_github_app_flutter/common/repositories/user_repository.dart'; 5 | import 'package:gsy_github_app_flutter/model/user.dart'; 6 | import 'package:sqflite/sqflite.dart'; 7 | 8 | /// 数据库管理 9 | /// Created by guoshuyu 10 | /// Date: 2018-08-03 11 | 12 | class SqlManager { 13 | static const _VERSION = 1; 14 | 15 | static const _NAME = "gsy_github_app_flutter.db"; 16 | 17 | static Database? _database; 18 | 19 | ///初始化 20 | static init() async { 21 | // open the database 22 | var databasesPath = await getDatabasesPath(); 23 | var userRes = await UserRepository.getUserInfoLocal(); 24 | String dbName = _NAME; 25 | if (userRes != null && userRes.result) { 26 | User? user = userRes.data; 27 | if (user != null && user.login != null) { 28 | dbName = "${user.login!}_$_NAME"; 29 | } 30 | } 31 | String path = databasesPath + dbName; 32 | if (Platform.isIOS) { 33 | path = "$databasesPath/$dbName"; 34 | } 35 | _database = await openDatabase(path, version: _VERSION, 36 | onCreate: (Database db, int version) async { 37 | // When creating the db, create the table 38 | //await db.execute("CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value INTEGER, num REAL)"); 39 | }); 40 | } 41 | 42 | /// 表是否存在 43 | static isTableExits(String tableName) async { 44 | await getCurrentDatabase(); 45 | var res = await _database?.rawQuery( 46 | "select * from Sqlite_master where type = 'table' and name = '$tableName'"); 47 | return res != null && res.isNotEmpty; 48 | } 49 | 50 | ///获取当前数据库对象 51 | static Future getCurrentDatabase() async { 52 | if (_database == null) { 53 | await init(); 54 | } 55 | return _database; 56 | } 57 | 58 | ///关闭 59 | static close() { 60 | _database?.close(); 61 | _database = null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/db/sql_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | /** 4 | * 数据库表 5 | * Created by guoshuyu 6 | * Date: 2018-08-03 7 | */ 8 | import 'package:gsy_github_app_flutter/db/sql_manager.dart'; 9 | import 'package:meta/meta.dart'; 10 | import 'package:sqflite/sqflite.dart'; 11 | 12 | ///基类 13 | abstract class BaseDbProvider { 14 | bool isTableExits = false; 15 | 16 | tableSqlString(); 17 | 18 | tableName(); 19 | 20 | tableBaseString(String name, String columnId) { 21 | return ''' 22 | create table $name ( 23 | $columnId integer primary key autoincrement, 24 | '''; 25 | } 26 | 27 | Future getDataBase() async { 28 | return await open(); 29 | } 30 | 31 | @mustCallSuper 32 | prepare(name, String? createSql) async { 33 | isTableExits = await SqlManager.isTableExits(name); 34 | if (!isTableExits) { 35 | Database? db = await SqlManager.getCurrentDatabase(); 36 | return await db?.execute(createSql!); 37 | } 38 | } 39 | 40 | @mustCallSuper 41 | open() async { 42 | if (!isTableExits) { 43 | await prepare(tableName(), tableSqlString()); 44 | } 45 | return await SqlManager.getCurrentDatabase(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/env/config_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:gsy_github_app_flutter/common/config/config.dart'; 4 | import 'package:gsy_github_app_flutter/common/logger.dart'; 5 | import 'package:gsy_github_app_flutter/env/env_config.dart'; 6 | 7 | ///往下共享环境配置 8 | class ConfigWrapper extends StatelessWidget { 9 | const ConfigWrapper({super.key, this.config, this.child}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | ///设置 Config.DEBUG 的静态变量 14 | Config.DEBUG = config?.debug; 15 | if (kDebugMode) { 16 | printLog("ConfigWrapper build ${Config.DEBUG}"); 17 | } 18 | return _InheritedConfig(config: config, child: child!); 19 | } 20 | 21 | static EnvConfig? of(BuildContext context) { 22 | final _InheritedConfig inheritedConfig = 23 | context.dependOnInheritedWidgetOfExactType<_InheritedConfig>()!; 24 | return inheritedConfig.config; 25 | } 26 | 27 | final EnvConfig? config; 28 | 29 | final Widget? child; 30 | } 31 | 32 | class _InheritedConfig extends InheritedWidget { 33 | const _InheritedConfig( 34 | {required this.config, required super.child}); 35 | 36 | final EnvConfig? config; 37 | 38 | @override 39 | bool updateShouldNotify(_InheritedConfig oldWidget) => 40 | config != oldWidget.config; 41 | } 42 | -------------------------------------------------------------------------------- /lib/env/dev.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'dev.g.dart'; 4 | 5 | @JsonLiteral('env_json_dev.json', asConst: true) 6 | Map get config => _$configJsonLiteral; -------------------------------------------------------------------------------- /lib/env/dev.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'dev.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonLiteralGenerator 7 | // ************************************************************************** 8 | 9 | const _$configJsonLiteral = {'env': 'dev', 'debug': true}; 10 | -------------------------------------------------------------------------------- /lib/env/env_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'env_config.g.dart'; 4 | 5 | ///环境配置 6 | @JsonSerializable(createToJson: false) 7 | class EnvConfig { 8 | final String? env; 9 | final bool? debug; 10 | 11 | EnvConfig({ 12 | this.env, 13 | this.debug, 14 | }); 15 | 16 | factory EnvConfig.fromJson(Map json) => _$EnvConfigFromJson(json); 17 | } 18 | -------------------------------------------------------------------------------- /lib/env/env_config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'env_config.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | EnvConfig _$EnvConfigFromJson(Map json) => EnvConfig( 10 | env: json['env'] as String?, 11 | debug: json['debug'] as bool?, 12 | ); 13 | -------------------------------------------------------------------------------- /lib/env/env_json_dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "dev", 3 | "debug": true 4 | } -------------------------------------------------------------------------------- /lib/env/env_json_prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": "prod", 3 | "debug": false 4 | } -------------------------------------------------------------------------------- /lib/env/prod.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'prod.g.dart'; 4 | 5 | @JsonLiteral('env_json_prod.json', asConst: true) 6 | Map get config => _$configJsonLiteral; -------------------------------------------------------------------------------- /lib/env/prod.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'prod.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonLiteralGenerator 7 | // ************************************************************************** 8 | 9 | const _$configJsonLiteral = {'env': 'prod', 'debug': false}; 10 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/gestures.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:gsy_github_app_flutter/app.dart'; 6 | import 'package:gsy_github_app_flutter/common/logger.dart'; 7 | import 'package:gsy_github_app_flutter/env/config_wrapper.dart'; 8 | import 'package:gsy_github_app_flutter/env/env_config.dart'; 9 | import 'package:gsy_github_app_flutter/page/error_page.dart'; 10 | 11 | import 'env/dev.dart'; 12 | 13 | void main() { 14 | runZonedGuarded(() { 15 | ErrorWidget.builder = (FlutterErrorDetails details) { 16 | Zone.current.handleUncaughtError(details.exception, details.stack!); 17 | ///此处仅为展示,正规的实现方式参考 _defaultErrorWidgetBuilder 通过自定义 RenderErrorBox 实现 18 | return ErrorPage( 19 | "${details.exception}\n ${details.stack}", details); 20 | }; 21 | runApp(ConfigWrapper( 22 | config: EnvConfig.fromJson(config), 23 | child: const FlutterReduxApp(), 24 | )); 25 | ///屏幕刷新率和显示率不一致时的优化,必须挪动到 runApp 之后 26 | GestureBinding.instance.resamplingEnabled = true; 27 | }, (Object obj, StackTrace stack) { 28 | talker.error('Catch Dart error:', obj, stack); 29 | printLog(obj); 30 | printLog(stack); 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /lib/main_prod.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/gestures.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:gsy_github_app_flutter/app.dart'; 6 | import 'package:gsy_github_app_flutter/env/config_wrapper.dart'; 7 | import 'package:gsy_github_app_flutter/env/env_config.dart'; 8 | import 'package:gsy_github_app_flutter/page/error_page.dart'; 9 | 10 | import 'env/prod.dart'; 11 | 12 | void main() { 13 | runZonedGuarded(() { 14 | ErrorWidget.builder = (FlutterErrorDetails details) { 15 | Zone.current.handleUncaughtError(details.exception, details.stack!); 16 | ///此处仅为展示,正规的实现方式参考 _defaultErrorWidgetBuilder 通过自定义 RenderErrorBox 实现 17 | return ErrorPage( 18 | "${details.exception}\n ${details.stack}", 19 | details); 20 | }; 21 | runApp(ConfigWrapper( 22 | config: EnvConfig.fromJson(config), 23 | child: const FlutterReduxApp(), 24 | )); 25 | ///屏幕刷新率和显示率不一致时的优化 26 | GestureBinding.instance.resamplingEnabled = true; 27 | }, (Object obj, StackTrace stack) { 28 | ///do not thing 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/model/branch.dart: -------------------------------------------------------------------------------- 1 | import 'package:built_value/built_value.dart'; 2 | import 'package:built_value/serializer.dart'; 3 | 4 | part 'branch.g.dart'; 5 | 6 | abstract class Branch implements Built { 7 | static Serializer get serializer => _$branchSerializer; 8 | 9 | 10 | String? get name; 11 | 12 | 13 | @BuiltValueField(wireName: 'tarball_url') 14 | String? get tarballUrl; 15 | 16 | 17 | @BuiltValueField(wireName: 'zipball_url') 18 | String? get zipballUrl; 19 | 20 | 21 | Branch._(); 22 | factory Branch([void Function(BranchBuilder)? updates]) = _$Branch; 23 | } -------------------------------------------------------------------------------- /lib/model/commitFile.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'commitFile.g.dart'; 9 | 10 | @JsonSerializable() 11 | class CommitFile { 12 | String? sha; 13 | @JsonKey(name: "filename") 14 | String? fileName; 15 | String? status; 16 | int? additions; 17 | int? deletions; 18 | int? changes; 19 | @JsonKey(name: "blob_url") 20 | String? blobUrl; 21 | @JsonKey(name: "raw_url") 22 | String? rawUrl; 23 | @JsonKey(name: "contents_url") 24 | String? contentsUrl; 25 | String? patch; 26 | 27 | CommitFile( 28 | this.sha, 29 | this.fileName, 30 | this.status, 31 | this.additions, 32 | this.deletions, 33 | this.changes, 34 | this.blobUrl, 35 | this.rawUrl, 36 | this.contentsUrl, 37 | this.patch, 38 | ); 39 | 40 | factory CommitFile.fromJson(Map json) => _$CommitFileFromJson(json); 41 | 42 | Map toJson() => _$CommitFileToJson(this); 43 | } 44 | -------------------------------------------------------------------------------- /lib/model/commitFile.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'commitFile.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommitFile _$CommitFileFromJson(Map json) => CommitFile( 10 | json['sha'] as String?, 11 | json['filename'] as String?, 12 | json['status'] as String?, 13 | (json['additions'] as num?)?.toInt(), 14 | (json['deletions'] as num?)?.toInt(), 15 | (json['changes'] as num?)?.toInt(), 16 | json['blob_url'] as String?, 17 | json['raw_url'] as String?, 18 | json['contents_url'] as String?, 19 | json['patch'] as String?, 20 | ); 21 | 22 | Map _$CommitFileToJson(CommitFile instance) => 23 | { 24 | 'sha': instance.sha, 25 | 'filename': instance.fileName, 26 | 'status': instance.status, 27 | 'additions': instance.additions, 28 | 'deletions': instance.deletions, 29 | 'changes': instance.changes, 30 | 'blob_url': instance.blobUrl, 31 | 'raw_url': instance.rawUrl, 32 | 'contents_url': instance.contentsUrl, 33 | 'patch': instance.patch, 34 | }; 35 | -------------------------------------------------------------------------------- /lib/model/commit_comment.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | /** 5 | * Created by guoshuyu 6 | * Date: 2018-07-31 7 | */ 8 | 9 | part 'commit_comment.g.dart'; 10 | 11 | @JsonSerializable() 12 | class CommitComment{ 13 | int? id; 14 | String? body; 15 | String? path; 16 | int? position; 17 | int? line; 18 | @JsonKey(name: "commit_id") 19 | String? commitId; 20 | @JsonKey(name: "created_at") 21 | DateTime? createdAt; 22 | @JsonKey(name: "updated_at") 23 | DateTime? updatedAt; 24 | @JsonKey(name: "html_url") 25 | String? htmlUrl; 26 | String? url; 27 | User? user; 28 | 29 | CommitComment( 30 | this.id, 31 | this.body, 32 | this.path, 33 | this.position, 34 | this.line, 35 | this.commitId, 36 | this.createdAt, 37 | this.updatedAt, 38 | this.htmlUrl, 39 | this.url, 40 | this.user, 41 | ); 42 | 43 | factory CommitComment.fromJson(Map json) => _$CommitCommentFromJson(json); 44 | 45 | Map toJson() => _$CommitCommentToJson(this); 46 | } 47 | -------------------------------------------------------------------------------- /lib/model/commit_comment.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'commit_comment.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommitComment _$CommitCommentFromJson(Map json) => 10 | CommitComment( 11 | (json['id'] as num?)?.toInt(), 12 | json['body'] as String?, 13 | json['path'] as String?, 14 | (json['position'] as num?)?.toInt(), 15 | (json['line'] as num?)?.toInt(), 16 | json['commit_id'] as String?, 17 | json['created_at'] == null 18 | ? null 19 | : DateTime.parse(json['created_at'] as String), 20 | json['updated_at'] == null 21 | ? null 22 | : DateTime.parse(json['updated_at'] as String), 23 | json['html_url'] as String?, 24 | json['url'] as String?, 25 | json['user'] == null 26 | ? null 27 | : User.fromJson(json['user'] as Map), 28 | ); 29 | 30 | Map _$CommitCommentToJson(CommitComment instance) => 31 | { 32 | 'id': instance.id, 33 | 'body': instance.body, 34 | 'path': instance.path, 35 | 'position': instance.position, 36 | 'line': instance.line, 37 | 'commit_id': instance.commitId, 38 | 'created_at': instance.createdAt?.toIso8601String(), 39 | 'updated_at': instance.updatedAt?.toIso8601String(), 40 | 'html_url': instance.htmlUrl, 41 | 'url': instance.url, 42 | 'user': instance.user, 43 | }; 44 | -------------------------------------------------------------------------------- /lib/model/commit_git_info.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/commit_git_user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | /** 5 | * Created by guoshuyu 6 | * Date: 2018-07-31 7 | */ 8 | 9 | part 'commit_git_info.g.dart'; 10 | 11 | @JsonSerializable() 12 | class CommitGitInfo { 13 | String? message; 14 | String? url; 15 | @JsonKey(name: "comment_count") 16 | int? commentCount; 17 | CommitGitUser? author; 18 | CommitGitUser? committer; 19 | 20 | CommitGitInfo( 21 | this.message, 22 | this.url, 23 | this.commentCount, 24 | this.author, 25 | this.committer, 26 | ); 27 | 28 | factory CommitGitInfo.fromJson(Map json) => _$CommitGitInfoFromJson(json); 29 | 30 | 31 | Map toJson() => _$CommitGitInfoToJson(this); 32 | } 33 | -------------------------------------------------------------------------------- /lib/model/commit_git_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'commit_git_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommitGitInfo _$CommitGitInfoFromJson(Map json) => 10 | CommitGitInfo( 11 | json['message'] as String?, 12 | json['url'] as String?, 13 | (json['comment_count'] as num?)?.toInt(), 14 | json['author'] == null 15 | ? null 16 | : CommitGitUser.fromJson(json['author'] as Map), 17 | json['committer'] == null 18 | ? null 19 | : CommitGitUser.fromJson(json['committer'] as Map), 20 | ); 21 | 22 | Map _$CommitGitInfoToJson(CommitGitInfo instance) => 23 | { 24 | 'message': instance.message, 25 | 'url': instance.url, 26 | 'comment_count': instance.commentCount, 27 | 'author': instance.author, 28 | 'committer': instance.committer, 29 | }; 30 | -------------------------------------------------------------------------------- /lib/model/commit_git_user.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'commit_git_user.g.dart'; 9 | 10 | @JsonSerializable() 11 | class CommitGitUser{ 12 | String? name; 13 | String? email; 14 | DateTime? date; 15 | 16 | CommitGitUser(this.name, this.email, this.date); 17 | 18 | factory CommitGitUser.fromJson(Map json) => _$CommitGitUserFromJson(json); 19 | 20 | 21 | Map toJson() => _$CommitGitUserToJson(this); 22 | } 23 | -------------------------------------------------------------------------------- /lib/model/commit_git_user.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'commit_git_user.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommitGitUser _$CommitGitUserFromJson(Map json) => 10 | CommitGitUser( 11 | json['name'] as String?, 12 | json['email'] as String?, 13 | json['date'] == null ? null : DateTime.parse(json['date'] as String), 14 | ); 15 | 16 | Map _$CommitGitUserToJson(CommitGitUser instance) => 17 | { 18 | 'name': instance.name, 19 | 'email': instance.email, 20 | 'date': instance.date?.toIso8601String(), 21 | }; 22 | -------------------------------------------------------------------------------- /lib/model/commit_stats.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'commit_stats.g.dart'; 9 | 10 | @JsonSerializable() 11 | class CommitStats { 12 | int? total; 13 | int? additions; 14 | int? deletions; 15 | 16 | CommitStats(this.total, this.additions, this.deletions); 17 | 18 | factory CommitStats.fromJson(Map json) => _$CommitStatsFromJson(json); 19 | 20 | Map toJson() => _$CommitStatsToJson(this); 21 | } 22 | -------------------------------------------------------------------------------- /lib/model/commit_stats.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'commit_stats.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommitStats _$CommitStatsFromJson(Map json) => CommitStats( 10 | (json['total'] as num?)?.toInt(), 11 | (json['additions'] as num?)?.toInt(), 12 | (json['deletions'] as num?)?.toInt(), 13 | ); 14 | 15 | Map _$CommitStatsToJson(CommitStats instance) => 16 | { 17 | 'total': instance.total, 18 | 'additions': instance.additions, 19 | 'deletions': instance.deletions, 20 | }; 21 | -------------------------------------------------------------------------------- /lib/model/commits_comparison.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/commitFile.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | import 'repo_commit.dart'; 5 | 6 | /** 7 | * Created by guoshuyu 8 | * Date: 2018-07-31 9 | */ 10 | 11 | part 'commits_comparison.g.dart'; 12 | 13 | @JsonSerializable() 14 | class CommitsComparison{ 15 | String? url; 16 | @JsonKey(name: "html_url") 17 | String? htmlUrl; 18 | @JsonKey(name: "base_commit") 19 | RepoCommit? baseCommit; 20 | @JsonKey(name: "merge_base_commit") 21 | RepoCommit? mergeBaseCommit; 22 | String? status; 23 | @JsonKey(name: "total_commits") 24 | int? totalCommits; 25 | List? commits; 26 | List? files; 27 | 28 | CommitsComparison( 29 | this.url, 30 | this.htmlUrl, 31 | this.baseCommit, 32 | this.mergeBaseCommit, 33 | this.status, 34 | this.totalCommits, 35 | this.commits, 36 | this.files, 37 | ); 38 | 39 | factory CommitsComparison.fromJson(Map json) => _$CommitsComparisonFromJson(json); 40 | 41 | Map toJson() => _$CommitsComparisonToJson(this); 42 | } 43 | -------------------------------------------------------------------------------- /lib/model/commits_comparison.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'commits_comparison.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommitsComparison _$CommitsComparisonFromJson(Map json) => 10 | CommitsComparison( 11 | json['url'] as String?, 12 | json['html_url'] as String?, 13 | json['base_commit'] == null 14 | ? null 15 | : RepoCommit.fromJson(json['base_commit'] as Map), 16 | json['merge_base_commit'] == null 17 | ? null 18 | : RepoCommit.fromJson( 19 | json['merge_base_commit'] as Map), 20 | json['status'] as String?, 21 | (json['total_commits'] as num?)?.toInt(), 22 | (json['commits'] as List?) 23 | ?.map((e) => RepoCommit.fromJson(e as Map)) 24 | .toList(), 25 | (json['files'] as List?) 26 | ?.map((e) => CommitFile.fromJson(e as Map)) 27 | .toList(), 28 | ); 29 | 30 | Map _$CommitsComparisonToJson(CommitsComparison instance) => 31 | { 32 | 'url': instance.url, 33 | 'html_url': instance.htmlUrl, 34 | 'base_commit': instance.baseCommit, 35 | 'merge_base_commit': instance.mergeBaseCommit, 36 | 'status': instance.status, 37 | 'total_commits': instance.totalCommits, 38 | 'commits': instance.commits, 39 | 'files': instance.files, 40 | }; 41 | -------------------------------------------------------------------------------- /lib/model/common_list_datatype.dart: -------------------------------------------------------------------------------- 1 | enum CommonListDataType { 2 | follower("follower"), 3 | followed("followed"), 4 | userRepos('user_repos'), 5 | repoStar("repo_star"), 6 | userStar("user_star"), 7 | repoWatcher("repo_watcher"), 8 | repoFork("repo_fork"), 9 | repoRelease("repoRelease"), 10 | repoTag("repo_tag"), 11 | notify("notify"), 12 | history("history"), 13 | topics("topics"), 14 | userBeStared("user_be_stared"), 15 | userOrgs("user_orgs"); 16 | 17 | final String value; 18 | 19 | const CommonListDataType(this.value); 20 | } 21 | -------------------------------------------------------------------------------- /lib/model/download_source.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'download_source.g.dart'; 9 | 10 | @JsonSerializable() 11 | class DownloadSource { 12 | String? url; 13 | bool? isSourceCode; 14 | String? name; 15 | int? size; 16 | 17 | DownloadSource( 18 | this.url, 19 | this.isSourceCode, 20 | this.name, 21 | this.size, 22 | ); 23 | 24 | factory DownloadSource.fromJson(Map json) => _$DownloadSourceFromJson(json); 25 | 26 | Map toJson() => _$DownloadSourceToJson(this); 27 | } 28 | -------------------------------------------------------------------------------- /lib/model/download_source.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'download_source.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | DownloadSource _$DownloadSourceFromJson(Map json) => 10 | DownloadSource( 11 | json['url'] as String?, 12 | json['isSourceCode'] as bool?, 13 | json['name'] as String?, 14 | (json['size'] as num?)?.toInt(), 15 | ); 16 | 17 | Map _$DownloadSourceToJson(DownloadSource instance) => 18 | { 19 | 'url': instance.url, 20 | 'isSourceCode': instance.isSourceCode, 21 | 'name': instance.name, 22 | 'size': instance.size, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/model/event.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | import 'event_payload.dart'; 5 | import 'repository.dart'; 6 | 7 | /** 8 | * Created by guoshuyu 9 | * Date: 2018-07-31 10 | */ 11 | 12 | part 'event.g.dart'; 13 | 14 | @JsonSerializable() 15 | class Event { 16 | String? id; 17 | String? type; 18 | User? actor; 19 | Repository? repo; 20 | User? org; 21 | EventPayload? payload; 22 | @JsonKey(name: "public") 23 | bool? isPublic; 24 | @JsonKey(name: "created_at") 25 | DateTime? createdAt; 26 | 27 | Event( 28 | this.id, 29 | this.type, 30 | this.actor, 31 | this.repo, 32 | this.org, 33 | this.payload, 34 | this.isPublic, 35 | this.createdAt, 36 | ); 37 | 38 | factory Event.fromJson(Map json) => _$EventFromJson(json); 39 | 40 | Map toJson() => _$EventToJson(this); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /lib/model/event.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'event.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Event _$EventFromJson(Map json) => Event( 10 | json['id'] as String?, 11 | json['type'] as String?, 12 | json['actor'] == null 13 | ? null 14 | : User.fromJson(json['actor'] as Map), 15 | json['repo'] == null 16 | ? null 17 | : Repository.fromJson(json['repo'] as Map), 18 | json['org'] == null 19 | ? null 20 | : User.fromJson(json['org'] as Map), 21 | json['payload'] == null 22 | ? null 23 | : EventPayload.fromJson(json['payload'] as Map), 24 | json['public'] as bool?, 25 | json['created_at'] == null 26 | ? null 27 | : DateTime.parse(json['created_at'] as String), 28 | ); 29 | 30 | Map _$EventToJson(Event instance) => { 31 | 'id': instance.id, 32 | 'type': instance.type, 33 | 'actor': instance.actor, 34 | 'repo': instance.repo, 35 | 'org': instance.org, 36 | 'payload': instance.payload, 37 | 'public': instance.isPublic, 38 | 'created_at': instance.createdAt?.toIso8601String(), 39 | }; 40 | -------------------------------------------------------------------------------- /lib/model/event_payload.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/issue.dart'; 2 | import 'package:gsy_github_app_flutter/model/issue_event.dart'; 3 | import 'package:gsy_github_app_flutter/model/push_event_commit.dart'; 4 | import 'package:gsy_github_app_flutter/model/release.dart'; 5 | import 'package:json_annotation/json_annotation.dart'; 6 | 7 | /** 8 | * Created by guoshuyu 9 | * Date: 2018-07-31 10 | */ 11 | 12 | part 'event_payload.g.dart'; 13 | 14 | @JsonSerializable() 15 | class EventPayload { 16 | 17 | @JsonKey(name: "push_id") 18 | int? pushId; 19 | int? size; 20 | @JsonKey(name: "distinct_size") 21 | int? distinctSize; 22 | String? ref; 23 | String? head; 24 | String? before; 25 | List? commits; 26 | 27 | String? action; 28 | @JsonKey(name: "ref_type") 29 | String? refType; 30 | @JsonKey(name: "master_branch") 31 | String? masterBranch; 32 | String? description; 33 | @JsonKey(name: "pusher_type") 34 | String? pusherType; 35 | 36 | Release? release; 37 | Issue? issue; 38 | IssueEvent? comment; 39 | 40 | EventPayload(); 41 | 42 | factory EventPayload.fromJson(Map json) => _$EventPayloadFromJson(json); 43 | 44 | Map toJson() => _$EventPayloadToJson(this); 45 | } 46 | -------------------------------------------------------------------------------- /lib/model/event_payload.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'event_payload.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | EventPayload _$EventPayloadFromJson(Map json) => EventPayload() 10 | ..pushId = (json['push_id'] as num?)?.toInt() 11 | ..size = (json['size'] as num?)?.toInt() 12 | ..distinctSize = (json['distinct_size'] as num?)?.toInt() 13 | ..ref = json['ref'] as String? 14 | ..head = json['head'] as String? 15 | ..before = json['before'] as String? 16 | ..commits = (json['commits'] as List?) 17 | ?.map((e) => PushEventCommit.fromJson(e as Map)) 18 | .toList() 19 | ..action = json['action'] as String? 20 | ..refType = json['ref_type'] as String? 21 | ..masterBranch = json['master_branch'] as String? 22 | ..description = json['description'] as String? 23 | ..pusherType = json['pusher_type'] as String? 24 | ..release = json['release'] == null 25 | ? null 26 | : Release.fromJson(json['release'] as Map) 27 | ..issue = json['issue'] == null 28 | ? null 29 | : Issue.fromJson(json['issue'] as Map) 30 | ..comment = json['comment'] == null 31 | ? null 32 | : IssueEvent.fromJson(json['comment'] as Map); 33 | 34 | Map _$EventPayloadToJson(EventPayload instance) => 35 | { 36 | 'push_id': instance.pushId, 37 | 'size': instance.size, 38 | 'distinct_size': instance.distinctSize, 39 | 'ref': instance.ref, 40 | 'head': instance.head, 41 | 'before': instance.before, 42 | 'commits': instance.commits, 43 | 'action': instance.action, 44 | 'ref_type': instance.refType, 45 | 'master_branch': instance.masterBranch, 46 | 'description': instance.description, 47 | 'pusher_type': instance.pusherType, 48 | 'release': instance.release, 49 | 'issue': instance.issue, 50 | 'comment': instance.comment, 51 | }; 52 | -------------------------------------------------------------------------------- /lib/model/file_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'file_model.g.dart'; 9 | 10 | @JsonSerializable() 11 | class FileModel { 12 | String? name; 13 | String? path; 14 | String? sha; 15 | int? size; 16 | String? url; 17 | @JsonKey(name: "html_url") 18 | String? htmlUrl; 19 | @JsonKey(name: "git_url") 20 | String? gitUrl; 21 | @JsonKey(name: "download_url") 22 | String? downloadUrl; 23 | @JsonKey(name: "type") 24 | String? type; 25 | 26 | FileModel( 27 | this.name, 28 | this.path, 29 | this.sha, 30 | this.size, 31 | this.url, 32 | this.htmlUrl, 33 | this.gitUrl, 34 | this.downloadUrl, 35 | this.type, 36 | ); 37 | 38 | factory FileModel.fromJson(Map json) => _$FileModelFromJson(json); 39 | 40 | Map toJson() => _$FileModelToJson(this); 41 | } 42 | -------------------------------------------------------------------------------- /lib/model/file_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'file_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | FileModel _$FileModelFromJson(Map json) => FileModel( 10 | json['name'] as String?, 11 | json['path'] as String?, 12 | json['sha'] as String?, 13 | (json['size'] as num?)?.toInt(), 14 | json['url'] as String?, 15 | json['html_url'] as String?, 16 | json['git_url'] as String?, 17 | json['download_url'] as String?, 18 | json['type'] as String?, 19 | ); 20 | 21 | Map _$FileModelToJson(FileModel instance) => { 22 | 'name': instance.name, 23 | 'path': instance.path, 24 | 'sha': instance.sha, 25 | 'size': instance.size, 26 | 'url': instance.url, 27 | 'html_url': instance.htmlUrl, 28 | 'git_url': instance.gitUrl, 29 | 'download_url': instance.downloadUrl, 30 | 'type': instance.type, 31 | }; 32 | -------------------------------------------------------------------------------- /lib/model/issue.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | /** 5 | * Created by guoshuyu 6 | * Date: 2018-07-31 7 | */ 8 | 9 | part 'issue.g.dart'; 10 | 11 | @JsonSerializable() 12 | class Issue { 13 | int? id; 14 | int? number; 15 | String? title; 16 | String? state; 17 | bool? locked; 18 | @JsonKey(name: "comments") 19 | int? commentNum; 20 | 21 | @JsonKey(name: "created_at") 22 | DateTime? createdAt; 23 | @JsonKey(name: "updated_at") 24 | DateTime? updatedAt; 25 | @JsonKey(name: "closed_at") 26 | DateTime? closedAt; 27 | String? body; 28 | @JsonKey(name: "body_html") 29 | String? bodyHtml; 30 | 31 | User? user; 32 | @JsonKey(name: "repository_url") 33 | String? repoUrl; 34 | @JsonKey(name: "html_url") 35 | String? htmlUrl; 36 | @JsonKey(name: "closed_by") 37 | User? closeBy; 38 | 39 | 40 | 41 | Issue( 42 | this.id, 43 | this.number, 44 | this.title, 45 | this.state, 46 | this.locked, 47 | this.commentNum, 48 | this.createdAt, 49 | this.updatedAt, 50 | this.closedAt, 51 | this.body, 52 | this.bodyHtml, 53 | this.user, 54 | this.repoUrl, 55 | this.htmlUrl, 56 | this.closeBy, 57 | ); 58 | 59 | factory Issue.fromJson(Map json) => _$IssueFromJson(json); 60 | 61 | Map toJson() => _$IssueToJson(this); 62 | } 63 | -------------------------------------------------------------------------------- /lib/model/issue.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'issue.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Issue _$IssueFromJson(Map json) => Issue( 10 | (json['id'] as num?)?.toInt(), 11 | (json['number'] as num?)?.toInt(), 12 | json['title'] as String?, 13 | json['state'] as String?, 14 | json['locked'] as bool?, 15 | (json['comments'] as num?)?.toInt(), 16 | json['created_at'] == null 17 | ? null 18 | : DateTime.parse(json['created_at'] as String), 19 | json['updated_at'] == null 20 | ? null 21 | : DateTime.parse(json['updated_at'] as String), 22 | json['closed_at'] == null 23 | ? null 24 | : DateTime.parse(json['closed_at'] as String), 25 | json['body'] as String?, 26 | json['body_html'] as String?, 27 | json['user'] == null 28 | ? null 29 | : User.fromJson(json['user'] as Map), 30 | json['repository_url'] as String?, 31 | json['html_url'] as String?, 32 | json['closed_by'] == null 33 | ? null 34 | : User.fromJson(json['closed_by'] as Map), 35 | ); 36 | 37 | Map _$IssueToJson(Issue instance) => { 38 | 'id': instance.id, 39 | 'number': instance.number, 40 | 'title': instance.title, 41 | 'state': instance.state, 42 | 'locked': instance.locked, 43 | 'comments': instance.commentNum, 44 | 'created_at': instance.createdAt?.toIso8601String(), 45 | 'updated_at': instance.updatedAt?.toIso8601String(), 46 | 'closed_at': instance.closedAt?.toIso8601String(), 47 | 'body': instance.body, 48 | 'body_html': instance.bodyHtml, 49 | 'user': instance.user, 50 | 'repository_url': instance.repoUrl, 51 | 'html_url': instance.htmlUrl, 52 | 'closed_by': instance.closeBy, 53 | }; 54 | -------------------------------------------------------------------------------- /lib/model/issue_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | /** 5 | * Created by guoshuyu 6 | * Date: 2018-07-31 7 | */ 8 | 9 | part 'issue_event.g.dart'; 10 | 11 | @JsonSerializable() 12 | class IssueEvent{ 13 | int? id; 14 | User? user; 15 | @JsonKey(name: "created_at") 16 | DateTime? createdAt; 17 | @JsonKey(name: "updated_at") 18 | DateTime? updatedAt; 19 | @JsonKey(name: "author_association") 20 | String? authorAssociation; 21 | String? body; 22 | @JsonKey(name: "body_html") 23 | String? bodyHtml; 24 | @JsonKey(name: "event") 25 | String? type; 26 | @JsonKey(name: "html_url") 27 | String? htmlUrl; 28 | 29 | IssueEvent( 30 | this.id, 31 | this.user, 32 | this.createdAt, 33 | this.updatedAt, 34 | this.authorAssociation, 35 | this.body, 36 | this.bodyHtml, 37 | this.type, 38 | this.htmlUrl, 39 | ); 40 | 41 | factory IssueEvent.fromJson(Map json) => _$IssueEventFromJson(json); 42 | 43 | Map toJson() => _$IssueEventToJson(this); 44 | } 45 | -------------------------------------------------------------------------------- /lib/model/issue_event.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'issue_event.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | IssueEvent _$IssueEventFromJson(Map json) => IssueEvent( 10 | (json['id'] as num?)?.toInt(), 11 | json['user'] == null 12 | ? null 13 | : User.fromJson(json['user'] as Map), 14 | json['created_at'] == null 15 | ? null 16 | : DateTime.parse(json['created_at'] as String), 17 | json['updated_at'] == null 18 | ? null 19 | : DateTime.parse(json['updated_at'] as String), 20 | json['author_association'] as String?, 21 | json['body'] as String?, 22 | json['body_html'] as String?, 23 | json['event'] as String?, 24 | json['html_url'] as String?, 25 | ); 26 | 27 | Map _$IssueEventToJson(IssueEvent instance) => 28 | { 29 | 'id': instance.id, 30 | 'user': instance.user, 31 | 'created_at': instance.createdAt?.toIso8601String(), 32 | 'updated_at': instance.updatedAt?.toIso8601String(), 33 | 'author_association': instance.authorAssociation, 34 | 'body': instance.body, 35 | 'body_html': instance.bodyHtml, 36 | 'event': instance.type, 37 | 'html_url': instance.htmlUrl, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/model/license.dart: -------------------------------------------------------------------------------- 1 | /// Created by guoshuyu 2 | /// Date: 2018-07-31 3 | library; 4 | import 'package:json_annotation/json_annotation.dart'; 5 | 6 | /** 7 | * Created by guoshuyu 8 | * Date: 2018-07-31 9 | */ 10 | 11 | part 'license.g.dart'; 12 | 13 | @JsonSerializable() 14 | class License { 15 | 16 | String? name; 17 | 18 | License(this.name); 19 | 20 | factory License.fromJson(Map json) => _$LicenseFromJson(json); 21 | 22 | Map toJson() => _$LicenseToJson(this); 23 | } 24 | -------------------------------------------------------------------------------- /lib/model/license.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'license.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | License _$LicenseFromJson(Map json) => License( 10 | json['name'] as String?, 11 | ); 12 | 13 | Map _$LicenseToJson(License instance) => { 14 | 'name': instance.name, 15 | }; 16 | -------------------------------------------------------------------------------- /lib/model/notification.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/notification_subject.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | import 'repository.dart'; 5 | 6 | /** 7 | * Created by guoshuyu 8 | * Date: 2018-07-31 9 | */ 10 | 11 | part 'notification.g.dart'; 12 | 13 | @JsonSerializable() 14 | class Notification { 15 | String? id; 16 | bool? unread; 17 | String? reason; 18 | @JsonKey(name: "updated_at") 19 | DateTime? updateAt; 20 | @JsonKey(name: "last_read_at") 21 | DateTime? lastReadAt; 22 | Repository? repository; 23 | NotificationSubject? subject; 24 | 25 | Notification(this.id, this.unread, this.reason, this.updateAt, this.lastReadAt, this.repository, this.subject); 26 | 27 | factory Notification.fromJson(Map json) => _$NotificationFromJson(json); 28 | 29 | Map toJson() => _$NotificationToJson(this); 30 | } 31 | -------------------------------------------------------------------------------- /lib/model/notification.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'notification.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Notification _$NotificationFromJson(Map json) => Notification( 10 | json['id'] as String?, 11 | json['unread'] as bool?, 12 | json['reason'] as String?, 13 | json['updated_at'] == null 14 | ? null 15 | : DateTime.parse(json['updated_at'] as String), 16 | json['last_read_at'] == null 17 | ? null 18 | : DateTime.parse(json['last_read_at'] as String), 19 | json['repository'] == null 20 | ? null 21 | : Repository.fromJson(json['repository'] as Map), 22 | json['subject'] == null 23 | ? null 24 | : NotificationSubject.fromJson( 25 | json['subject'] as Map), 26 | ); 27 | 28 | Map _$NotificationToJson(Notification instance) => 29 | { 30 | 'id': instance.id, 31 | 'unread': instance.unread, 32 | 'reason': instance.reason, 33 | 'updated_at': instance.updateAt?.toIso8601String(), 34 | 'last_read_at': instance.lastReadAt?.toIso8601String(), 35 | 'repository': instance.repository, 36 | 'subject': instance.subject, 37 | }; 38 | -------------------------------------------------------------------------------- /lib/model/notification_subject.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'notification_subject.g.dart'; 9 | 10 | @JsonSerializable() 11 | class NotificationSubject { 12 | String? title; 13 | String? url; 14 | String? type; 15 | 16 | NotificationSubject(this.title, this.url, this.type); 17 | 18 | factory NotificationSubject.fromJson(Map json) => _$NotificationSubjectFromJson(json); 19 | 20 | Map toJson() => _$NotificationSubjectToJson(this); 21 | } 22 | -------------------------------------------------------------------------------- /lib/model/notification_subject.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'notification_subject.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | NotificationSubject _$NotificationSubjectFromJson(Map json) => 10 | NotificationSubject( 11 | json['title'] as String?, 12 | json['url'] as String?, 13 | json['type'] as String?, 14 | ); 15 | 16 | Map _$NotificationSubjectToJson( 17 | NotificationSubject instance) => 18 | { 19 | 'title': instance.title, 20 | 'url': instance.url, 21 | 'type': instance.type, 22 | }; 23 | -------------------------------------------------------------------------------- /lib/model/push_commit.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/commitFile.dart'; 2 | import 'package:gsy_github_app_flutter/model/commit_git_info.dart'; 3 | import 'package:gsy_github_app_flutter/model/commit_stats.dart'; 4 | import 'package:gsy_github_app_flutter/model/user.dart'; 5 | import 'package:json_annotation/json_annotation.dart'; 6 | 7 | import 'repo_commit.dart'; 8 | 9 | /** 10 | * Created by guoshuyu 11 | * Date: 2018-07-31 12 | */ 13 | 14 | part 'push_commit.g.dart'; 15 | 16 | @JsonSerializable() 17 | class PushCommit { 18 | List? files; 19 | 20 | CommitStats? stats; 21 | 22 | String? sha; 23 | String? url; 24 | @JsonKey(name: "html_url") 25 | String? htmlUrl; 26 | @JsonKey(name: "comments_url") 27 | String? commentsUrl; 28 | 29 | CommitGitInfo? commit; 30 | User? author; 31 | User? committer; 32 | List? parents; 33 | 34 | PushCommit( 35 | this.files, 36 | this.stats, 37 | this.sha, 38 | this.url, 39 | this.htmlUrl, 40 | this.commentsUrl, 41 | this.commit, 42 | this.author, 43 | this.committer, 44 | this.parents, 45 | ); 46 | 47 | factory PushCommit.fromJson(Map json) => _$PushCommitFromJson(json); 48 | 49 | Map toJson() => _$PushCommitToJson(this); 50 | } 51 | -------------------------------------------------------------------------------- /lib/model/push_commit.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'push_commit.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PushCommit _$PushCommitFromJson(Map json) => PushCommit( 10 | (json['files'] as List?) 11 | ?.map((e) => CommitFile.fromJson(e as Map)) 12 | .toList(), 13 | json['stats'] == null 14 | ? null 15 | : CommitStats.fromJson(json['stats'] as Map), 16 | json['sha'] as String?, 17 | json['url'] as String?, 18 | json['html_url'] as String?, 19 | json['comments_url'] as String?, 20 | json['commit'] == null 21 | ? null 22 | : CommitGitInfo.fromJson(json['commit'] as Map), 23 | json['author'] == null 24 | ? null 25 | : User.fromJson(json['author'] as Map), 26 | json['committer'] == null 27 | ? null 28 | : User.fromJson(json['committer'] as Map), 29 | (json['parents'] as List?) 30 | ?.map((e) => RepoCommit.fromJson(e as Map)) 31 | .toList(), 32 | ); 33 | 34 | Map _$PushCommitToJson(PushCommit instance) => 35 | { 36 | 'files': instance.files, 37 | 'stats': instance.stats, 38 | 'sha': instance.sha, 39 | 'url': instance.url, 40 | 'html_url': instance.htmlUrl, 41 | 'comments_url': instance.commentsUrl, 42 | 'commit': instance.commit, 43 | 'author': instance.author, 44 | 'committer': instance.committer, 45 | 'parents': instance.parents, 46 | }; 47 | -------------------------------------------------------------------------------- /lib/model/push_event_commit.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | /** 5 | * Created by guoshuyu 6 | * Date: 2018-07-31 7 | */ 8 | 9 | part 'push_event_commit.g.dart'; 10 | 11 | @JsonSerializable() 12 | class PushEventCommit { 13 | String? sha; 14 | User? author; 15 | String? message; 16 | bool? distinct; 17 | String? url; 18 | 19 | PushEventCommit( 20 | this.sha, 21 | this.author, 22 | this.message, 23 | this.distinct, 24 | this.url, 25 | ); 26 | 27 | factory PushEventCommit.fromJson(Map json) => _$PushEventCommitFromJson(json); 28 | 29 | Map toJson() => _$PushEventCommitToJson(this); 30 | } 31 | -------------------------------------------------------------------------------- /lib/model/push_event_commit.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'push_event_commit.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PushEventCommit _$PushEventCommitFromJson(Map json) => 10 | PushEventCommit( 11 | json['sha'] as String?, 12 | json['author'] == null 13 | ? null 14 | : User.fromJson(json['author'] as Map), 15 | json['message'] as String?, 16 | json['distinct'] as bool?, 17 | json['url'] as String?, 18 | ); 19 | 20 | Map _$PushEventCommitToJson(PushEventCommit instance) => 21 | { 22 | 'sha': instance.sha, 23 | 'author': instance.author, 24 | 'message': instance.message, 25 | 'distinct': instance.distinct, 26 | 'url': instance.url, 27 | }; 28 | -------------------------------------------------------------------------------- /lib/model/release.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/release_asset.dart'; 2 | import 'package:gsy_github_app_flutter/model/user.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | /** 6 | * Created by guoshuyu 7 | * Date: 2018-07-31 8 | */ 9 | 10 | part 'release.g.dart'; 11 | 12 | @JsonSerializable() 13 | class Release { 14 | int? id; 15 | @JsonKey(name: "tag_name") 16 | String? tagName; 17 | @JsonKey(name: "target_commitish") 18 | String? targetCommitish; 19 | String? name; 20 | String? body; 21 | @JsonKey(name: "body_html") 22 | String? bodyHtml; 23 | @JsonKey(name: "tarball_url") 24 | String? tarballUrl; 25 | @JsonKey(name: "zipball_url") 26 | String? zipballUrl; 27 | 28 | bool? draft; 29 | @JsonKey(name: "prerelease") 30 | bool? preRelease; 31 | @JsonKey(name: "created_at") 32 | DateTime? createdAt; 33 | @JsonKey(name: "published_at") 34 | DateTime? publishedAt; 35 | 36 | User? author; 37 | List? assets; 38 | 39 | Release( 40 | this.id, 41 | this.tagName, 42 | this.targetCommitish, 43 | this.name, 44 | this.body, 45 | this.bodyHtml, 46 | this.tarballUrl, 47 | this.zipballUrl, 48 | this.draft, 49 | this.preRelease, 50 | this.createdAt, 51 | this.publishedAt, 52 | this.author, 53 | this.assets, 54 | ); 55 | 56 | factory Release.fromJson(Map json) => 57 | _$ReleaseFromJson(json); 58 | 59 | Map toJson() => _$ReleaseToJson(this); 60 | } 61 | -------------------------------------------------------------------------------- /lib/model/release.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'release.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Release _$ReleaseFromJson(Map json) => Release( 10 | (json['id'] as num?)?.toInt(), 11 | json['tag_name'] as String?, 12 | json['target_commitish'] as String?, 13 | json['name'] as String?, 14 | json['body'] as String?, 15 | json['body_html'] as String?, 16 | json['tarball_url'] as String?, 17 | json['zipball_url'] as String?, 18 | json['draft'] as bool?, 19 | json['prerelease'] as bool?, 20 | json['created_at'] == null 21 | ? null 22 | : DateTime.parse(json['created_at'] as String), 23 | json['published_at'] == null 24 | ? null 25 | : DateTime.parse(json['published_at'] as String), 26 | json['author'] == null 27 | ? null 28 | : User.fromJson(json['author'] as Map), 29 | (json['assets'] as List?) 30 | ?.map((e) => ReleaseAsset.fromJson(e as Map)) 31 | .toList(), 32 | ); 33 | 34 | Map _$ReleaseToJson(Release instance) => { 35 | 'id': instance.id, 36 | 'tag_name': instance.tagName, 37 | 'target_commitish': instance.targetCommitish, 38 | 'name': instance.name, 39 | 'body': instance.body, 40 | 'body_html': instance.bodyHtml, 41 | 'tarball_url': instance.tarballUrl, 42 | 'zipball_url': instance.zipballUrl, 43 | 'draft': instance.draft, 44 | 'prerelease': instance.preRelease, 45 | 'created_at': instance.createdAt?.toIso8601String(), 46 | 'published_at': instance.publishedAt?.toIso8601String(), 47 | 'author': instance.author, 48 | 'assets': instance.assets, 49 | }; 50 | -------------------------------------------------------------------------------- /lib/model/release_asset.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/user.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | /** 5 | * Created by guoshuyu 6 | * Date: 2018-07-31 7 | */ 8 | 9 | part 'release_asset.g.dart'; 10 | 11 | @JsonSerializable() 12 | class ReleaseAsset { 13 | int? id; 14 | String? name; 15 | String? label; 16 | User? uploader; 17 | @JsonKey(name: "content_type") 18 | String? contentType; 19 | String? state; 20 | int? size; 21 | int? downloadCout; 22 | @JsonKey(name: "created_at") 23 | DateTime? createdAt; 24 | @JsonKey(name: "updated_at") 25 | DateTime? updatedAt; 26 | @JsonKey(name: "browser_download_url") 27 | String? downloadUrl; 28 | 29 | ReleaseAsset( 30 | this.id, 31 | this.name, 32 | this.label, 33 | this.uploader, 34 | this.contentType, 35 | this.state, 36 | this.size, 37 | this.downloadCout, 38 | this.createdAt, 39 | this.updatedAt, 40 | this.downloadUrl, 41 | ); 42 | 43 | factory ReleaseAsset.fromJson(Map json) => _$ReleaseAssetFromJson(json); 44 | Map toJson() => _$ReleaseAssetToJson(this); 45 | } 46 | -------------------------------------------------------------------------------- /lib/model/release_asset.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'release_asset.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ReleaseAsset _$ReleaseAssetFromJson(Map json) => ReleaseAsset( 10 | (json['id'] as num?)?.toInt(), 11 | json['name'] as String?, 12 | json['label'] as String?, 13 | json['uploader'] == null 14 | ? null 15 | : User.fromJson(json['uploader'] as Map), 16 | json['content_type'] as String?, 17 | json['state'] as String?, 18 | (json['size'] as num?)?.toInt(), 19 | (json['downloadCout'] as num?)?.toInt(), 20 | json['created_at'] == null 21 | ? null 22 | : DateTime.parse(json['created_at'] as String), 23 | json['updated_at'] == null 24 | ? null 25 | : DateTime.parse(json['updated_at'] as String), 26 | json['browser_download_url'] as String?, 27 | ); 28 | 29 | Map _$ReleaseAssetToJson(ReleaseAsset instance) => 30 | { 31 | 'id': instance.id, 32 | 'name': instance.name, 33 | 'label': instance.label, 34 | 'uploader': instance.uploader, 35 | 'content_type': instance.contentType, 36 | 'state': instance.state, 37 | 'size': instance.size, 38 | 'downloadCout': instance.downloadCout, 39 | 'created_at': instance.createdAt?.toIso8601String(), 40 | 'updated_at': instance.updatedAt?.toIso8601String(), 41 | 'browser_download_url': instance.downloadUrl, 42 | }; 43 | -------------------------------------------------------------------------------- /lib/model/repo_commit.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/model/commit_git_info.dart'; 2 | import 'package:gsy_github_app_flutter/model/user.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | /** 6 | * Created by guoshuyu 7 | * Date: 2018-07-31 8 | */ 9 | 10 | part 'repo_commit.g.dart'; 11 | 12 | @JsonSerializable() 13 | class RepoCommit { 14 | String? sha; 15 | String? url; 16 | @JsonKey(name: "html_url") 17 | String? htmlUrl; 18 | @JsonKey(name: "comments_url") 19 | String? commentsUrl; 20 | 21 | CommitGitInfo? commit; 22 | User? author; 23 | User? committer; 24 | List? parents; 25 | 26 | RepoCommit( 27 | this.sha, 28 | this.url, 29 | this.htmlUrl, 30 | this.commentsUrl, 31 | this.commit, 32 | this.author, 33 | this.committer, 34 | this.parents, 35 | ); 36 | 37 | factory RepoCommit.fromJson(Map json) => _$RepoCommitFromJson(json); 38 | Map toJson() => _$RepoCommitToJson(this); 39 | } 40 | -------------------------------------------------------------------------------- /lib/model/repo_commit.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'repo_commit.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | RepoCommit _$RepoCommitFromJson(Map json) => RepoCommit( 10 | json['sha'] as String?, 11 | json['url'] as String?, 12 | json['html_url'] as String?, 13 | json['comments_url'] as String?, 14 | json['commit'] == null 15 | ? null 16 | : CommitGitInfo.fromJson(json['commit'] as Map), 17 | json['author'] == null 18 | ? null 19 | : User.fromJson(json['author'] as Map), 20 | json['committer'] == null 21 | ? null 22 | : User.fromJson(json['committer'] as Map), 23 | (json['parents'] as List?) 24 | ?.map((e) => RepoCommit.fromJson(e as Map)) 25 | .toList(), 26 | ); 27 | 28 | Map _$RepoCommitToJson(RepoCommit instance) => 29 | { 30 | 'sha': instance.sha, 31 | 'url': instance.url, 32 | 'html_url': instance.htmlUrl, 33 | 'comments_url': instance.commentsUrl, 34 | 'commit': instance.commit, 35 | 'author': instance.author, 36 | 'committer': instance.committer, 37 | 'parents': instance.parents, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/model/repository_permissions.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-07-31 6 | */ 7 | 8 | part 'repository_permissions.g.dart'; 9 | 10 | @JsonSerializable() 11 | class RepositoryPermissions { 12 | bool? admin; 13 | bool? push; 14 | bool? pull; 15 | 16 | RepositoryPermissions( 17 | this.admin, 18 | this.push, 19 | this.pull, 20 | ); 21 | 22 | factory RepositoryPermissions.fromJson(Map json) => _$RepositoryPermissionsFromJson(json); 23 | Map toJson() => _$RepositoryPermissionsToJson(this); 24 | } 25 | -------------------------------------------------------------------------------- /lib/model/repository_permissions.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'repository_permissions.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | RepositoryPermissions _$RepositoryPermissionsFromJson( 10 | Map json) => 11 | RepositoryPermissions( 12 | json['admin'] as bool?, 13 | json['push'] as bool?, 14 | json['pull'] as bool?, 15 | ); 16 | 17 | Map _$RepositoryPermissionsToJson( 18 | RepositoryPermissions instance) => 19 | { 20 | 'admin': instance.admin, 21 | 'push': instance.push, 22 | 'pull': instance.pull, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/model/search_user_ql.dart: -------------------------------------------------------------------------------- 1 | class SearchUserQL { 2 | final int? followers; 3 | final String? name; 4 | final String? avatarUrl; 5 | final String? bio; 6 | final String? login; 7 | final String? lang; 8 | 9 | SearchUserQL({ 10 | this.followers, 11 | this.name, 12 | this.avatarUrl, 13 | this.bio, 14 | this.login, 15 | this.lang, 16 | }); 17 | 18 | static fromMap(Map map) { 19 | String? lang; 20 | if (map["lang"] != null && 21 | map["lang"]["nodes"] != null && 22 | map["lang"]["nodes"].length > 0 && 23 | map["lang"]["nodes"][0]["languages"] != null && 24 | map["lang"]["nodes"][0]["languages"]["nodes"] != null && 25 | map["lang"]["nodes"][0]["languages"]["nodes"].length > 0) { 26 | lang = map["lang"]["nodes"][0]["languages"]["nodes"][0]["name"]; 27 | } 28 | return SearchUserQL( 29 | followers: map["followers"]?["totalCount"], 30 | name: map["name"], 31 | avatarUrl: map["avatarUrl"], 32 | bio: map["bio"], 33 | login: map["login"], 34 | lang: lang, 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/model/template.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | 4 | part 'template.g.dart'; 5 | 6 | @JsonSerializable() 7 | class Template { 8 | 9 | String? name; 10 | 11 | int? id; 12 | 13 | 14 | @JsonKey(name: "push_id") 15 | int? pushId; 16 | 17 | Template(this.name, this.id, this.pushId); 18 | 19 | 20 | factory Template.fromJson(Map json) => _$TemplateFromJson(json); 21 | Map toJson() => _$TemplateToJson(this); 22 | } 23 | -------------------------------------------------------------------------------- /lib/model/template.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'template.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Template _$TemplateFromJson(Map json) => Template( 10 | json['name'] as String?, 11 | (json['id'] as num?)?.toInt(), 12 | (json['push_id'] as num?)?.toInt(), 13 | ); 14 | 15 | Map _$TemplateToJson(Template instance) => { 16 | 'name': instance.name, 17 | 'id': instance.id, 18 | 'push_id': instance.pushId, 19 | }; 20 | -------------------------------------------------------------------------------- /lib/model/trending_repo_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-08-07 6 | */ 7 | part 'trending_repo_model.g.dart'; 8 | 9 | @JsonSerializable() 10 | class TrendingRepoModel { 11 | String? fullName; 12 | String? url; 13 | 14 | String? description; 15 | String? language; 16 | String? meta; 17 | List? contributors; 18 | String? contributorsUrl; 19 | 20 | String? starCount; 21 | String? forkCount; 22 | String? name; 23 | 24 | String? reposName; 25 | 26 | TrendingRepoModel( 27 | this.fullName, 28 | this.url, 29 | this.description, 30 | this.language, 31 | this.meta, 32 | this.contributors, 33 | this.contributorsUrl, 34 | this.starCount, 35 | this.name, 36 | this.reposName, 37 | this.forkCount, 38 | ); 39 | 40 | TrendingRepoModel.empty(); 41 | 42 | factory TrendingRepoModel.fromJson(Map json) => _$TrendingRepoModelFromJson(json); 43 | 44 | Map toJson() => _$TrendingRepoModelToJson(this); 45 | } 46 | -------------------------------------------------------------------------------- /lib/model/trending_repo_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'trending_repo_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TrendingRepoModel _$TrendingRepoModelFromJson(Map json) => 10 | TrendingRepoModel( 11 | json['fullName'] as String?, 12 | json['url'] as String?, 13 | json['description'] as String?, 14 | json['language'] as String?, 15 | json['meta'] as String?, 16 | (json['contributors'] as List?) 17 | ?.map((e) => e as String) 18 | .toList(), 19 | json['contributorsUrl'] as String?, 20 | json['starCount'] as String?, 21 | json['name'] as String?, 22 | json['reposName'] as String?, 23 | json['forkCount'] as String?, 24 | ); 25 | 26 | Map _$TrendingRepoModelToJson(TrendingRepoModel instance) => 27 | { 28 | 'fullName': instance.fullName, 29 | 'url': instance.url, 30 | 'description': instance.description, 31 | 'language': instance.language, 32 | 'meta': instance.meta, 33 | 'contributors': instance.contributors, 34 | 'contributorsUrl': instance.contributorsUrl, 35 | 'starCount': instance.starCount, 36 | 'forkCount': instance.forkCount, 37 | 'name': instance.name, 38 | 'reposName': instance.reposName, 39 | }; 40 | -------------------------------------------------------------------------------- /lib/model/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'user.g.dart'; 4 | 5 | @JsonSerializable() 6 | class User { 7 | User( 8 | this.login, 9 | this.id, 10 | this.node_id, 11 | this.avatar_url, 12 | this.gravatar_id, 13 | this.url, 14 | this.html_url, 15 | this.followers_url, 16 | this.following_url, 17 | this.gists_url, 18 | this.starred_url, 19 | this.subscriptions_url, 20 | this.organizations_url, 21 | this.repos_url, 22 | this.events_url, 23 | this.received_events_url, 24 | this.type, 25 | this.site_admin, 26 | this.name, 27 | this.company, 28 | this.blog, 29 | this.location, 30 | this.email, 31 | this.starred, 32 | this.bio, 33 | this.public_repos, 34 | this.public_gists, 35 | this.followers, 36 | this.following, 37 | this.created_at, 38 | this.updated_at, 39 | this.private_gists, 40 | this.total_private_repos, 41 | this.owned_private_repos, 42 | this.disk_usage, 43 | this.collaborators, 44 | this.two_factor_authentication); 45 | 46 | String? login; 47 | int? id; 48 | String? node_id; 49 | String? avatar_url; 50 | String? gravatar_id; 51 | String? url; 52 | String? html_url; 53 | String? followers_url; 54 | String? following_url; 55 | String? gists_url; 56 | String? starred_url; 57 | String? subscriptions_url; 58 | String? organizations_url; 59 | String? repos_url; 60 | String? events_url; 61 | String? received_events_url; 62 | String? type; 63 | bool? site_admin; 64 | String? name; 65 | String? company; 66 | String? blog; 67 | String? location; 68 | String? email; 69 | String? starred; 70 | String? bio; 71 | int? public_repos; 72 | int? public_gists; 73 | int? followers; 74 | int? following; 75 | DateTime? created_at; 76 | DateTime? updated_at; 77 | int? private_gists; 78 | int? total_private_repos; 79 | int? owned_private_repos; 80 | int? disk_usage; 81 | int? collaborators; 82 | bool? two_factor_authentication; 83 | 84 | 85 | factory User.fromJson(Map json) => _$UserFromJson(json); 86 | 87 | 88 | Map toJson() => _$UserToJson(this); 89 | 90 | // 命名构造函数 91 | User.empty(); 92 | 93 | } 94 | -------------------------------------------------------------------------------- /lib/model/user_org.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | /** 4 | * Created by guoshuyu 5 | * Date: 2018-08-10 6 | */ 7 | part 'user_org.g.dart'; 8 | 9 | @JsonSerializable() 10 | class UserOrg { 11 | String? login; 12 | int? id; 13 | String? url; 14 | String? description; 15 | @JsonKey(name: "node_id") 16 | String? nodeId; 17 | @JsonKey(name: "repos_url") 18 | String? reposUrl; 19 | @JsonKey(name: "events_url") 20 | String? eventsUrl; 21 | @JsonKey(name: "hooks_url") 22 | String? hooksUrl; 23 | @JsonKey(name: "issues_url") 24 | String? issuesUrl; 25 | @JsonKey(name: "members_url") 26 | String? membersUrl; 27 | @JsonKey(name: "public_members_url") 28 | String? publicMembersUrl; 29 | @JsonKey(name: "avatar_url") 30 | String? avatarUrl; 31 | 32 | UserOrg( 33 | this.login, 34 | this.id, 35 | this.url, 36 | this.description, 37 | this.nodeId, 38 | this.reposUrl, 39 | this.eventsUrl, 40 | this.hooksUrl, 41 | this.issuesUrl, 42 | this.membersUrl, 43 | this.publicMembersUrl, 44 | this.avatarUrl, 45 | ); 46 | 47 | factory UserOrg.fromJson(Map json) => _$UserOrgFromJson(json); 48 | 49 | 50 | Map toJson() => _$UserOrgToJson(this); 51 | 52 | // 命名构造函数 53 | UserOrg.empty(); 54 | } 55 | -------------------------------------------------------------------------------- /lib/model/user_org.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'user_org.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | UserOrg _$UserOrgFromJson(Map json) => UserOrg( 10 | json['login'] as String?, 11 | (json['id'] as num?)?.toInt(), 12 | json['url'] as String?, 13 | json['description'] as String?, 14 | json['node_id'] as String?, 15 | json['repos_url'] as String?, 16 | json['events_url'] as String?, 17 | json['hooks_url'] as String?, 18 | json['issues_url'] as String?, 19 | json['members_url'] as String?, 20 | json['public_members_url'] as String?, 21 | json['avatar_url'] as String?, 22 | ); 23 | 24 | Map _$UserOrgToJson(UserOrg instance) => { 25 | 'login': instance.login, 26 | 'id': instance.id, 27 | 'url': instance.url, 28 | 'description': instance.description, 29 | 'node_id': instance.nodeId, 30 | 'repos_url': instance.reposUrl, 31 | 'events_url': instance.eventsUrl, 32 | 'hooks_url': instance.hooksUrl, 33 | 'issues_url': instance.issuesUrl, 34 | 'members_url': instance.membersUrl, 35 | 'public_members_url': instance.publicMembersUrl, 36 | 'avatar_url': instance.avatarUrl, 37 | }; 38 | -------------------------------------------------------------------------------- /lib/page/dynamic/dynamic_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/common/config/config.dart'; 2 | import 'package:gsy_github_app_flutter/common/repositories/event_repository.dart'; 3 | import 'package:gsy_github_app_flutter/widget/pull/gsy_pull_new_load_widget.dart'; 4 | 5 | /// Created by guoshuyu 6 | /// on 2019/3/23. 7 | class DynamicBloc { 8 | final GSYPullLoadWidgetControl pullLoadWidgetControl = 9 | GSYPullLoadWidgetControl(); 10 | 11 | int _page = 1; 12 | 13 | requestRefresh(String? userName, {doNextFlag = true}) async { 14 | pageReset(); 15 | var res = 16 | await EventRepository.getEventReceived(userName, page: _page, needDb: true); 17 | changeLoadMoreStatus(getLoadMoreStatus(res)); 18 | refreshData(res); 19 | if (doNextFlag) { 20 | await doNext(res); 21 | } 22 | return res; 23 | } 24 | 25 | requestLoadMore(String? userName) async { 26 | pageUp(); 27 | var res = await EventRepository.getEventReceived(userName, page: _page); 28 | changeLoadMoreStatus(getLoadMoreStatus(res)); 29 | loadMoreData(res); 30 | return res; 31 | } 32 | 33 | pageReset() { 34 | _page = 1; 35 | } 36 | 37 | pageUp() { 38 | _page++; 39 | } 40 | 41 | getLoadMoreStatus(res) { 42 | return (res != null && 43 | res.data != null && 44 | res.data.length == Config.PAGE_SIZE); 45 | } 46 | 47 | doNext(res) async { 48 | if (res?.next != null) { 49 | var resNext = await res.next(); 50 | if (resNext != null && resNext.result) { 51 | changeLoadMoreStatus(getLoadMoreStatus(resNext)); 52 | refreshData(resNext); 53 | } 54 | } 55 | } 56 | 57 | ///列表数据长度 58 | int? getDataLength() { 59 | return pullLoadWidgetControl.dataList?.length; 60 | } 61 | 62 | ///修改加载更多 63 | changeLoadMoreStatus(bool needLoadMore) { 64 | pullLoadWidgetControl.needLoadMore = needLoadMore; 65 | } 66 | 67 | ///是否需要头部 68 | changeNeedHeaderStatus(bool needHeader) { 69 | pullLoadWidgetControl.needHeader = needHeader; 70 | } 71 | 72 | ///刷新列表数据 73 | refreshData(res) { 74 | if (res != null) { 75 | pullLoadWidgetControl.dataList = res.data; 76 | } 77 | } 78 | 79 | ///加载更多数据 80 | loadMoreData(res) { 81 | if (res != null) { 82 | pullLoadWidgetControl.addList(res.data); 83 | } 84 | } 85 | 86 | ///清理数据 87 | clearData() { 88 | refreshData([]); 89 | } 90 | 91 | ///列表数据 92 | get dataList => pullLoadWidgetControl.dataList; 93 | 94 | void dispose() { 95 | pullLoadWidgetControl.dispose(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/page/honor_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/common/localization/extension.dart'; 3 | import 'package:gsy_github_app_flutter/common/utils/navigator_utils.dart'; 4 | import 'package:gsy_github_app_flutter/page/repos/widget/repos_item.dart'; 5 | 6 | /// 荣耀list 7 | /// Created by guoshuyu 8 | /// on 2018/7/22. 9 | class HonorListPage extends StatefulWidget { 10 | final List? list; 11 | 12 | const HonorListPage(this.list, {super.key}); 13 | 14 | @override 15 | _HonorListPageState createState() => _HonorListPageState(); 16 | } 17 | 18 | class _HonorListPageState extends State { 19 | _renderItem(item) { 20 | ReposViewModel reposViewModel = ReposViewModel.fromMap(item); 21 | return ReposItem(reposViewModel, onPressed: () { 22 | NavigatorUtils.goReposDetail( 23 | context, reposViewModel.ownerName, reposViewModel.repositoryName); 24 | }); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Scaffold( 30 | appBar: AppBar( 31 | title: Text( 32 | context.l10n.user_tab_honor, 33 | maxLines: 1, 34 | overflow: TextOverflow.ellipsis, 35 | )), 36 | body: ListView.builder( 37 | itemBuilder: (context, index) { 38 | return _renderItem(widget.list![index]); 39 | }, 40 | itemCount: widget.list!.length, 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/page/photoview_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 3 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 4 | import 'package:gsy_github_app_flutter/widget/gsy_common_option_widget.dart'; 5 | import 'package:gsy_github_app_flutter/widget/gsy_title_bar.dart'; 6 | import 'package:photo_view/photo_view.dart'; 7 | 8 | /// 图片预览 9 | /// Created by guoshuyu 10 | /// Date: 2018-08-09 11 | 12 | class PhotoViewPage extends StatelessWidget { 13 | static const String sName = "PhotoViewPage"; 14 | 15 | const PhotoViewPage({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final String? url = ModalRoute.of(context)!.settings.arguments as String?; 20 | return Scaffold( 21 | floatingActionButton: FloatingActionButton( 22 | child: const Icon(Icons.file_download), 23 | onPressed: () { 24 | /* CommonUtils.saveImage(url).then((res) { 25 | if (res != null) { 26 | Fluttertoast.showToast(msg: res); 27 | if (Platform.isAndroid) { 28 | const updateAlbum = const MethodChannel( 29 | 'com.shuyu.gsygithub.gsygithubflutter/UpdateAlbumPlugin'); 30 | updateAlbum.invokeMethod('updateAlbum', { 31 | 'path': res, 32 | 'name': CommonUtils.splitFileNameByPath(res) 33 | }); 34 | } 35 | } 36 | });*/ 37 | }, 38 | ), 39 | appBar: AppBar( 40 | title: 41 | GSYTitleBar("", rightWidget: GSYCommonOptionWidget(url: url)), 42 | ), 43 | body: Container( 44 | color: Colors.black, 45 | child: PhotoView( 46 | imageProvider: 47 | NetworkImage(url ?? GSYICons.DEFAULT_REMOTE_PIC), 48 | loadingBuilder: ( 49 | BuildContext context, 50 | ImageChunkEvent? event, 51 | ) { 52 | return Stack( 53 | children: [ 54 | Center( 55 | child: Image.asset(GSYICons.DEFAULT_IMAGE, 56 | height: 180.0, width: 180.0)), 57 | const Center( 58 | child: SpinKitFoldingCube( 59 | color: Colors.white30, size: 60.0)), 60 | ], 61 | ); 62 | }), 63 | )); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/page/push/widget/push_coed_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/model/commitFile.dart'; 3 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 4 | import 'package:gsy_github_app_flutter/widget/gsy_card_item.dart'; 5 | 6 | /// 推送修改代码Item 7 | /// Created by guoshuyu 8 | /// Date: 2018-07-27 9 | 10 | class PushCodeItem extends StatelessWidget { 11 | final PushCodeItemViewModel pushCodeItemViewModel; 12 | final VoidCallback onPressed; 13 | 14 | const PushCodeItem(this.pushCodeItemViewModel, this.onPressed, {super.key}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ 19 | Container( 20 | ///修改文件路径 21 | margin: const EdgeInsets.only(left: 10.0, top: 5.0, right: 10.0, bottom: 0.0), 22 | child: Text( 23 | pushCodeItemViewModel.path, 24 | style: GSYConstant.smallSubLightText, 25 | ), 26 | ), 27 | GSYCardItem( 28 | ///修改文件名 29 | margin: const EdgeInsets.only(left: 10.0, top: 5.0, right: 10.0, bottom: 5.0), 30 | child: ListTile( 31 | title: Text(pushCodeItemViewModel.name!, style: GSYConstant.smallSubText), 32 | leading: const Icon( 33 | GSYICons.REPOS_ITEM_FILE, 34 | size: 15.0, 35 | ), 36 | onTap: () { 37 | onPressed(); 38 | }, 39 | ), 40 | ), 41 | ]); 42 | } 43 | } 44 | 45 | class PushCodeItemViewModel { 46 | late String path; 47 | String? name; 48 | String? patch; 49 | 50 | String? blob_url; 51 | 52 | PushCodeItemViewModel(); 53 | 54 | PushCodeItemViewModel.fromMap(CommitFile map) { 55 | String filename = map.fileName!; 56 | List nameSplit = filename.split("/"); 57 | name = nameSplit[nameSplit.length - 1]; 58 | path = filename; 59 | patch = map.patch; 60 | blob_url = map.blobUrl; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/page/release/widget/release_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/model/release.dart'; 3 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 4 | import 'package:gsy_github_app_flutter/common/utils/common_utils.dart'; 5 | import 'package:gsy_github_app_flutter/widget/gsy_card_item.dart'; 6 | 7 | /// 版本TagItem 8 | /// Created by guoshuyu 9 | /// Date: 2018-07-30 10 | 11 | class ReleaseItem extends StatelessWidget { 12 | final ReleaseItemViewModel releaseItemViewModel; 13 | 14 | final GestureTapCallback? onPressed; 15 | final GestureLongPressCallback? onLongPress; 16 | 17 | const ReleaseItem(this.releaseItemViewModel, {super.key, this.onPressed, this.onLongPress}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return GSYCardItem( 22 | child: InkWell( 23 | onTap: onPressed, 24 | onLongPress: onLongPress, 25 | child: Padding( 26 | padding: const EdgeInsets.only(left: 10.0, top: 15.0, right: 10.0, bottom: 15.0), 27 | child: Row( 28 | children: [ 29 | Expanded(child: Text(releaseItemViewModel.actionTitle!, style: GSYConstant.smallTextBold)), 30 | Text(releaseItemViewModel.actionTime ?? "", style: GSYConstant.smallSubText), 31 | ], 32 | ), 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | 39 | class ReleaseItemViewModel { 40 | String? actionTime; 41 | String? actionTitle; 42 | String? actionMode; 43 | String? actionTarget; 44 | String? actionTargetHtml; 45 | String? body; 46 | 47 | ReleaseItemViewModel(); 48 | 49 | ReleaseItemViewModel.fromMap(Release map) { 50 | if (map.publishedAt != null) { 51 | actionTime = CommonUtils.getNewsTimeStr(map.publishedAt!); 52 | } 53 | actionTitle = map.name ?? map.tagName; 54 | actionTarget = map.targetCommitish; 55 | actionTargetHtml = map.bodyHtml; 56 | body = map.body ?? ""; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/page/repos/repository_detail_readme_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_spinkit/flutter_spinkit.dart'; 3 | import 'package:gsy_github_app_flutter/common/localization/extension.dart'; 4 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 5 | import 'package:gsy_github_app_flutter/common/utils/common_utils.dart'; 6 | import 'package:gsy_github_app_flutter/page/repos/provider/repos_detail_provider.dart'; 7 | import 'package:gsy_github_app_flutter/widget/markdown/gsy_markdown_widget.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | /// Readme 11 | /// Created by guoshuyu 12 | /// Date: 2018-07-18 13 | 14 | class RepositoryDetailReadmePage extends StatefulWidget { 15 | const RepositoryDetailReadmePage({super.key}); 16 | 17 | @override 18 | RepositoryDetailReadmePageState createState() => 19 | RepositoryDetailReadmePageState(); 20 | } 21 | 22 | class RepositoryDetailReadmePageState extends State 23 | with AutomaticKeepAliveClientMixin { 24 | RepositoryDetailReadmePageState(); 25 | 26 | Future? request; 27 | 28 | refreshReadme() { 29 | context.read().refreshReadme(); 30 | } 31 | 32 | @override 33 | bool get wantKeepAlive => true; 34 | 35 | @override 36 | void initState() { 37 | super.initState(); 38 | refreshReadme(); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | super.dispose(); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | super.build(context); 49 | 50 | ///展示 select 51 | var markdownData = 52 | context.select((p) => p.markdownData); 53 | var rp = context.read(); 54 | var widget = (markdownData == null) 55 | ? Center( 56 | child: Container( 57 | width: 200.0, 58 | height: 200.0, 59 | padding: const EdgeInsets.all(4.0), 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | children: [ 63 | SpinKitDoubleBounce(color: Theme.of(context).primaryColor), 64 | Container(width: 10.0), 65 | Text(context.l10n.loading_text, 66 | style: GSYConstant.middleText), 67 | ], 68 | ), 69 | ), 70 | ) 71 | : GSYMarkdownWidget( 72 | markdownData: markdownData, 73 | baseUrl: getRawBaseUrl( 74 | repoName: rp.reposName, 75 | userName: rp.userName, 76 | branch: rp.currentBranch)); 77 | 78 | return widget; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/page/search/search_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:gsy_github_app_flutter/common/config/config.dart'; 3 | import 'package:gsy_github_app_flutter/common/repositories/repos_repository.dart'; 4 | import 'package:gsy_github_app_flutter/page/search/widget/gsy_search_drawer.dart'; 5 | 6 | class SearchBLoC { 7 | 8 | ///搜索仓库还是人 9 | int selectIndex = 0; 10 | 11 | ///搜索文件 12 | String? get searchText { 13 | return textEditingController.text; 14 | } 15 | 16 | ///排序类型 17 | String? type = searchFilterType[0].value; 18 | 19 | ///排序 20 | String? sort = sortType[0].value; 21 | 22 | ///过滤语言 23 | String? language = searchLanguageType[0].value; 24 | 25 | final TextEditingController textEditingController = TextEditingController(); 26 | 27 | 28 | ///获取搜索数据 29 | getDataLogic(int page) async { 30 | return await ReposRepository.searchRepositoryRequest(searchText, language, type, sort, 31 | selectIndex == 0 ? null : 'user', page, Config.PAGE_SIZE); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /lib/page/trend/trend_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:gsy_github_app_flutter/common/repositories/data_result.dart'; 2 | import 'package:gsy_github_app_flutter/common/repositories/repos_repository.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | ///展示非注解 riverpod ,并且针对处理数据库 & 服务器数据请求,两个 provider 先后顺序 6 | 7 | bool trendLoadingState = false; 8 | bool trendRequestedState = false; 9 | 10 | final trendFirstProvider = 11 | FutureProvider.family((ref, query) async { 12 | var (since, selectType) = query; 13 | trendLoadingState = true; 14 | var res = await ReposRepository.getTrendRequest( 15 | since: since, languageType: selectType); 16 | trendLoadingState = false; 17 | trendRequestedState = true; 18 | if (res != null && res.result) { 19 | return res; 20 | } 21 | return null; 22 | }); 23 | 24 | final trendSecondProvider = 25 | FutureProvider.family((ref, query) async { 26 | trendLoadingState = true; 27 | final res = await ref.watch(trendFirstProvider(query).future); 28 | trendLoadingState = false; 29 | trendRequestedState = true; 30 | if (res != null && res.next != null) { 31 | var resNext = await res.next?.call(); 32 | if (resNext != null && resNext.result) { 33 | return res; 34 | } 35 | } 36 | return null; 37 | }); 38 | -------------------------------------------------------------------------------- /lib/page/trend/trend_user_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_refresh/easy_refresh.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | import 'package:gsy_github_app_flutter/common/localization/extension.dart'; 5 | import 'package:gsy_github_app_flutter/common/utils/navigator_utils.dart'; 6 | import 'package:gsy_github_app_flutter/model/search_user_ql.dart'; 7 | import 'package:gsy_github_app_flutter/page/trend/trend_user_provider.dart'; 8 | import 'package:gsy_github_app_flutter/page/user/widget/user_item.dart'; 9 | 10 | class TrendUserPage extends ConsumerStatefulWidget { 11 | const TrendUserPage({super.key}); 12 | 13 | @override 14 | _TrendUserPageState createState() => _TrendUserPageState(); 15 | } 16 | 17 | class _TrendUserPageState extends ConsumerState { 18 | String? endCursor; 19 | 20 | _renderItem(SearchUserQL data, int index) { 21 | return UserItem(UserItemViewModel.fromQL(data, index + 1), onPressed: () { 22 | NavigatorUtils.goPerson(context, data.login); 23 | }); 24 | } 25 | 26 | Future loadData(WidgetRef ref, {bool isRefresh = false}) async { 27 | /// getTrendUserProvider 这里只有一处地方使用,所以不需要做局部单实例共享 28 | final result = await ref.read( 29 | searchTrendUserRequestProvider("China", isRefresh, cursor: endCursor) 30 | .future); 31 | if (result != null) { 32 | var (dataList, cursor) = result; 33 | endCursor = cursor; 34 | } 35 | } 36 | 37 | requestRefresh() async { 38 | endCursor = null; 39 | await loadData(ref, isRefresh: true); 40 | } 41 | 42 | requestLoadMore() async { 43 | await loadData(ref); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | var dataList = ref.watch(trendCNUserListProvider); 49 | return Scaffold( 50 | appBar: AppBar( 51 | title: Text( 52 | context.l10n.trend_user_title, 53 | maxLines: 1, 54 | overflow: TextOverflow.ellipsis, 55 | )), 56 | body: EasyRefresh( 57 | header: const MaterialHeader(), 58 | footer: const BezierFooter(), 59 | refreshOnStart: true, 60 | onRefresh: requestRefresh, 61 | onLoad: requestLoadMore, 62 | child: ListView.builder( 63 | itemBuilder: (_, int index) => _renderItem(dataList[index], index), 64 | itemCount: dataList.length, 65 | ), 66 | )); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/page/trend/trend_user_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:gsy_github_app_flutter/common/repositories/user_repository.dart'; 3 | import 'package:gsy_github_app_flutter/model/search_user_ql.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | ///展示 riverpod ,并且支持翻页场景 7 | 8 | part 'trend_user_provider.g.dart'; 9 | 10 | ///无需释放,这样内存里就会保存着列表,下次进来不会空数据 11 | @Riverpod(keepAlive: true) 12 | class TrendCNUserList extends _$TrendCNUserList { 13 | 14 | ///如果调用 ref.refresh ,数据会被重置 15 | @override 16 | List build() { 17 | return []; 18 | } 19 | 20 | void setList(List list) { 21 | state = list; 22 | } 23 | 24 | void addList(List list) { 25 | state.addAll(list); 26 | ///需要为新的列表,不然不会触发更新 27 | state = [...state, ...list]; 28 | } 29 | 30 | void clear() { 31 | state = []; 32 | } 33 | } 34 | 35 | @riverpod 36 | Future<(List, String)?> searchTrendUserRequest( 37 | Ref ref, String location, bool isRefresh, 38 | {String? cursor}) async { 39 | var result = 40 | await UserRepository.searchTrendUserRequest("China", cursor: cursor); 41 | if (result.data != null) { 42 | var value = result.data; 43 | var trendRef = ref.read(trendCNUserListProvider.notifier); 44 | if (isRefresh) { 45 | trendRef.setList(value.$1); 46 | } else { 47 | trendRef.addList(value.$1); 48 | } 49 | // 这里 refresh 会导致数据在更新后又被清空 50 | //var _ = ref.refresh(trendCNUserListProvider.notifier); 51 | //如果是 ref.invalidate ,则是标记过时,下次读取时触发,延迟更新 52 | return result.value; 53 | } 54 | return null; 55 | } 56 | -------------------------------------------------------------------------------- /lib/page/user/base_person_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:gsy_github_app_flutter/common/repositories/repos_repository.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'base_person_provider.g.dart'; 6 | 7 | 8 | ///指定作用域,让该 provider as scoped ,[]表示不依赖其他,让其每次使用都在上下文独立 9 | @Riverpod(dependencies: []) 10 | Future fetchHonorData(Ref ref, String userName) async { 11 | var res = await ReposRepository.getUserRepository100StatusRequest(userName); 12 | if (res != null && res.result) { 13 | return HonorModel.fromJson(res.data); 14 | } 15 | return null; 16 | } 17 | 18 | class HonorModel { 19 | int? beStaredCount; 20 | List? honorList; 21 | 22 | HonorModel.fromJson(Map map) { 23 | beStaredCount = map["stared"]; 24 | honorList = map["list"]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/provider/app_state_provider.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'app_state_provider.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$appGrepStateHash() => r'2d597c1ee2158b81668c77d0c4c4773dae175e41'; 10 | 11 | /// 控制 App 灰度效果 12 | /// 13 | /// Copied from [AppGrepState]. 14 | @ProviderFor(AppGrepState) 15 | final appGrepStateProvider = 16 | AutoDisposeNotifierProvider.internal( 17 | AppGrepState.new, 18 | name: r'appGrepStateProvider', 19 | debugGetCreateSourceHash: 20 | const bool.fromEnvironment('dart.vm.product') ? null : _$appGrepStateHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$AppGrepState = AutoDisposeNotifier; 26 | String _$appLocalStateHash() => r'094022d96deb55273c2bc53466ad2bf5ee8bdce0'; 27 | 28 | /// 控制 App 语言 29 | /// 30 | /// Copied from [AppLocalState]. 31 | @ProviderFor(AppLocalState) 32 | final appLocalStateProvider = 33 | AutoDisposeNotifierProvider.internal( 34 | AppLocalState.new, 35 | name: r'appLocalStateProvider', 36 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 37 | ? null 38 | : _$appLocalStateHash, 39 | dependencies: null, 40 | allTransitiveDependencies: null, 41 | ); 42 | 43 | typedef _$AppLocalState = AutoDisposeNotifier; 44 | String _$appThemeStateHash() => r'a02ca99fb2b47827f007b77c8d1d371cb171b17e'; 45 | 46 | /// 控制 App 主题 47 | /// 48 | /// Copied from [AppThemeState]. 49 | @ProviderFor(AppThemeState) 50 | final appThemeStateProvider = 51 | AutoDisposeNotifierProvider.internal( 52 | AppThemeState.new, 53 | name: r'appThemeStateProvider', 54 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 55 | ? null 56 | : _$appThemeStateHash, 57 | dependencies: null, 58 | allTransitiveDependencies: null, 59 | ); 60 | 61 | typedef _$AppThemeState = AutoDisposeNotifier; 62 | // ignore_for_file: type=lint 63 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package 64 | -------------------------------------------------------------------------------- /lib/redux/gsy_state.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: implicit_call_tearoffs 2 | 3 | import 'package:gsy_github_app_flutter/model/user.dart'; 4 | import 'package:gsy_github_app_flutter/redux/login_redux.dart'; 5 | import 'package:gsy_github_app_flutter/redux/user_redux.dart'; 6 | import 'package:redux/redux.dart'; 7 | 8 | import 'middleware/epic_middleware.dart'; 9 | 10 | /** 11 | * Redux全局State 12 | * Created by guoshuyu 13 | * Date: 2018-07-16 14 | */ 15 | 16 | ///全局Redux store 的对象,保存State数据 17 | class GSYState { 18 | ///用户信息 19 | User? userInfo; 20 | 21 | 22 | ///是否登录 23 | bool? login; 24 | 25 | ///构造方法 26 | GSYState( 27 | {this.userInfo, 28 | this.login,}); 29 | } 30 | 31 | ///创建 Reducer 32 | ///源码中 Reducer 是一个方法 typedef State Reducer(State state, dynamic action); 33 | ///我们自定义了 appReducer 用于创建 store 34 | GSYState appReducer(GSYState state, action) { 35 | return GSYState( 36 | ///通过 UserReducer 将 GSYState 内的 userInfo 和 action 关联在一起 37 | userInfo: UserReducer(state.userInfo, action), 38 | login: LoginReducer(state.login, action), 39 | ); 40 | } 41 | 42 | final List> middleware = [ 43 | EpicMiddleware(loginEpic), 44 | EpicMiddleware(userInfoEpic), 45 | EpicMiddleware(oauthEpic), 46 | UserInfoMiddleware(), 47 | LoginMiddleware(), 48 | ]; 49 | -------------------------------------------------------------------------------- /lib/redux/middleware/combine_epics.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:rxdart/streams.dart'; 4 | 5 | import 'epic.dart'; 6 | import 'epic_store.dart'; 7 | 8 | /// Combines a list of [Epic]s into one. 9 | /// 10 | /// Rather than having one massive [Epic] that handles every possible type of 11 | /// action, it's best to break [Epic]s down into smaller, more manageable and 12 | /// testable units. This way we could have a `searchEpic`, a `chatEpic`, 13 | /// and an `updateProfileEpic`, for example. 14 | /// 15 | /// However, the [EpicMiddleware] accepts only one [Epic]. So what are we to do? 16 | /// Fear not: redux_epics includes class for combining [Epic]s together! 17 | /// 18 | /// Example: 19 | /// 20 | /// final epic = combineEpics([ 21 | /// searchEpic, 22 | /// chatEpic, 23 | /// updateProfileEpic, 24 | /// ]); 25 | Epic combineEpics(List> epics) { 26 | return (Stream actions, EpicStore store) { 27 | return MergeStream( 28 | epics.map((epic) => epic(actions, store)).toList()); 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /lib/redux/middleware/epic_middleware.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:redux/redux.dart'; 4 | import 'package:rxdart/transformers.dart'; 5 | 6 | import 'epic.dart'; 7 | import 'epic_store.dart'; 8 | 9 | /// A [Redux](https://pub.dartlang.org/packages/redux) middleware that passes 10 | /// a stream of dispatched actions to the given [Epic]. 11 | /// 12 | /// It is recommended that you put your `EpicMiddleware` first when constructing 13 | /// the list of middleware for your store so any actions dispatched from 14 | /// your [Epic] will be intercepted by the remaining Middleware. 15 | /// 16 | /// Example: 17 | /// 18 | /// var epicMiddleware = new EpicMiddleware(new ExampleEpic()); 19 | /// var store = new Store, Action>(reducer, 20 | /// initialState: [], middleware: [epicMiddleware]); 21 | class EpicMiddleware extends MiddlewareClass { 22 | final StreamController _actions = 23 | StreamController.broadcast(); 24 | final StreamController> _epics = 25 | StreamController.broadcast(sync: true); 26 | 27 | final bool supportAsyncGenerators; 28 | Epic _epic; 29 | bool _isSubscribed = false; 30 | 31 | EpicMiddleware(Epic epic, {this.supportAsyncGenerators = true}) 32 | : _epic = epic; 33 | 34 | @override 35 | void call(Store store, dynamic action, NextDispatcher next) { 36 | if (!_isSubscribed) { 37 | _epics.stream 38 | .switchMap((epic) => epic(_actions.stream, EpicStore(store))) 39 | .listen(store.dispatch); 40 | 41 | _epics.add(_epic); 42 | 43 | _isSubscribed = true; 44 | } 45 | 46 | next(action); 47 | 48 | if (supportAsyncGenerators) { 49 | // Future.delayed is an ugly hack to support async* functions. 50 | // 51 | // See: https://github.com/dart-lang/sdk/issues/33818 52 | Future.delayed(Duration.zero, () { 53 | _actions.add(action); 54 | }); 55 | } else { 56 | _actions.add(action); 57 | } 58 | } 59 | 60 | /// Gets or replaces the epic currently used by the middleware. 61 | /// 62 | /// Replacing epics is considered an advanced API. You might need this if your 63 | /// app grows large and want to instantiate Epics on the fly, rather than 64 | /// as a whole up front. 65 | Epic get epic => _epic; 66 | 67 | set epic(Epic newEpic) { 68 | _epic = newEpic; 69 | 70 | _epics.add(newEpic); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/redux/middleware/epic_store.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:redux/redux.dart'; 4 | 5 | /// A stripped-down Redux [Store]. Removes unsupported [Store] methods. 6 | /// 7 | /// Due to the way streams are implemented with Dart, it's impossible to 8 | /// perform `store.dispatch` from within an [Epic] or observe the store directly. 9 | class EpicStore { 10 | final Store _store; 11 | 12 | EpicStore(this._store); 13 | 14 | /// Returns the current state of the redux store 15 | State get state => _store.state; 16 | 17 | Stream get onChange => _store.onChange; 18 | 19 | /// through to the reducer. 20 | dynamic dispatch(dynamic action) { 21 | return _store.dispatch(action); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/redux/user_redux.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: implicit_call_tearoffs 2 | 3 | import 'package:gsy_github_app_flutter/common/logger.dart'; 4 | import 'package:gsy_github_app_flutter/common/repositories/user_repository.dart'; 5 | import 'package:gsy_github_app_flutter/model/user.dart'; 6 | import 'package:gsy_github_app_flutter/redux/gsy_state.dart'; 7 | import 'package:redux/redux.dart'; 8 | import 'package:rxdart/rxdart.dart'; 9 | 10 | import 'middleware/epic_store.dart'; 11 | 12 | 13 | /** 14 | * 用户相关Redux 15 | * Created by guoshuyu 16 | * Date: 2018-07-16 17 | */ 18 | 19 | /// redux 的 combineReducers, 通过 TypedReducer 将 UpdateUserAction 与 reducers 关联起来 20 | final UserReducer = combineReducers([ 21 | TypedReducer(_updateLoaded), 22 | ]); 23 | 24 | /// 如果有 UpdateUserAction 发起一个请求时 25 | /// 就会调用到 _updateLoaded 26 | /// _updateLoaded 这里接受一个新的userInfo,并返回 27 | User? _updateLoaded(User? user, action) { 28 | user = action.userInfo; 29 | return user; 30 | } 31 | 32 | ///定一个 UpdateUserAction ,用于发起 userInfo 的的改变 33 | ///类名随你喜欢定义,只要通过上面TypedReducer绑定就好 34 | class UpdateUserAction { 35 | final User? userInfo; 36 | 37 | UpdateUserAction(this.userInfo); 38 | } 39 | 40 | class FetchUserAction {} 41 | 42 | class UserInfoMiddleware implements MiddlewareClass { 43 | @override 44 | void call(Store store, dynamic action, NextDispatcher next) { 45 | if (action is UpdateUserAction) { 46 | printLog("*********** UserInfoMiddleware *********** "); 47 | } 48 | // Make sure to forward actions to the next middleware in the chain! 49 | next(action); 50 | } 51 | } 52 | 53 | Stream userInfoEpic( 54 | Stream actions, EpicStore store) { 55 | // Use the async* function to make easier 56 | Stream loadUserInfo() async* { 57 | printLog("*********** userInfoEpic _loadUserInfo ***********"); 58 | var res = await UserRepository.getUserInfo(null); 59 | yield UpdateUserAction(res.data); 60 | } 61 | 62 | return actions 63 | // to UpdateUserAction actions 64 | .whereType() 65 | // Don't start until the 10ms 66 | .debounce(((_) => TimerStream(true, const Duration(milliseconds: 10)))) 67 | .switchMap((action) => loadUserInfo()); 68 | } 69 | -------------------------------------------------------------------------------- /lib/test/demo_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/test/demo_page.dart'; 3 | 4 | class DemoApp extends StatelessWidget { 5 | const DemoApp({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const MaterialApp(home: DemoPage()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/test/demo_mixins.dart: -------------------------------------------------------------------------------- 1 | /// Created by guoshuyu 2 | /// Date: 2018-10-12 3 | // ignore_for_file: avoid_print, annotate_overrides 4 | 5 | library; 6 | 7 | import 'package:flutter/foundation.dart'; 8 | 9 | abstract class Base { 10 | a() { 11 | if (kDebugMode) { 12 | print("base a()"); 13 | } 14 | } 15 | 16 | b() { 17 | print("base b()"); 18 | } 19 | 20 | c() { 21 | print("base c()"); 22 | } 23 | } 24 | 25 | mixin A on Base { 26 | a() { 27 | print("A.a()"); 28 | //super.a(); 29 | } 30 | 31 | b() { 32 | if (kDebugMode) { 33 | print("A.b()"); 34 | } 35 | super.b(); 36 | } 37 | } 38 | 39 | mixin A2 on Base { 40 | a() { 41 | print("A2.a()"); 42 | super.a(); 43 | } 44 | } 45 | 46 | class B extends Base { 47 | a() { 48 | print("B.a()"); 49 | super.a(); 50 | } 51 | 52 | b() { 53 | if (kDebugMode) { 54 | print("B.b()"); 55 | } 56 | super.b(); 57 | } 58 | 59 | c() { 60 | if (kDebugMode) { 61 | print("B.c()"); 62 | } 63 | super.c(); 64 | } 65 | } 66 | 67 | class G extends B with A, A2 { 68 | 69 | } 70 | 71 | 72 | testMixins() { 73 | G t = G(); 74 | t.a(); 75 | t.b(); 76 | t.c(); 77 | } 78 | 79 | 80 | ///I/flutter (13627): A2.a() 81 | ///I/flutter (13627): A.a() 82 | ///I/flutter (13627): B.a() 83 | ///I/flutter (13627): base a() 84 | ///I/flutter (13627): A.b() 85 | ///I/flutter (13627): B.b() 86 | ///I/flutter (13627): base b() 87 | ///I/flutter (13627): B.c() 88 | ///I/flutter (13627): base c() -------------------------------------------------------------------------------- /lib/test/demo_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DemoPage extends StatefulWidget { 4 | const DemoPage({super.key}); 5 | 6 | @override 7 | _DemoPageState createState() => _DemoPageState(); 8 | } 9 | 10 | class _DemoPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | ///一个页面的开始 14 | ///如果是新页面,会自带返回按键 15 | return Scaffold( 16 | ///背景样式 17 | backgroundColor: Colors.blue, 18 | 19 | ///标题栏,当然不仅仅是标题栏 20 | appBar: AppBar( 21 | ///这个title是一个Widget 22 | title: const Text("Title"), 23 | ), 24 | ///正式的页面开始 25 | ///一个ListView,20个Item 26 | body: ListView.builder( 27 | itemBuilder: (context, index) { 28 | return Card( 29 | ///设置阴影的深度 30 | elevation: 5.0, 31 | ///增加圆角 32 | shape: const RoundedRectangleBorder( 33 | borderRadius: BorderRadius.all(Radius.circular(10.0))), 34 | color: Colors.white, 35 | margin: const EdgeInsets.only(left: 30.0, right: 30.0, top: 30), 36 | child: Container( 37 | alignment: Alignment.centerLeft, 38 | padding: const EdgeInsets.symmetric(horizontal: 10), 39 | height: 80, 40 | child: Text("显示文本 $index"), 41 | ), 42 | ); 43 | }, 44 | itemCount: 20, 45 | ), 46 | floatingActionButton: Builder(builder: (builderContext) { 47 | ///利用 builder 的 builderContext 48 | ///才能获取到 Scaffold.of(builderContext) 的 ScaffoldState 49 | return FloatingActionButton( 50 | onPressed: () { 51 | ScaffoldMessenger.of(builderContext) 52 | .showSnackBar(const SnackBar(content: Text("SnackBar"))); 53 | }, 54 | ); 55 | }), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/test/demo_text_field_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | class DemoTextFieldPage extends StatefulWidget { 5 | const DemoTextFieldPage({super.key}); 6 | 7 | @override 8 | _DemoTextFieldPageState createState() => _DemoTextFieldPageState(); 9 | } 10 | 11 | class _DemoTextFieldPageState extends State { 12 | final TextEditingController controller = TextEditingController(); 13 | final FocusNode focusNode = FocusNode(); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | appBar: AppBar( 19 | title: const Text("DemoTextFieldPage"), 20 | ), 21 | body: Container( 22 | alignment: Alignment.center, 23 | padding: const EdgeInsets.symmetric(horizontal: 10), 24 | child: TextField( 25 | controller: controller, 26 | obscureText: true, 27 | keyboardType: TextInputType.number, 28 | focusNode: focusNode, 29 | decoration: const InputDecoration( 30 | hintText: "请输入密码", 31 | icon: Icon(Icons.keyboard), 32 | prefix: Icon(Icons.person), 33 | suffix: Icon(Icons.remove_red_eye), 34 | labelText: "labelText", 35 | helperText: "helperText", 36 | counterText: "counterText", 37 | enabledBorder: OutlineInputBorder( 38 | /*边角*/ 39 | borderRadius: BorderRadius.all( 40 | Radius.circular(10), //边角为30 41 | ), 42 | borderSide: BorderSide( 43 | color: Colors.blue, //边线颜色为黄色 44 | width: 2, //边线宽度为2 45 | ), 46 | ), 47 | focusedBorder: OutlineInputBorder( 48 | borderSide: BorderSide( 49 | color: Colors.black, 50 | width: 5, 51 | )), 52 | ), 53 | inputFormatters: [ 54 | FilteringTextInputFormatter.digitsOnly 55 | ], 56 | ), 57 | ), 58 | floatingActionButton: FloatingActionButton( 59 | onPressed: () { 60 | controller.text = ""; 61 | FocusScope.of(context).requestFocus(FocusNode()); 62 | }, 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/test/demo_user_store.dart: -------------------------------------------------------------------------------- 1 | /// Created by guoshuyu 2 | /// Date: 2018-08-06 3 | library; 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_redux/flutter_redux.dart'; 7 | import 'package:gsy_github_app_flutter/model/user.dart'; 8 | import 'package:gsy_github_app_flutter/redux/gsy_state.dart'; 9 | 10 | class DemoUseStorePage extends StatelessWidget { 11 | const DemoUseStorePage({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | ///通过 StoreConnector 关联 GSYState 中的 User 16 | return StoreConnector( 17 | ///通过 converter 将 GSYState 中的 userInfo返回 18 | converter: (store) => store.state.userInfo, 19 | ///在 userInfo 中返回实际渲染的控件 20 | builder: (context, userInfo) { 21 | return Text( 22 | userInfo!.name!, 23 | style: Theme.of(context).textTheme.headlineMedium, 24 | ); 25 | }, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/test/demo_widget.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: no_logic_in_create_state 2 | 3 | import 'dart:async'; 4 | 5 | import 'package:flutter/material.dart'; 6 | 7 | class DEMOWidget extends StatelessWidget { 8 | final String? text; 9 | 10 | const DEMOWidget(this.text, {super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | //这里返回你需要的控件 15 | //这里末尾有没有的逗号,对于格式化代码而已是不一样的。 16 | return Container( 17 | color: Colors.white, 18 | child: Text(text ?? "这就是无状态DMEO"), 19 | ); 20 | } 21 | } 22 | 23 | class DemoStateWidget extends StatefulWidget { 24 | 25 | final String text; 26 | 27 | const DemoStateWidget(this.text, {super.key}); 28 | 29 | @override 30 | _DemoStateWidgetState createState() => _DemoStateWidgetState(text); 31 | } 32 | 33 | class _DemoStateWidgetState extends State with AutomaticKeepAliveClientMixin { 34 | 35 | String? text; 36 | 37 | _DemoStateWidgetState(this.text); 38 | 39 | @override 40 | void initState() { 41 | ///初始化,这个函数在生命周期中只调用一次 42 | super.initState(); 43 | } 44 | 45 | @override 46 | void dispose() { 47 | ///销毁 48 | super.dispose(); 49 | } 50 | 51 | @override 52 | void didChangeDependencies() { 53 | ///在initState之后调 Called when a dependency of this [State] object changes. 54 | super.didChangeDependencies(); 55 | Future.delayed(const Duration(seconds: 1), () { 56 | setState(() { 57 | text = "这就变了数值"; 58 | }); 59 | return true; 60 | }); 61 | } 62 | 63 | 64 | @override 65 | bool get wantKeepAlive => true; 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | super.build(context); 70 | return Text(text ?? "这就是有状态DMEO"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/widget/anima/curves_bezier.dart: -------------------------------------------------------------------------------- 1 | //import 'package:flutter/material.dart'; 2 | //import 'package:vector_math/vector_math.dart'; 3 | 4 | /*class CurveBezier extends Curve { 5 | final quadraticCurve = new QuadraticBezier( 6 | [new Vector2(-4.0, 1.0), new Vector2(-2.0, -1.0), new Vector2(1.0, 1.0)]); 7 | 8 | @override 9 | double transformInternal(double t) { 10 | return quadraticCurve.pointAt(t).s; 11 | } 12 | }*/ 13 | -------------------------------------------------------------------------------- /lib/widget/animated_background.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:simple_animations/simple_animations.dart'; 3 | import 'package:supercharged/supercharged.dart'; 4 | 5 | enum _ColorTween { color1, color2 } 6 | 7 | class AnimatedBackground extends StatelessWidget { 8 | const AnimatedBackground({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final tween = MovieTween() 13 | ..tween(_ColorTween.color1, 14 | ColorTween(begin: const Color(0xffD38312), end: Colors.lightBlue.shade900), 15 | duration: 3.seconds, curve: Curves.easeIn) 16 | ..tween( 17 | _ColorTween.color2, 18 | ColorTween(begin: const Color(0xffA83279), end: Colors.blue.shade600), 19 | duration: 3.seconds, 20 | ); 21 | 22 | return MirrorAnimationBuilder( 23 | tween: tween, 24 | duration: tween.duration, 25 | builder: (context, value, child) { 26 | return Container( 27 | decoration: BoxDecoration( 28 | gradient: LinearGradient( 29 | begin: Alignment.topCenter, 30 | end: Alignment.bottomCenter, 31 | colors: [ 32 | value.get(_ColorTween.color1), 33 | value.get(_ColorTween.color2) 34 | ])), 35 | ); 36 | }, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/widget/gsy_bottom_action_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class GSYBottomAppBar extends StatelessWidget { 4 | const GSYBottomAppBar({super.key, 5 | this.color, 6 | this.fabLocation, 7 | this.shape, 8 | this.rowContents, 9 | }); 10 | 11 | final Color? color; 12 | final FloatingActionButtonLocation? fabLocation; 13 | final NotchedShape? shape; 14 | final List? rowContents; 15 | 16 | static final List kCenterLocations = 17 | [ 18 | FloatingActionButtonLocation.centerDocked, 19 | FloatingActionButtonLocation.centerFloat, 20 | ]; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return BottomAppBar( 25 | color: color, 26 | shape: shape, 27 | child: Row(children: rowContents!), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/widget/gsy_card_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 3 | 4 | /// Card Widget 5 | /// Created by guoshuyu 6 | /// Date: 2018-07-16 7 | class GSYCardItem extends StatelessWidget { 8 | final Widget child; 9 | final EdgeInsets? margin; 10 | final Color? color; 11 | final RoundedRectangleBorder? shape; 12 | final double elevation; 13 | 14 | const GSYCardItem( 15 | {super.key, required this.child, 16 | this.margin, 17 | this.color, 18 | this.shape, 19 | this.elevation = 5.0}); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | EdgeInsets? margin = this.margin; 24 | RoundedRectangleBorder? shape = this.shape; 25 | Color? color = this.color; 26 | margin ??= 27 | const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0, bottom: 10.0); 28 | shape ??= const RoundedRectangleBorder( 29 | borderRadius: BorderRadius.all(Radius.circular(4.0))); 30 | color ??= GSYColors.cardWhite; 31 | return Card( 32 | elevation: elevation, 33 | shape: shape, 34 | color: color, 35 | margin: margin, 36 | child: child); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/widget/gsy_common_option_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/common/localization/extension.dart'; 3 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 4 | import 'package:gsy_github_app_flutter/common/utils/common_utils.dart'; 5 | import 'package:share_plus/share_plus.dart'; 6 | 7 | /// Created by guoshuyu 8 | /// Date: 2018-07-26 9 | class GSYCommonOptionWidget extends StatelessWidget { 10 | final List? otherList; 11 | 12 | final String? url; 13 | 14 | const GSYCommonOptionWidget({super.key, this.otherList, String? url}) 15 | : url = (url == null) ? GSYConstant.app_default_share_url : url; 16 | 17 | _renderHeaderPopItem(List list) { 18 | return PopupMenuButton( 19 | child: const Icon(GSYICons.MORE), 20 | onSelected: (model) { 21 | model.selected(model); 22 | }, 23 | itemBuilder: (BuildContext context) { 24 | return _renderHeaderPopItemChild(list); 25 | }, 26 | ); 27 | } 28 | 29 | _renderHeaderPopItemChild(List data) { 30 | List> list = []; 31 | for (GSYOptionModel item in data) { 32 | list.add(PopupMenuItem( 33 | value: item, 34 | child: Text(item.name), 35 | )); 36 | } 37 | return list; 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | List constList = [ 43 | GSYOptionModel(context.l10n.option_web, context.l10n.option_web, (model) { 44 | CommonUtils.launchOutURL(url, context); 45 | }), 46 | GSYOptionModel(context.l10n.option_copy, context.l10n.option_copy, 47 | (model) { 48 | CommonUtils.copy(url ?? "", context); 49 | }), 50 | GSYOptionModel(context.l10n.option_share, context.l10n.option_share, 51 | (model) { 52 | Share.share(context.l10n.option_share_title + (url ?? "")); 53 | }), 54 | ]; 55 | var list = [...constList, ...?otherList]; 56 | return _renderHeaderPopItem(list); 57 | } 58 | } 59 | 60 | class GSYOptionModel { 61 | final String name; 62 | final String value; 63 | final PopupMenuItemSelected selected; 64 | 65 | GSYOptionModel(this.name, this.value, this.selected); 66 | } 67 | -------------------------------------------------------------------------------- /lib/widget/gsy_flex_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 充满的button 4 | /// Created by guoshuyu 5 | /// Date: 2018-07-16 6 | class GSYFlexButton extends StatelessWidget { 7 | final String? text; 8 | 9 | final Color? color; 10 | 11 | final Color textColor; 12 | 13 | final VoidCallback? onPress; 14 | 15 | final double fontSize; 16 | final int maxLines; 17 | 18 | final MainAxisAlignment mainAxisAlignment; 19 | 20 | const GSYFlexButton( 21 | {super.key, 22 | this.text, 23 | this.color, 24 | this.textColor = Colors.black, 25 | this.onPress, 26 | this.fontSize = 20.0, 27 | this.mainAxisAlignment = MainAxisAlignment.center, 28 | this.maxLines = 1}); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return ElevatedButton( 33 | style: TextButton.styleFrom( 34 | backgroundColor: color, 35 | padding: const EdgeInsets.only( 36 | left: 20.0, top: 10.0, right: 20.0, bottom: 10.0)), 37 | child: Flex( 38 | mainAxisAlignment: mainAxisAlignment, 39 | direction: Axis.horizontal, 40 | children: [ 41 | Expanded( 42 | child: Text(text!, 43 | style: TextStyle( 44 | color: textColor, fontSize: fontSize, height: 1), 45 | textAlign: TextAlign.center, 46 | maxLines: maxLines, 47 | overflow: TextOverflow.ellipsis), 48 | ) 49 | ], 50 | ), 51 | onPressed: () { 52 | onPress?.call(); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/widget/gsy_icon_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 带图标Icon的文本,可调节 4 | /// Created by guoshuyu 5 | /// Date: 2018-07-16 6 | class GSYIConText extends StatelessWidget { 7 | final String? iconText; 8 | 9 | final IconData iconData; 10 | 11 | final TextStyle textStyle; 12 | 13 | final Color iconColor; 14 | 15 | final double padding; 16 | 17 | final double iconSize; 18 | 19 | final VoidCallback? onPressed; 20 | 21 | final MainAxisAlignment mainAxisAlignment; 22 | 23 | final MainAxisSize mainAxisSize; 24 | 25 | final double textWidth; 26 | 27 | const GSYIConText( 28 | this.iconData, 29 | this.iconText, 30 | this.textStyle, 31 | this.iconColor, 32 | this.iconSize, {super.key, 33 | this.padding = 0.0, 34 | this.onPressed, 35 | this.mainAxisAlignment = MainAxisAlignment.start, 36 | this.mainAxisSize = MainAxisSize.max, 37 | this.textWidth = -1, 38 | }); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | Widget showText = (textWidth == -1) 43 | ? Text( 44 | iconText ?? "", 45 | style: textStyle 46 | .merge(const TextStyle(textBaseline: TextBaseline.alphabetic)), 47 | overflow: TextOverflow.ellipsis, 48 | maxLines: 1, 49 | ) 50 | : SizedBox( 51 | width: textWidth, 52 | child: 53 | 54 | ///显示数量文本 55 | Text( 56 | iconText!, 57 | style: textStyle 58 | .merge(const TextStyle(textBaseline: TextBaseline.alphabetic)), 59 | overflow: TextOverflow.ellipsis, 60 | maxLines: 1, 61 | )); 62 | 63 | return Row( 64 | textBaseline: TextBaseline.alphabetic, 65 | mainAxisAlignment: mainAxisAlignment, 66 | mainAxisSize: mainAxisSize, 67 | crossAxisAlignment: CrossAxisAlignment.baseline, 68 | children: [ 69 | Icon( 70 | iconData, 71 | size: iconSize, 72 | color: iconColor, 73 | ), 74 | Padding(padding: EdgeInsets.all(padding)), 75 | showText 76 | ], 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/widget/gsy_input_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 带图标的输入框 4 | class GSYInputWidget extends StatefulWidget { 5 | final bool obscureText; 6 | 7 | final String? hintText; 8 | 9 | final IconData? iconData; 10 | 11 | final ValueChanged? onChanged; 12 | 13 | final TextStyle? textStyle; 14 | 15 | final TextEditingController? controller; 16 | 17 | const GSYInputWidget( 18 | {super.key, 19 | this.hintText, 20 | this.iconData, 21 | this.onChanged, 22 | this.textStyle, 23 | this.controller, 24 | this.obscureText = false}); 25 | 26 | @override 27 | _GSYInputWidgetState createState() => _GSYInputWidgetState(); 28 | } 29 | 30 | /// State for [GSYInputWidget] widgets. 31 | class _GSYInputWidgetState extends State { 32 | @override 33 | Widget build(BuildContext context) { 34 | return TextField( 35 | controller: widget.controller, 36 | onChanged: widget.onChanged, 37 | obscureText: widget.obscureText, 38 | decoration: InputDecoration( 39 | hintText: widget.hintText, 40 | icon: widget.iconData == null ? null : Icon(widget.iconData), 41 | ), 42 | magnifierConfiguration: TextMagnifierConfiguration(magnifierBuilder: ( 43 | BuildContext context, 44 | MagnifierController controller, 45 | ValueNotifier magnifierInfo, 46 | ) { 47 | return null; 48 | })); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/widget/gsy_title_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// title 控件 4 | /// Created by guoshuyu 5 | /// on 2018/7/24. 6 | class GSYTitleBar extends StatelessWidget { 7 | final String? title; 8 | 9 | final IconData? iconData; 10 | 11 | final ValueChanged? onRightIconPressed; 12 | 13 | final bool needRightLocalIcon; 14 | 15 | final Widget? rightWidget; 16 | 17 | final GlobalKey rightKey = GlobalKey(); 18 | 19 | GSYTitleBar(this.title, 20 | {super.key, this.iconData, 21 | this.onRightIconPressed, 22 | this.needRightLocalIcon = false, 23 | this.rightWidget}); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | Widget? widget = rightWidget; 28 | if (rightWidget == null) { 29 | widget = (needRightLocalIcon) 30 | ? IconButton( 31 | icon: Icon( 32 | iconData, 33 | key: rightKey, 34 | size: 19.0, 35 | ), 36 | onPressed: () { 37 | RenderBox renderBox2 = 38 | rightKey.currentContext?.findRenderObject() as RenderBox; 39 | var position = renderBox2.localToGlobal(Offset.zero); 40 | var size = renderBox2.size; 41 | var centerPosition = Offset( 42 | position.dx + size.width / 2, 43 | position.dy + size.height / 2, 44 | ); 45 | onRightIconPressed?.call(centerPosition); 46 | }) 47 | : Container(); 48 | } 49 | return Row( 50 | children: [ 51 | Expanded( 52 | child: Text( 53 | title!, 54 | maxLines: 1, 55 | overflow: TextOverflow.ellipsis, 56 | ), 57 | ), 58 | widget! 59 | ], 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/widget/gsy_user_icon_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/common/style/gsy_style.dart'; 3 | 4 | /// 头像Icon 5 | /// Created by guoshuyu 6 | /// Date: 2018-07-30 7 | 8 | class GSYUserIconWidget extends StatelessWidget { 9 | final String? image; 10 | final VoidCallback? onPressed; 11 | final double width; 12 | final double height; 13 | final EdgeInsetsGeometry? padding; 14 | 15 | const GSYUserIconWidget( 16 | {super.key, this.image, 17 | this.onPressed, 18 | this.width = 30.0, 19 | this.height = 30.0, 20 | this.padding}); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return RawMaterialButton( 25 | materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 26 | padding: 27 | padding ?? const EdgeInsets.only(top: 4.0, right: 5.0, left: 5.0), 28 | constraints: const BoxConstraints(minWidth: 0.0, minHeight: 0.0), 29 | onPressed: onPressed, 30 | child: ClipOval( 31 | child: FadeInImage( 32 | placeholder: const AssetImage( 33 | GSYICons.DEFAULT_USER_ICON, 34 | ), 35 | image: NetworkImage(image ?? GSYICons.DEFAULT_REMOTE_PIC), 36 | //预览图 37 | fit: BoxFit.fitWidth, 38 | width: width, 39 | height: height, 40 | ), 41 | )); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/widget/menu/flutter_radial_menu.dart: -------------------------------------------------------------------------------- 1 | library flutter_radial_menu; 2 | 3 | export 'src/radial_menu.dart'; 4 | export 'src/radial_menu_item.dart'; 5 | -------------------------------------------------------------------------------- /lib/widget/menu/src/radial_menu_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RadialMenuButton extends StatelessWidget { 4 | const RadialMenuButton({super.key, 5 | required this.child, 6 | this.backgroundColor, 7 | this.onPressed, 8 | }); 9 | 10 | final Widget child; 11 | final Color? backgroundColor; 12 | final VoidCallback? onPressed; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final Color color = backgroundColor ?? Theme.of(context).primaryColor; 17 | 18 | return Semantics( 19 | button: true, 20 | enabled: true, 21 | child: Material( 22 | type: MaterialType.circle, 23 | color: color, 24 | child: InkWell( 25 | onTap: onPressed, 26 | child: child, 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/widget/menu/src/radial_menu_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const double _defaultButtonSize = 48.0; 4 | 5 | /// An item in a [RadialMenu]. 6 | /// 7 | /// The type `T` is the type of the value the entry represents. All the entries 8 | /// in a given menu must represent values with consistent types. 9 | class RadialMenuItem extends StatelessWidget { 10 | /// Creates a circular action button for an item in a [RadialMenu]. 11 | /// 12 | /// The [child] argument is required. 13 | const RadialMenuItem({ 14 | super.key, 15 | required this.child, 16 | this.value, 17 | this.tooltip, 18 | this.size = _defaultButtonSize, 19 | this.backgroundColor, 20 | this.iconColor, 21 | // this.iconSize: 24.0, 22 | }) : assert(child != null); 23 | 24 | /// The widget below this widget in the tree. 25 | /// 26 | /// Typically an [Icon] widget. 27 | final Widget? child; 28 | 29 | /// The value to return if the user selects this menu item. 30 | /// 31 | /// Eventually returned in a call to [RadialMenu.onSelected]. 32 | final T? value; 33 | 34 | /// Text that describes the action that will occur when the button is pressed. 35 | /// 36 | /// This text is displayed when the user long-presses on the button and is 37 | /// used for accessibility. 38 | final String? tooltip; 39 | 40 | /// The color to use when filling the button. 41 | /// 42 | /// Defaults to the primary color of the current theme. 43 | final Color? backgroundColor; 44 | 45 | /// The size of the button. 46 | /// 47 | /// Defaults to 48.0. 48 | final double size; 49 | 50 | /// The color to use when painting the child icon. 51 | /// 52 | /// Defaults to the primary icon theme color. 53 | final Color? iconColor; 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | final Color? iconColor = 58 | this.iconColor ?? Theme.of(context).primaryIconTheme.color; 59 | 60 | Widget? result; 61 | 62 | if (child != null) { 63 | result = Center( 64 | child: IconTheme.merge( 65 | data: IconThemeData( 66 | color: iconColor, 67 | ), 68 | child: child ?? Container(), 69 | ), 70 | ); 71 | } 72 | 73 | if (tooltip != null) { 74 | result = Tooltip( 75 | message: tooltip!, 76 | child: result, 77 | ); 78 | } 79 | 80 | result = SizedBox( 81 | width: size, 82 | height: size, 83 | child: result, 84 | ); 85 | 86 | return result; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/widget/never_overscroll_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | ///去除ScrollView的Material效果 4 | class NeverOverScrollIndicator extends StatelessWidget { 5 | final bool needOverload; 6 | 7 | final Widget? child; 8 | 9 | const NeverOverScrollIndicator({super.key, this.child, this.needOverload = true}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return ScrollConfiguration( 14 | behavior: NeverOverScrollBehavior(needOverload: needOverload), 15 | child: child!, 16 | ); 17 | } 18 | } 19 | 20 | class NeverOverScrollBehavior extends ScrollBehavior { 21 | final bool needOverload; 22 | 23 | const NeverOverScrollBehavior({this.needOverload = true}); 24 | 25 | @override 26 | Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) { 27 | return child; 28 | } 29 | 30 | @override 31 | ScrollPhysics getScrollPhysics(BuildContext context) { 32 | switch (getPlatform(context)) { 33 | case TargetPlatform.iOS: 34 | if (needOverload) { 35 | return const BouncingScrollPhysics(); 36 | } 37 | return const ClampingScrollPhysics(); 38 | case TargetPlatform.android: 39 | case TargetPlatform.fuchsia: 40 | default: 41 | return const ClampingScrollPhysics(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/widget/only_share_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | ///往下共享环境配置 4 | class OnlyShareInstanceWidget extends StatelessWidget { 5 | const OnlyShareInstanceWidget({super.key, this.value, this.child}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return _OnlyShareInstanceModel(value: value, child: child!); 10 | } 11 | 12 | static V? of(BuildContext context) { 13 | final _OnlyShareInstanceModel? inheritedConfig = 14 | context.dependOnInheritedWidgetOfExactType<_OnlyShareInstanceModel>(); 15 | return inheritedConfig?.value; 16 | } 17 | 18 | final T? value; 19 | 20 | final Widget? child; 21 | } 22 | 23 | class _OnlyShareInstanceModel extends InheritedWidget { 24 | const _OnlyShareInstanceModel({required this.value, required super.child}); 25 | 26 | final T? value; 27 | 28 | ///不需要刷新数据,直接返回false 29 | @override 30 | bool updateShouldNotify(_OnlyShareInstanceModel oldWidget) => false; 31 | } -------------------------------------------------------------------------------- /lib/widget/particle/particle_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:simple_animations/simple_animations.dart'; 5 | import 'package:supercharged/supercharged.dart'; 6 | 7 | enum ParticleOffsetProps { x, y } 8 | 9 | class ParticleModel { 10 | late MovieTween tween; 11 | late double size; 12 | late Duration duration; 13 | late Duration startTime; 14 | Random random; 15 | 16 | ParticleModel(this.random) { 17 | restart(); 18 | shuffle(); 19 | } 20 | 21 | restart() { 22 | final startPosition = Offset(-0.2 + 1.4 * random.nextDouble(), 1.2); 23 | final endPosition = Offset(-0.2 + 1.4 * random.nextDouble(), -0.2); 24 | 25 | tween = MovieTween() 26 | ..tween(ParticleOffsetProps.x, 27 | Tween(begin: startPosition.dx, end: endPosition.dx), 28 | duration: 2.seconds) 29 | ..tween(ParticleOffsetProps.y, 30 | Tween(begin: startPosition.dy, end: endPosition.dy), 31 | duration: 2.seconds); 32 | 33 | duration = 3000.milliseconds + random.nextInt(6000).milliseconds; 34 | startTime = DateTime.now().duration(); 35 | size = 0.2 + random.nextDouble() * 0.4; 36 | } 37 | 38 | void shuffle() { 39 | startTime -= (random.nextDouble() * duration.inMilliseconds) 40 | .round() 41 | .milliseconds; 42 | } 43 | 44 | checkIfParticleNeedsToBeRestarted() { 45 | if (progress() == 1.0) { 46 | restart(); 47 | } 48 | } 49 | 50 | double progress() { 51 | return ((DateTime.now().duration() - startTime) / duration) 52 | .clamp(0.0, 1.0) 53 | .toDouble(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/widget/particle/particle_painter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:gsy_github_app_flutter/widget/particle/particle_model.dart'; 3 | import 'package:simple_animations/simple_animations.dart'; 4 | 5 | class ParticlePainter extends CustomPainter { 6 | List particles; 7 | 8 | ParticlePainter(this.particles); 9 | 10 | @override 11 | void paint(Canvas canvas, Size size) { 12 | final paint = Paint()..color = Colors.white.withAlpha(50); 13 | 14 | for (var particle in particles) { 15 | final progress = particle.progress(); 16 | final Movie animation = 17 | particle.tween.transform(progress); 18 | final position = Offset( 19 | animation.get(ParticleOffsetProps.x) * size.width, 20 | animation.get(ParticleOffsetProps.y) * size.height, 21 | ); 22 | canvas.drawCircle(position, size.width * 0.2 * particle.size, paint); 23 | } 24 | } 25 | 26 | @override 27 | bool shouldRepaint(CustomPainter oldDelegate) => true; 28 | } -------------------------------------------------------------------------------- /lib/widget/particle/particle_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:gsy_github_app_flutter/widget/particle/particle_model.dart'; 5 | import 'package:gsy_github_app_flutter/widget/particle/particle_painter.dart'; 6 | import 'package:simple_animations/simple_animations.dart'; 7 | import 'package:supercharged/supercharged.dart'; 8 | 9 | class ParticlesWidget extends StatefulWidget { 10 | final int numberOfParticles; 11 | 12 | const ParticlesWidget(this.numberOfParticles, {super.key}); 13 | 14 | @override 15 | _ParticlesWidgetState createState() => _ParticlesWidgetState(); 16 | } 17 | 18 | class _ParticlesWidgetState extends State 19 | with WidgetsBindingObserver { 20 | final Random random = Random(); 21 | 22 | final List particles = []; 23 | 24 | @override 25 | void initState() { 26 | widget.numberOfParticles.times(() => particles.add(ParticleModel(random))); 27 | WidgetsBinding.instance.addObserver(this); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | void didChangeAppLifecycleState(AppLifecycleState state) { 33 | // 从后台切回来时重置粒子 34 | if (state == AppLifecycleState.resumed) { 35 | for (var particle in particles) { 36 | particle.restart(); 37 | particle.shuffle(); 38 | } 39 | } 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return LoopAnimationBuilder( 45 | tween: ConstantTween(1), 46 | duration: const Duration(seconds: 1), 47 | builder: (context, child, dynamic _) { 48 | _simulateParticles(); 49 | return CustomPaint( 50 | painter: ParticlePainter(particles), 51 | ); 52 | }, 53 | ); 54 | } 55 | 56 | _simulateParticles() { 57 | for (var particle in particles) { 58 | particle.checkIfParticleNeedsToBeRestarted(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/widget/pull/gsy_flare_mutli_pull_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flare_flutter/flare.dart'; 2 | import 'package:flare_flutter/flare_controller.dart'; 3 | 4 | mixin GSYFlarePullMutliController implements FlareController { 5 | late ActorAnimation? _loadingAnimation; 6 | late ActorAnimation? _successAnimation; 7 | late ActorAnimation? _pullAnimation; 8 | late ActorAnimation? _cometAnimation; 9 | 10 | double pulledExtentFlare = 0; 11 | bool _isSurround = false; 12 | 13 | final double _refreshTriggerPullDistance = 140; 14 | 15 | double _successTime = 0.0; 16 | double _loadingTime = 0.0; 17 | double _cometTime = 0.0; 18 | 19 | @override 20 | void initialize(FlutterActorArtboard artboard) { 21 | _pullAnimation = artboard.getAnimation("pull"); 22 | _successAnimation = artboard.getAnimation("success"); 23 | _loadingAnimation = artboard.getAnimation("loading"); 24 | _cometAnimation = artboard.getAnimation("idle comet"); 25 | } 26 | 27 | @override 28 | void setViewTransform(Mat2D viewTransform) {} 29 | 30 | @override 31 | bool advance(FlutterActorArtboard artboard, double elapsed) { 32 | double animationPosition = pulledExtentFlare / _refreshTriggerPullDistance; 33 | animationPosition *= animationPosition; 34 | _cometTime += elapsed; 35 | _cometAnimation?.apply(_cometTime % _cometAnimation!.duration, artboard, 1.0); 36 | _pullAnimation?.apply( 37 | _pullAnimation!.duration * animationPosition, artboard, 1.0); 38 | if (_isSurround) { 39 | _successTime += elapsed; 40 | if (_successTime >= _successAnimation!.duration) { 41 | _loadingTime += elapsed; 42 | } 43 | } else { 44 | _successTime = _loadingTime = 0.0; 45 | } 46 | if (_successTime >= _successAnimation!.duration) { 47 | _loadingAnimation?.apply( 48 | _loadingTime % _loadingAnimation!.duration, artboard, 1.0); 49 | } else if (_successTime > 0.0) { 50 | _successAnimation?.apply(_successTime, artboard, 1.0); 51 | } 52 | return true; 53 | } 54 | 55 | void onRefreshing() { 56 | _isSurround = true; 57 | } 58 | 59 | void onRefreshEnd() { 60 | _isSurround = false; 61 | } 62 | 63 | bool get getPlayAuto; 64 | } 65 | -------------------------------------------------------------------------------- /lib/widget/pull/gsy_flare_pull_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flare_flutter/flare.dart'; 2 | import 'package:flare_flutter/flare_controller.dart'; 3 | 4 | mixin GSYFlarePullController implements FlareController { 5 | late ActorAnimation? _pullAnimation; 6 | 7 | double pulledExtentFlare = 0; 8 | final double _speed = 2.0; 9 | double _rockTime = 0.0; 10 | 11 | @override 12 | void initialize(FlutterActorArtboard artboard) { 13 | _pullAnimation = artboard.getAnimation("Earth Moving"); 14 | } 15 | 16 | @override 17 | void setViewTransform(Mat2D viewTransform) {} 18 | 19 | @override 20 | bool advance(FlutterActorArtboard artboard, double elapsed) { 21 | if (getPlayAuto) { 22 | _rockTime += elapsed * _speed; 23 | _pullAnimation?.apply(_rockTime % _pullAnimation!.duration, artboard, 1.0); 24 | return true; 25 | } 26 | var pullExtent = (pulledExtentFlare > refreshTriggerPullDistance) 27 | ? pulledExtentFlare - refreshTriggerPullDistance 28 | : pulledExtentFlare; 29 | double animationPosition = pullExtent / refreshTriggerPullDistance; 30 | animationPosition *= animationPosition; 31 | _rockTime = _pullAnimation!.duration * animationPosition; 32 | _pullAnimation?.apply(_rockTime, artboard, 1.0); 33 | return true; 34 | } 35 | 36 | bool get getPlayAuto; 37 | 38 | double get refreshTriggerPullDistance => 140; 39 | } 40 | -------------------------------------------------------------------------------- /lib/widget/pull/nested/gsy_sliver_header_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/rendering.dart'; 5 | 6 | ///动态头部处理 7 | class GSYSliverHeaderDelegate extends SliverPersistentHeaderDelegate { 8 | GSYSliverHeaderDelegate( 9 | {required this.minHeight, 10 | required this.maxHeight, 11 | required this.snapConfig, 12 | required this.vSyncs, 13 | this.child, 14 | this.builder, 15 | this.changeSize = false}); 16 | 17 | final double minHeight; 18 | final double maxHeight; 19 | final Widget? child; 20 | final Builder? builder; 21 | final TickerProvider vSyncs; 22 | final bool changeSize; 23 | final FloatingHeaderSnapConfiguration snapConfig; 24 | AnimationController? animationController; 25 | 26 | @override 27 | double get minExtent => minHeight; 28 | 29 | @override 30 | double get maxExtent => math.max(maxHeight, minHeight); 31 | 32 | @override 33 | Widget build( 34 | BuildContext context, double shrinkOffset, bool overlapsContent) { 35 | if (builder != null) { 36 | return builder!(context, shrinkOffset, overlapsContent); 37 | } 38 | return child!; 39 | } 40 | 41 | @override 42 | TickerProvider get vsync => vSyncs; 43 | 44 | @override 45 | bool shouldRebuild(GSYSliverHeaderDelegate oldDelegate) { 46 | return true; 47 | } 48 | 49 | @override 50 | FloatingHeaderSnapConfiguration get snapConfiguration => snapConfig; 51 | } 52 | 53 | typedef Builder = Widget Function( 54 | BuildContext context, double shrinkOffset, bool overlapsContent); 55 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/logo.png -------------------------------------------------------------------------------- /p1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/p1.png -------------------------------------------------------------------------------- /p2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/p2.png -------------------------------------------------------------------------------- /p3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/p3.png -------------------------------------------------------------------------------- /p4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/p4.png -------------------------------------------------------------------------------- /register0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/register0.png -------------------------------------------------------------------------------- /register1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/register1.jpg -------------------------------------------------------------------------------- /static/file/Space-Demo.flr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/file/Space-Demo.flr -------------------------------------------------------------------------------- /static/file/flare_flutter_logo_.flr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/file/flare_flutter_logo_.flr -------------------------------------------------------------------------------- /static/file/launch.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/file/launch.riv -------------------------------------------------------------------------------- /static/file/loading_world_now.flr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/file/loading_world_now.flr -------------------------------------------------------------------------------- /static/font/akronim.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/font/akronim.ttf -------------------------------------------------------------------------------- /static/font/google_kavivanar.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/font/google_kavivanar.ttf -------------------------------------------------------------------------------- /static/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/font/iconfont.eot -------------------------------------------------------------------------------- /static/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/font/iconfont.ttf -------------------------------------------------------------------------------- /static/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/font/iconfont.woff -------------------------------------------------------------------------------- /static/images/default_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/images/default_img.png -------------------------------------------------------------------------------- /static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/images/logo.png -------------------------------------------------------------------------------- /static/images/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/static/images/welcome.png -------------------------------------------------------------------------------- /thanks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/thanks.jpg -------------------------------------------------------------------------------- /theme.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CarGuo/gsy_github_app_flutter/263497b5f78a63d8e775b229528b35ced177e1b8/theme.gif --------------------------------------------------------------------------------