├── .gitattributes ├── .gitignore ├── .idea ├── FlutterExampleApp_ExtensionRead.iml ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── libraries │ ├── Dart_Packages.xml │ ├── Dart_SDK.xml │ └── Flutter_Plugins.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── .packages ├── README.md ├── README_CN.md ├── android ├── .gradle │ ├── 4.10.2 │ │ ├── fileChanges │ │ │ └── last-build.bin │ │ ├── fileContent │ │ │ └── fileContent.lock │ │ ├── fileHashes │ │ │ ├── fileHashes.bin │ │ │ ├── fileHashes.lock │ │ │ └── resourceHashesCache.bin │ │ ├── gc.properties │ │ ├── javaCompile │ │ │ ├── classAnalysis.bin │ │ │ ├── jarAnalysis.bin │ │ │ ├── javaCompile.lock │ │ │ └── taskHistory.bin │ │ └── taskHistory │ │ │ ├── taskHistory.bin │ │ │ └── taskHistory.lock │ ├── buildOutputCleanup │ │ ├── buildOutputCleanup.lock │ │ ├── cache.properties │ │ └── outputFiles.bin │ └── vcs-1 │ │ └── gc.properties ├── ERKeyStore.jks ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ ├── com │ │ │ └── toeii │ │ │ │ └── read │ │ │ │ └── MainActivity.java │ │ └── io │ │ │ └── flutter │ │ │ └── plugins │ │ │ └── GeneratedPluginRegistrant.java │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── logo_extension_read.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── logo_extension_read.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── logo_extension_read.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── logo_extension_read.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── logo_extension_read.png │ │ └── splash_extension_read.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── flutter_extension_read_android.iml ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── key.properties ├── local.properties └── settings.gradle ├── flutter_extension_read.iml ├── images ├── apk_download_code.png ├── app_demo.gif ├── app_fotojet_01.jpg ├── app_fotojet_02.jpg ├── flutter_extension_read_egg.jpg ├── logo_extension_read.png └── splash_extension_read.png ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Generated.xcconfig │ └── Release.xcconfig ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── GeneratedPluginRegistrant.h │ ├── GeneratedPluginRegistrant.m │ ├── Info.plist │ └── main.m ├── lib ├── Main.dart ├── model │ ├── BrowseRecordBean.dart │ ├── CommunityBean.dart │ ├── CommunityBean.g.dart │ ├── CommunityItemBean.dart │ ├── CommunityItemBean.g.dart │ ├── HomeDailyBean.dart │ ├── HomeDailyBean.g.dart │ ├── HomeRecommendBean.dart │ ├── HomeRecommendBean.g.dart │ ├── PersonalBean.dart │ ├── PersonalBean.g.dart │ ├── PersonalInfoBean.dart │ └── PersonalInfoBean.g.dart ├── service │ ├── ERAppConfig.dart │ ├── database │ │ └── DatabaseHelper.dart │ ├── net │ │ └── ERAppHttpClient.dart │ ├── redux │ │ ├── ERAppState.dart │ │ └── ThemeRedux.dart │ └── storage │ │ └── LocalStorage.dart └── view │ ├── page │ ├── BrowseRecordPage.dart │ ├── CommunityPage.dart │ ├── HomeDailyPage.dart │ ├── HomePage.dart │ ├── HomeRecommendPage.dart │ ├── MainPage.dart │ ├── PaperDetailPage.dart │ ├── PersonalPage.dart │ ├── SplashPage.dart │ ├── UserPage.dart │ └── WebLoadPage.dart │ └── widget │ ├── BarBottomDivider.dart │ ├── EasyListView.dart │ ├── ExpandKToolbar.dart │ ├── FullButton.dart │ ├── HomeDrawer.dart │ ├── NotEmptyText.dart │ └── SimpleViewPlayer.dart ├── pubspec.lock ├── pubspec.yaml └── 拓意阅读v1.0.apk /.gitattributes: -------------------------------------------------------------------------------- 1 | *.d linguist-language=Dart 2 | *.java linguist-language=Dart 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | .packages 4 | .pub/ 5 | build/ 6 | build/app/ 7 | .flutter-plugins 8 | *.iml 9 | .idea/ 10 | .gradle/ 11 | ignoreConfig.dart 12 | android/.gradle/ 13 | .idea/ -------------------------------------------------------------------------------- /.idea/FlutterExampleApp_ExtensionRead.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 30 | 252 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.packages: -------------------------------------------------------------------------------- 1 | # Generated by pub on 2019-03-08 20:04:20.962958. 2 | after_layout:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/after_layout-1.0.7/lib/ 3 | analyzer:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/analyzer-0.34.2/lib/ 4 | args:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/args-1.5.1/lib/ 5 | async:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/async-2.0.8/lib/ 6 | boolean_selector:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/boolean_selector-1.0.4/lib/ 7 | build:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/build-1.1.0/lib/ 8 | build_config:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/build_config-0.3.1+4/lib/ 9 | build_resolvers:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/build_resolvers-0.2.3/lib/ 10 | build_runner:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/build_runner-1.1.3/lib/ 11 | build_runner_core:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/build_runner_core-1.1.3/lib/ 12 | built_collection:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/built_collection-4.1.0/lib/ 13 | built_value:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/built_value-6.2.0/lib/ 14 | charcode:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/charcode-1.1.2/lib/ 15 | code_builder:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/code_builder-3.1.3/lib/ 16 | collection:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/collection-1.14.11/lib/ 17 | convert:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/convert-2.1.1/lib/ 18 | cookie_jar:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/cookie_jar-0.0.7/lib/ 19 | crypto:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/crypto-2.0.6/lib/ 20 | csslib:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/csslib-0.14.6/lib/ 21 | cupertino_icons:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/cupertino_icons-0.1.2/lib/ 22 | dart_style:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/dart_style-1.2.2/lib/ 23 | dio:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/dio-1.0.13/lib/ 24 | event_bus:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/event_bus-1.0.1/lib/ 25 | fixnum:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/fixnum-0.10.9/lib/ 26 | flutter:file:///Users/Toeii/Library/Flutter/packages/flutter/lib/ 27 | flutter_page_indicator:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/flutter_page_indicator-0.0.3/lib/ 28 | flutter_redux:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/flutter_redux-0.5.3/lib/ 29 | flutter_swiper:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/flutter_swiper-1.1.4/lib/ 30 | flutter_webview_plugin:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/flutter_webview_plugin-0.3.0+2/lib/ 31 | fluttertoast:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/fluttertoast-3.0.1/lib/ 32 | front_end:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/front_end-0.1.9/lib/ 33 | glob:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/glob-1.1.7/lib/ 34 | graphs:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/graphs-0.2.0/lib/ 35 | html:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/html-0.13.3+3/lib/ 36 | http:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/http-0.12.0+1/lib/ 37 | http_multi_server:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/http_multi_server-2.0.5/lib/ 38 | http_parser:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/http_parser-3.1.3/lib/ 39 | io:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/io-0.3.3/lib/ 40 | js:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/js-0.6.1+1/lib/ 41 | json_annotation:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/json_annotation-2.0.0/lib/ 42 | json_rpc_2:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/json_rpc_2-2.0.9/lib/ 43 | json_serializable:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/json_serializable-2.0.1/lib/ 44 | kernel:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/kernel-0.3.9/lib/ 45 | logging:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/logging-0.11.3+2/lib/ 46 | matcher:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/matcher-0.12.3+1/lib/ 47 | meta:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/meta-1.1.6/lib/ 48 | mime:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/mime-0.9.6+2/lib/ 49 | multi_server_socket:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/multi_server_socket-1.0.2/lib/ 50 | node_preamble:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/node_preamble-1.4.4/lib/ 51 | package_config:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/package_config-1.0.5/lib/ 52 | package_resolver:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/package_resolver-1.0.6/lib/ 53 | path:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/path-1.6.2/lib/ 54 | pedantic:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/pedantic-1.4.0/lib/ 55 | photo_view:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/photo_view-0.0.11/lib/ 56 | plugin:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/plugin-0.2.0+3/lib/ 57 | pool:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/pool-1.4.0/lib/ 58 | pub_semver:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/pub_semver-1.4.2/lib/ 59 | pubspec_parse:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/pubspec_parse-0.1.4/lib/ 60 | quiver:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/quiver-2.0.1/lib/ 61 | redux:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/redux-3.0.0/lib/ 62 | screen:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/screen-0.0.4/lib/ 63 | shared_preferences:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.4.3/lib/ 64 | shelf:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/shelf-0.7.4/lib/ 65 | shelf_packages_handler:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/shelf_packages_handler-1.0.4/lib/ 66 | shelf_static:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/shelf_static-0.2.8/lib/ 67 | shelf_web_socket:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/shelf_web_socket-0.2.2+4/lib/ 68 | sky_engine:file:///Users/Toeii/Library/Flutter/bin/cache/pkg/sky_engine/lib/ 69 | source_gen:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/source_gen-0.9.4/lib/ 70 | source_map_stack_trace:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/source_map_stack_trace-1.1.5/lib/ 71 | source_maps:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/source_maps-0.10.8/lib/ 72 | source_span:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/source_span-1.4.1/lib/ 73 | sqflite:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/sqflite-1.0.0/lib/ 74 | stack_trace:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/stack_trace-1.9.3/lib/ 75 | stream_channel:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/stream_channel-1.6.8/lib/ 76 | stream_transform:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/stream_transform-0.0.14+1/lib/ 77 | string_scanner:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/string_scanner-1.0.4/lib/ 78 | synchronized:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/synchronized-1.5.3+2/lib/ 79 | term_glyph:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/term_glyph-1.0.1/lib/ 80 | test:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/test-1.5.1+1/lib/ 81 | test_api:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/test_api-0.2.1/lib/ 82 | test_core:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/test_core-0.2.0+1/lib/ 83 | timing:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/timing-0.1.1+1/lib/ 84 | transformer_page_view:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/transformer_page_view-0.1.4/lib/ 85 | typed_data:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/typed_data-1.1.6/lib/ 86 | utf:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/utf-0.9.0+5/lib/ 87 | vector_math:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/vector_math-2.0.8/lib/ 88 | video_player:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/video_player-0.10.0+2/lib/ 89 | vm_service_client:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/vm_service_client-0.2.6/lib/ 90 | watcher:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/watcher-0.9.7+10/lib/ 91 | web_socket_channel:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/web_socket_channel-1.0.9/lib/ 92 | yaml:file:///Users/Toeii/.pub-cache/hosted/pub.flutter-io.cn/yaml-2.1.15/lib/ 93 | flutter_extension_read:lib/ 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/toeii/FlutterExampleApp_ExtensionRead/blob/master/images/logo_extension_read.png) 2 | # Flutter Extension Read App 3 | 4 | Build legendary Extension Read App in flutter. 5 | [中文点我](https://github.com/toeii/FlutterExampleApp_ExtensionRead/blob/master/README_CN.md) 6 | 7 | ## Other Versions 8 | 9 | [================ExtensionRead in Android Jetpack================](https://github.com/toeii/JetPackExampleApp_ExtensionRead) 10 | 11 | ### Screenshots 12 | 13 | 14 | 15 | ### Egg 16 | 17 | 18 | ### Download 19 | #### Android apk 20 | 21 | 22 | #### iOS ipa 23 | sorry :cold_sweat: lack of certificate 24 | 25 | ### Framework 26 | 27 | | Lib (SDK v1.2.43) | 28 | | -------------------------- | 29 | | **dio** | 30 | | **shared_preferences** | 31 | | **fluttertoast** | 32 | | **flutter_redux** | 33 | | **cupertino_icons** | 34 | | **json_annotation** | 35 | | **json_serializable** | 36 | | **screen** | 37 | | **event_bus** | 38 | | **flutter_swiper** | 39 | | **get_version** | 40 | | **flutter_webview_plugin** | 41 | | **sqflite** | 42 | | **photo_view** | 43 | 44 | ### Show some :heart: and star the repo to support the project 45 | 46 | ## License 47 | 48 | Copyright 2018 Toeii 49 | 50 | Licensed under the Apache License, Version 2.0 (the "License"); 51 | you may not use this file except in compliance with the License. 52 | You may obtain a copy of the License at 53 | 54 | http://www.apache.org/licenses/LICENSE-2.0 55 | 56 | Unless required by applicable law or agreed to in writing, software 57 | distributed under the License is distributed on an "AS IS" BASIS, 58 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 59 | See the License for the specific language governing permissions and 60 | limitations under the License. 61 | 62 | 63 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | ![](https://github.com/toeii/FlutterExampleApp_ExtensionRead/blob/master/images/logo_extension_read.png) 2 | # 拓意阅读 3 | 4 | 一款清新简单的信息类App。应用数据来源开眼api,项目简单易懂,对想学习Flutter的人非常友好 5 | 6 | ## 其他版本 7 | 8 | [================拓意阅读Android Jetpack版================](https://github.com/toeii/JetPackExampleApp_ExtensionRead) 9 | 10 | ### 截图 11 | 12 | 13 | 14 | ### 脑图 15 | 16 | 17 | ### 下载 18 | #### Android apk 19 | 20 | 21 | #### iOS ipa 22 | 暂无 :cold_sweat: 23 | 24 | ### 第三方框架 25 | 26 | | 库 (SDK v1.2.43) | 27 | | -------------------------- | 28 | | **dio** | 29 | | **shared_preferences** | 30 | | **fluttertoast** | 31 | | **flutter_redux** | 32 | | **cupertino_icons** | 33 | | **json_annotation** | 34 | | **json_serializable** | 35 | | **screen** | 36 | | **event_bus** | 37 | | **flutter_swiper** | 38 | | **get_version** | 39 | | **flutter_webview_plugin** | 40 | | **sqflite** | 41 | | **photo_view** | 42 | 43 | ### 如果对你有所帮助:heart:,欢迎提出issues和start 44 | 45 | ## License 46 | 47 | Copyright 2018 Toeii 48 | 49 | Licensed under the Apache License, Version 2.0 (the "License"); 50 | you may not use this file except in compliance with the License. 51 | You may obtain a copy of the License at 52 | 53 | http://www.apache.org/licenses/LICENSE-2.0 54 | 55 | Unless required by applicable law or agreed to in writing, software 56 | distributed under the License is distributed on an "AS IS" BASIS, 57 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 58 | See the License for the specific language governing permissions and 59 | limitations under the License. 60 | 61 | 62 | -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileContent/fileContent.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/fileContent/fileContent.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/gc.properties -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/classAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/javaCompile/classAnalysis.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/jarAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/javaCompile/jarAnalysis.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/javaCompile.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/javaCompile/javaCompile.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/javaCompile/taskHistory.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/taskHistory/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/taskHistory/taskHistory.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/taskHistory/taskHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/4.10.2/taskHistory/taskHistory.lock -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Thu Dec 27 00:16:06 CST 2018 2 | gradle.version=4.10.2 3 | -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /android/.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /android/ERKeyStore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/ERKeyStore.jks -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | def keystorePropertiesFile = rootProject.file("key.properties") 28 | def keystoreProperties = new Properties() 29 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 30 | 31 | android { 32 | compileSdkVersion 28 33 | 34 | lintOptions { 35 | disable 'InvalidPackage' 36 | } 37 | 38 | defaultConfig { 39 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 40 | applicationId "com.toeii.flutterextensionread" 41 | minSdkVersion 16 42 | targetSdkVersion 28 43 | versionCode flutterVersionCode.toInteger() 44 | versionName flutterVersionName 45 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 46 | } 47 | 48 | signingConfigs { 49 | release { 50 | keyAlias keystoreProperties['keyAlias'] 51 | keyPassword keystoreProperties['keyPassword'] 52 | storeFile file(keystoreProperties['storeFile']) 53 | storePassword keystoreProperties['storePassword'] 54 | } 55 | } 56 | 57 | buildTypes { 58 | release { 59 | signingConfig signingConfigs.release 60 | } 61 | } 62 | 63 | compileOptions { 64 | sourceCompatibility JavaVersion.VERSION_1_8 65 | targetCompatibility JavaVersion.VERSION_1_8 66 | } 67 | 68 | } 69 | 70 | flutter { 71 | source '../..' 72 | } 73 | 74 | dependencies { 75 | testImplementation 'junit:junit:4.12' 76 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 77 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 78 | } 79 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 20 | 27 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/toeii/read/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.toeii.read; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import io.flutter.plugin.common.PluginRegistry; 4 | import com.flutter_webview_plugin.FlutterWebviewPlugin; 5 | import io.github.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin; 6 | import flutter.plugins.screen.screen.ScreenPlugin; 7 | import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin; 8 | import com.tekartik.sqflite.SqflitePlugin; 9 | import io.flutter.plugins.videoplayer.VideoPlayerPlugin; 10 | 11 | /** 12 | * Generated file. Do not edit. 13 | */ 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(PluginRegistry registry) { 16 | if (alreadyRegisteredWith(registry)) { 17 | return; 18 | } 19 | FlutterWebviewPlugin.registerWith(registry.registrarFor("com.flutter_webview_plugin.FlutterWebviewPlugin")); 20 | FluttertoastPlugin.registerWith(registry.registrarFor("io.github.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin")); 21 | ScreenPlugin.registerWith(registry.registrarFor("flutter.plugins.screen.screen.ScreenPlugin")); 22 | SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")); 23 | SqflitePlugin.registerWith(registry.registrarFor("com.tekartik.sqflite.SqflitePlugin")); 24 | VideoPlayerPlugin.registerWith(registry.registrarFor("io.flutter.plugins.videoplayer.VideoPlayerPlugin")); 25 | } 26 | 27 | private static boolean alreadyRegisteredWith(PluginRegistry registry) { 28 | final String key = GeneratedPluginRegistrant.class.getCanonicalName(); 29 | if (registry.hasPlugin(key)) { 30 | return true; 31 | } 32 | registry.registrarFor(key); 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/logo_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-hdpi/logo_extension_read.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/logo_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-mdpi/logo_extension_read.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/logo_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xhdpi/logo_extension_read.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/logo_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xxhdpi/logo_extension_read.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/logo_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xxxhdpi/logo_extension_read.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/splash_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/app/src/main/res/mipmap-xxxhdpi/splash_extension_read.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/flutter_extension_read_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=123456 2 | keyPassword=123456 3 | keyAlias=default 4 | storeFile=/Users/Toeii/FlutterProjects/FlutterExampleApp_ExtensionRead/android/ERKeyStore.jks -------------------------------------------------------------------------------- /android/local.properties: -------------------------------------------------------------------------------- 1 | sdk.dir=/Users/Toeii/Library/Android/sdk 2 | flutter.sdk=/Users/Toeii/Library/Flutter 3 | flutter.versionName=1.0.0 4 | flutter.versionCode=1 5 | flutter.buildMode=release -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /flutter_extension_read.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /images/apk_download_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/apk_download_code.png -------------------------------------------------------------------------------- /images/app_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/app_demo.gif -------------------------------------------------------------------------------- /images/app_fotojet_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/app_fotojet_01.jpg -------------------------------------------------------------------------------- /images/app_fotojet_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/app_fotojet_02.jpg -------------------------------------------------------------------------------- /images/flutter_extension_read_egg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/flutter_extension_read_egg.jpg -------------------------------------------------------------------------------- /images/logo_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/logo_extension_read.png -------------------------------------------------------------------------------- /images/splash_extension_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/images/splash_extension_read.png -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Generated.xcconfig: -------------------------------------------------------------------------------- 1 | // This is a generated file; do not edit or check into version control. 2 | FLUTTER_ROOT=/Users/Toeii/Library/Flutter 3 | FLUTTER_APPLICATION_PATH=/Users/Toeii/FlutterProjects/FlutterExampleApp_ExtensionRead 4 | FLUTTER_TARGET=lib/main.dart 5 | FLUTTER_BUILD_DIR=build 6 | SYMROOT=${SOURCE_ROOT}/../build/ios 7 | FLUTTER_FRAMEWORK_DIR=/Users/Toeii/Library/Flutter/bin/cache/artifacts/engine/ios 8 | FLUTTER_BUILD_NAME=1.0.0 9 | FLUTTER_BUILD_NUMBER=1 10 | -------------------------------------------------------------------------------- /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, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/GeneratedPluginRegistrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #ifndef GeneratedPluginRegistrant_h 6 | #define GeneratedPluginRegistrant_h 7 | 8 | #import 9 | 10 | @interface GeneratedPluginRegistrant : NSObject 11 | + (void)registerWithRegistry:(NSObject*)registry; 12 | @end 13 | 14 | #endif /* GeneratedPluginRegistrant_h */ 15 | -------------------------------------------------------------------------------- /ios/Runner/GeneratedPluginRegistrant.m: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #import "GeneratedPluginRegistrant.h" 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | #import 12 | 13 | @implementation GeneratedPluginRegistrant 14 | 15 | + (void)registerWithRegistry:(NSObject*)registry { 16 | [FlutterWebviewPlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterWebviewPlugin"]]; 17 | [FluttertoastPlugin registerWithRegistrar:[registry registrarForPlugin:@"FluttertoastPlugin"]]; 18 | [ScreenPlugin registerWithRegistrar:[registry registrarForPlugin:@"ScreenPlugin"]]; 19 | [FLTSharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharedPreferencesPlugin"]]; 20 | [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; 21 | [FLTVideoPlayerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTVideoPlayerPlugin"]]; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_extension_read 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/Main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 3 | import 'package:flutter_extension_read/service/redux/ERAppState.dart'; 4 | import 'package:flutter_extension_read/view/page/MainPage.dart'; 5 | import 'package:flutter_redux/flutter_redux.dart'; 6 | import 'package:redux/redux.dart'; 7 | 8 | void main() => runApp(MyApp()); 9 | 10 | class MyApp extends StatelessWidget { 11 | 12 | final store = new Store( 13 | appReducer, 14 | ///init store 15 | initialState: new ERAppState( 16 | themeData: new ThemeData( 17 | primarySwatch: ERAppConfig.primarySwatch, 18 | ), 19 | ), 20 | ); 21 | 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return new StoreProvider( 26 | store: store, 27 | child: new StoreBuilder(builder: (context, store) { 28 | return new MaterialApp( 29 | title: '拓意阅读', 30 | debugShowCheckedModeBanner: true, 31 | home: new MainPage(), 32 | theme: store.state.themeData, 33 | ); 34 | }), 35 | ); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/model/BrowseRecordBean.dart: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by toeii 3 | * Date: 2019-01-16 4 | */ 5 | ///浏览记录 6 | class BrowseRecordBean extends Object { 7 | 8 | int _id; 9 | String _pointId; 10 | String _title; 11 | String _content; 12 | String _url; 13 | String _image; 14 | 15 | BrowseRecordBean(this._id,this._pointId,this._title, this._content,this._url,this._image); 16 | 17 | 18 | BrowseRecordBean.map(dynamic obj) { 19 | this._id = obj['id']; 20 | this._pointId = obj['pointId']; 21 | this._title = obj['title']; 22 | this._content = obj['content']; 23 | this._url = obj['url']; 24 | this._image = obj['image']; 25 | } 26 | 27 | int get id => _id; 28 | String get pointId => _pointId; 29 | String get title => _title; 30 | String get content => _content; 31 | String get url => _url; 32 | String get image => _image; 33 | 34 | Map toMap() { 35 | var map = new Map(); 36 | if (_id != null) { 37 | map['id'] = _id; 38 | } 39 | map['pointId'] = _pointId; 40 | map['title'] = _title; 41 | map['content'] = _content; 42 | map['url'] = _url; 43 | map['image'] = _image; 44 | return map; 45 | } 46 | 47 | BrowseRecordBean.fromMap(Map map) { 48 | this._id = map['id']; 49 | this._pointId = map['pointId']; 50 | this._title = map['title']; 51 | this._content = map['content']; 52 | this._url = map['url']; 53 | this._image = map['image']; 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /lib/model/CommunityBean.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'CommunityBean.g.dart'; 4 | 5 | /** 6 | * Created by toeii 7 | * Date: 2019-01-16 8 | */ 9 | ///社区banner 10 | @JsonSerializable() 11 | class CommunityBean extends Object { 12 | 13 | @JsonKey(name: 'itemList') 14 | List itemList; 15 | 16 | @JsonKey(name: 'count') 17 | int count; 18 | 19 | @JsonKey(name: 'total') 20 | int total; 21 | 22 | @JsonKey(name: 'nextPageUrl') 23 | String nextPageUrl; 24 | 25 | @JsonKey(name: 'adExist') 26 | bool adExist; 27 | 28 | CommunityBean(this.itemList,this.count,this.total,this.nextPageUrl,this.adExist,); 29 | 30 | factory CommunityBean.fromJson(Map srcJson) => _$CommunityBeanFromJson(srcJson); 31 | 32 | Map toJson() => _$CommunityBeanToJson(this); 33 | 34 | } 35 | 36 | 37 | @JsonSerializable() 38 | class TagItemList extends Object { 39 | 40 | @JsonKey(name: 'type') 41 | String type; 42 | 43 | @JsonKey(name: 'data') 44 | Data data; 45 | 46 | @JsonKey(name: 'id') 47 | int id; 48 | 49 | @JsonKey(name: 'adIndex') 50 | int adIndex; 51 | 52 | TagItemList(this.type,this.data,this.id,this.adIndex,); 53 | 54 | factory TagItemList.fromJson(Map srcJson) => _$TagItemListFromJson(srcJson); 55 | 56 | Map toJson() => _$TagItemListToJson(this); 57 | 58 | } 59 | 60 | 61 | @JsonSerializable() 62 | class Data extends Object { 63 | 64 | @JsonKey(name: 'dataType') 65 | String dataType; 66 | 67 | @JsonKey(name: 'header') 68 | Header header; 69 | 70 | @JsonKey(name: 'itemList') 71 | List itemList; 72 | 73 | @JsonKey(name: 'count') 74 | int count; 75 | 76 | @JsonKey(name: 'tagName') 77 | String tagName; 78 | 79 | @JsonKey(name: 'tagId') 80 | int tagId; 81 | 82 | @JsonKey(name: 'bgPicture') 83 | String bgPicture; 84 | 85 | @JsonKey(name: 'actionUrl') 86 | String actionUrl; 87 | 88 | @JsonKey(name: 'seenCount') 89 | int seenCount; 90 | 91 | @JsonKey(name: 'ifTagIndex') 92 | bool ifTagIndex; 93 | 94 | Data(this.dataType,this.header,this.itemList,this.count, 95 | this.tagName,this.tagId,this.bgPicture,this.actionUrl,this.seenCount, 96 | this.ifTagIndex,); 97 | 98 | factory Data.fromJson(Map srcJson) => _$DataFromJson(srcJson); 99 | 100 | Map toJson() => _$DataToJson(this); 101 | 102 | } 103 | 104 | 105 | @JsonSerializable() 106 | class Header extends Object { 107 | 108 | @JsonKey(name: 'id') 109 | int id; 110 | 111 | @JsonKey(name: 'title') 112 | String title; 113 | 114 | @JsonKey(name: 'font') 115 | String font; 116 | 117 | @JsonKey(name: 'textAlign') 118 | String textAlign; 119 | 120 | @JsonKey(name: 'actionUrl') 121 | String actionUrl; 122 | 123 | @JsonKey(name: 'rightText') 124 | String rightText; 125 | 126 | @JsonKey(name: 'issuerName') 127 | String issuerName; 128 | 129 | @JsonKey(name: 'followType') 130 | String followType; 131 | 132 | @JsonKey(name: 'icon') 133 | String icon; 134 | 135 | Header(this.id,this.title,this.font,this.textAlign,this.actionUrl,this.rightText,this.issuerName,this.followType,this.icon,); 136 | 137 | factory Header.fromJson(Map srcJson) => _$HeaderFromJson(srcJson); 138 | 139 | Map toJson() => _$HeaderToJson(this); 140 | 141 | } 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /lib/model/CommunityBean.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'CommunityBean.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommunityBean _$CommunityBeanFromJson(Map json) { 10 | return CommunityBean( 11 | (json['itemList'] as List) 12 | ?.map((e) => e == null 13 | ? null 14 | : TagItemList.fromJson(e as Map)) 15 | ?.toList(), 16 | json['count'] as int, 17 | json['total'] as int, 18 | json['nextPageUrl'] as String, 19 | json['adExist'] as bool); 20 | } 21 | 22 | Map _$CommunityBeanToJson(CommunityBean instance) => 23 | { 24 | 'itemList': instance.itemList, 25 | 'count': instance.count, 26 | 'total': instance.total, 27 | 'nextPageUrl': instance.nextPageUrl, 28 | 'adExist': instance.adExist 29 | }; 30 | 31 | TagItemList _$TagItemListFromJson(Map json) { 32 | return TagItemList( 33 | json['type'] as String, 34 | json['data'] == null 35 | ? null 36 | : Data.fromJson(json['data'] as Map), 37 | json['id'] as int, 38 | json['adIndex'] as int); 39 | } 40 | 41 | Map _$TagItemListToJson(TagItemList instance) => 42 | { 43 | 'type': instance.type, 44 | 'data': instance.data, 45 | 'id': instance.id, 46 | 'adIndex': instance.adIndex 47 | }; 48 | 49 | Data _$DataFromJson(Map json) { 50 | return Data( 51 | json['dataType'] as String, 52 | json['header'] == null 53 | ? null 54 | : Header.fromJson(json['header'] as Map), 55 | (json['itemList'] as List) 56 | ?.map((e) => e == null 57 | ? null 58 | : TagItemList.fromJson(e as Map)) 59 | ?.toList(), 60 | json['count'] as int, 61 | json['tagName'] as String, 62 | json['tagId'] as int, 63 | json['bgPicture'] as String, 64 | json['actionUrl'] as String, 65 | json['seenCount'] as int, 66 | json['ifTagIndex'] as bool); 67 | } 68 | 69 | Map _$DataToJson(Data instance) => { 70 | 'dataType': instance.dataType, 71 | 'header': instance.header, 72 | 'itemList': instance.itemList, 73 | 'count': instance.count, 74 | 'tagName': instance.tagName, 75 | 'tagId': instance.tagId, 76 | 'bgPicture': instance.bgPicture, 77 | 'actionUrl': instance.actionUrl, 78 | 'seenCount': instance.seenCount, 79 | 'ifTagIndex': instance.ifTagIndex 80 | }; 81 | 82 | Header _$HeaderFromJson(Map json) { 83 | return Header( 84 | json['id'] as int, 85 | json['title'] as String, 86 | json['font'] as String, 87 | json['textAlign'] as String, 88 | json['actionUrl'] as String, 89 | json['rightText'] as String, 90 | json['issuerName'] as String, 91 | json['followType'] as String, 92 | json['icon'] as String); 93 | } 94 | 95 | Map _$HeaderToJson(Header instance) => { 96 | 'id': instance.id, 97 | 'title': instance.title, 98 | 'font': instance.font, 99 | 'textAlign': instance.textAlign, 100 | 'actionUrl': instance.actionUrl, 101 | 'rightText': instance.rightText, 102 | 'issuerName': instance.issuerName, 103 | 'followType': instance.followType, 104 | 'icon': instance.icon 105 | }; 106 | -------------------------------------------------------------------------------- /lib/model/CommunityItemBean.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'CommunityItemBean.g.dart'; 4 | 5 | /** 6 | * Created by toeii 7 | * Date: 2019-01-16 8 | */ 9 | ///社区列表 10 | @JsonSerializable() 11 | class CommunityItemBean extends Object { 12 | 13 | @JsonKey(name: 'itemList') 14 | List itemList; 15 | 16 | @JsonKey(name: 'count') 17 | int count; 18 | 19 | @JsonKey(name: 'total') 20 | int total; 21 | 22 | @JsonKey(name: 'nextPageUrl') 23 | String nextPageUrl; 24 | 25 | @JsonKey(name: 'adExist') 26 | bool adExist; 27 | 28 | CommunityItemBean(this.itemList,this.count,this.total,this.nextPageUrl,this.adExist,); 29 | 30 | factory CommunityItemBean.fromJson(Map srcJson) => _$CommunityItemBeanFromJson(srcJson); 31 | 32 | Map toJson() => _$CommunityItemBeanToJson(this); 33 | 34 | } 35 | 36 | 37 | @JsonSerializable() 38 | class ItemList extends Object { 39 | 40 | @JsonKey(name: 'type') 41 | String type; 42 | 43 | @JsonKey(name: 'data') 44 | Data data; 45 | 46 | @JsonKey(name: 'id') 47 | int id; 48 | 49 | 50 | ItemList(this.type,this.data,this.id,); 51 | 52 | factory ItemList.fromJson(Map srcJson) => _$ItemListFromJson(srcJson); 53 | 54 | Map toJson() => _$ItemListToJson(this); 55 | 56 | } 57 | 58 | 59 | @JsonSerializable() 60 | class Header extends Object { 61 | 62 | @JsonKey(name: 'id') 63 | int id; 64 | 65 | @JsonKey(name: 'actionUrl') 66 | String actionUrl; 67 | 68 | @JsonKey(name: 'icon') 69 | String icon; 70 | 71 | @JsonKey(name: 'iconType') 72 | String iconType; 73 | 74 | @JsonKey(name: 'showHateVideo') 75 | bool showHateVideo; 76 | 77 | @JsonKey(name: 'followType') 78 | String followType; 79 | 80 | @JsonKey(name: 'tagId') 81 | int tagId; 82 | 83 | @JsonKey(name: 'issuerName') 84 | String issuerName; 85 | 86 | @JsonKey(name: 'topShow') 87 | bool topShow; 88 | 89 | Header(this.id,this.actionUrl,this.icon,this.iconType,this.showHateVideo,this.followType,this.tagId,this.issuerName,this.topShow,); 90 | 91 | factory Header.fromJson(Map srcJson) => _$HeaderFromJson(srcJson); 92 | 93 | Map toJson() => _$HeaderToJson(this); 94 | 95 | } 96 | 97 | 98 | @JsonSerializable() 99 | class Content extends Object { 100 | 101 | @JsonKey(name: 'type') 102 | String type; 103 | 104 | @JsonKey(name: 'data') 105 | Data data; 106 | 107 | @JsonKey(name: 'id') 108 | int id; 109 | 110 | Content(this.type,this.data,this.id,); 111 | 112 | factory Content.fromJson(Map srcJson) => _$ContentFromJson(srcJson); 113 | 114 | Map toJson() => _$ContentToJson(this); 115 | 116 | } 117 | 118 | 119 | @JsonSerializable() 120 | class Data extends Object { 121 | 122 | @JsonKey(name: 'header') 123 | Header header; 124 | 125 | @JsonKey(name: 'content') 126 | Content content; 127 | 128 | @JsonKey(name: 'dataType') 129 | String dataType; 130 | 131 | @JsonKey(name: 'id') 132 | int id; 133 | 134 | @JsonKey(name: 'title') 135 | String title; 136 | 137 | @JsonKey(name: 'description') 138 | String description; 139 | 140 | @JsonKey(name: 'library') 141 | String library; 142 | 143 | @JsonKey(name: 'tags') 144 | List tags; 145 | 146 | @JsonKey(name: 'consumption') 147 | Consumption consumption; 148 | 149 | @JsonKey(name: 'owner') 150 | Owner owner; 151 | 152 | @JsonKey(name: 'playUrl') 153 | String playUrl; 154 | 155 | @JsonKey(name: 'cover') 156 | Cover cover; 157 | 158 | Data(this.header,this.content,this.dataType,this.id,this.title,this.description, 159 | this.library,this.tags,this.consumption,this.owner,this.playUrl,this.cover,); 160 | 161 | factory Data.fromJson(Map srcJson) => _$DataFromJson(srcJson); 162 | 163 | Map toJson() => _$DataToJson(this); 164 | 165 | } 166 | 167 | 168 | @JsonSerializable() 169 | class Tags extends Object { 170 | 171 | @JsonKey(name: 'id') 172 | int id; 173 | 174 | @JsonKey(name: 'name') 175 | String name; 176 | 177 | @JsonKey(name: 'actionUrl') 178 | String actionUrl; 179 | 180 | @JsonKey(name: 'desc') 181 | String desc; 182 | 183 | @JsonKey(name: 'bgPicture') 184 | String bgPicture; 185 | 186 | @JsonKey(name: 'headerImage') 187 | String headerImage; 188 | 189 | @JsonKey(name: 'tagRecType') 190 | String tagRecType; 191 | 192 | @JsonKey(name: 'tagStatus') 193 | String tagStatus; 194 | 195 | @JsonKey(name: 'ifShow') 196 | bool ifShow; 197 | 198 | Tags(this.id,this.name,this.actionUrl,this.desc,this.bgPicture,this.headerImage,this.tagRecType, 199 | this.tagStatus,this.ifShow,); 200 | 201 | factory Tags.fromJson(Map srcJson) => _$TagsFromJson(srcJson); 202 | 203 | Map toJson() => _$TagsToJson(this); 204 | 205 | } 206 | 207 | 208 | @JsonSerializable() 209 | class Consumption extends Object { 210 | 211 | @JsonKey(name: 'collectionCount') 212 | int collectionCount; 213 | 214 | @JsonKey(name: 'shareCount') 215 | int shareCount; 216 | 217 | @JsonKey(name: 'replyCount') 218 | int replyCount; 219 | 220 | @JsonKey(name: 'playCount') 221 | int playCount; 222 | 223 | Consumption(this.collectionCount,this.shareCount,this.replyCount,this.playCount,); 224 | 225 | factory Consumption.fromJson(Map srcJson) => _$ConsumptionFromJson(srcJson); 226 | 227 | Map toJson() => _$ConsumptionToJson(this); 228 | 229 | } 230 | 231 | 232 | @JsonSerializable() 233 | class Owner extends Object { 234 | 235 | @JsonKey(name: 'uid') 236 | int uid; 237 | 238 | @JsonKey(name: 'nickname') 239 | String nickname; 240 | 241 | @JsonKey(name: 'avatar') 242 | String avatar; 243 | 244 | @JsonKey(name: 'userType') 245 | String userType; 246 | 247 | @JsonKey(name: 'ifPgc') 248 | bool ifPgc; 249 | 250 | @JsonKey(name: 'description') 251 | String description; 252 | 253 | @JsonKey(name: 'gender') 254 | String gender; 255 | 256 | @JsonKey(name: 'actionUrl') 257 | String actionUrl; 258 | 259 | @JsonKey(name: 'followed') 260 | bool followed; 261 | 262 | @JsonKey(name: 'limitVideoOpen') 263 | bool limitVideoOpen; 264 | 265 | @JsonKey(name: 'library') 266 | String library; 267 | 268 | @JsonKey(name: 'expert') 269 | bool expert; 270 | 271 | @JsonKey(name: 'username') 272 | String username; 273 | 274 | @JsonKey(name: 'uploadStatus') 275 | String uploadStatus; 276 | 277 | Owner(this.uid,this.nickname,this.avatar,this.userType,this.ifPgc,this.description,this.gender,this.actionUrl,this.followed,this.limitVideoOpen,this.library,this.expert,this.username,this.uploadStatus,); 278 | 279 | factory Owner.fromJson(Map srcJson) => _$OwnerFromJson(srcJson); 280 | 281 | Map toJson() => _$OwnerToJson(this); 282 | 283 | } 284 | 285 | 286 | @JsonSerializable() 287 | class Cover extends Object { 288 | 289 | @JsonKey(name: 'feed') 290 | String feed; 291 | 292 | @JsonKey(name: 'detail') 293 | String detail; 294 | 295 | Cover(this.feed,this.detail,); 296 | 297 | factory Cover.fromJson(Map srcJson) => _$CoverFromJson(srcJson); 298 | 299 | Map toJson() => _$CoverToJson(this); 300 | 301 | } 302 | 303 | 304 | -------------------------------------------------------------------------------- /lib/model/CommunityItemBean.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'CommunityItemBean.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | CommunityItemBean _$CommunityItemBeanFromJson(Map json) { 10 | return CommunityItemBean( 11 | (json['itemList'] as List) 12 | ?.map((e) => 13 | e == null ? null : ItemList.fromJson(e as Map)) 14 | ?.toList(), 15 | json['count'] as int, 16 | json['total'] as int, 17 | json['nextPageUrl'] as String, 18 | json['adExist'] as bool); 19 | } 20 | 21 | Map _$CommunityItemBeanToJson(CommunityItemBean instance) => 22 | { 23 | 'itemList': instance.itemList, 24 | 'count': instance.count, 25 | 'total': instance.total, 26 | 'nextPageUrl': instance.nextPageUrl, 27 | 'adExist': instance.adExist 28 | }; 29 | 30 | ItemList _$ItemListFromJson(Map json) { 31 | return ItemList( 32 | json['type'] as String, 33 | json['data'] == null 34 | ? null 35 | : Data.fromJson(json['data'] as Map), 36 | json['id'] as int); 37 | } 38 | 39 | Map _$ItemListToJson(ItemList instance) => { 40 | 'type': instance.type, 41 | 'data': instance.data, 42 | 'id': instance.id 43 | }; 44 | 45 | Header _$HeaderFromJson(Map json) { 46 | return Header( 47 | json['id'] as int, 48 | json['actionUrl'] as String, 49 | json['icon'] as String, 50 | json['iconType'] as String, 51 | json['showHateVideo'] as bool, 52 | json['followType'] as String, 53 | json['tagId'] as int, 54 | json['issuerName'] as String, 55 | json['topShow'] as bool); 56 | } 57 | 58 | Map _$HeaderToJson(Header instance) => { 59 | 'id': instance.id, 60 | 'actionUrl': instance.actionUrl, 61 | 'icon': instance.icon, 62 | 'iconType': instance.iconType, 63 | 'showHateVideo': instance.showHateVideo, 64 | 'followType': instance.followType, 65 | 'tagId': instance.tagId, 66 | 'issuerName': instance.issuerName, 67 | 'topShow': instance.topShow 68 | }; 69 | 70 | Content _$ContentFromJson(Map json) { 71 | return Content( 72 | json['type'] as String, 73 | json['data'] == null 74 | ? null 75 | : Data.fromJson(json['data'] as Map), 76 | json['id'] as int); 77 | } 78 | 79 | Map _$ContentToJson(Content instance) => { 80 | 'type': instance.type, 81 | 'data': instance.data, 82 | 'id': instance.id 83 | }; 84 | 85 | Data _$DataFromJson(Map json) { 86 | return Data( 87 | json['header'] == null 88 | ? null 89 | : Header.fromJson(json['header'] as Map), 90 | json['content'] == null 91 | ? null 92 | : Content.fromJson(json['content'] as Map), 93 | json['dataType'] as String, 94 | json['id'] as int, 95 | json['title'] as String, 96 | json['description'] as String, 97 | json['library'] as String, 98 | (json['tags'] as List) 99 | ?.map((e) => 100 | e == null ? null : Tags.fromJson(e as Map)) 101 | ?.toList(), 102 | json['consumption'] == null 103 | ? null 104 | : Consumption.fromJson(json['consumption'] as Map), 105 | json['owner'] == null 106 | ? null 107 | : Owner.fromJson(json['owner'] as Map), 108 | json['playUrl'] as String, 109 | json['cover'] == null 110 | ? null 111 | : Cover.fromJson(json['cover'] as Map)); 112 | } 113 | 114 | Map _$DataToJson(Data instance) => { 115 | 'header': instance.header, 116 | 'content': instance.content, 117 | 'dataType': instance.dataType, 118 | 'id': instance.id, 119 | 'title': instance.title, 120 | 'description': instance.description, 121 | 'library': instance.library, 122 | 'tags': instance.tags, 123 | 'consumption': instance.consumption, 124 | 'owner': instance.owner, 125 | 'playUrl': instance.playUrl, 126 | 'cover': instance.cover 127 | }; 128 | 129 | Tags _$TagsFromJson(Map json) { 130 | return Tags( 131 | json['id'] as int, 132 | json['name'] as String, 133 | json['actionUrl'] as String, 134 | json['desc'] as String, 135 | json['bgPicture'] as String, 136 | json['headerImage'] as String, 137 | json['tagRecType'] as String, 138 | json['tagStatus'] as String, 139 | json['ifShow'] as bool); 140 | } 141 | 142 | Map _$TagsToJson(Tags instance) => { 143 | 'id': instance.id, 144 | 'name': instance.name, 145 | 'actionUrl': instance.actionUrl, 146 | 'desc': instance.desc, 147 | 'bgPicture': instance.bgPicture, 148 | 'headerImage': instance.headerImage, 149 | 'tagRecType': instance.tagRecType, 150 | 'tagStatus': instance.tagStatus, 151 | 'ifShow': instance.ifShow 152 | }; 153 | 154 | Consumption _$ConsumptionFromJson(Map json) { 155 | return Consumption(json['collectionCount'] as int, json['shareCount'] as int, 156 | json['replyCount'] as int, json['playCount'] as int); 157 | } 158 | 159 | Map _$ConsumptionToJson(Consumption instance) => 160 | { 161 | 'collectionCount': instance.collectionCount, 162 | 'shareCount': instance.shareCount, 163 | 'replyCount': instance.replyCount, 164 | 'playCount': instance.playCount 165 | }; 166 | 167 | Owner _$OwnerFromJson(Map json) { 168 | return Owner( 169 | json['uid'] as int, 170 | json['nickname'] as String, 171 | json['avatar'] as String, 172 | json['userType'] as String, 173 | json['ifPgc'] as bool, 174 | json['description'] as String, 175 | json['gender'] as String, 176 | json['actionUrl'] as String, 177 | json['followed'] as bool, 178 | json['limitVideoOpen'] as bool, 179 | json['library'] as String, 180 | json['expert'] as bool, 181 | json['username'] as String, 182 | json['uploadStatus'] as String); 183 | } 184 | 185 | Map _$OwnerToJson(Owner instance) => { 186 | 'uid': instance.uid, 187 | 'nickname': instance.nickname, 188 | 'avatar': instance.avatar, 189 | 'userType': instance.userType, 190 | 'ifPgc': instance.ifPgc, 191 | 'description': instance.description, 192 | 'gender': instance.gender, 193 | 'actionUrl': instance.actionUrl, 194 | 'followed': instance.followed, 195 | 'limitVideoOpen': instance.limitVideoOpen, 196 | 'library': instance.library, 197 | 'expert': instance.expert, 198 | 'username': instance.username, 199 | 'uploadStatus': instance.uploadStatus 200 | }; 201 | 202 | Cover _$CoverFromJson(Map json) { 203 | return Cover(json['feed'] as String, json['detail'] as String); 204 | } 205 | 206 | Map _$CoverToJson(Cover instance) => 207 | {'feed': instance.feed, 'detail': instance.detail}; 208 | -------------------------------------------------------------------------------- /lib/model/PersonalBean.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | part 'PersonalBean.g.dart'; 3 | /** 4 | * Created by toeii 5 | * Date: 2019-01-16 6 | */ 7 | ///个人信息 8 | @JsonSerializable() 9 | class PersonalBean extends Object { 10 | 11 | @JsonKey(name: 'itemList') 12 | List itemList; 13 | 14 | @JsonKey(name: 'count') 15 | int count; 16 | 17 | @JsonKey(name: 'total') 18 | int total; 19 | 20 | @JsonKey(name: 'nextPageUrl') 21 | String nextPageUrl; 22 | 23 | @JsonKey(name: 'adExist') 24 | bool adExist; 25 | 26 | PersonalBean(this.itemList,this.count,this.total,this.nextPageUrl,this.adExist,); 27 | 28 | factory PersonalBean.fromJson(Map srcJson) => _$PersonalBeanFromJson(srcJson); 29 | 30 | Map toJson() => _$PersonalBeanToJson(this); 31 | 32 | } 33 | 34 | 35 | @JsonSerializable() 36 | class ItemList extends Object { 37 | 38 | @JsonKey(name: 'type') 39 | String type; 40 | 41 | @JsonKey(name: 'data') 42 | Data data; 43 | 44 | @JsonKey(name: 'id') 45 | int id; 46 | 47 | @JsonKey(name: 'adIndex') 48 | int adIndex; 49 | 50 | ItemList(this.type,this.data,this.id,this.adIndex,); 51 | 52 | factory ItemList.fromJson(Map srcJson) => _$ItemListFromJson(srcJson); 53 | 54 | Map toJson() => _$ItemListToJson(this); 55 | 56 | } 57 | 58 | 59 | @JsonSerializable() 60 | class Data extends Object { 61 | 62 | @JsonKey(name: 'dataType') 63 | String dataType; 64 | 65 | @JsonKey(name: 'id') 66 | int id; 67 | 68 | @JsonKey(name: 'title') 69 | String title; 70 | 71 | @JsonKey(name: 'description') 72 | String description; 73 | 74 | @JsonKey(name: 'library') 75 | String library; 76 | 77 | @JsonKey(name: 'tags') 78 | List tags; 79 | 80 | @JsonKey(name: 'consumption') 81 | Consumption consumption; 82 | 83 | @JsonKey(name: 'resourceType') 84 | String resourceType; 85 | 86 | @JsonKey(name: 'provider') 87 | Provider provider; 88 | 89 | @JsonKey(name: 'category') 90 | String category; 91 | 92 | @JsonKey(name: 'author') 93 | Author author; 94 | 95 | @JsonKey(name: 'cover') 96 | Cover cover; 97 | 98 | @JsonKey(name: 'playUrl') 99 | String playUrl; 100 | 101 | @JsonKey(name: 'duration') 102 | int duration; 103 | 104 | @JsonKey(name: 'webUrl') 105 | WebUrl webUrl; 106 | 107 | @JsonKey(name: 'releaseTime') 108 | int releaseTime; 109 | 110 | @JsonKey(name: 'playInfo') 111 | List playInfo; 112 | 113 | @JsonKey(name: 'ad') 114 | bool ad; 115 | 116 | @JsonKey(name: 'type') 117 | String type; 118 | 119 | @JsonKey(name: 'titlePgc') 120 | String titlePgc; 121 | 122 | @JsonKey(name: 'descriptionPgc') 123 | String descriptionPgc; 124 | 125 | @JsonKey(name: 'ifLimitVideo') 126 | bool ifLimitVideo; 127 | 128 | @JsonKey(name: 'searchWeight') 129 | int searchWeight; 130 | 131 | @JsonKey(name: 'idx') 132 | int idx; 133 | 134 | @JsonKey(name: 'date') 135 | int date; 136 | 137 | @JsonKey(name: 'labelList') 138 | List labelList; 139 | 140 | @JsonKey(name: 'descriptionEditor') 141 | String descriptionEditor; 142 | 143 | @JsonKey(name: 'collected') 144 | bool collected; 145 | 146 | @JsonKey(name: 'played') 147 | bool played; 148 | 149 | @JsonKey(name: 'subtitles') 150 | List subtitles; 151 | 152 | Data(this.dataType,this.id,this.title,this.description,this.library,this.tags,this.consumption,this.resourceType,this.provider,this.category,this.author,this.cover,this.playUrl,this.duration,this.webUrl,this.releaseTime,this.playInfo,this.ad,this.type,this.titlePgc,this.descriptionPgc,this.ifLimitVideo,this.searchWeight,this.idx,this.date,this.labelList,this.descriptionEditor,this.collected,this.played,this.subtitles,); 153 | 154 | factory Data.fromJson(Map srcJson) => _$DataFromJson(srcJson); 155 | 156 | Map toJson() => _$DataToJson(this); 157 | 158 | } 159 | 160 | 161 | @JsonSerializable() 162 | class Tags extends Object { 163 | 164 | @JsonKey(name: 'id') 165 | int id; 166 | 167 | @JsonKey(name: 'name') 168 | String name; 169 | 170 | @JsonKey(name: 'actionUrl') 171 | String actionUrl; 172 | 173 | @JsonKey(name: 'desc') 174 | String desc; 175 | 176 | @JsonKey(name: 'bgPicture') 177 | String bgPicture; 178 | 179 | @JsonKey(name: 'headerImage') 180 | String headerImage; 181 | 182 | @JsonKey(name: 'tagRecType') 183 | String tagRecType; 184 | 185 | @JsonKey(name: 'communityIndex') 186 | int communityIndex; 187 | 188 | Tags(this.id,this.name,this.actionUrl,this.desc,this.bgPicture,this.headerImage,this.tagRecType,this.communityIndex,); 189 | 190 | factory Tags.fromJson(Map srcJson) => _$TagsFromJson(srcJson); 191 | 192 | Map toJson() => _$TagsToJson(this); 193 | 194 | } 195 | 196 | 197 | @JsonSerializable() 198 | class Consumption extends Object { 199 | 200 | @JsonKey(name: 'collectionCount') 201 | int collectionCount; 202 | 203 | @JsonKey(name: 'shareCount') 204 | int shareCount; 205 | 206 | @JsonKey(name: 'replyCount') 207 | int replyCount; 208 | 209 | Consumption(this.collectionCount,this.shareCount,this.replyCount,); 210 | 211 | factory Consumption.fromJson(Map srcJson) => _$ConsumptionFromJson(srcJson); 212 | 213 | Map toJson() => _$ConsumptionToJson(this); 214 | 215 | } 216 | 217 | 218 | @JsonSerializable() 219 | class Provider extends Object { 220 | 221 | @JsonKey(name: 'name') 222 | String name; 223 | 224 | @JsonKey(name: 'alias') 225 | String alias; 226 | 227 | @JsonKey(name: 'icon') 228 | String icon; 229 | 230 | Provider(this.name,this.alias,this.icon,); 231 | 232 | factory Provider.fromJson(Map srcJson) => _$ProviderFromJson(srcJson); 233 | 234 | Map toJson() => _$ProviderToJson(this); 235 | 236 | } 237 | 238 | 239 | @JsonSerializable() 240 | class Author extends Object { 241 | 242 | @JsonKey(name: 'id') 243 | int id; 244 | 245 | @JsonKey(name: 'icon') 246 | String icon; 247 | 248 | @JsonKey(name: 'name') 249 | String name; 250 | 251 | @JsonKey(name: 'description') 252 | String description; 253 | 254 | @JsonKey(name: 'link') 255 | String link; 256 | 257 | @JsonKey(name: 'latestReleaseTime') 258 | int latestReleaseTime; 259 | 260 | @JsonKey(name: 'videoNum') 261 | int videoNum; 262 | 263 | @JsonKey(name: 'follow') 264 | Follow follow; 265 | 266 | @JsonKey(name: 'shield') 267 | Shield shield; 268 | 269 | @JsonKey(name: 'approvedNotReadyVideoCount') 270 | int approvedNotReadyVideoCount; 271 | 272 | @JsonKey(name: 'ifPgc') 273 | bool ifPgc; 274 | 275 | @JsonKey(name: 'recSort') 276 | int recSort; 277 | 278 | @JsonKey(name: 'expert') 279 | bool expert; 280 | 281 | Author(this.id,this.icon,this.name,this.description,this.link,this.latestReleaseTime,this.videoNum,this.follow,this.shield,this.approvedNotReadyVideoCount,this.ifPgc,this.recSort,this.expert,); 282 | 283 | factory Author.fromJson(Map srcJson) => _$AuthorFromJson(srcJson); 284 | 285 | Map toJson() => _$AuthorToJson(this); 286 | 287 | } 288 | 289 | 290 | @JsonSerializable() 291 | class Follow extends Object { 292 | 293 | @JsonKey(name: 'itemType') 294 | String itemType; 295 | 296 | @JsonKey(name: 'itemId') 297 | int itemId; 298 | 299 | @JsonKey(name: 'followed') 300 | bool followed; 301 | 302 | Follow(this.itemType,this.itemId,this.followed,); 303 | 304 | factory Follow.fromJson(Map srcJson) => _$FollowFromJson(srcJson); 305 | 306 | Map toJson() => _$FollowToJson(this); 307 | 308 | } 309 | 310 | 311 | @JsonSerializable() 312 | class Shield extends Object { 313 | 314 | @JsonKey(name: 'itemType') 315 | String itemType; 316 | 317 | @JsonKey(name: 'itemId') 318 | int itemId; 319 | 320 | @JsonKey(name: 'shielded') 321 | bool shielded; 322 | 323 | Shield(this.itemType,this.itemId,this.shielded,); 324 | 325 | factory Shield.fromJson(Map srcJson) => _$ShieldFromJson(srcJson); 326 | 327 | Map toJson() => _$ShieldToJson(this); 328 | 329 | } 330 | 331 | 332 | @JsonSerializable() 333 | class Cover extends Object { 334 | 335 | @JsonKey(name: 'feed') 336 | String feed; 337 | 338 | @JsonKey(name: 'detail') 339 | String detail; 340 | 341 | @JsonKey(name: 'blurred') 342 | String blurred; 343 | 344 | Cover(this.feed,this.detail,this.blurred,); 345 | 346 | factory Cover.fromJson(Map srcJson) => _$CoverFromJson(srcJson); 347 | 348 | Map toJson() => _$CoverToJson(this); 349 | 350 | } 351 | 352 | 353 | @JsonSerializable() 354 | class WebUrl extends Object { 355 | 356 | @JsonKey(name: 'raw') 357 | String raw; 358 | 359 | @JsonKey(name: 'forWeibo') 360 | String forWeibo; 361 | 362 | WebUrl(this.raw,this.forWeibo,); 363 | 364 | factory WebUrl.fromJson(Map srcJson) => _$WebUrlFromJson(srcJson); 365 | 366 | Map toJson() => _$WebUrlToJson(this); 367 | 368 | } 369 | 370 | 371 | @JsonSerializable() 372 | class PlayInfo extends Object { 373 | 374 | @JsonKey(name: 'height') 375 | int height; 376 | 377 | @JsonKey(name: 'width') 378 | int width; 379 | 380 | @JsonKey(name: 'urlList') 381 | List urlList; 382 | 383 | @JsonKey(name: 'name') 384 | String name; 385 | 386 | @JsonKey(name: 'type') 387 | String type; 388 | 389 | @JsonKey(name: 'url') 390 | String url; 391 | 392 | PlayInfo(this.height,this.width,this.urlList,this.name,this.type,this.url,); 393 | 394 | factory PlayInfo.fromJson(Map srcJson) => _$PlayInfoFromJson(srcJson); 395 | 396 | Map toJson() => _$PlayInfoToJson(this); 397 | 398 | } 399 | 400 | 401 | @JsonSerializable() 402 | class UrlList extends Object { 403 | 404 | @JsonKey(name: 'name') 405 | String name; 406 | 407 | @JsonKey(name: 'url') 408 | String url; 409 | 410 | @JsonKey(name: 'size') 411 | int size; 412 | 413 | UrlList(this.name,this.url,this.size,); 414 | 415 | factory UrlList.fromJson(Map srcJson) => _$UrlListFromJson(srcJson); 416 | 417 | Map toJson() => _$UrlListToJson(this); 418 | 419 | } 420 | 421 | 422 | -------------------------------------------------------------------------------- /lib/model/PersonalBean.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'PersonalBean.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PersonalBean _$PersonalBeanFromJson(Map json) { 10 | return PersonalBean( 11 | (json['itemList'] as List) 12 | ?.map((e) => 13 | e == null ? null : ItemList.fromJson(e as Map)) 14 | ?.toList(), 15 | json['count'] as int, 16 | json['total'] as int, 17 | json['nextPageUrl'] as String, 18 | json['adExist'] as bool); 19 | } 20 | 21 | Map _$PersonalBeanToJson(PersonalBean instance) => 22 | { 23 | 'itemList': instance.itemList, 24 | 'count': instance.count, 25 | 'total': instance.total, 26 | 'nextPageUrl': instance.nextPageUrl, 27 | 'adExist': instance.adExist 28 | }; 29 | 30 | ItemList _$ItemListFromJson(Map json) { 31 | return ItemList( 32 | json['type'] as String, 33 | json['data'] == null 34 | ? null 35 | : Data.fromJson(json['data'] as Map), 36 | json['id'] as int, 37 | json['adIndex'] as int); 38 | } 39 | 40 | Map _$ItemListToJson(ItemList instance) => { 41 | 'type': instance.type, 42 | 'data': instance.data, 43 | 'id': instance.id, 44 | 'adIndex': instance.adIndex 45 | }; 46 | 47 | Data _$DataFromJson(Map json) { 48 | return Data( 49 | json['dataType'] as String, 50 | json['id'] as int, 51 | json['title'] as String, 52 | json['description'] as String, 53 | json['library'] as String, 54 | (json['tags'] as List) 55 | ?.map((e) => 56 | e == null ? null : Tags.fromJson(e as Map)) 57 | ?.toList(), 58 | json['consumption'] == null 59 | ? null 60 | : Consumption.fromJson(json['consumption'] as Map), 61 | json['resourceType'] as String, 62 | json['provider'] == null 63 | ? null 64 | : Provider.fromJson(json['provider'] as Map), 65 | json['category'] as String, 66 | json['author'] == null 67 | ? null 68 | : Author.fromJson(json['author'] as Map), 69 | json['cover'] == null 70 | ? null 71 | : Cover.fromJson(json['cover'] as Map), 72 | json['playUrl'] as String, 73 | json['duration'] as int, 74 | json['webUrl'] == null 75 | ? null 76 | : WebUrl.fromJson(json['webUrl'] as Map), 77 | json['releaseTime'] as int, 78 | (json['playInfo'] as List) 79 | ?.map((e) => 80 | e == null ? null : PlayInfo.fromJson(e as Map)) 81 | ?.toList(), 82 | json['ad'] as bool, 83 | json['type'] as String, 84 | json['titlePgc'] as String, 85 | json['descriptionPgc'] as String, 86 | json['ifLimitVideo'] as bool, 87 | json['searchWeight'] as int, 88 | json['idx'] as int, 89 | json['date'] as int, 90 | json['labelList'] as List, 91 | json['descriptionEditor'] as String, 92 | json['collected'] as bool, 93 | json['played'] as bool, 94 | json['subtitles'] as List); 95 | } 96 | 97 | Map _$DataToJson(Data instance) => { 98 | 'dataType': instance.dataType, 99 | 'id': instance.id, 100 | 'title': instance.title, 101 | 'description': instance.description, 102 | 'library': instance.library, 103 | 'tags': instance.tags, 104 | 'consumption': instance.consumption, 105 | 'resourceType': instance.resourceType, 106 | 'provider': instance.provider, 107 | 'category': instance.category, 108 | 'author': instance.author, 109 | 'cover': instance.cover, 110 | 'playUrl': instance.playUrl, 111 | 'duration': instance.duration, 112 | 'webUrl': instance.webUrl, 113 | 'releaseTime': instance.releaseTime, 114 | 'playInfo': instance.playInfo, 115 | 'ad': instance.ad, 116 | 'type': instance.type, 117 | 'titlePgc': instance.titlePgc, 118 | 'descriptionPgc': instance.descriptionPgc, 119 | 'ifLimitVideo': instance.ifLimitVideo, 120 | 'searchWeight': instance.searchWeight, 121 | 'idx': instance.idx, 122 | 'date': instance.date, 123 | 'labelList': instance.labelList, 124 | 'descriptionEditor': instance.descriptionEditor, 125 | 'collected': instance.collected, 126 | 'played': instance.played, 127 | 'subtitles': instance.subtitles 128 | }; 129 | 130 | Tags _$TagsFromJson(Map json) { 131 | return Tags( 132 | json['id'] as int, 133 | json['name'] as String, 134 | json['actionUrl'] as String, 135 | json['desc'] as String, 136 | json['bgPicture'] as String, 137 | json['headerImage'] as String, 138 | json['tagRecType'] as String, 139 | json['communityIndex'] as int); 140 | } 141 | 142 | Map _$TagsToJson(Tags instance) => { 143 | 'id': instance.id, 144 | 'name': instance.name, 145 | 'actionUrl': instance.actionUrl, 146 | 'desc': instance.desc, 147 | 'bgPicture': instance.bgPicture, 148 | 'headerImage': instance.headerImage, 149 | 'tagRecType': instance.tagRecType, 150 | 'communityIndex': instance.communityIndex 151 | }; 152 | 153 | Consumption _$ConsumptionFromJson(Map json) { 154 | return Consumption(json['collectionCount'] as int, json['shareCount'] as int, 155 | json['replyCount'] as int); 156 | } 157 | 158 | Map _$ConsumptionToJson(Consumption instance) => 159 | { 160 | 'collectionCount': instance.collectionCount, 161 | 'shareCount': instance.shareCount, 162 | 'replyCount': instance.replyCount 163 | }; 164 | 165 | Provider _$ProviderFromJson(Map json) { 166 | return Provider( 167 | json['name'] as String, json['alias'] as String, json['icon'] as String); 168 | } 169 | 170 | Map _$ProviderToJson(Provider instance) => { 171 | 'name': instance.name, 172 | 'alias': instance.alias, 173 | 'icon': instance.icon 174 | }; 175 | 176 | Author _$AuthorFromJson(Map json) { 177 | return Author( 178 | json['id'] as int, 179 | json['icon'] as String, 180 | json['name'] as String, 181 | json['description'] as String, 182 | json['link'] as String, 183 | json['latestReleaseTime'] as int, 184 | json['videoNum'] as int, 185 | json['follow'] == null 186 | ? null 187 | : Follow.fromJson(json['follow'] as Map), 188 | json['shield'] == null 189 | ? null 190 | : Shield.fromJson(json['shield'] as Map), 191 | json['approvedNotReadyVideoCount'] as int, 192 | json['ifPgc'] as bool, 193 | json['recSort'] as int, 194 | json['expert'] as bool); 195 | } 196 | 197 | Map _$AuthorToJson(Author instance) => { 198 | 'id': instance.id, 199 | 'icon': instance.icon, 200 | 'name': instance.name, 201 | 'description': instance.description, 202 | 'link': instance.link, 203 | 'latestReleaseTime': instance.latestReleaseTime, 204 | 'videoNum': instance.videoNum, 205 | 'follow': instance.follow, 206 | 'shield': instance.shield, 207 | 'approvedNotReadyVideoCount': instance.approvedNotReadyVideoCount, 208 | 'ifPgc': instance.ifPgc, 209 | 'recSort': instance.recSort, 210 | 'expert': instance.expert 211 | }; 212 | 213 | Follow _$FollowFromJson(Map json) { 214 | return Follow(json['itemType'] as String, json['itemId'] as int, 215 | json['followed'] as bool); 216 | } 217 | 218 | Map _$FollowToJson(Follow instance) => { 219 | 'itemType': instance.itemType, 220 | 'itemId': instance.itemId, 221 | 'followed': instance.followed 222 | }; 223 | 224 | Shield _$ShieldFromJson(Map json) { 225 | return Shield(json['itemType'] as String, json['itemId'] as int, 226 | json['shielded'] as bool); 227 | } 228 | 229 | Map _$ShieldToJson(Shield instance) => { 230 | 'itemType': instance.itemType, 231 | 'itemId': instance.itemId, 232 | 'shielded': instance.shielded 233 | }; 234 | 235 | Cover _$CoverFromJson(Map json) { 236 | return Cover(json['feed'] as String, json['detail'] as String, 237 | json['blurred'] as String); 238 | } 239 | 240 | Map _$CoverToJson(Cover instance) => { 241 | 'feed': instance.feed, 242 | 'detail': instance.detail, 243 | 'blurred': instance.blurred 244 | }; 245 | 246 | WebUrl _$WebUrlFromJson(Map json) { 247 | return WebUrl(json['raw'] as String, json['forWeibo'] as String); 248 | } 249 | 250 | Map _$WebUrlToJson(WebUrl instance) => 251 | {'raw': instance.raw, 'forWeibo': instance.forWeibo}; 252 | 253 | PlayInfo _$PlayInfoFromJson(Map json) { 254 | return PlayInfo( 255 | json['height'] as int, 256 | json['width'] as int, 257 | (json['urlList'] as List) 258 | ?.map((e) => 259 | e == null ? null : UrlList.fromJson(e as Map)) 260 | ?.toList(), 261 | json['name'] as String, 262 | json['type'] as String, 263 | json['url'] as String); 264 | } 265 | 266 | Map _$PlayInfoToJson(PlayInfo instance) => { 267 | 'height': instance.height, 268 | 'width': instance.width, 269 | 'urlList': instance.urlList, 270 | 'name': instance.name, 271 | 'type': instance.type, 272 | 'url': instance.url 273 | }; 274 | 275 | UrlList _$UrlListFromJson(Map json) { 276 | return UrlList( 277 | json['name'] as String, json['url'] as String, json['size'] as int); 278 | } 279 | 280 | Map _$UrlListToJson(UrlList instance) => { 281 | 'name': instance.name, 282 | 'url': instance.url, 283 | 'size': instance.size 284 | }; 285 | -------------------------------------------------------------------------------- /lib/model/PersonalInfoBean.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'PersonalInfoBean.g.dart'; 4 | 5 | 6 | @JsonSerializable() 7 | class PersonalInfoBean extends Object { 8 | 9 | @JsonKey(name: 'tabInfo') 10 | TabInfo tabInfo; 11 | 12 | @JsonKey(name: 'pgcInfo') 13 | PgcInfo pgcInfo; 14 | 15 | PersonalInfoBean(this.tabInfo,this.pgcInfo,); 16 | 17 | factory PersonalInfoBean.fromJson(Map srcJson) => _$PersonalInfoBeanFromJson(srcJson); 18 | 19 | Map toJson() => _$PersonalInfoBeanToJson(this); 20 | 21 | } 22 | 23 | 24 | @JsonSerializable() 25 | class TabInfo extends Object { 26 | 27 | @JsonKey(name: 'tabList') 28 | List tabList; 29 | 30 | @JsonKey(name: 'defaultIdx') 31 | int defaultIdx; 32 | 33 | TabInfo(this.tabList,this.defaultIdx,); 34 | 35 | factory TabInfo.fromJson(Map srcJson) => _$TabInfoFromJson(srcJson); 36 | 37 | Map toJson() => _$TabInfoToJson(this); 38 | 39 | } 40 | 41 | 42 | @JsonSerializable() 43 | class TabList extends Object { 44 | 45 | @JsonKey(name: 'id') 46 | int id; 47 | 48 | @JsonKey(name: 'name') 49 | String name; 50 | 51 | @JsonKey(name: 'apiUrl') 52 | String apiUrl; 53 | 54 | @JsonKey(name: 'tabType') 55 | int tabType; 56 | 57 | @JsonKey(name: 'nameType') 58 | int nameType; 59 | 60 | TabList(this.id,this.name,this.apiUrl,this.tabType,this.nameType,); 61 | 62 | factory TabList.fromJson(Map srcJson) => _$TabListFromJson(srcJson); 63 | 64 | Map toJson() => _$TabListToJson(this); 65 | 66 | } 67 | 68 | 69 | @JsonSerializable() 70 | class PgcInfo extends Object { 71 | 72 | @JsonKey(name: 'dataType') 73 | String dataType; 74 | 75 | @JsonKey(name: 'id') 76 | int id; 77 | 78 | @JsonKey(name: 'icon') 79 | String icon; 80 | 81 | @JsonKey(name: 'name') 82 | String name; 83 | 84 | @JsonKey(name: 'brief') 85 | String brief; 86 | 87 | @JsonKey(name: 'description') 88 | String description; 89 | 90 | @JsonKey(name: 'actionUrl') 91 | String actionUrl; 92 | 93 | @JsonKey(name: 'area') 94 | String area; 95 | 96 | @JsonKey(name: 'gender') 97 | String gender; 98 | 99 | @JsonKey(name: 'registDate') 100 | int registDate; 101 | 102 | @JsonKey(name: 'followCount') 103 | int followCount; 104 | 105 | @JsonKey(name: 'follow') 106 | Follow follow; 107 | 108 | @JsonKey(name: 'self') 109 | bool self; 110 | 111 | @JsonKey(name: 'cover') 112 | String cover; 113 | 114 | @JsonKey(name: 'videoCount') 115 | int videoCount; 116 | 117 | @JsonKey(name: 'shareCount') 118 | int shareCount; 119 | 120 | @JsonKey(name: 'collectCount') 121 | int collectCount; 122 | 123 | @JsonKey(name: 'myFollowCount') 124 | int myFollowCount; 125 | 126 | @JsonKey(name: 'medalsNum') 127 | int medalsNum; 128 | 129 | @JsonKey(name: 'shield') 130 | Shield shield; 131 | 132 | @JsonKey(name: 'expert') 133 | bool expert; 134 | 135 | PgcInfo(this.dataType,this.id,this.icon,this.name,this.brief,this.description,this.actionUrl,this.area,this.gender,this.registDate,this.followCount,this.follow,this.self,this.cover,this.videoCount,this.shareCount,this.collectCount,this.myFollowCount,this.medalsNum,this.shield,this.expert,); 136 | 137 | factory PgcInfo.fromJson(Map srcJson) => _$PgcInfoFromJson(srcJson); 138 | 139 | Map toJson() => _$PgcInfoToJson(this); 140 | 141 | } 142 | 143 | 144 | @JsonSerializable() 145 | class Follow extends Object { 146 | 147 | @JsonKey(name: 'itemType') 148 | String itemType; 149 | 150 | @JsonKey(name: 'itemId') 151 | int itemId; 152 | 153 | @JsonKey(name: 'followed') 154 | bool followed; 155 | 156 | Follow(this.itemType,this.itemId,this.followed,); 157 | 158 | factory Follow.fromJson(Map srcJson) => _$FollowFromJson(srcJson); 159 | 160 | Map toJson() => _$FollowToJson(this); 161 | 162 | } 163 | 164 | 165 | @JsonSerializable() 166 | class Shield extends Object { 167 | 168 | @JsonKey(name: 'itemType') 169 | String itemType; 170 | 171 | @JsonKey(name: 'itemId') 172 | int itemId; 173 | 174 | @JsonKey(name: 'shielded') 175 | bool shielded; 176 | 177 | Shield(this.itemType,this.itemId,this.shielded,); 178 | 179 | factory Shield.fromJson(Map srcJson) => _$ShieldFromJson(srcJson); 180 | 181 | Map toJson() => _$ShieldToJson(this); 182 | 183 | } 184 | 185 | 186 | -------------------------------------------------------------------------------- /lib/model/PersonalInfoBean.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'PersonalInfoBean.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PersonalInfoBean _$PersonalInfoBeanFromJson(Map json) { 10 | return PersonalInfoBean( 11 | json['tabInfo'] == null 12 | ? null 13 | : TabInfo.fromJson(json['tabInfo'] as Map), 14 | json['pgcInfo'] == null 15 | ? null 16 | : PgcInfo.fromJson(json['pgcInfo'] as Map)); 17 | } 18 | 19 | Map _$PersonalInfoBeanToJson(PersonalInfoBean instance) => 20 | {'tabInfo': instance.tabInfo, 'pgcInfo': instance.pgcInfo}; 21 | 22 | TabInfo _$TabInfoFromJson(Map json) { 23 | return TabInfo( 24 | (json['tabList'] as List) 25 | ?.map((e) => 26 | e == null ? null : TabList.fromJson(e as Map)) 27 | ?.toList(), 28 | json['defaultIdx'] as int); 29 | } 30 | 31 | Map _$TabInfoToJson(TabInfo instance) => { 32 | 'tabList': instance.tabList, 33 | 'defaultIdx': instance.defaultIdx 34 | }; 35 | 36 | TabList _$TabListFromJson(Map json) { 37 | return TabList( 38 | json['id'] as int, 39 | json['name'] as String, 40 | json['apiUrl'] as String, 41 | json['tabType'] as int, 42 | json['nameType'] as int); 43 | } 44 | 45 | Map _$TabListToJson(TabList instance) => { 46 | 'id': instance.id, 47 | 'name': instance.name, 48 | 'apiUrl': instance.apiUrl, 49 | 'tabType': instance.tabType, 50 | 'nameType': instance.nameType 51 | }; 52 | 53 | PgcInfo _$PgcInfoFromJson(Map json) { 54 | return PgcInfo( 55 | json['dataType'] as String, 56 | json['id'] as int, 57 | json['icon'] as String, 58 | json['name'] as String, 59 | json['brief'] as String, 60 | json['description'] as String, 61 | json['actionUrl'] as String, 62 | json['area'] as String, 63 | json['gender'] as String, 64 | json['registDate'] as int, 65 | json['followCount'] as int, 66 | json['follow'] == null 67 | ? null 68 | : Follow.fromJson(json['follow'] as Map), 69 | json['self'] as bool, 70 | json['cover'] as String, 71 | json['videoCount'] as int, 72 | json['shareCount'] as int, 73 | json['collectCount'] as int, 74 | json['myFollowCount'] as int, 75 | json['medalsNum'] as int, 76 | json['shield'] == null 77 | ? null 78 | : Shield.fromJson(json['shield'] as Map), 79 | json['expert'] as bool); 80 | } 81 | 82 | Map _$PgcInfoToJson(PgcInfo instance) => { 83 | 'dataType': instance.dataType, 84 | 'id': instance.id, 85 | 'icon': instance.icon, 86 | 'name': instance.name, 87 | 'brief': instance.brief, 88 | 'description': instance.description, 89 | 'actionUrl': instance.actionUrl, 90 | 'area': instance.area, 91 | 'gender': instance.gender, 92 | 'registDate': instance.registDate, 93 | 'followCount': instance.followCount, 94 | 'follow': instance.follow, 95 | 'self': instance.self, 96 | 'cover': instance.cover, 97 | 'videoCount': instance.videoCount, 98 | 'shareCount': instance.shareCount, 99 | 'collectCount': instance.collectCount, 100 | 'myFollowCount': instance.myFollowCount, 101 | 'medalsNum': instance.medalsNum, 102 | 'shield': instance.shield, 103 | 'expert': instance.expert 104 | }; 105 | 106 | Follow _$FollowFromJson(Map json) { 107 | return Follow(json['itemType'] as String, json['itemId'] as int, 108 | json['followed'] as bool); 109 | } 110 | 111 | Map _$FollowToJson(Follow instance) => { 112 | 'itemType': instance.itemType, 113 | 'itemId': instance.itemId, 114 | 'followed': instance.followed 115 | }; 116 | 117 | Shield _$ShieldFromJson(Map json) { 118 | return Shield(json['itemType'] as String, json['itemId'] as int, 119 | json['shielded'] as bool); 120 | } 121 | 122 | Map _$ShieldToJson(Shield instance) => { 123 | 'itemType': instance.itemType, 124 | 'itemId': instance.itemId, 125 | 'shielded': instance.shielded 126 | }; 127 | -------------------------------------------------------------------------------- /lib/service/ERAppConfig.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_extension_read/service/redux/ThemeRedux.dart'; 3 | import 'package:flutter_extension_read/view/widget/FullButton.dart'; 4 | import 'package:redux/redux.dart'; 5 | /** 6 | * Created by toeii 7 | * Date: 2019-01-16 8 | */ 9 | ///全局配置类 10 | class ERAppConfig { 11 | 12 | static final String BASE_URL = "http://baobab.kaiyanapp.com/api/v4/"; 13 | static final String BASE_URL_V5 = "http://baobab.kaiyanapp.com/api/v5/"; 14 | static final String BASE_URL_V6 = "http://baobab.kaiyanapp.com/api/v6/"; 15 | static final String DEF_IMAGE_URL = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3916306502,1143475951&fm=26&gp=0.jpg"; 16 | 17 | static const String PLATFORM_VERSION = "1.0"; 18 | 19 | static const THEME_COLOR = "theme-color"; 20 | 21 | ///===============Theme============== 22 | 23 | static const int primaryValue = 0xFF2196F3; 24 | static const int primaryLightValue = 0xFFBBDEFB; 25 | static const int primaryDarkValue = 0xFF1976D2; 26 | 27 | static List getThemeListColor() { 28 | return [ 29 | primarySwatch, 30 | Colors.brown, 31 | Colors.deepPurple, 32 | Colors.teal, 33 | Colors.amber, 34 | Colors.blueGrey, 35 | Colors.deepOrange, 36 | ]; 37 | } 38 | 39 | static const MaterialColor primarySwatch = const MaterialColor( 40 | primaryValue, 41 | const { 42 | 50: const Color(primaryLightValue), 43 | 100: const Color(primaryLightValue), 44 | 200: const Color(primaryLightValue), 45 | 300: const Color(primaryLightValue), 46 | 400: const Color(primaryLightValue), 47 | 500: const Color(primaryValue), 48 | 600: const Color(primaryDarkValue), 49 | 700: const Color(primaryDarkValue), 50 | 800: const Color(primaryDarkValue), 51 | 900: const Color(primaryDarkValue), 52 | }, 53 | ); 54 | 55 | static pushTheme(Store store, int index) { 56 | ThemeData themeData; 57 | List colors = getThemeListColor(); 58 | themeData = new ThemeData(primarySwatch: colors[index], platform: TargetPlatform.iOS); 59 | store.dispatch(new RefreshThemeDataAction(themeData)); 60 | } 61 | 62 | static Future showCommitOptionDialog( 63 | BuildContext context, 64 | List commitMaps, 65 | ValueChanged onTap, { 66 | width = 250.0, 67 | height = 400.0, 68 | List colorList, 69 | }) { 70 | return showDialog( 71 | context: context, 72 | builder: (BuildContext context) { 73 | return Center( 74 | child: new Container( 75 | width: width, 76 | height: height, 77 | padding: new EdgeInsets.all(4.0), 78 | margin: new EdgeInsets.all(20.0), 79 | decoration: new BoxDecoration( 80 | color: Color(0xFFFFFFFF), 81 | borderRadius: BorderRadius.all(Radius.circular(4.0)), 82 | ), 83 | child: new ListView.builder( 84 | itemCount: commitMaps.length, 85 | itemBuilder: (context, index) { 86 | return FullButton( 87 | maxLines: 2, 88 | mainAxisAlignment: MainAxisAlignment.start, 89 | fontSize: 14.0, 90 | color: colorList != null ? colorList[index] : Theme.of(context).primaryColor, 91 | text: commitMaps[index], 92 | textColor: Color(0xFFFFFFFF), 93 | onPress: () { 94 | Navigator.pop(context); 95 | onTap(index); 96 | }, 97 | ); 98 | }), 99 | ), 100 | ); 101 | }); 102 | } 103 | 104 | 105 | } -------------------------------------------------------------------------------- /lib/service/database/DatabaseHelper.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:async'; 3 | 4 | import 'package:flutter_extension_read/model/BrowseRecordBean.dart'; 5 | import 'package:sqflite/sqflite.dart'; 6 | import 'package:path/path.dart'; 7 | /** 8 | * Created by toeii 9 | * Date: 2019-01-16 10 | */ 11 | ///DB 12 | class DatabaseHelper { 13 | static final DatabaseHelper _instance = new DatabaseHelper.internal(); 14 | 15 | factory DatabaseHelper() => _instance; 16 | 17 | static Database _db; 18 | 19 | DatabaseHelper.internal(); 20 | 21 | Future get db async { 22 | if (_db != null) { 23 | return _db; 24 | } 25 | _db = await initDb(); 26 | 27 | return _db; 28 | } 29 | 30 | initDb() async { 31 | String databasesPath = await getDatabasesPath(); 32 | String path = join(databasesPath, 'Logs.db'); 33 | 34 | // await deleteDatabase(path); // just for testing 35 | 36 | var db = await openDatabase(path, version: 1, onCreate: _onCreate); 37 | return db; 38 | } 39 | 40 | void _onCreate(Database db, int newVersion) async { 41 | await db.execute( 42 | 'CREATE TABLE $tableNote($columnId INTEGER PRIMARY KEY AUTOINCREMENT, $pointId TEXT, $title TEXT, $content TEXT, $url TEXT, $image TEXT)'); 43 | } 44 | 45 | Future close() async { 46 | var dbClient = await db; 47 | return dbClient.close(); 48 | } 49 | 50 | 51 | ///===========Note=========== 52 | final String tableNote = 'VisitorLogTable'; 53 | final String columnId = 'id'; 54 | final String pointId = 'pointId'; 55 | final String title = 'title'; 56 | final String content = 'content'; 57 | final String url = 'url'; 58 | final String image = 'image'; 59 | 60 | Future saveNote(BrowseRecordBean note) async { 61 | var dbClient = await db; 62 | var result = await dbClient.insert(tableNote, note.toMap()); 63 | // var result = await dbClient.rawInsert( 64 | // 'INSERT INTO $tableNote ($columnTitle, $columnDescription) VALUES (\'${note.title}\', \'${note.description}\')'); 65 | 66 | return result; 67 | } 68 | 69 | Future getAllNotes() async { 70 | var dbClient = await db; 71 | // var result = await dbClient.query(tableNote, columns: [ 72 | // columnId, pointId, title, content, url, image]); 73 | var result = await dbClient.rawQuery('SELECT * FROM $tableNote'); 74 | 75 | return result.toList(); 76 | } 77 | 78 | Future getCount() async { 79 | var dbClient = await db; 80 | return Sqflite.firstIntValue( 81 | await dbClient.rawQuery('SELECT COUNT(*) FROM $tableNote')); 82 | } 83 | 84 | Future getNote(int id) async { 85 | var dbClient = await db; 86 | List result = await dbClient.query(tableNote, 87 | columns: [ 88 | columnId, 89 | pointId, 90 | title, 91 | content, 92 | url, 93 | image, 94 | ], 95 | where: '$columnId = ?', 96 | whereArgs: [id]); 97 | // var result = await dbClient.rawQuery('SELECT * FROM $tableNote WHERE $columnId = $id'); 98 | 99 | if (result.length > 0) { 100 | return new BrowseRecordBean.fromMap(result.first); 101 | } 102 | 103 | return null; 104 | } 105 | 106 | Future deleteNote(String id) async { 107 | var dbClient = await db; 108 | return await dbClient.delete( 109 | tableNote, where: '$columnId = ?', whereArgs: [id]); 110 | // return await dbClient.rawDelete('DELETE FROM $tableNote WHERE $columnId = $id'); 111 | } 112 | 113 | Future cleanNote() async { 114 | var dbClient = await db; 115 | return await dbClient.delete(tableNote); 116 | // return await dbClient.rawDelete('DELETE FROM $tableNote WHERE $columnId = $id'); 117 | } 118 | 119 | Future updateNote(BrowseRecordBean note) async { 120 | var dbClient = await db; 121 | return await dbClient.update( 122 | tableNote, note.toMap(), where: "$columnId = ?", whereArgs: [note.id]); 123 | // return await dbClient.rawUpdate( 124 | // 'UPDATE $tableNote SET $columnTitle = \'${note.title}\', $columnDescription = \'${note.description}\' WHERE $columnId = ${note.id}'); 125 | } 126 | 127 | ///===========other=========== 128 | 129 | 130 | } -------------------------------------------------------------------------------- /lib/service/net/ERAppHttpClient.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 3 | /** 4 | * Created by toeii 5 | * Date: 2019-01-16 6 | */ 7 | ///网络处理类 8 | class ERAppHttpClient { 9 | static const String GET = "get"; 10 | static const String POST = "post"; 11 | 12 | static void get(String url, Function callBack, 13 | {Map params, Function errorCallBack}) async { 14 | _request(url, callBack, 15 | method: GET, params: params, errorCallBack: errorCallBack); 16 | } 17 | 18 | static void post(String url, Function callBack, 19 | {Map params, Function errorCallBack}) async { 20 | _request(url, callBack, 21 | method: POST, params: params, errorCallBack: errorCallBack); 22 | } 23 | 24 | static void _request(String url, Function callBack, 25 | {String method, 26 | Map params, 27 | Function errorCallBack}) async { 28 | print(" url :<" + method + ">" + url); 29 | 30 | if (params != null && params.isNotEmpty) { 31 | print(" params :" + params.toString()); 32 | } 33 | 34 | String errorMsg = ""; 35 | int statusCode; 36 | 37 | try { 38 | Response response; 39 | 40 | Options options = new Options( 41 | headers: { 42 | 'Accept': '*/*', 43 | 'Accept-Encoding': 'gzip, deflate, br', 44 | 'Accept-Language': 'zh-CN,zh;q=0.9', 45 | 'Connection': 'keep-alive', 46 | 'User-Agent': 47 | 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36', 48 | }, 49 | ); 50 | Dio dio = new Dio(options); 51 | 52 | if (method == GET) { 53 | if (params != null && params.isNotEmpty) { 54 | StringBuffer sb = new StringBuffer("?"); 55 | params.forEach((key, value) { 56 | sb.write("$key" + "=" + "$value" + "&"); 57 | }); 58 | String paramStr = sb.toString(); 59 | paramStr = paramStr.substring(0, paramStr.length - 1); 60 | url += paramStr; 61 | } 62 | response = await dio.get(url); 63 | } else { 64 | if (params != null && params.isNotEmpty) { 65 | response = await dio.post(url, data: params); 66 | } else { 67 | response = await dio.post(url); 68 | } 69 | } 70 | 71 | statusCode = response.statusCode; 72 | 73 | if (statusCode < 0) { 74 | errorMsg = " error code:" + statusCode.toString(); 75 | _handError(errorCallBack, errorMsg); 76 | return; 77 | } 78 | 79 | if (callBack != null && statusCode == 200) { 80 | callBack(response.data); 81 | print(" response data:" + response.data); 82 | } 83 | } catch (exception) { 84 | _handError(errorCallBack, exception.toString()); 85 | } 86 | } 87 | 88 | static void _handError(Function errorCallback, String errorMsg) { 89 | if (errorCallback != null) { 90 | errorCallback(errorMsg); 91 | } 92 | print(" errorMsg :" + errorMsg); 93 | } 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /lib/service/redux/ERAppState.dart: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by toeii 3 | * Date: 2019-01-16 4 | */ 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_extension_read/service/redux/ThemeRedux.dart'; 7 | /** 8 | * Created by toeii 9 | * Date: 2019-01-16 10 | */ 11 | ///全局Redux 12 | class ERAppState { 13 | 14 | ///主题数据 15 | ThemeData themeData; 16 | 17 | ERAppState({this.themeData}); 18 | 19 | } 20 | 21 | ERAppState appReducer(ERAppState state, action) { 22 | return ERAppState( 23 | themeData: ThemeDataReducer(state.themeData, action), 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/service/redux/ThemeRedux.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:redux/redux.dart'; 3 | 4 | /** 5 | * Created by toeii 6 | * Date: 2019-01-16 7 | */ 8 | ///主题Redux 9 | final ThemeDataReducer = combineReducers([ 10 | TypedReducer(_refresh), 11 | ]); 12 | 13 | ThemeData _refresh(ThemeData themeData, action) { 14 | themeData = action.themeData; 15 | return themeData; 16 | } 17 | 18 | class RefreshThemeDataAction { 19 | final ThemeData themeData; 20 | RefreshThemeDataAction(this.themeData); 21 | } 22 | -------------------------------------------------------------------------------- /lib/service/storage/LocalStorage.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | /** 3 | * Created by toeii 4 | * Date: 2019-01-16 5 | */ 6 | ///SharedPreferences 7 | class LocalStorage { 8 | 9 | static save(String key, value) async { 10 | SharedPreferences prefs = await SharedPreferences.getInstance(); 11 | prefs.setString(key, value); 12 | } 13 | 14 | static get(String key) async { 15 | SharedPreferences prefs = await SharedPreferences.getInstance(); 16 | return prefs.get(key); 17 | } 18 | 19 | static remove(String key) async { 20 | SharedPreferences prefs = await SharedPreferences.getInstance(); 21 | prefs.remove(key); 22 | } 23 | } -------------------------------------------------------------------------------- /lib/view/page/BrowseRecordPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_extension_read/model/BrowseRecordBean.dart'; 6 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 7 | import 'package:flutter_extension_read/service/database/DatabaseHelper.dart'; 8 | import 'package:flutter_extension_read/view/page/WebLoadPage.dart'; 9 | import 'package:flutter_extension_read/view/widget/EasyListView.dart'; 10 | import 'package:flutter_extension_read/view/widget/NotEmptyText.dart'; 11 | /** 12 | * Created by toeii 13 | * Date: 2019-01-16 14 | */ 15 | ///浏览记录 16 | class BrowseRecordPage extends StatefulWidget { 17 | 18 | @override 19 | _BrowseRecordPageState createState() => _BrowseRecordPageState(); 20 | 21 | } 22 | 23 | class _BrowseRecordPageState extends State { 24 | 25 | int page = 0; 26 | int itemCount = 20; 27 | bool hasNextPage = true; 28 | bool isLoadData = true; 29 | var foregroundWidget = Container( alignment: AlignmentDirectional.center, child: CircularProgressIndicator()); 30 | 31 | List datas = []; 32 | 33 | @override 34 | bool get wantKeepAlive => true; 35 | 36 | DatabaseHelper _databaseHelper; 37 | 38 | @override 39 | void initState() { 40 | super.initState(); 41 | _databaseHelper = new DatabaseHelper(); 42 | initData(); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return new WillPopScope( 48 | child: new Scaffold( 49 | appBar: new AppBar( 50 | title: new Text('浏览记录'), 51 | centerTitle: true, 52 | ), 53 | body: new Container( 54 | color: Colors.white, 55 | child: RefreshIndicator( 56 | onRefresh: _refresh, 57 | child: EasyListView( 58 | itemCount: datas.length, 59 | itemBuilder: getItemBuilder, 60 | loadMore: hasNextPage, 61 | onLoadMore: _requestMoreData, 62 | footerBuilder: isLoadData?null:footerBuilder, 63 | loadMoreWhenNoData: true, 64 | foregroundWidget: isLoadData?foregroundWidget:null, 65 | ), 66 | ), 67 | )), 68 | onWillPop:() { 69 | Navigator.pop(context); 70 | }, 71 | ); 72 | 73 | } 74 | 75 | Future _refresh() async { 76 | if (null != datas && datas.length > 0) { 77 | datas.clear(); 78 | } 79 | page = 1; 80 | initData(); 81 | return; 82 | } 83 | 84 | _requestMoreData() { 85 | // page++; 86 | // initData(); 87 | setState(() { 88 | hasNextPage = false; 89 | }); 90 | } 91 | 92 | var footerBuilder = (context) => Container( 93 | height: 30.0, 94 | alignment: AlignmentDirectional.center, 95 | child: Text( 96 | "没有更多了", 97 | style: TextStyle( 98 | fontSize: 14.0, 99 | color: Colors.grey, 100 | ), 101 | ), 102 | ); 103 | 104 | 105 | Future initData() async { 106 | List Notes; 107 | await _databaseHelper.getAllNotes().then((value) => { 108 | Notes = value, 109 | }); 110 | 111 | List browseRecordList = []; 112 | for (var childrenItem in Notes){ 113 | browseRecordList.add(new BrowseRecordBean( 114 | BrowseRecordBean.fromMap(childrenItem).id, 115 | BrowseRecordBean.fromMap(childrenItem).pointId, 116 | BrowseRecordBean.fromMap(childrenItem).title, 117 | BrowseRecordBean.fromMap(childrenItem).content, 118 | BrowseRecordBean.fromMap(childrenItem).url, 119 | BrowseRecordBean.fromMap(childrenItem).image)); 120 | } 121 | 122 | if (null != browseRecordList) { 123 | if(page>1){ 124 | setState(() { 125 | hasNextPage = browseRecordList.length > 0; 126 | datas += browseRecordList; 127 | isLoadData = false; 128 | }); 129 | }else{ 130 | setState(() { 131 | datas = browseRecordList; 132 | isLoadData = false; 133 | }); 134 | } 135 | } 136 | } 137 | 138 | 139 | Widget getItemBuilder(BuildContext context,int index) { 140 | return new GestureDetector( 141 | onTap: () { 142 | Navigator.push( 143 | context, 144 | new MaterialPageRoute(builder: (context) => new WebLoadPage(title:datas[index].title,url:datas[index].url)), 145 | ); 146 | }, 147 | child: new Container( 148 | alignment: AlignmentDirectional.center, 149 | child: new Container( 150 | height: 120, 151 | width: window.physicalSize.width, 152 | padding: const EdgeInsets.fromLTRB(5, 5, 5, 5), 153 | child: new Column( 154 | crossAxisAlignment: CrossAxisAlignment.start, 155 | mainAxisSize: MainAxisSize.max, 156 | mainAxisAlignment: MainAxisAlignment.center, 157 | children: [ 158 | new Row( 159 | children: [ 160 | new Container( 161 | margin: const EdgeInsets.fromLTRB(5, 0, 5, 0), 162 | width: 88, 163 | height: 68, 164 | decoration: new BoxDecoration( 165 | shape: BoxShape.rectangle, 166 | borderRadius: new BorderRadius.circular(4.0), 167 | image: new DecorationImage( 168 | image: new NetworkImage(null!=datas[index].image?datas[index].image:ERAppConfig.DEF_IMAGE_URL), 169 | fit: BoxFit.cover), 170 | ), 171 | ), 172 | 173 | new Container( 174 | height: 90, 175 | width: window.physicalSize.width/3.8, 176 | margin: const EdgeInsets.fromLTRB(5, 0, 0, 0), 177 | child:new Column( 178 | crossAxisAlignment: CrossAxisAlignment.start, 179 | mainAxisSize: MainAxisSize.max, 180 | mainAxisAlignment: MainAxisAlignment.center, 181 | children: [ 182 | new NotEmptyText(null!=datas[index].title?datas[index].title:'未知标题', 183 | style: new TextStyle( 184 | color: Colors.black, 185 | fontSize: 16, 186 | decoration: TextDecoration.none, 187 | ), 188 | maxLines:2, 189 | textAlign: TextAlign.left, 190 | overflow: TextOverflow.ellipsis, 191 | ), 192 | new NotEmptyText(null!=datas[index].content?datas[index].content:'...', 193 | style: new TextStyle( 194 | color: Colors.grey, 195 | fontSize: 14, 196 | decoration: TextDecoration.none, 197 | ), 198 | maxLines:2, 199 | textAlign: TextAlign.left, 200 | overflow: TextOverflow.ellipsis, 201 | ), 202 | ], 203 | ), 204 | ), 205 | ], 206 | ), 207 | new Divider(), 208 | ], 209 | ), 210 | ), 211 | ), 212 | ); 213 | 214 | } 215 | 216 | 217 | } 218 | -------------------------------------------------------------------------------- /lib/view/page/HomeDailyPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:ui'; 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_extension_read/model/BrowseRecordBean.dart'; 7 | import 'package:flutter_extension_read/model/HomeDailyBean.dart'; 8 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 9 | import 'package:flutter_extension_read/service/database/DatabaseHelper.dart'; 10 | import 'package:flutter_extension_read/service/net/ERAppHttpClient.dart'; 11 | import 'package:flutter_extension_read/view/page/PaperDetailPage.dart'; 12 | import 'package:flutter_extension_read/view/page/PersonalPage.dart'; 13 | import 'package:flutter_extension_read/view/page/WebLoadPage.dart'; 14 | import 'package:flutter_extension_read/view/widget/EasyListView.dart'; 15 | import 'package:flutter_extension_read/view/widget/NotEmptyText.dart'; 16 | /** 17 | * Created by toeii 18 | * Date: 2019-01-16 19 | */ 20 | ///日报 21 | class HomeDailyPage extends StatefulWidget { 22 | 23 | @override 24 | _HomeDailyPageState createState() => _HomeDailyPageState(); 25 | 26 | } 27 | 28 | class _HomeDailyPageState extends State with AutomaticKeepAliveClientMixin{ 29 | 30 | Size _sizeWH = window.physicalSize; 31 | 32 | int page = 0; 33 | bool hasNextPage = true; 34 | bool isLoadData = true; 35 | var foregroundWidget = Container( alignment: AlignmentDirectional.center, child: CircularProgressIndicator()); 36 | 37 | List datas = []; 38 | 39 | @override 40 | bool get wantKeepAlive => true; 41 | 42 | DatabaseHelper _databaseHelper; 43 | 44 | @override 45 | void initState() { 46 | super.initState(); 47 | _databaseHelper = new DatabaseHelper(); 48 | initData(); 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | return new Container( 54 | child: RefreshIndicator( 55 | onRefresh: _refresh, 56 | child: EasyListView( 57 | itemCount: datas.length, 58 | itemBuilder: getLvItemView, 59 | loadMore: hasNextPage, 60 | onLoadMore: _requestMoreData, 61 | footerBuilder: isLoadData?null:footerBuilder, 62 | foregroundWidget: isLoadData?foregroundWidget:null, 63 | ), 64 | ), 65 | ); 66 | } 67 | 68 | Future _refresh() async { 69 | if (null != datas && datas.length > 0) { 70 | datas.clear(); 71 | } 72 | page = 1; 73 | initData(); 74 | return; 75 | } 76 | 77 | _requestMoreData() { 78 | // page++; 79 | // initData(); 80 | setState(() { 81 | hasNextPage = false; 82 | }); 83 | } 84 | 85 | var footerBuilder = (context) => Container( 86 | height: 30.0, 87 | alignment: AlignmentDirectional.center, 88 | child: Text( 89 | "没有更多了", 90 | style: TextStyle( 91 | fontSize: 14.0, 92 | color: Colors.grey, 93 | ), 94 | ), 95 | ); 96 | 97 | 98 | void initData(){ 99 | String requestUrl = ERAppConfig.BASE_URL_V5 + "/index/tab/feed?udid=55b862f0d6714f609bd6e45947f8789f0ff90f48&date="+new DateTime.now().millisecondsSinceEpoch.toString()+"&num="+page.toString(); 100 | ERAppHttpClient.get(requestUrl, (data) { 101 | if(null != data) { 102 | HomeDailyBean homeDailyBean = HomeDailyBean.fromJson(data); 103 | if (null != homeDailyBean) { 104 | if(page>1){ 105 | setState(() { 106 | hasNextPage = homeDailyBean.itemList.length > 0; 107 | datas += homeDailyBean.itemList; 108 | isLoadData = false; 109 | }); 110 | }else{ 111 | setState(() { 112 | datas = homeDailyBean.itemList; 113 | isLoadData = false; 114 | }); 115 | } 116 | } 117 | } 118 | }); 119 | } 120 | 121 | Widget getLvItemView(BuildContext context,int index) { 122 | if(datas.length==0)return null; 123 | 124 | if(null != datas[index].data && datas[index].data.dataType == "FollowCard"){ 125 | return new Container( 126 | alignment: AlignmentDirectional.center, 127 | margin:const EdgeInsets.fromLTRB(10,10,10,0), 128 | width: _sizeWH.width, 129 | child:new Column( 130 | children: [ 131 | 132 | new GestureDetector( 133 | onTap: () { 134 | if(null != datas[index].data.content.data.author){ 135 | Navigator.push( 136 | context, 137 | new MaterialPageRoute( 138 | builder: (context) => new PersonalPage(id:datas[index].data.content.data.author.id)), 139 | ); 140 | } 141 | }, 142 | child:new Row( 143 | children: [ 144 | new Container( 145 | margin:const EdgeInsets.fromLTRB(0,0,10,10), 146 | width: 56, 147 | height: 56, 148 | decoration: new BoxDecoration( 149 | shape: BoxShape.circle, 150 | image: new DecorationImage( 151 | image: new NetworkImage(null!=datas[index].data.content.data.author?datas[index].data.content.data.author.icon:ERAppConfig.DEF_IMAGE_URL), 152 | fit: BoxFit.cover), 153 | ), 154 | ), 155 | new Container( 156 | width: _sizeWH.width/3.5, 157 | height: 70, 158 | child: new Column( 159 | crossAxisAlignment: CrossAxisAlignment.start, 160 | mainAxisAlignment: MainAxisAlignment.start, 161 | children: [ 162 | new Container( 163 | height: 5, 164 | ), 165 | new NotEmptyText( 166 | null!=datas[index].data.content.data.author?datas[index].data.content.data.author.name:"用户10000", 167 | maxLines: 1, 168 | overflow: TextOverflow.ellipsis, 169 | style: new TextStyle( 170 | color: Colors.black, 171 | fontSize: 16, 172 | fontWeight: FontWeight.bold, 173 | ) 174 | ), 175 | new NotEmptyText( 176 | "发布:"+(null!=datas[index].data.content.data.title?datas[index].data.content.data.title:""), 177 | maxLines: 2, 178 | overflow: TextOverflow.ellipsis, 179 | style: new TextStyle( 180 | color: Colors.black87, 181 | fontSize: 14, 182 | ) 183 | ), 184 | ], 185 | ), 186 | ) 187 | ], 188 | ) 189 | ), 190 | 191 | new GestureDetector( 192 | onTap: () { 193 | 194 | _databaseHelper.saveNote(new BrowseRecordBean(datas[index].data.content.data.id,datas[index].data.content.data.id.toString(),datas[index].data.content.data.title,datas[index].data.content.data.description,datas[index].data.content.data.webUrl.raw,datas[index].data.content.data.cover.feed)); 195 | 196 | Navigator.push( 197 | context, 198 | new MaterialPageRoute(builder: (context) => new PaperDetailPage( 199 | id:datas[index].data.content.data.id.toString(), 200 | playUrl:datas[index].data.content.data.playUrl, 201 | title:datas[index].data.content.data.title, 202 | type:"#"+datas[index].data.content.data.category, 203 | desc:datas[index].data.content.data.description, 204 | authorId:datas[index].data.content.data.id.toString(), 205 | authorIcon:datas[index].data.content.data.author.icon, 206 | authorName:datas[index].data.content.data.author.name, 207 | authorDesc:datas[index].data.content.data.author.description, 208 | )), 209 | ); 210 | 211 | }, 212 | child: new Column(children: [ 213 | new NotEmptyText( 214 | null != datas[index].data.content.data.description?datas[index].data.content.data.description:"", 215 | softWrap: true, 216 | maxLines: 3, 217 | overflow: TextOverflow.ellipsis, 218 | style: new TextStyle( 219 | color: Colors.black54, 220 | fontSize: 13, 221 | )), 222 | new Container( 223 | width: _sizeWH.width, 224 | height: 240, 225 | margin:const EdgeInsets.fromLTRB(0,10,0,0), 226 | decoration: new BoxDecoration( 227 | color: Color(0xFFF3F3F5), 228 | shape: BoxShape.rectangle, 229 | borderRadius: new BorderRadius.circular(4.0), 230 | image: new DecorationImage( 231 | image: new NetworkImage(null != datas[index].data.content.data.cover?datas[index].data.content.data.cover.feed:ERAppConfig.DEF_IMAGE_URL), 232 | fit: BoxFit.cover), 233 | ), 234 | ), 235 | ], 236 | ) 237 | ), 238 | 239 | new Divider(), 240 | ], 241 | ), 242 | ); 243 | }else{ 244 | return new Container( 245 | height: 0, 246 | ); 247 | } 248 | 249 | } 250 | 251 | 252 | 253 | } -------------------------------------------------------------------------------- /lib/view/page/HomePage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_extension_read/service/database/DatabaseHelper.dart'; 3 | import 'package:flutter_extension_read/view/page/BrowseRecordPage.dart'; 4 | import 'package:flutter_extension_read/view/page/HomeDailyPage.dart'; 5 | import 'package:flutter_extension_read/view/page/HomeRecommendPage.dart'; 6 | import 'package:flutter_extension_read/view/page/WebLoadPage.dart'; 7 | import 'package:flutter_extension_read/view/widget/HomeDrawer.dart'; 8 | import 'package:fluttertoast/fluttertoast.dart'; 9 | /** 10 | * Created by toeii 11 | * Date: 2019-01-16 12 | */ 13 | ///首页 14 | class HomePage extends StatefulWidget { 15 | 16 | @override 17 | _HomePageState createState() => _HomePageState(); 18 | 19 | } 20 | 21 | class _HomePageState extends State with SingleTickerProviderStateMixin{ 22 | 23 | TabController _tabController; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | _tabController = new TabController(length: 2, vsync: this); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | _tabController.dispose(); 34 | super.dispose(); 35 | } 36 | 37 | TabBarView getTabBarView(var tabs) { 38 | return new TabBarView( 39 | children: tabs, 40 | controller: _tabController, 41 | ); 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return new Scaffold( 47 | appBar: new PreferredSize( 48 | child: new AppBar( 49 | title:new TabBar( 50 | tabs: [ 51 | new Tab(text: "推荐", ), 52 | new Tab(text: "日报",), 53 | ], 54 | controller: _tabController, 55 | isScrollable: true, 56 | ), 57 | ), 58 | preferredSize: Size.fromHeight(38), 59 | ), 60 | 61 | body: getTabBarView([ 62 | new HomeRecommendPage(), 63 | new HomeDailyPage(), 64 | ]), 65 | 66 | 67 | drawer: new HomeDrawer(), 68 | 69 | ); 70 | } 71 | 72 | 73 | } 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /lib/view/page/MainPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 3 | import 'package:flutter_extension_read/view/page/CommunityPage.dart'; 4 | import 'package:flutter_extension_read/view/page/HomePage.dart'; 5 | import 'package:flutter_extension_read/view/page/UserPage.dart'; 6 | import 'package:sqflite/sqflite.dart'; 7 | 8 | class MainPage extends StatefulWidget { 9 | MainPage({Key key}) : super(key: key); 10 | 11 | @override 12 | _MainPageState createState() => _MainPageState(); 13 | 14 | } 15 | 16 | class _MainPageState extends State { 17 | 18 | int _pageIndex = 0; 19 | List _bodyPages = []; 20 | 21 | String _platformVersion = ERAppConfig.PLATFORM_VERSION; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _bodyPages 27 | ..add(new HomePage()) 28 | ..add(new CommunityPage()) 29 | ..add(new UserPage()); 30 | 31 | initPlatformState(); 32 | 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | 38 | return Scaffold( 39 | body: Stack( 40 | children: [ 41 | new Offstage( 42 | offstage: _pageIndex!=0, 43 | child: _bodyPages[0], 44 | ), 45 | new Offstage( 46 | offstage: _pageIndex!=1, 47 | child: _bodyPages[1], 48 | ), 49 | new Offstage( 50 | offstage: _pageIndex!=2, 51 | child: _bodyPages[2], 52 | ), 53 | ], 54 | ), 55 | // body:_bodyPages[_pageIndex], 56 | bottomNavigationBar: 57 | Container( 58 | height: 55, 59 | child: new BottomNavigationBar( 60 | items: [ 61 | new BottomNavigationBarItem( 62 | icon:new Icon(Icons.home, size: 24.0),title:new Text("首页",style : new TextStyle(fontSize: 10,)) 63 | ), 64 | new BottomNavigationBarItem( 65 | icon:new Icon(Icons.group, size: 24.0),title:new Text("社区",style : new TextStyle(fontSize: 10,)) 66 | ), 67 | new BottomNavigationBarItem( 68 | icon:new Icon(Icons.person, size: 24.0),title:new Text("我的",style : new TextStyle(fontSize: 10,)) 69 | ), 70 | ], 71 | type: BottomNavigationBarType.fixed, 72 | currentIndex: _pageIndex, 73 | onTap: (index) { 74 | setState(() { 75 | _pageIndex = index; 76 | }); 77 | }, 78 | ), 79 | ), 80 | ); 81 | 82 | } 83 | 84 | Future initPlatformState() async { 85 | String platformVersion; 86 | try { 87 | platformVersion = await Sqflite.platformVersion; 88 | } on Exception { 89 | platformVersion = "Failed to get platform version"; 90 | } 91 | if (!mounted) return; 92 | 93 | setState(() { 94 | _platformVersion = platformVersion; 95 | }); 96 | 97 | print("running on: " + _platformVersion); 98 | } 99 | 100 | 101 | } 102 | 103 | -------------------------------------------------------------------------------- /lib/view/page/SplashPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | 4 | import 'package:flutter_extension_read/view/page/MainPage.dart'; 5 | 6 | /** 7 | * Created by toeii 8 | * Date: 2019-01-16 9 | */ 10 | ///开屏页(没业务处理暂时搁置) 11 | class SplashPage extends StatefulWidget{ 12 | 13 | SplashPage({Key key}):super(key:key); 14 | @override 15 | _SplashPage createState()=> new _SplashPage(); 16 | 17 | } 18 | 19 | class _SplashPage extends State{ 20 | 21 | bool isStartHomePage = false; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return new Container( 26 | child:Image.asset("images/splash_extension_read.png",fit: BoxFit.fill,), 27 | ); 28 | } 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | countDown(); 34 | } 35 | 36 | void countDown() { 37 | var duration = new Duration(seconds: 2); 38 | new Future.delayed(duration, goToHomePage); 39 | } 40 | 41 | void goToHomePage(){ 42 | if(!isStartHomePage){ 43 | Navigator.of(context).pushAndRemoveUntil( 44 | new MaterialPageRoute(builder: (context) => 45 | new MainPage()), (Route rout)=>false 46 | ); 47 | isStartHomePage=true; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/view/page/UserPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 3 | import 'package:flutter_extension_read/service/database/DatabaseHelper.dart'; 4 | import 'package:flutter_extension_read/service/redux/ERAppState.dart'; 5 | import 'package:flutter_extension_read/view/page/BrowseRecordPage.dart'; 6 | import 'package:flutter_extension_read/view/page/WebLoadPage.dart'; 7 | import 'package:flutter_extension_read/view/widget/EasyListView.dart'; 8 | import 'dart:ui'; 9 | 10 | import 'package:flutter_redux/flutter_redux.dart'; 11 | import 'package:fluttertoast/fluttertoast.dart'; 12 | import 'package:redux/redux.dart'; 13 | /** 14 | * Created by toeii 15 | * Date: 2019-01-16 16 | */ 17 | ///我的 18 | class UserPage extends StatefulWidget { 19 | 20 | @override 21 | _UserPageState createState() => _UserPageState(); 22 | 23 | } 24 | 25 | class _UserPageState extends State { 26 | 27 | static Size _sizeWH = window.physicalSize; 28 | static var items = ["项目主页","浏览记录","切换主题","关于作者","清除缓存"]; 29 | 30 | Widget itemBuilder(BuildContext context,int index) { 31 | return 32 | new GestureDetector( 33 | child: new Container( 34 | alignment: AlignmentDirectional.center, 35 | child:_getLvItemChildView(context,index), 36 | ), 37 | ); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Scaffold( 43 | appBar: new PreferredSize( 44 | child: new AppBar( 45 | flexibleSpace: FlexibleSpaceBar( 46 | centerTitle: true, 47 | title:new Container( 48 | padding:const EdgeInsets.fromLTRB(0,40,0,0), 49 | child: new Column( 50 | crossAxisAlignment: CrossAxisAlignment.center, 51 | mainAxisAlignment: MainAxisAlignment.center, 52 | mainAxisSize: MainAxisSize.max, 53 | children: [ 54 | new Container( 55 | width: 90, 56 | height: 90, 57 | child : new CircleAvatar( 58 | backgroundImage:new AssetImage("images/logo_extension_read.png"), 59 | ), 60 | ), 61 | new Container( 62 | margin:const EdgeInsets.fromLTRB(0,20,0,0), 63 | child: new Text("拓意阅读 v1.0",style: new TextStyle(fontSize: 18,color: Colors.white,fontWeight: FontWeight.bold),), 64 | ), 65 | ], 66 | ), 67 | ) 68 | ), 69 | ), 70 | preferredSize: Size.fromHeight(250), 71 | ), 72 | 73 | body: new EasyListView( 74 | // headerBuilder: headerBuilder, 75 | itemCount: items.length, 76 | itemBuilder: itemBuilder, 77 | ), 78 | 79 | ); 80 | } 81 | 82 | 83 | 84 | Widget _getLvItemChildView(BuildContext context,int index) { 85 | return 86 | new StoreBuilder( 87 | builder: (context, store) { 88 | return new GestureDetector( 89 | onTap: () { 90 | if(items[index] == "项目主页"){ 91 | Navigator.push( 92 | context, 93 | new MaterialPageRoute(builder: (context) => new WebLoadPage(title:'拓意阅读',url:'https://github.com/toeii/FlutterExampleApp_ExtensionRead')), 94 | ); 95 | }else if(items[index] == "浏览记录"){ 96 | Navigator.push( 97 | context, 98 | new MaterialPageRoute(builder: (context) => new BrowseRecordPage(),) 99 | ); 100 | }else if(items[index] == "切换主题"){ 101 | showThemeDialog(context, store); 102 | }else if(items[index] == "关于作者"){ 103 | Navigator.push( 104 | context, 105 | new MaterialPageRoute(builder: (context) => new WebLoadPage(title:'关于作者',url:'https://github.com/toeii')), 106 | ); 107 | }else if(items[index] == "清除缓存"){ 108 | new DatabaseHelper().cleanNote(); 109 | 110 | Fluttertoast.showToast( 111 | msg: "清除成功!", 112 | toastLength: Toast.LENGTH_SHORT, 113 | gravity: ToastGravity.BOTTOM, 114 | timeInSecForIos: 1, 115 | backgroundColor: Colors.black, 116 | textColor: Colors.white, 117 | fontSize: 16.0 118 | ); 119 | } 120 | }, 121 | child:new Container( 122 | height: 100, 123 | child: new Center( 124 | child: new Text(items[index],style: new TextStyle(fontSize: 16,color: Colors.grey), 125 | ), 126 | ) 127 | ), 128 | ); 129 | } 130 | ); 131 | 132 | } 133 | 134 | showThemeDialog(BuildContext context, Store store) { 135 | List list = [ 136 | "默认主题", 137 | "主题1", 138 | "主题2", 139 | "主题3", 140 | "主题4", 141 | "主题5", 142 | "主题6", 143 | ]; 144 | ERAppConfig.showCommitOptionDialog(context, list, (index) { 145 | ERAppConfig.pushTheme(store, index); 146 | }, colorList: ERAppConfig.getThemeListColor()); 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /lib/view/page/WebLoadPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | /** 4 | * Created by toeii 5 | * Date: 2019-01-16 6 | */ 7 | ///加载网页 8 | class WebLoadPage extends StatefulWidget { 9 | var title = ""; 10 | var url = ""; 11 | 12 | @override 13 | _WebLoadPageState createState() => _WebLoadPageState(); 14 | 15 | WebLoadPage({Key key,@required this.title,@required this.url}):super(key:key); 16 | 17 | } 18 | 19 | class _WebLoadPageState extends State { 20 | TextEditingController controller = TextEditingController(); 21 | FlutterWebviewPlugin flutterWebviewPlugin = FlutterWebviewPlugin(); 22 | var foregroundWidget = new Container( 23 | alignment: AlignmentDirectional.center, 24 | child:CircularProgressIndicator(), 25 | ); 26 | 27 | launchUrl() { 28 | setState(() { 29 | widget.url = controller.text; 30 | flutterWebviewPlugin.reloadUrl(widget.url); 31 | }); 32 | } 33 | 34 | @override 35 | void initState() { 36 | super.initState(); 37 | flutterWebviewPlugin.onStateChanged.listen((WebViewStateChanged wvs) { 38 | if(wvs.type.toString() == 'WebViewState.finishLoad'){ 39 | //web page finishLoad 40 | setState(() { 41 | foregroundWidget = null; 42 | }); 43 | } 44 | }); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return WebviewScaffold( 50 | appBar: AppBar( 51 | title: TextField( 52 | autofocus: false, 53 | controller: controller, 54 | textInputAction: TextInputAction.go, 55 | onSubmitted: (url) => launchUrl(), 56 | style: TextStyle(color: Colors.white), 57 | decoration: InputDecoration( 58 | border: InputBorder.none, 59 | hintText: widget.title, 60 | hintStyle: TextStyle(color: Colors.white), 61 | ), 62 | ), 63 | ), 64 | url: widget.url, 65 | withZoom: false, 66 | ); 67 | } 68 | } -------------------------------------------------------------------------------- /lib/view/widget/BarBottomDivider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /** 3 | * Created by toeii 4 | * Date: 2019-01-16 5 | */ 6 | ///处理appbar下线高度 7 | class BarBottomDivider extends Divider implements PreferredSizeWidget { 8 | BarBottomDivider({ 9 | Key key, 10 | height = 1.0, 11 | indent = 0.0, 12 | color, 13 | }) : assert(height >= 0.0), 14 | super( 15 | key: key, 16 | height: height, 17 | indent: indent, 18 | color: color, 19 | ) { 20 | preferredSize = Size(double.infinity, height); 21 | } 22 | 23 | @override 24 | Size preferredSize; 25 | } -------------------------------------------------------------------------------- /lib/view/widget/EasyListView.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | /** 6 | * Created by toeii 7 | * Date: 2019-01-16 8 | */ 9 | /// 10 | class EasyListView extends StatefulWidget { 11 | 12 | final int itemCount; 13 | final WidgetBuilder headerBuilder; 14 | final WidgetBuilder loadMoreItemBuilder; 15 | final IndexedWidgetBuilder itemBuilder; 16 | final IndexedWidgetBuilder dividerBuilder; 17 | final bool loadMore; 18 | final bool loadMoreWhenNoData; 19 | final VoidCallback onLoadMore; 20 | final ScrollPhysics physics; 21 | final ScrollController controller; 22 | final NestedScrollViewHeaderSliversBuilder headerSliverBuilder; 23 | final Widget foregroundWidget; 24 | final EdgeInsetsGeometry padding; 25 | final bool isSliverMode; 26 | final WidgetBuilder footerBuilder; 27 | 28 | EasyListView({ 29 | @required this.itemCount, 30 | @required this.itemBuilder, 31 | this.headerBuilder, 32 | this.footerBuilder, 33 | this.loadMore = false, 34 | this.onLoadMore, 35 | this.loadMoreWhenNoData = false, 36 | this.loadMoreItemBuilder, 37 | this.dividerBuilder, 38 | this.physics, 39 | this.headerSliverBuilder, 40 | this.controller, 41 | this.foregroundWidget, 42 | this.padding, 43 | this.isSliverMode = false, 44 | }) : assert(itemBuilder != null); 45 | 46 | @override 47 | State createState() { 48 | return EasyListViewState(); 49 | } 50 | 51 | 52 | } 53 | 54 | enum ItemType { header, footer, loadMore, data, dividerData } 55 | 56 | class EasyListViewState extends State { 57 | 58 | @override 59 | Widget build(BuildContext context) => widget.headerSliverBuilder != null 60 | ? NestedScrollView( 61 | headerSliverBuilder: widget.headerSliverBuilder, 62 | body: MediaQuery.removePadding( 63 | context: context, 64 | removeTop: true, 65 | child: _buildList(), 66 | ), 67 | ) 68 | : _buildList(); 69 | 70 | Widget _itemBuilder(context, index) { 71 | var headerCount = _headerCount(); 72 | var totalItemCount = _dataItemCount() + headerCount + _footerCount(); 73 | switch (_itemType(index, totalItemCount)) { 74 | case ItemType.header: 75 | return widget.headerBuilder(context); 76 | case ItemType.footer: 77 | return widget.footerBuilder(context); 78 | case ItemType.loadMore: 79 | return _buildLoadMoreItem(); 80 | case ItemType.dividerData: 81 | return _buildDividerWithData(index, index - headerCount); 82 | case ItemType.data: 83 | default: 84 | return widget.itemBuilder(context, index - headerCount); 85 | } 86 | } 87 | 88 | _buildList() { 89 | var headerCount = _headerCount(); 90 | var totalItemCount = _dataItemCount() + headerCount + _footerCount(); 91 | var children = [ 92 | widget.isSliverMode 93 | ? CustomScrollView( 94 | slivers: List.generate( 95 | totalItemCount, (index) => _itemBuilder(context, index)), 96 | ) 97 | : ListView.builder( 98 | physics: widget.physics, 99 | padding: widget.padding, 100 | controller: widget.controller, 101 | itemCount: totalItemCount, 102 | itemBuilder: _itemBuilder, 103 | ) 104 | ]; 105 | if (widget.foregroundWidget != null) children.add(widget.foregroundWidget); 106 | return Stack(children: children); 107 | } 108 | 109 | ItemType _itemType(itemIndex, totalItemCount) { 110 | if (_isHeader(itemIndex)) { 111 | return ItemType.header; 112 | } else if (_isLoadMore(itemIndex, totalItemCount)) { 113 | if ((widget.loadMoreWhenNoData || 114 | (!widget.loadMoreWhenNoData && widget.itemCount > 0)) && 115 | widget.onLoadMore != null) { 116 | Timer(Duration(milliseconds: 50), widget.onLoadMore); 117 | } 118 | return ItemType.loadMore; 119 | } else if (_isFooter(itemIndex, totalItemCount)) { 120 | return ItemType.footer; 121 | } else if (_hasDivider()) { 122 | return ItemType.dividerData; 123 | } else { 124 | return ItemType.data; 125 | } 126 | } 127 | 128 | Widget _buildLoadMoreItem() { 129 | if ((widget.loadMoreWhenNoData || 130 | (!widget.loadMoreWhenNoData && widget.itemCount > 0)) && 131 | widget.onLoadMore != null) { 132 | Timer(Duration(milliseconds: 50), widget.onLoadMore); 133 | } 134 | return widget.loadMoreItemBuilder != null 135 | ? widget.loadMoreItemBuilder(context) 136 | : widget.isSliverMode 137 | ? SliverList(delegate: SliverChildListDelegate([_defaultLoadMore])) 138 | : _defaultLoadMore; 139 | } 140 | 141 | Widget _buildDividerWithData(index, dataIndex) => index.isEven 142 | ? widget.dividerBuilder != null 143 | ? widget.dividerBuilder(context, dataIndex ~/ 2) 144 | : widget.isSliverMode 145 | ? SliverList(delegate: SliverChildListDelegate([_defaultDivider])) 146 | : _defaultDivider 147 | : widget.itemBuilder(context, dataIndex ~/ 2); 148 | 149 | bool _isHeader(itemIndex) => _hasHeader() && itemIndex == 0; 150 | 151 | bool _isLoadMore(itemIndex, total) => 152 | widget.loadMore && itemIndex == total - 1; 153 | 154 | bool _isFooter(itemIndex, total) => _hasFooter() && itemIndex == total - 1; 155 | 156 | int _headerCount() => _hasHeader() ? 1 : 0; 157 | 158 | int _footerCount() => (_hasFooter() || widget.loadMore) ? 1 : 0; 159 | 160 | int _dataItemCount() => 161 | _hasDivider() ? widget.itemCount * 2 - 1 : widget.itemCount; 162 | 163 | bool _hasDivider() => widget.dividerBuilder != null; 164 | 165 | bool _hasHeader() => widget.headerBuilder != null; 166 | 167 | bool _hasFooter() => widget.footerBuilder != null; 168 | 169 | final _defaultLoadMore = Container( 170 | padding: const EdgeInsets.all(4.0), 171 | // child: const Center( 172 | // child: const CircularProgressIndicator(), 173 | // ), 174 | ); 175 | 176 | final _defaultDivider = const Divider(color: Colors.grey); 177 | 178 | 179 | } 180 | -------------------------------------------------------------------------------- /lib/view/widget/ExpandKToolbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /** 3 | * Created by toeii 4 | * Date: 2019-01-16 5 | */ 6 | /// 7 | class ExpandKToolbar extends AppBar implements PreferredSizeWidget { 8 | ExpandKToolbar({ 9 | Key key, 10 | leading, 11 | automaticallyImplyLeading = true, 12 | title, 13 | actions, 14 | flexibleSpace, 15 | bottom, 16 | elevation = 4.0, 17 | backgroundColor, 18 | brightness, 19 | iconTheme, 20 | textTheme, 21 | primary = true, 22 | centerTitle, 23 | titleSpacing = NavigationToolbar.kMiddleSpacing, 24 | toolbarOpacity = 1.0, 25 | bottomOpacity = 1.0, 26 | }) : assert(automaticallyImplyLeading != null), 27 | assert(elevation != null), 28 | assert(primary != null), 29 | assert(titleSpacing != null), 30 | assert(toolbarOpacity != null), 31 | assert(bottomOpacity != null), 32 | preferredSize = Size.fromHeight((bottom?.preferredSize?.height ?? 0.0)), 33 | super(key: key); 34 | 35 | @override 36 | Size preferredSize; 37 | } -------------------------------------------------------------------------------- /lib/view/widget/FullButton.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /** 3 | * Created by toeii 4 | * Date: 2019-01-16 5 | */ 6 | ///填充button 7 | class FullButton extends StatelessWidget { 8 | final String text; 9 | 10 | final Color color; 11 | 12 | final Color textColor; 13 | 14 | final VoidCallback onPress; 15 | 16 | final double fontSize; 17 | final int maxLines; 18 | 19 | final MainAxisAlignment mainAxisAlignment; 20 | 21 | FullButton( 22 | {Key key, this.text, this.color, this.textColor, this.onPress, this.fontSize = 20.0, this.mainAxisAlignment = MainAxisAlignment.center, this.maxLines = 1}) 23 | : super(key: key); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return new RaisedButton( 28 | padding: new EdgeInsets.only(left: 20.0, top: 10.0, right: 20.0, bottom: 10.0), 29 | textColor: textColor, 30 | color: color, 31 | child: new Flex( 32 | mainAxisAlignment: mainAxisAlignment, 33 | direction: Axis.horizontal, 34 | children: [new Text(text, style: new TextStyle(fontSize: fontSize), maxLines: maxLines, overflow:TextOverflow.ellipsis)], 35 | ), 36 | onPressed: () { 37 | this.onPress?.call(); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/view/widget/HomeDrawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_extension_read/service/ERAppConfig.dart'; 4 | import 'package:flutter_extension_read/service/redux/ERAppState.dart'; 5 | import 'package:flutter_redux/flutter_redux.dart'; 6 | import 'package:flutter_extension_read/service/database/DatabaseHelper.dart'; 7 | import 'package:flutter_extension_read/view/page/BrowseRecordPage.dart'; 8 | import 'package:flutter_extension_read/view/page/WebLoadPage.dart'; 9 | import 'package:fluttertoast/fluttertoast.dart'; 10 | import 'package:redux/redux.dart'; 11 | 12 | /** 13 | * Created by toeii 14 | * Date: 2019-01-16 15 | */ 16 | ///首页侧边栏 17 | class HomeDrawer extends StatelessWidget { 18 | 19 | showThemeDialog(BuildContext context, Store store) { 20 | List list = [ 21 | "默认主题", 22 | "主题1", 23 | "主题2", 24 | "主题3", 25 | "主题4", 26 | "主题5", 27 | "主题6", 28 | ]; 29 | ERAppConfig.showCommitOptionDialog(context, list, (index) { 30 | ERAppConfig.pushTheme(store, index); 31 | }, colorList: ERAppConfig.getThemeListColor()); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return new Material( 37 | child: new StoreBuilder( 38 | builder: (context, store) { 39 | return new Container( 40 | width: 240, 41 | child:new Drawer( 42 | child: new ListView( 43 | padding: const EdgeInsets.only(), 44 | children: [ 45 | new UserAccountsDrawerHeader( 46 | accountName: new Text('拓意阅读 v1.0'), 47 | accountEmail: new Text('本项目仅供学习与参考'), 48 | currentAccountPicture: new CircleAvatar( 49 | backgroundImage:new AssetImage("images/logo_extension_read.png"), 50 | ), 51 | ), 52 | new ListTile(leading: Icon(Icons.work),title: Text('项目主页'), 53 | onTap: () { 54 | Navigator.push( 55 | context, 56 | new MaterialPageRoute(builder: (context) => new WebLoadPage(title:'拓意阅读',url:'https://github.com/toeii/FlutterExampleApp_ExtensionRead')), 57 | ); 58 | } 59 | ), 60 | new ListTile(leading: Icon(Icons.visibility),title: Text('浏览记录'), 61 | onTap: () { 62 | Navigator.push( 63 | context, 64 | new MaterialPageRoute(builder: (context) => new BrowseRecordPage(),) 65 | ); 66 | } 67 | ), 68 | new ListTile(leading: Icon(Icons.build),title: Text('切换主题'), 69 | onTap: () { 70 | showThemeDialog(context, store); 71 | }), 72 | new ListTile(leading: Icon(Icons.email),title: Text('关于作者'), 73 | onTap: () { 74 | Navigator.push( 75 | context, 76 | new MaterialPageRoute(builder: (context) => new WebLoadPage(title:'关于作者',url:'https://github.com/toeii')), 77 | ); 78 | }), 79 | new ListTile(leading: Icon(Icons.swap_vertical_circle),title: Text('清除缓存'), 80 | onTap: () { 81 | new DatabaseHelper().cleanNote(); 82 | 83 | Fluttertoast.showToast( 84 | msg: "清除成功!", 85 | toastLength: Toast.LENGTH_SHORT, 86 | gravity: ToastGravity.BOTTOM, 87 | timeInSecForIos: 1, 88 | backgroundColor: Colors.black, 89 | textColor: Colors.white, 90 | fontSize: 16.0 91 | ); 92 | } 93 | ), 94 | // new AboutListTile(), 95 | ], 96 | ), 97 | ), 98 | ); 99 | }, 100 | ), 101 | ); 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /lib/view/widget/NotEmptyText.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | /** 4 | * Created by toeii 5 | * Date: 2019-01-16 6 | */ 7 | ///处理Text空数据报错 8 | class NotEmptyText extends StatelessWidget { 9 | /// Creates a text widget. 10 | /// 11 | /// If the [style] argument is null, the text will use the style from the 12 | /// closest enclosing [DefaultTextStyle]. 13 | NotEmptyText(this.data, { 14 | Key key, 15 | this.style, 16 | this.textAlign, 17 | this.textDirection, 18 | this.locale, 19 | this.softWrap, 20 | this.overflow, 21 | this.textScaleFactor, 22 | this.maxLines, 23 | this.semanticsLabel, 24 | this.textSpan, 25 | }) : super(key: key); 26 | 27 | /// Creates a text widget with a [TextSpan]. 28 | NotEmptyText.rich(this.textSpan, { 29 | Key key, 30 | this.style, 31 | this.textAlign, 32 | this.textDirection, 33 | this.locale, 34 | this.softWrap, 35 | this.overflow, 36 | this.textScaleFactor, 37 | this.maxLines, 38 | this.semanticsLabel, 39 | }): assert(textSpan != null), 40 | data = null, 41 | super(key: key); 42 | 43 | /// The text to display. 44 | /// 45 | /// This will be null if a [textSpan] is provided instead. 46 | String data = ""; 47 | 48 | /// The text to display as a [TextSpan]. 49 | /// 50 | /// This will be null if [data] is provided instead. 51 | final TextSpan textSpan; 52 | 53 | /// If non-null, the style to use for this text. 54 | /// 55 | /// If the style's "inherit" property is true, the style will be merged with 56 | /// the closest enclosing [DefaultTextStyle]. Otherwise, the style will 57 | /// replace the closest enclosing [DefaultTextStyle]. 58 | final TextStyle style; 59 | 60 | /// How the text should be aligned horizontally. 61 | final TextAlign textAlign; 62 | 63 | /// The directionality of the text. 64 | /// 65 | /// This decides how [textAlign] values like [TextAlign.start] and 66 | /// [TextAlign.end] are interpreted. 67 | /// 68 | /// This is also used to disambiguate how to render bidirectional text. For 69 | /// example, if the [data] is an English phrase followed by a Hebrew phrase, 70 | /// in a [TextDirection.ltr] context the English phrase will be on the left 71 | /// and the Hebrew phrase to its right, while in a [TextDirection.rtl] 72 | /// context, the English phrase will be on the right and the Hebrew phrase on 73 | /// its left. 74 | /// 75 | /// Defaults to the ambient [Directionality], if any. 76 | final TextDirection textDirection; 77 | 78 | /// Used to select a font when the same Unicode character can 79 | /// be rendered differently, depending on the locale. 80 | /// 81 | /// It's rarely necessary to set this property. By default its value 82 | /// is inherited from the enclosing app with `Localizations.localeOf(context)`. 83 | /// 84 | /// See [RenderParagraph.locale] for more information. 85 | final Locale locale; 86 | 87 | /// Whether the text should break at soft line breaks. 88 | /// 89 | /// If false, the glyphs in the text will be positioned as if there was unlimited horizontal space. 90 | final bool softWrap; 91 | 92 | /// How visual overflow should be handled. 93 | final TextOverflow overflow; 94 | 95 | /// The number of font pixels for each logical pixel. 96 | /// 97 | /// For example, if the text scale factor is 1.5, text will be 50% larger than 98 | /// the specified font size. 99 | /// 100 | /// The value given to the constructor as textScaleFactor. If null, will 101 | /// use the [MediaQueryData.textScaleFactor] obtained from the ambient 102 | /// [MediaQuery], or 1.0 if there is no [MediaQuery] in scope. 103 | final double textScaleFactor; 104 | 105 | /// An optional maximum number of lines for the text to span, wrapping if necessary. 106 | /// If the text exceeds the given number of lines, it will be truncated according 107 | /// to [overflow]. 108 | /// 109 | /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the 110 | /// edge of the box. 111 | /// 112 | /// If this is null, but there is an ambient [DefaultTextStyle] that specifies 113 | /// an explicit number for its [DefaultTextStyle.maxLines], then the 114 | /// [DefaultTextStyle] value will take precedence. You can use a [RichText] 115 | /// widget directly to entirely override the [DefaultTextStyle]. 116 | final int maxLines; 117 | 118 | /// An alternative semantics label for this text. 119 | /// 120 | /// If present, the semantics of this widget will contain this value instead 121 | /// of the actual text. 122 | /// 123 | /// This is useful for replacing abbreviations or shorthands with the full 124 | /// text value: 125 | /// 126 | /// ```dart 127 | /// Text(r'$$', semanticsLabel: 'Double dollars') 128 | /// 129 | /// ``` 130 | final String semanticsLabel; 131 | 132 | @override 133 | Widget build(BuildContext context) { 134 | final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); 135 | TextStyle effectiveTextStyle = style; 136 | if (style == null || style.inherit) 137 | effectiveTextStyle = defaultTextStyle.style.merge(style); 138 | if (MediaQuery.boldTextOverride(context)) 139 | effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold)); 140 | Widget result = RichText( 141 | textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start, 142 | textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null. 143 | locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null 144 | softWrap: softWrap ?? defaultTextStyle.softWrap, 145 | overflow: overflow ?? defaultTextStyle.overflow, 146 | textScaleFactor: textScaleFactor ?? MediaQuery.textScaleFactorOf(context), 147 | maxLines: maxLines ?? defaultTextStyle.maxLines, 148 | text: TextSpan( 149 | style: effectiveTextStyle, 150 | text: data, 151 | children: textSpan != null ? [textSpan] : null, 152 | ), 153 | ); 154 | if (semanticsLabel != null) { 155 | result = Semantics( 156 | textDirection: textDirection, 157 | label: semanticsLabel, 158 | child: ExcludeSemantics( 159 | child: result, 160 | ) 161 | ); 162 | } 163 | return result; 164 | } 165 | 166 | @override 167 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 168 | super.debugFillProperties(properties); 169 | properties.add(StringProperty('data', data, showName: false)); 170 | if (textSpan != null) { 171 | properties.add(textSpan.toDiagnosticsNode(name: 'textSpan', style: DiagnosticsTreeStyle.transition)); 172 | } 173 | style?.debugFillProperties(properties); 174 | properties.add(EnumProperty('textAlign', textAlign, defaultValue: null)); 175 | properties.add(EnumProperty('textDirection', textDirection, defaultValue: null)); 176 | properties.add(DiagnosticsProperty('locale', locale, defaultValue: null)); 177 | properties.add(FlagProperty('softWrap', value: softWrap, ifTrue: 'wrapping at box width', ifFalse: 'no wrapping except at line break characters', showName: true)); 178 | properties.add(EnumProperty('overflow', overflow, defaultValue: null)); 179 | properties.add(DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null)); 180 | properties.add(IntProperty('maxLines', maxLines, defaultValue: null)); 181 | if (semanticsLabel != null) { 182 | properties.add(StringProperty('semanticsLabel', semanticsLabel)); 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /lib/view/widget/SimpleViewPlayer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:video_player/video_player.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:screen/screen.dart'; 5 | 6 | class SimpleViewPlayer extends StatefulWidget { 7 | final String source; 8 | bool isFullScreen; 9 | 10 | SimpleViewPlayer(this.source, {this.isFullScreen: false}); 11 | 12 | @override 13 | _SimpleViewPlayerState createState() => _SimpleViewPlayerState(); 14 | } 15 | 16 | class _SimpleViewPlayerState extends State { 17 | VideoPlayerController controller; 18 | VoidCallback listener; 19 | bool hideBottom = true; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | listener = () { 25 | if (!mounted) { 26 | return; 27 | } 28 | setState(() {}); 29 | }; 30 | controller = VideoPlayerController.network(widget.source); 31 | controller.initialize(); 32 | controller.setLooping(true); 33 | controller.addListener(listener); 34 | controller.play(); 35 | Screen.keepOn(true); 36 | if (widget.isFullScreen) { 37 | SystemChrome.setEnabledSystemUIOverlays([]); 38 | SystemChrome.setPreferredOrientations([ 39 | DeviceOrientation.landscapeLeft, 40 | DeviceOrientation.landscapeRight, 41 | ]); 42 | } 43 | } 44 | 45 | @override 46 | void dispose() { 47 | controller.dispose(); 48 | Screen.keepOn(false); 49 | if (widget.isFullScreen) { 50 | SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); 51 | SystemChrome.setPreferredOrientations([ 52 | DeviceOrientation.portraitUp, 53 | DeviceOrientation.portraitDown, 54 | DeviceOrientation.landscapeLeft, 55 | DeviceOrientation.landscapeRight, 56 | ]); 57 | } 58 | super.dispose(); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | return Scaffold( 64 | body: PlayView( 65 | controller, 66 | allowFullScreen: !widget.isFullScreen, 67 | ), 68 | ); 69 | } 70 | } 71 | 72 | class PlayView extends StatefulWidget { 73 | VideoPlayerController controller; 74 | bool allowFullScreen; 75 | 76 | PlayView(this.controller, {this.allowFullScreen: true}); 77 | 78 | @override 79 | _PlayViewState createState() => _PlayViewState(); 80 | } 81 | 82 | class _PlayViewState extends State { 83 | VideoPlayerController get controller => widget.controller; 84 | bool hideBottom = true; 85 | 86 | void onClickPlay() { 87 | if (!controller.value.initialized) { 88 | return; 89 | } 90 | setState(() { 91 | hideBottom = false; 92 | }); 93 | if (controller.value.isPlaying) { 94 | controller.pause(); 95 | } else { 96 | Future.delayed(const Duration(seconds: 3), () { 97 | if (!mounted) { 98 | return; 99 | } 100 | if (!controller.value.initialized) { 101 | return; 102 | } 103 | if (controller.value.isPlaying && !hideBottom) { 104 | setState(() { 105 | hideBottom = true; 106 | }); 107 | } 108 | }); 109 | controller.play(); 110 | } 111 | } 112 | 113 | void onClickFullScreen() { 114 | if (MediaQuery.of(context).orientation == Orientation.portrait) { 115 | // current portrait , enter fullscreen 116 | SystemChrome.setEnabledSystemUIOverlays([]); 117 | SystemChrome.setPreferredOrientations([ 118 | DeviceOrientation.landscapeLeft, 119 | DeviceOrientation.landscapeRight, 120 | ]); 121 | Navigator.of(context) 122 | .push(PageRouteBuilder( 123 | settings: RouteSettings(isInitialRoute: false), 124 | pageBuilder: ( 125 | BuildContext context, 126 | Animation animation, 127 | Animation secondaryAnimation, 128 | ) { 129 | return AnimatedBuilder( 130 | animation: animation, 131 | builder: (BuildContext context, Widget child) { 132 | return Scaffold( 133 | resizeToAvoidBottomPadding: false, 134 | body: PlayView(controller), 135 | ); 136 | }, 137 | ); 138 | }, 139 | )) 140 | .then((value) { 141 | // exit fullscreen 142 | SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); 143 | SystemChrome.setPreferredOrientations([ 144 | DeviceOrientation.portraitUp, 145 | DeviceOrientation.portraitDown, 146 | DeviceOrientation.landscapeLeft, 147 | DeviceOrientation.landscapeRight, 148 | ]); 149 | }); 150 | } 151 | } 152 | 153 | void onClickExitFullScreen() { 154 | if (MediaQuery.of(context).orientation == Orientation.landscape) { 155 | // current landscape , exit fullscreen 156 | Navigator.of(context).pop(); 157 | } 158 | } 159 | 160 | @override 161 | Widget build(BuildContext context) { 162 | Color primaryColor = Theme.of(context).primaryColor; 163 | if (controller.value.initialized) { 164 | final Size size = controller.value.size; 165 | return GestureDetector( 166 | child: Container( 167 | color: Colors.black, 168 | child: Stack( 169 | children: [ 170 | Center( 171 | child: AspectRatio( 172 | aspectRatio: size.width / size.height, 173 | child: VideoPlayer(controller), 174 | )), 175 | Align( 176 | alignment: Alignment.bottomCenter, 177 | child: hideBottom 178 | ? Container() 179 | : Opacity( 180 | opacity: 0.8, 181 | child: Container( 182 | height: 30.0, 183 | color: Colors.grey, 184 | child: Row( 185 | mainAxisSize: MainAxisSize.max, 186 | children: [ 187 | GestureDetector( 188 | child: Container( 189 | child: controller.value.isPlaying 190 | ? Icon( 191 | Icons.pause, 192 | color: primaryColor, 193 | ) 194 | : Icon( 195 | Icons.play_arrow, 196 | color: primaryColor, 197 | ), 198 | ), 199 | onTap: onClickPlay, 200 | ), 201 | Container( 202 | padding: EdgeInsets.symmetric( 203 | horizontal: 5.0), 204 | child: Center( 205 | child: Text( 206 | "${controller.value.position.toString().split(".")[0]}", 207 | style: 208 | TextStyle(color: Colors.white), 209 | ), 210 | )), 211 | Expanded( 212 | child: VideoProgressIndicator( 213 | controller, 214 | allowScrubbing: true, 215 | padding: EdgeInsets.symmetric( 216 | horizontal: 1.0, vertical: 1.0), 217 | colors: VideoProgressColors( 218 | playedColor: primaryColor), 219 | )), 220 | Container( 221 | padding: EdgeInsets.symmetric( 222 | horizontal: 5.0), 223 | child: Center( 224 | child: Text( 225 | "${controller.value.duration.toString().split(".")[0]}", 226 | style: 227 | TextStyle(color: Colors.white), 228 | ), 229 | )), 230 | Container( 231 | child: widget.allowFullScreen 232 | ? Container( 233 | child: MediaQuery.of(context) 234 | .orientation == 235 | Orientation.portrait 236 | ? GestureDetector( 237 | child: Icon( 238 | Icons.fullscreen, 239 | color: primaryColor, 240 | ), 241 | onTap: onClickFullScreen, 242 | ) 243 | : GestureDetector( 244 | child: Icon( 245 | Icons.fullscreen_exit, 246 | color: primaryColor, 247 | ), 248 | onTap: 249 | onClickExitFullScreen, 250 | ), 251 | ) 252 | : Container(), 253 | ) 254 | ], 255 | )), 256 | )), 257 | Align( 258 | alignment: Alignment.center, 259 | child: controller.value.isPlaying 260 | ? Container() 261 | : Icon( 262 | Icons.play_circle_filled, 263 | color: primaryColor, 264 | size: 48.0, 265 | ), 266 | ) 267 | ], 268 | )), 269 | onTap: onClickPlay, 270 | ); 271 | } else if (controller.value.hasError && !controller.value.isPlaying) { 272 | return Container( 273 | color: Colors.black, 274 | child: Center( 275 | child: RaisedButton( 276 | onPressed: () { 277 | controller.initialize(); 278 | controller.setLooping(true); 279 | controller.play(); 280 | }, 281 | shape: new RoundedRectangleBorder( 282 | borderRadius: new BorderRadius.circular(30.0)), 283 | child: Text("play error, try again!"), 284 | ), 285 | ), 286 | ); 287 | } else { 288 | return Container( 289 | color: Colors.black, 290 | child: Center( 291 | child: CircularProgressIndicator(), 292 | ), 293 | ); 294 | } 295 | } 296 | } -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_extension_read 2 | description: A new Flutter application. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.0.0-dev.68.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^0.1.2 22 | flutter_swiper: ^1.1.3 23 | video_player: ^0.10.0+2 24 | screen: ^0.0.4 25 | flutter_redux: ^0.5.2 26 | flutter_webview_plugin: ^0.3.0+2 27 | dio: ^1.0.13 28 | json_annotation: ^2.0.0 29 | fluttertoast: ^3.0.0 30 | photo_view: ^0.0.10 31 | event_bus: ^1.0.1 32 | sqflite: ^1.0.0 33 | shared_preferences: ^0.4.2 34 | 35 | dev_dependencies: 36 | build_runner: ^1.1.1 37 | json_serializable: ^2.0.0 38 | # flutter_test: 39 | # sdk: flutter 40 | 41 | 42 | # For information on the generic Dart part of this file, see the 43 | # following page: https://www.dartlang.org/tools/pub/pubspec 44 | 45 | # The following section is specific to Flutter. 46 | flutter: 47 | 48 | # The following line ensures that the Material Icons font is 49 | # included with your application, so that you can use the icons in 50 | # the material Icons class. 51 | uses-material-design: true 52 | 53 | # To add assets to your application, add an assets section, like this: 54 | # assets: 55 | # - images/a_dot_burr.jpeg 56 | # - images/a_dot_ham.jpeg 57 | assets: 58 | - images/logo_extension_read.png 59 | # An image asset can refer to one or more resolution-specific "variants", see 60 | # https://flutter.io/assets-and-images/#resolution-aware. 61 | 62 | # For details regarding adding assets from package dependencies, see 63 | # https://flutter.io/assets-and-images/#from-packages 64 | 65 | # To add custom fonts to your application, add a fonts section here, 66 | # in this "flutter" section. Each entry in this list should have a 67 | # "family" key with the font family name, and a "fonts" key with a 68 | # list giving the asset and other descriptors for the font. For 69 | # example: 70 | # fonts: 71 | # - family: Schyler 72 | # fonts: 73 | # - asset: fonts/Schyler-Regular.ttf 74 | # - asset: fonts/Schyler-Italic.ttf 75 | # style: italic 76 | # - family: Trajan Pro 77 | # fonts: 78 | # - asset: fonts/TrajanPro.ttf 79 | # - asset: fonts/TrajanPro_Bold.ttf 80 | # weight: 700 81 | # 82 | # For details regarding fonts from package dependencies, 83 | # see https://flutter.io/custom-fonts/#from-packages 84 | -------------------------------------------------------------------------------- /拓意阅读v1.0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toeii/FlutterExampleApp_ExtensionRead/43f4073295d4a09c186cdea48593b17817ecdd3b/拓意阅读v1.0.apk --------------------------------------------------------------------------------