├── res └── values │ └── strings_en.arb ├── android ├── settings_aar.gradle ├── gradle.properties ├── .gitignore ├── .idea │ ├── caches │ │ └── build_file_checksums.ser │ ├── vcs.xml │ ├── libraries │ │ ├── Gradle__com_google_code_findbugs_jsr305_2_0_1_jar.xml │ │ ├── Gradle____local_aars____Users_mac_Project_Flutter_JD_CP_flutter_eyepetizer_build_app_intermediates_flutter_debug_libs_jar_unspecified_jar.xml │ │ ├── Gradle__io_flutter_x86_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml │ │ ├── Gradle__io_flutter_x86_64_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml │ │ ├── Gradle__io_flutter_arm64_v8a_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml │ │ ├── Gradle__io_flutter_armeabi_v7a_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml │ │ ├── Gradle__androidx_arch_core_core_common_2_0_0_jar.xml │ │ ├── Gradle__androidx_collection_collection_1_0_0_jar.xml │ │ ├── Gradle__androidx_lifecycle_lifecycle_common_2_0_0_jar.xml │ │ ├── Gradle__androidx_lifecycle_lifecycle_common_java8_2_0_0_jar.xml │ │ ├── Gradle__androidx_loader_loader_1_0_0_aar.xml │ │ ├── Gradle__junit_junit_4_12_jar.xml │ │ ├── Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml │ │ ├── Gradle__androidx_customview_customview_1_0_0_aar.xml │ │ ├── Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml │ │ ├── Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml │ │ ├── Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml │ │ ├── Gradle__net_sf_kxml_kxml2_2_3_0_jar.xml │ │ ├── Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml │ │ ├── Gradle__androidx_lifecycle_lifecycle_runtime_2_0_0_aar.xml │ │ ├── Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml │ │ ├── Gradle__androidx_lifecycle_lifecycle_viewmodel_2_0_0_aar.xml │ │ ├── Gradle__javax_inject_javax_inject_1_jar.xml │ │ ├── Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml │ │ ├── Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml │ │ ├── Gradle__com_squareup_javawriter_2_1_1_jar.xml │ │ ├── Gradle__org_hamcrest_hamcrest_core_1_3_jar.xml │ │ ├── Gradle__org_jetbrains_annotations_13_0_jar.xml │ │ ├── Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml │ │ ├── Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml │ │ ├── Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml │ │ ├── Gradle__androidx_versionedparcelable_versionedparcelable_1_0_0_aar.xml │ │ ├── Gradle__org_hamcrest_hamcrest_library_1_3_jar.xml │ │ ├── Gradle__androidx_annotation_annotation_1_0_0_jar.xml │ │ ├── Gradle__androidx_annotation_annotation_1_1_0_jar.xml │ │ ├── Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml │ │ ├── Gradle__org_hamcrest_hamcrest_integration_1_3_jar.xml │ │ ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_21_jar.xml │ │ ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_50_jar.xml │ │ ├── Gradle__androidx_core_core_1_0_0_aar.xml │ │ ├── Gradle__androidx_print_print_1_0_0_aar.xml │ │ ├── Gradle__androidx_test_runner_1_1_1_aar.xml │ │ ├── Gradle__androidx_test_monitor_1_1_1_aar.xml │ │ ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_21_jar.xml │ │ ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_50_jar.xml │ │ ├── Gradle__androidx_fragment_fragment_1_0_0_aar.xml │ │ ├── Gradle__io_flutter_flutter_embedding_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml │ │ ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_21_jar.xml │ │ ├── Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_50_jar.xml │ │ ├── Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml │ │ ├── Gradle__androidx_test_espresso_espresso_core_3_1_1_aar.xml │ │ ├── Gradle__androidx_coordinatorlayout_coordinatorlayout_1_0_0_aar.xml │ │ ├── Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml │ │ └── Gradle__androidx_test_espresso_espresso_idling_resource_3_1_1_aar.xml │ ├── runConfigurations.xml │ ├── gradle.xml │ ├── modules.xml │ ├── misc.xml │ └── codeStyles │ │ └── Project.xml ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── bg_splash.png │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── xml │ │ │ │ │ └── network_security_config.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── flutter │ │ │ │ │ └── flutter_eyepetizer │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle ├── build.gradle ├── android.iml └── flutter_eyepetizer_android.iml ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 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-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata ├── .gitignore ├── Podfile.lock └── Podfile ├── lib ├── widget │ ├── util.dart │ ├── no_data_widget.dart │ ├── empty_widget.dart │ ├── load_more_widget.dart │ └── nested_scroll_view_inner_scroll_position_key_widget.dart ├── provider │ ├── base_model.dart │ ├── author_issue_model.dart │ ├── author_main_page_model.dart │ ├── rank_page_model.dart │ ├── hot_page_model.dart │ ├── discovery_category_model.dart │ ├── follow_list_model.dart │ ├── discovery_follow_model.dart │ ├── author_works_page_model.dart │ ├── refresh_loadmore_model.dart │ ├── home_page_model.dart │ ├── provider_widget.dart │ └── author_details_model.dart ├── util │ ├── time_util.dart │ ├── global.dart │ ├── constant.dart │ ├── logger_util.dart │ └── fluro_convert_util.dart ├── pages │ ├── home │ │ ├── time_title_item.dart │ │ └── home_page.dart │ ├── discovery │ │ ├── category │ │ │ ├── category_list_model.dart │ │ │ └── category_list_page.dart │ │ ├── category_item_widget.dart │ │ ├── follow │ │ │ └── follow_list_page.dart │ │ └── follow_item_widget.dart │ ├── video │ │ ├── video_details_model.dart │ │ └── video_related_page.dart │ ├── splash │ │ └── splash_page.dart │ ├── hot │ │ ├── hot_page.dart │ │ └── rank_page.dart │ ├── author │ │ ├── issue │ │ │ ├── author_issue_page.dart │ │ │ └── issue_item_widget.dart │ │ ├── works │ │ │ └── author_works_page.dart │ │ └── main │ │ │ ├── author_main_page.dart │ │ │ └── video_welcom_page.dart │ ├── search │ │ └── search_page_item.dart │ └── bottom_navigator.dart ├── main.dart ├── data │ ├── entity │ │ ├── category_entity.dart │ │ └── tab_info_entity.dart │ └── remote_repository.dart ├── http │ └── http.dart └── router │ └── router_manager.dart ├── .idea ├── .gitignore ├── encodings.xml ├── dictionaries │ └── mac.xml ├── vcs.xml ├── misc.xml ├── libraries │ ├── Flutter_for_Android.xml │ ├── KotlinJavaRuntime.xml │ ├── Flutter_Plugins.xml │ └── Dart_SDK.xml ├── runConfigurations │ └── main_dart.xml └── modules.xml ├── images ├── bg_title.png ├── bg_splash.png ├── icon_like.png ├── icon_share.png ├── ic_hot_normal.png ├── icon_avatar.png ├── icon_comment.png ├── icon_no_data.png ├── img_load_fail.png ├── ic_home_normal.png ├── ic_hot_selected.png ├── ic_mine_normal.png ├── icon_like_grey.png ├── icon_share_grey.png ├── ic_home_selected.png ├── ic_mine_selected.png ├── icon_arrow_right.png ├── icon_comment_grey.png ├── icon_share_white.png ├── ic_discovery_normal.png ├── icon_default_avatar.png └── ic_discovery_selected.png ├── gif └── gif_eyepetizer.gif ├── .metadata ├── .gitignore ├── test └── widget_test.dart ├── flutter_eyepetizer.iml ├── .flutter-plugins-dependencies └── pubspec.yaml /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/widget/util.dart: -------------------------------------------------------------------------------- 1 | ///get type from T 2 | Type typeOf() => T; 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Project exclude paths 3 | /. 4 | 5 | # Project exclude paths 6 | /. -------------------------------------------------------------------------------- /images/bg_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/bg_title.png -------------------------------------------------------------------------------- /images/bg_splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/bg_splash.png -------------------------------------------------------------------------------- /images/icon_like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_like.png -------------------------------------------------------------------------------- /images/icon_share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_share.png -------------------------------------------------------------------------------- /gif/gif_eyepetizer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/gif/gif_eyepetizer.gif -------------------------------------------------------------------------------- /images/ic_hot_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_hot_normal.png -------------------------------------------------------------------------------- /images/icon_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_avatar.png -------------------------------------------------------------------------------- /images/icon_comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_comment.png -------------------------------------------------------------------------------- /images/icon_no_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_no_data.png -------------------------------------------------------------------------------- /images/img_load_fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/img_load_fail.png -------------------------------------------------------------------------------- /images/ic_home_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_home_normal.png -------------------------------------------------------------------------------- /images/ic_hot_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_hot_selected.png -------------------------------------------------------------------------------- /images/ic_mine_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_mine_normal.png -------------------------------------------------------------------------------- /images/icon_like_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_like_grey.png -------------------------------------------------------------------------------- /images/icon_share_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_share_grey.png -------------------------------------------------------------------------------- /images/ic_home_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_home_selected.png -------------------------------------------------------------------------------- /images/ic_mine_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_mine_selected.png -------------------------------------------------------------------------------- /images/icon_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_arrow_right.png -------------------------------------------------------------------------------- /images/icon_comment_grey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_comment_grey.png -------------------------------------------------------------------------------- /images/icon_share_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_share_white.png -------------------------------------------------------------------------------- /images/ic_discovery_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_discovery_normal.png -------------------------------------------------------------------------------- /images/icon_default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/icon_default_avatar.png -------------------------------------------------------------------------------- /images/ic_discovery_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/images/ic_discovery_selected.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java -------------------------------------------------------------------------------- /android/.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/bg_splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/app/src/main/res/mipmap-xhdpi/bg_splash.png -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /.idea/dictionaries/mac.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fluro 5 | 6 | 7 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/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/JD-CP/flutter_eyepetizer/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JD-CP/flutter_eyepetizer/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /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-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_for_Android.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 83a8a575eed882c49b629effedf7c95b6184515d 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /lib/provider/base_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | /// model 基类 5 | abstract class BaseModel extends ChangeNotifier { 6 | 7 | bool isInit; 8 | 9 | /// 初始化当前页 10 | /// true:初始化 11 | /// false:已初始化 12 | initPage({isInit = false}) { 13 | this.isInit = isInit; 14 | notifyListeners(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__com_google_code_findbugs_jsr305_2_0_1_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/flutter/flutter_eyepetizer/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.flutter.flutter_eyepetizer 2 | 3 | import android.os.Bundle 4 | import io.flutter.app.FlutterActivity 5 | import io.flutter.plugins.GeneratedPluginRegistrant 6 | 7 | class MainActivity: FlutterActivity() { 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | GeneratedPluginRegistrant.registerWith(this) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle____local_aars____Users_mac_Project_Flutter_JD_CP_flutter_eyepetizer_build_app_intermediates_flutter_debug_libs_jar_unspecified_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/util/time_util.dart: -------------------------------------------------------------------------------- 1 | 2 | class TimeUtil { 3 | 4 | static String formatDuration(duration) { 5 | var minute = duration ~/ 60; 6 | var second = duration % 60; 7 | var str; 8 | if (minute <= 9) { 9 | if (second <= 9) { 10 | str = "0$minute:0$second"; 11 | } else { 12 | str = "0$minute:$second"; 13 | } 14 | } else { 15 | if (second <= 9) { 16 | str = "$minute:0$second"; 17 | } else { 18 | str = "$minute:$second"; 19 | } 20 | } 21 | return str; 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/util/global.dart: -------------------------------------------------------------------------------- 1 | //import 'package:flutter_screenutil/flutter_screenutil.dart'; 2 | 3 | //double fsWidth10 = ScreenUtil.getInstance().setWidth(10); 4 | //double fsWidth15 = ScreenUtil.getInstance().setWidth(15); 5 | // 6 | //double fsHeight10 = ScreenUtil.getInstance().setHeight(10); 7 | //double fsHeight400 = ScreenUtil.getInstance().setHeight(400); 8 | // 9 | //double fsWidth22 = ScreenUtil.getInstance().setWidth(40); 10 | //double fsHeight22 = ScreenUtil.getInstance().setHeight(40); 11 | // 12 | //double fsSize15 = ScreenUtil.getInstance().setSp(30); 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__io_flutter_x86_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__io_flutter_x86_64_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__io_flutter_arm64_v8a_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__io_flutter_armeabi_v7a_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/pages/home/time_title_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 时间标题 item 4 | class TimeTitleItem extends StatelessWidget { 5 | final String timeTitle; 6 | 7 | TimeTitleItem({Key key, this.timeTitle}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container( 12 | alignment: Alignment.center, 13 | padding: EdgeInsets.all(13), 14 | child: Text( 15 | timeTitle, 16 | style: TextStyle( 17 | fontSize: 15, 18 | color: Colors.black, 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | /lib/generated 33 | pubspec.lock -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_arch_core_core_common_2_0_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_collection_collection_1_0_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/KotlinJavaRuntime.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_2_0_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_common_java8_2_0_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_loader_loader_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.72' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.6.3' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__junit_junit_4_12_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_viewpager_viewpager_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_customview_customview_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/util/constant.dart: -------------------------------------------------------------------------------- 1 | class Constant { 2 | static final baseUrl = 'http://baobab.kaiyanapp.com/'; 3 | 4 | static final homePageUrl = '${baseUrl}api/v2/feed?num=1'; 5 | 6 | static final categoryUrl = '${baseUrl}api/v4/categories'; 7 | 8 | static final followUrl = '${baseUrl}api/v4/tabs/follow'; 9 | 10 | static final rankListUrl = '${baseUrl}api/v4/rankList'; 11 | 12 | static final categoryDetailsUrl = '${baseUrl}api/v4/categories/videoList?'; 13 | 14 | static final videoRelatedUrl = '${baseUrl}api/v4/video/related?'; 15 | 16 | static final keywordUrl = '${baseUrl}api/v3/queries/hot'; 17 | 18 | static final searchUrl = '${baseUrl}api/v1/search?&num=10&start=10'; 19 | 20 | static final authorUrl = '${baseUrl}api/v4/pgcs/detail/tab'; 21 | } 22 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_arch_core_core_runtime_2_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_documentfile_documentfile_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_interpolator_interpolator_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__net_sf_kxml_kxml2_2_3_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_cursoradapter_cursoradapter_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_runtime_2_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_2_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_viewmodel_2_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__javax_inject_javax_inject_1_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_ui_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_slidingpanelayout_slidingpanelayout_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__com_squareup_javawriter_2_1_1_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_hamcrest_hamcrest_core_1_3_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_annotations_13_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_lifecycle_lifecycle_livedata_core_2_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_asynclayoutinflater_asynclayoutinflater_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_legacy_legacy_support_core_utils_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_versionedparcelable_versionedparcelable_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_hamcrest_hamcrest_library_1_3_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_annotation_annotation_1_0_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_annotation_annotation_1_1_0_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_localbroadcastmanager_localbroadcastmanager_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/widget/no_data_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class NoDataWidget extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Container( 7 | alignment: Alignment.center, 8 | child: Column( 9 | mainAxisAlignment: MainAxisAlignment.center, 10 | children: [ 11 | Image.asset( 12 | 'images/icon_no_data.png', 13 | width: 40, 14 | height: 40, 15 | ), 16 | Padding( 17 | padding: EdgeInsets.only(top: 5), 18 | child: Text( 19 | '暂无搜索数据哇', 20 | style: TextStyle( 21 | color: Colors.black45, 22 | fontSize: 13, 23 | ), 24 | ), 25 | ), 26 | ], 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/widget/empty_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EmptyWidget extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Container( 7 | color: Colors.white, 8 | alignment: Alignment.center, 9 | child: Column( 10 | mainAxisAlignment: MainAxisAlignment.center, 11 | children: [ 12 | Image.asset( 13 | 'images/icon_no_data.png', 14 | width: 35, 15 | height: 35, 16 | ), 17 | Padding( 18 | padding: EdgeInsets.only(top: 5), 19 | child: Text( 20 | '暂无搜索数据', 21 | style: TextStyle( 22 | color: Colors.grey, 23 | fontSize: 12, 24 | ), 25 | ), 26 | ), 27 | ], 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_hamcrest_hamcrest_integration_1_3_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_21_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_1_3_50_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/util/logger_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | 3 | class LoggerUtil { 4 | static LoggerUtil _loggerUtil; 5 | 6 | var _logger; 7 | var _loggerNoStack; 8 | 9 | LoggerUtil() { 10 | _logger = Logger( 11 | printer: PrettyPrinter(), 12 | ); 13 | 14 | _loggerNoStack = Logger( 15 | printer: PrettyPrinter(methodCount: 0), 16 | ); 17 | } 18 | 19 | static LoggerUtil instance() { 20 | if (null == _loggerUtil) { 21 | _loggerUtil = LoggerUtil(); 22 | } 23 | return _loggerUtil; 24 | } 25 | 26 | d(debugLog) { 27 | _logger.d(debugLog); 28 | } 29 | 30 | i(infoLog) { 31 | _logger.i(infoLog); 32 | } 33 | 34 | w(warnLog) { 35 | _loggerNoStack.w(warnLog); 36 | } 37 | 38 | e(errorLog) { 39 | _logger.e(errorLog); 40 | } 41 | 42 | v(verboseLog) { 43 | _loggerNoStack.v(verboseLog); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_core_core_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_print_print_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_test_runner_1_1_1_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_test_monitor_1_1_1_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_21_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_jdk7_1_3_50_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_fragment_fragment_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__io_flutter_flutter_embedding_debug_1_0_0_af04338413c3ed73316350f64248a152433073b6_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_21_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__org_jetbrains_kotlin_kotlin_stdlib_common_1_3_50_jar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_drawerlayout_drawerlayout_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_test_espresso_espresso_core_3_1_1_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_coordinatorlayout_coordinatorlayout_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_swiperefreshlayout_swiperefreshlayout_1_0_0_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /android/.idea/libraries/Gradle__androidx_test_espresso_espresso_idling_resource_3_1_1_aar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/pages/discovery/category/category_list_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 4 | import 'package:flutter_eyepetizer/data/remote_repository.dart'; 5 | import 'package:flutter_eyepetizer/provider/refresh_loadmore_model.dart'; 6 | import 'package:flutter_eyepetizer/util/constant.dart'; 7 | 8 | class CategoryListModel extends RefreshLoadMoreModel { 9 | 10 | final int id; 11 | 12 | CategoryListModel(this.id); 13 | 14 | @override 15 | Future loadData() async { 16 | var response = await Repository.getCategoryList( 17 | isRefresh ? Constant.categoryDetailsUrl : nextPageUrl, id); 18 | Map map = json.decode(response.toString()); 19 | var issueEntity = Issue.fromJson(map); 20 | nextPageUrl = issueEntity.nextPageUrl; 21 | var list = issueEntity.itemList; 22 | return list; 23 | } 24 | 25 | @override 26 | Future onRefresh() { 27 | isRefresh = true; 28 | return loadRemoteData(); 29 | } 30 | 31 | @override 32 | Future onLoadMore() { 33 | isRefresh = false; 34 | return loadRemoteData(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:fluro/fluro.dart'; 5 | import 'package:oktoast/oktoast.dart'; 6 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 7 | 8 | // 主入口 9 | void main() { 10 | Router router = new Router(); 11 | RouterManager.configureRouter(router); 12 | RouterManager.router = router; 13 | 14 | runApp( 15 | MyApp(), 16 | ); 17 | 18 | SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle( 19 | statusBarColor: Colors.transparent, 20 | ); 21 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 22 | } 23 | 24 | class MyApp extends StatefulWidget { 25 | @override 26 | State createState() => _MyHomePageState(); 27 | } 28 | 29 | class _MyHomePageState extends State { 30 | @override 31 | Widget build(BuildContext context) { 32 | return OKToast( 33 | child: MaterialApp( 34 | theme: ThemeData( 35 | primaryColor: Color(0xFFFFFFFF), 36 | ), 37 | onGenerateRoute: RouterManager.router.generator, 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_eyepetizer/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/provider/author_issue_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 6 | import 'package:flutter_eyepetizer/http/http.dart'; 7 | 8 | class AuthorIssueModel extends ChangeNotifier { 9 | List itemList = []; 10 | 11 | bool isInit; 12 | 13 | /// 初始化 14 | init(apiUrl) async { 15 | initPage(true); 16 | await loadData(url: apiUrl); 17 | } 18 | 19 | void initPage(bool isInit) { 20 | this.isInit = isInit; 21 | notifyListeners(); 22 | } 23 | 24 | /// 加载数据 25 | Future> loadData({String url}) async { 26 | try { 27 | var response = await HttpUtil.buildDio().get( 28 | url, 29 | options: Options( 30 | headers: httpHeaders, 31 | ), 32 | ); 33 | Map map = json.decode(response.toString()); 34 | var issue = Issue.fromJson(map); 35 | itemList = issue.itemList; 36 | if (isInit) { 37 | initPage(false); 38 | } 39 | notifyListeners(); 40 | return itemList; 41 | } catch (e, s) { 42 | return null; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/provider/author_main_page_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 5 | import 'package:flutter_eyepetizer/http/http.dart'; 6 | 7 | class AuthorMainPageModel extends ChangeNotifier { 8 | List itemList = []; 9 | 10 | bool isInit; 11 | 12 | /// 初始化 13 | init(apiUrl) async { 14 | initPage(true); 15 | await loadData(url: apiUrl); 16 | } 17 | 18 | void initPage(bool isInit) { 19 | this.isInit = isInit; 20 | notifyListeners(); 21 | } 22 | 23 | /// 加载数据 24 | Future> loadData({String url}) async { 25 | try { 26 | var response = await HttpUtil.buildDio().get( 27 | url, 28 | options: Options( 29 | headers: httpHeaders, 30 | ), 31 | ); 32 | Map map = json.decode(response.toString()); 33 | var issue = Issue.fromJson(map); 34 | itemList = issue.itemList; 35 | if (isInit) { 36 | initPage(false); 37 | } 38 | notifyListeners(); 39 | return itemList; 40 | } catch (e, s) { 41 | return null; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/provider/rank_page_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 5 | import 'package:flutter_eyepetizer/http/http.dart'; 6 | import 'package:flutter_eyepetizer/provider/refresh_loadmore_model.dart'; 7 | import 'package:flutter_eyepetizer/util/logger_util.dart'; 8 | 9 | class RankPageModel extends RefreshLoadMoreModel { 10 | final String pageUrl; 11 | 12 | RankPageModel(this.pageUrl); 13 | 14 | @override 15 | Future loadData() async { 16 | LoggerUtil.instance().d("RankPageModel start http ---> $pageUrl"); 17 | var response = await HttpUtil.buildDio().get( 18 | pageUrl, 19 | options: Options( 20 | headers: httpHeaders, 21 | ), 22 | ); 23 | Map map = json.decode(response.toString()); 24 | var issueEntity = Issue.fromJson(map); 25 | var itemList = issueEntity.itemList; 26 | LoggerUtil.instance().v("RankPageModel http success ---> ${map.toString()}"); 27 | return itemList; 28 | } 29 | 30 | @override 31 | Future onRefresh() { 32 | return loadRemoteData(); 33 | } 34 | 35 | @override 36 | Future onLoadMore() { 37 | return null; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /lib/data/entity/category_entity.dart: -------------------------------------------------------------------------------- 1 | class CategoryEntity { 2 | String bgColor; 3 | int defaultAuthorId; 4 | String headerImage; 5 | int tagId; 6 | String name; 7 | String description; 8 | int id; 9 | String bgPicture; 10 | String alias; 11 | 12 | CategoryEntity({this.bgColor, this.defaultAuthorId, this.headerImage, this.tagId, this.name, this.description, this.id, this.bgPicture, this.alias}); 13 | 14 | CategoryEntity.fromJson(Map json) { 15 | bgColor = json['bgColor']; 16 | defaultAuthorId = json['defaultAuthorId']; 17 | headerImage = json['headerImage']; 18 | tagId = json['tagId']; 19 | name = json['name']; 20 | description = json['description']; 21 | id = json['id']; 22 | bgPicture = json['bgPicture']; 23 | alias = json['alias']; 24 | } 25 | 26 | Map toJson() { 27 | final Map data = new Map(); 28 | data['bgColor'] = this.bgColor; 29 | data['defaultAuthorId'] = this.defaultAuthorId; 30 | data['headerImage'] = this.headerImage; 31 | data['tagId'] = this.tagId; 32 | data['name'] = this.name; 33 | data['description'] = this.description; 34 | data['id'] = this.id; 35 | data['bgPicture'] = this.bgPicture; 36 | data['alias'] = this.alias; 37 | return data; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/util/fluro_convert_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | /// fluro 参数编码解码工具类 4 | class FluroConvertUtils { 5 | /// fluro 传递中文参数前,先转换,fluro 不支持中文传递 6 | static String fluroCnParamsEncode(String originalCn) { 7 | return jsonEncode(Utf8Encoder().convert(originalCn)); 8 | } 9 | 10 | /// fluro 传递后取出参数,解析 11 | static String fluroCnParamsDecode(String encodeCn) { 12 | var list = List(); 13 | 14 | ///字符串解码 15 | jsonDecode(encodeCn).forEach(list.add); 16 | String value = Utf8Decoder().convert(list); 17 | return value; 18 | } 19 | 20 | /// string 转为 int 21 | static int string2int(String str) { 22 | return int.parse(str); 23 | } 24 | 25 | /// string 转为 double 26 | static double string2double(String str) { 27 | return double.parse(str); 28 | } 29 | 30 | /// string 转为 bool 31 | static bool string2bool(String str) { 32 | if (str == 'true') { 33 | return true; 34 | } else { 35 | return false; 36 | } 37 | } 38 | 39 | /// object 转为 string json 40 | static String object2string(T t) { 41 | return fluroCnParamsEncode(jsonEncode(t)); 42 | } 43 | 44 | /// string json 转为 map 45 | static Map string2map(String str) { 46 | return json.decode(fluroCnParamsDecode(str)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 | -------------------------------------------------------------------------------- /lib/pages/video/video_details_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 6 | import 'package:flutter_eyepetizer/http/http.dart'; 7 | import 'package:flutter_eyepetizer/util/constant.dart'; 8 | 9 | class VideoDetailsModel extends ChangeNotifier { 10 | List itemList = []; 11 | 12 | int id; 13 | bool isInit; 14 | 15 | /// 初始化 16 | init(id) async { 17 | this.id = id; 18 | initPage(true); 19 | await loadData(); 20 | } 21 | 22 | void initPage(bool isInit) { 23 | this.isInit = isInit; 24 | notifyListeners(); 25 | } 26 | 27 | /// 加载数据 28 | Future> loadData() async { 29 | try { 30 | var response = await HttpUtil.buildDio().get( 31 | Constant.videoRelatedUrl, 32 | queryParameters: { 33 | "id": this.id, 34 | }, 35 | options: Options( 36 | headers: httpHeaders, 37 | ), 38 | ); 39 | Map map = json.decode(response.toString()); 40 | var issue = Issue.fromJson(map); 41 | itemList = issue.itemList; 42 | if (isInit) { 43 | initPage(false); 44 | } 45 | notifyListeners(); 46 | return itemList; 47 | } catch (e, s) { 48 | return null; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /flutter_eyepetizer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/provider/hot_page_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_eyepetizer/data/entity/tab_info_entity.dart'; 7 | import 'package:flutter_eyepetizer/http/http.dart'; 8 | import 'package:flutter_eyepetizer/util/constant.dart'; 9 | import '../pages/hot/rank_page.dart'; 10 | 11 | class HotPageModel extends ChangeNotifier { 12 | List tabPages = []; 13 | List tabItems = []; 14 | 15 | bool isInit; 16 | 17 | /// 初始化 18 | init() async { 19 | _initPage(true); 20 | await getTabInfo(); 21 | } 22 | 23 | void _initPage(bool isInit) { 24 | this.isInit = isInit; 25 | notifyListeners(); 26 | } 27 | 28 | getTabInfo() async { 29 | try { 30 | var response = await HttpUtil.buildDio().get( 31 | Constant.rankListUrl, 32 | options: Options(headers: httpHeaders), 33 | ); 34 | Map map = json.decode(response.toString()); 35 | var tabInfoEntity = TabInfoEntity.fromJson(map); 36 | this.tabItems = tabInfoEntity.tabInfo.tabList; 37 | this.tabPages = this 38 | .tabItems 39 | .map((tabInfoItem) => RankPage(pageUrl: tabInfoItem.apiUrl)) 40 | .toList(); 41 | _initPage(false); 42 | notifyListeners(); 43 | } catch (e, s) { 44 | print(e); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/data/remote_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:device_info/device_info.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:flutter_eyepetizer/http/http.dart'; 4 | 5 | class Repository { 6 | static Future getHomePageList(url) async { 7 | var response = await HttpUtil.buildDio().get( 8 | url, 9 | options: Options( 10 | connectTimeout: 5000, 11 | receiveTimeout: 5000, 12 | headers: httpHeaders, 13 | ), 14 | ); 15 | return response; 16 | } 17 | 18 | static Future getFollowList(url) async { 19 | var response = await HttpUtil.buildDio().get( 20 | url, 21 | options: Options( 22 | connectTimeout: 5000, 23 | receiveTimeout: 5000, 24 | headers: httpHeaders, 25 | ), 26 | ); 27 | return response; 28 | } 29 | 30 | static Future getCategoryList(url, id) async { 31 | DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); 32 | AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; 33 | var response = await HttpUtil.buildDio().get( 34 | url, 35 | queryParameters: { 36 | "id": id, 37 | "udid": 'd2807c895f0348a180148c9dfa6f2feeac0781b5', 38 | "deviceModel": androidInfo.device, 39 | }, 40 | options: Options( 41 | connectTimeout: 5000, 42 | receiveTimeout: 5000, 43 | headers: httpHeaders, 44 | ), 45 | ); 46 | return response; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lib/http/http.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | const httpHeaders = { 4 | 'Accept': 'application/json, text/plain, */*', 5 | 'Accept-Encoding': 'gzip, deflate, br', 6 | 'Accept-Language': 'zh-CN,zh;q=0.9', 7 | 'Connection': 'keep-alive', 8 | 'Content-Type': 'application/json', 9 | 'User-Agent': 10 | 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1', 11 | }; 12 | 13 | class HttpUtil { 14 | static Dio _dio; 15 | 16 | static Options _options = Options( 17 | connectTimeout: 5000, 18 | receiveTimeout: 5000, 19 | headers: httpHeaders, 20 | ); 21 | 22 | /// 构造 Dio 对象 23 | static Dio buildDio() { 24 | _dio ??= Dio(); 25 | 26 | /// _dio.interceptors.add(LogInterceptor()); 27 | return _dio; 28 | } 29 | 30 | /// 执行 get 请求 31 | static doGet( 32 | String url, { 33 | queryParameters, 34 | options, 35 | Function success, 36 | Function fail, 37 | }) async { 38 | print('http request url: $url'); 39 | try { 40 | Response response = await buildDio().get( 41 | url, 42 | queryParameters: queryParameters, 43 | options: _options, 44 | ); 45 | success(response); 46 | print('http response: $response'); 47 | } catch (exception) { 48 | fail(exception); 49 | print('http request fail: $url --- $exception'); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/widget/load_more_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 上拉加载 4 | class LoadMoreWidget extends StatelessWidget { 5 | /// 表示是否处于上拉加载状态 6 | final bool isLoadMore; 7 | 8 | LoadMoreWidget({Key key, this.isLoadMore}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | if (this.isLoadMore) { 13 | return Container( 14 | padding: EdgeInsets.symmetric(vertical: 15), 15 | child: Row( 16 | mainAxisAlignment: MainAxisAlignment.center, 17 | children: [ 18 | Text( 19 | '努力加载中... ', 20 | style: TextStyle( 21 | fontSize: 14, 22 | color: Colors.black54, 23 | ), 24 | ), 25 | SizedBox( 26 | width: 18, 27 | height: 18, 28 | child: CircularProgressIndicator( 29 | strokeWidth: 2.5, 30 | backgroundColor: Colors.deepPurple[600], 31 | ), 32 | ) 33 | ], 34 | ), 35 | ); 36 | } else { 37 | return Container( 38 | padding: EdgeInsets.symmetric(vertical: 15), 39 | alignment: Alignment.center, 40 | child: Text( 41 | '上拉加载更多', 42 | style: TextStyle( 43 | fontSize: 14, 44 | color: Colors.black54, 45 | ), 46 | ), 47 | ); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/provider/discovery_category_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter_eyepetizer/data/entity/category_entity.dart'; 6 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 7 | import 'package:flutter_eyepetizer/http/http.dart'; 8 | import 'package:flutter_eyepetizer/util/constant.dart'; 9 | 10 | class DiscoveryPageModel extends ChangeNotifier { 11 | bool isInit; 12 | 13 | List categoryList; 14 | 15 | String nextPageUrl; 16 | 17 | void initPage(bool isInit) { 18 | this.isInit = isInit; 19 | notifyListeners(); 20 | } 21 | 22 | /// 初始化 23 | init() async { 24 | initPage(true); 25 | await loadData(); 26 | } 27 | 28 | /// 加载数据 29 | loadData({String url}) async { 30 | try { 31 | /// 发起并发请求 32 | var response = await HttpUtil.buildDio().get( 33 | Constant.categoryUrl, 34 | options: Options( 35 | headers: httpHeaders, 36 | responseType: ResponseType.plain, 37 | ), 38 | ); 39 | var resJson = json.decode(response.toString()); 40 | var dataList = List.from( 41 | resJson.map((i) => CategoryEntity.fromJson(i)), 42 | ); 43 | 44 | initPage(false); 45 | this.categoryList = dataList; 46 | notifyListeners(); 47 | } catch (e, s) { 48 | print(e.toString()); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/provider/follow_list_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 6 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 7 | import 'package:flutter_eyepetizer/data/remote_repository.dart'; 8 | import 'package:flutter_eyepetizer/provider/refresh_loadmore_model.dart'; 9 | import 'package:flutter_eyepetizer/util/constant.dart'; 10 | import 'package:flutter_eyepetizer/util/logger_util.dart'; 11 | 12 | class FollowListModel extends RefreshLoadMoreModel { 13 | @override 14 | Future loadData() async { 15 | LoggerUtil.instance().d( 16 | "FollowListModel start http ---> ${isRefresh ? Constant.homePageUrl : nextPageUrl}"); 17 | var response = await Repository.getHomePageList( 18 | isRefresh ? Constant.followUrl : nextPageUrl, 19 | ); 20 | Map map = json.decode(response.toString()); 21 | var followEntity = Issue.fromJson(map); 22 | var list = followEntity.itemList; 23 | this.nextPageUrl = followEntity.nextPageUrl; 24 | LoggerUtil.instance() 25 | .v("FollowListModel http success ---> ${map.toString()}"); 26 | return list; 27 | } 28 | 29 | @override 30 | Future onRefresh() { 31 | isRefresh = true; 32 | return loadRemoteData(); 33 | } 34 | 35 | @override 36 | Future onLoadMore() { 37 | isRefresh = false; 38 | return loadRemoteData(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/flutter_eyepetizer_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/pages/discovery/category_item_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter_eyepetizer/data/entity/category_entity.dart'; 5 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 6 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 7 | 8 | class CategoryItemWidget extends StatelessWidget { 9 | final CategoryEntity item; 10 | 11 | CategoryItemWidget({Key key, this.item}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return GestureDetector( 16 | child: Stack( 17 | alignment: FractionalOffset(0.5, 0.5), 18 | children: [ 19 | Container( 20 | child: ClipRRect( 21 | borderRadius: BorderRadius.circular(0), 22 | child: CachedNetworkImage( 23 | imageUrl: this.item.bgPicture, 24 | fit: BoxFit.cover, 25 | ), 26 | ), 27 | ), 28 | Text( 29 | '#${this.item.name}', 30 | style: TextStyle(color: Colors.white, fontSize: 14), 31 | ), 32 | ], 33 | ), 34 | onTap: () { 35 | String itemJson = FluroConvertUtils.object2string(item); 36 | RouterManager.router.navigateTo( 37 | context, 38 | RouterManager.category + "?itemJson=$itemJson", 39 | transition: TransitionType.inFromRight, 40 | ); 41 | }, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /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/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_eyepetizer 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 | -------------------------------------------------------------------------------- /lib/provider/discovery_follow_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 6 | import 'package:flutter_eyepetizer/http/http.dart'; 7 | import 'package:flutter_eyepetizer/util/constant.dart'; 8 | 9 | class DiscoveryFollowModel extends ChangeNotifier { 10 | bool isInit; 11 | String nextPageUrl; 12 | 13 | List followItemList; 14 | 15 | /// 初始化 16 | init() async { 17 | await loadData(); 18 | } 19 | 20 | /// 加载数据 21 | loadData({String url}) async { 22 | try { 23 | /// 发起并发请求 24 | var response = await HttpUtil.buildDio().get( 25 | Constant.followUrl, 26 | options: Options(headers: httpHeaders), 27 | ); 28 | Map map = json.decode(response.toString()); 29 | var followEntity = Issue.fromJson(map); 30 | nextPageUrl = followEntity.nextPageUrl; 31 | var followItemList = followEntity.itemList; 32 | 33 | this.followItemList = followItemList; 34 | notifyListeners(); 35 | } catch (e, s) { 36 | print(e.toString()); 37 | } 38 | } 39 | 40 | loadNextPage() async { 41 | try { 42 | var response = await HttpUtil.buildDio().get( 43 | null == nextPageUrl ? Constant.followUrl : nextPageUrl, 44 | options: Options(headers: httpHeaders), 45 | ); 46 | Map map = json.decode(response.toString()); 47 | var followEntity = Issue.fromJson(map); 48 | nextPageUrl = followEntity.nextPageUrl; 49 | var followItemList = followEntity.itemList; 50 | this.followItemList = followItemList; 51 | notifyListeners(); 52 | } catch (e, s) { 53 | print(e.toString()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/provider/author_works_page_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 6 | import 'package:flutter_eyepetizer/http/http.dart'; 7 | import 'package:flutter_ijkplayer/flutter_ijkplayer.dart'; 8 | 9 | class AuthorWorksModel extends ChangeNotifier { 10 | List itemList = []; 11 | List list = []; 12 | 13 | bool isInit; 14 | String url; 15 | 16 | /// 初始化 17 | init(apiUrl) async { 18 | this.url = apiUrl; 19 | initPage(true); 20 | await loadData(); 21 | } 22 | 23 | void initPage(bool isInit) { 24 | this.isInit = isInit; 25 | notifyListeners(); 26 | } 27 | 28 | /// 加载数据 29 | Future> loadData() async { 30 | try { 31 | var response = await HttpUtil.buildDio().get( 32 | url, 33 | queryParameters: { 34 | "udid": 'd2807c895f0348a180148c9dfa6f2feeac0781b5', 35 | }, 36 | options: Options( 37 | headers: httpHeaders, 38 | ), 39 | ); 40 | Map map = json.decode(response.toString()); 41 | var issue = Issue.fromJson(map); 42 | this.itemList = issue.itemList; 43 | for (var item in itemList) { 44 | List playInfoList = item.data.playInfo; 45 | if (playInfoList.length > 1) { 46 | for (var playInfo in playInfoList) { 47 | if (playInfo.type == 'high') { 48 | list.add(DataSource.network(playInfo.url)); 49 | } 50 | } 51 | } else { 52 | list.add(DataSource.network(item.data.playUrl)); 53 | } 54 | } 55 | if (isInit) { 56 | initPage(false); 57 | } 58 | notifyListeners(); 59 | return itemList; 60 | } catch (e, s) { 61 | return null; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.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 | 28 | 29 | -------------------------------------------------------------------------------- /lib/provider/refresh_loadmore_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_eyepetizer/provider/base_model.dart'; 2 | import 'package:flutter_eyepetizer/util/logger_util.dart'; 3 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 4 | 5 | /// 带下拉刷新、上拉加载页的 model 基本封装 6 | abstract class RefreshLoadMoreModel extends BaseModel { 7 | RefreshController refreshController = RefreshController( 8 | initialRefresh: false, 9 | initialLoadStatus: LoadStatus.noMore, 10 | ); 11 | 12 | /// 下一页的请求地址 13 | String nextPageUrl; 14 | 15 | /// 是否刷新 16 | /// 当用户首次进入页面/手动下拉刷新时 ---> true 17 | /// 手动上拉加载 ---> false 18 | bool isRefresh = true; 19 | 20 | List dataList = []; 21 | 22 | init() async { 23 | initPage(isInit: true); 24 | await loadRemoteData(); 25 | } 26 | 27 | Future> loadRemoteData() async { 28 | try { 29 | var response = await loadData(); 30 | 31 | if (isInit) { 32 | initPage(isInit: false); 33 | } 34 | 35 | if (isRefresh) { 36 | if (dataList.isNotEmpty) { 37 | dataList.clear(); 38 | } 39 | refreshController.refreshCompleted(); 40 | if (null != nextPageUrl && nextPageUrl.isNotEmpty) { 41 | refreshController.loadComplete(); 42 | } 43 | } else { 44 | if (null != nextPageUrl && nextPageUrl.isNotEmpty) { 45 | refreshController.loadComplete(); 46 | } else { 47 | refreshController.loadNoData(); 48 | } 49 | } 50 | 51 | dataList.addAll(response); 52 | notifyListeners(); 53 | 54 | return dataList; 55 | } catch (e, s) { 56 | LoggerUtil.instance().e("http error - $nextPageUrl ---> $s"); 57 | refreshController.loadFailed(); 58 | return null; 59 | } 60 | } 61 | 62 | Future> loadData(); 63 | 64 | /// 下拉刷新 65 | Future> onRefresh(); 66 | 67 | /// 上拉加载 68 | Future> onLoadMore(); 69 | } 70 | -------------------------------------------------------------------------------- /lib/pages/splash/splash_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 3 | 4 | class SplashPage extends StatefulWidget { 5 | @override 6 | State createState() => SplashPageState(); 7 | } 8 | 9 | class SplashPageState extends State { 10 | @override 11 | void initState() { 12 | Future.delayed(Duration(seconds: 3), () { 13 | RouterManager.router 14 | .navigateTo(context, RouterManager.navigator, replace: true); 15 | }); 16 | super.initState(); 17 | } 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scaffold( 22 | body: Container( 23 | alignment: Alignment.bottomCenter, 24 | decoration: BoxDecoration( 25 | image: DecorationImage( 26 | fit: BoxFit.cover, 27 | image: AssetImage("images/bg_splash.png"), 28 | ), 29 | ), 30 | child: Container( 31 | height: 200, 32 | child: Column( 33 | mainAxisAlignment: MainAxisAlignment.center, 34 | children: [ 35 | Text( 36 | "精选视频推介,每日大开眼界", 37 | style: TextStyle( 38 | fontWeight: FontWeight.bold, 39 | fontStyle: FontStyle.italic, 40 | fontSize: 16, 41 | color: Colors.white, 42 | ), 43 | ), 44 | Padding( 45 | padding: EdgeInsets.only(top: 5), 46 | child: Text( 47 | "v1.0.4", 48 | style: TextStyle( 49 | fontWeight: FontWeight.bold, 50 | fontSize: 12, 51 | color: Colors.white, 52 | ), 53 | ), 54 | ) 55 | ], 56 | ), 57 | ), 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/data/entity/tab_info_entity.dart: -------------------------------------------------------------------------------- 1 | class TabInfoEntity { 2 | TabInfo tabInfo; 3 | 4 | TabInfoEntity({this.tabInfo}); 5 | 6 | TabInfoEntity.fromJson(Map json) { 7 | tabInfo = json['tabInfo'] != null ? new TabInfo.fromJson(json['tabInfo']) : null; 8 | } 9 | 10 | Map toJson() { 11 | final Map data = new Map(); 12 | if (this.tabInfo != null) { 13 | data['tabInfo'] = this.tabInfo.toJson(); 14 | } 15 | return data; 16 | } 17 | } 18 | 19 | class TabInfo { 20 | List tabList; 21 | int defaultIdx; 22 | 23 | TabInfo({this.tabList, this.defaultIdx}); 24 | 25 | TabInfo.fromJson(Map json) { 26 | if (json['tabList'] != null) { 27 | tabList = new List();(json['tabList'] as List).forEach((v) { tabList.add(new TabInfoItem.fromJson(v)); }); 28 | } 29 | defaultIdx = json['defaultIdx']; 30 | } 31 | 32 | Map toJson() { 33 | final Map data = new Map(); 34 | if (this.tabList != null) { 35 | data['tabList'] = this.tabList.map((v) => v.toJson()).toList(); 36 | } 37 | data['defaultIdx'] = this.defaultIdx; 38 | return data; 39 | } 40 | } 41 | 42 | class TabInfoItem { 43 | int nameType; 44 | String apiUrl; 45 | String name; 46 | int tabType; 47 | int id; 48 | dynamic adTrack; 49 | 50 | TabInfoItem({this.nameType, this.apiUrl, this.name, this.tabType, this.id, this.adTrack}); 51 | 52 | TabInfoItem.fromJson(Map json) { 53 | nameType = json['nameType']; 54 | apiUrl = json['apiUrl']; 55 | name = json['name']; 56 | tabType = json['tabType']; 57 | id = json['id']; 58 | adTrack = json['adTrack']; 59 | } 60 | 61 | Map toJson() { 62 | final Map data = new Map(); 63 | data['nameType'] = this.nameType; 64 | data['apiUrl'] = this.apiUrl; 65 | data['name'] = this.name; 66 | data['tabType'] = this.tabType; 67 | data['id'] = this.id; 68 | data['adTrack'] = this.adTrack; 69 | return data; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/provider/home_page_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 3 | import 'package:flutter_eyepetizer/data/remote_repository.dart'; 4 | import 'package:flutter_eyepetizer/provider/refresh_loadmore_model.dart'; 5 | import 'package:flutter_eyepetizer/util/constant.dart'; 6 | import 'package:flutter_eyepetizer/util/logger_util.dart'; 7 | 8 | class HomePageModel extends RefreshLoadMoreModel { 9 | 10 | @override 11 | Future loadData() async { 12 | LoggerUtil.instance().d("HomePageModel start http ---> ${isRefresh ? Constant.homePageUrl : nextPageUrl}"); 13 | var response = await Repository.getHomePageList( 14 | isRefresh ? Constant.homePageUrl : nextPageUrl); 15 | Map map = json.decode(response.toString()); 16 | var issueEntity = IssueEntity.fromJson(map); 17 | nextPageUrl = issueEntity.nextPageUrl; 18 | var list = issueEntity.issueList[0].itemList; 19 | list.removeWhere((item) { 20 | return item.type == 'banner2'; 21 | }); 22 | LoggerUtil.instance().v("HomePageModel http success ---> ${map.toString()}"); 23 | return list; 24 | } 25 | 26 | @override 27 | Future onRefresh() { 28 | isRefresh = true; 29 | return loadRemoteData(); 30 | } 31 | 32 | @override 33 | Future onLoadMore() { 34 | isRefresh = false; 35 | return loadRemoteData(); 36 | } 37 | 38 | /*Future> loadBanner() async { 39 | try { 40 | var response = await EptRepository.getHomePageList(Constant.homePageUrl); 41 | Map map = json.decode(response.toString()); 42 | var issueEntity = IssueEntity.fromJson(map); 43 | nextPageUrl = issueEntity.nextPageUrl; 44 | await loadData(url: nextPageUrl); 45 | var list = issueEntity.issueList[0].itemList; 46 | list.removeWhere((item) { 47 | return item.type == 'banner2'; 48 | }); 49 | bannerList.clear(); 50 | bannerList.addAll(list); 51 | return bannerList; 52 | } catch (e, s) { 53 | return null; 54 | } 55 | }*/ 56 | 57 | } 58 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 25 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/pages/hot/hot_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_eyepetizer/provider/hot_page_model.dart'; 3 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 4 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | /// 热门 8 | class HotPage extends StatefulWidget { 9 | @override 10 | State createState() => HotPageState(); 11 | } 12 | 13 | class HotPageState extends State with AutomaticKeepAliveClientMixin { 14 | @override 15 | Widget build(BuildContext context) { 16 | super.build(context); 17 | return ProviderWidget( 18 | model: HotPageModel(), 19 | onModelInitial: (model) { 20 | model.init(); 21 | }, 22 | builder: (context, model, child) { 23 | return DefaultTabController( 24 | length: model.tabItems.length, 25 | child: Scaffold( 26 | appBar: AppBar( 27 | title: Text('热门'), 28 | centerTitle: true, 29 | elevation: 0, 30 | ), 31 | body: HotPageWidget(), 32 | ), 33 | ); 34 | }, 35 | ); 36 | } 37 | 38 | @override 39 | bool get wantKeepAlive => true; 40 | } 41 | 42 | class HotPageWidget extends StatelessWidget { 43 | @override 44 | Widget build(BuildContext context) { 45 | HotPageModel model = Provider.of(context); 46 | if (model.isInit) { 47 | return LoadingWidget(); 48 | } 49 | return Column( 50 | children: [ 51 | Material( 52 | color: Colors.white, 53 | child: SizedBox( 54 | width: double.infinity, 55 | height: 48, 56 | child: TabBar( 57 | tabs: model.tabItems 58 | .map((tabItem) => Tab(text: tabItem.name)) 59 | .toList(), 60 | indicatorColor: Colors.black87, 61 | indicatorSize: TabBarIndicatorSize.label, 62 | ), 63 | ), 64 | ), 65 | Expanded( 66 | child: TabBarView( 67 | children: model.tabPages, 68 | ), 69 | ) 70 | ], 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/widget/nested_scroll_view_inner_scroll_position_key_widget.dart: -------------------------------------------------------------------------------- 1 | //pack your inner scrollables which are in NestedScrollView body 2 | //so that it can find the active scrollable 3 | //compare with NestedScrollViewInnerScrollPositionKeyBuilder 4 | import 'package:flutter/material.dart'; 5 | 6 | class NestedScrollViewInnerScrollPositionKeyWidget extends StatefulWidget { 7 | final Key scrollPositionKey; 8 | final Widget child; 9 | NestedScrollViewInnerScrollPositionKeyWidget( 10 | this.scrollPositionKey, this.child); 11 | 12 | static State of(BuildContext context) { 13 | return context.ancestorStateOfType( 14 | TypeMatcher<_NestedScrollViewInnerScrollPositionKeyWidgetState>()); 15 | } 16 | 17 | @override 18 | _NestedScrollViewInnerScrollPositionKeyWidgetState createState() => 19 | _NestedScrollViewInnerScrollPositionKeyWidgetState(); 20 | } 21 | 22 | class _NestedScrollViewInnerScrollPositionKeyWidgetState 23 | extends State { 24 | @override 25 | void initState() { 26 | // TODO: implement initState 27 | //WidgetsBinding.instance.addPostFrameCallback(_afterLayout); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | // TODO: implement dispose 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | //print(widget.scrollPositionKey); 40 | return widget.child; 41 | } 42 | 43 | // @override 44 | // void didChangeDependencies() { 45 | // // TODO: implement didChangeDependencies 46 | // //print("didChangeDependencies"+widget.scrollPositionKey.toString()); 47 | // super.didChangeDependencies(); 48 | // } 49 | // 50 | // @override 51 | // void didUpdateWidget(NestedScrollViewInnerScrollPositionKeyWidget oldWidget) { 52 | // // TODO: implement didUpdateWidget 53 | // //print("didUpdateWidget"+widget.scrollPositionKey.toString()+oldWidget.scrollPositionKey.toString()); 54 | // super.didUpdateWidget(oldWidget); 55 | // } 56 | 57 | // void _afterLayout(Duration timeStamp) { 58 | // final RenderBox renderBox = this.context.findRenderObject(); 59 | // final position = renderBox.localToGlobal(Offset.zero); 60 | // print("${widget.scrollPositionKey} POSITION : $position "); 61 | // } 62 | } 63 | -------------------------------------------------------------------------------- /lib/pages/discovery/follow/follow_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_eyepetizer/provider/follow_list_model.dart'; 3 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 4 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 5 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | import 'follow_item_details_widget.dart'; 9 | 10 | /// 推荐关注更多页 11 | class FollowListPage extends StatefulWidget { 12 | @override 13 | State createState() => FollowListPageState(); 14 | } 15 | 16 | class FollowListPageState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return ProviderWidget( 20 | model: FollowListModel(), 21 | onModelInitial: (model) { 22 | model.init(); 23 | }, 24 | builder: (context, model, child) { 25 | return Scaffold( 26 | appBar: AppBar( 27 | centerTitle: true, 28 | title: Text('热门关注'), 29 | elevation: 0, 30 | ), 31 | body: SmartRefresher( 32 | header: WaterDropHeader(), 33 | footer: ClassicFooter( 34 | loadStyle: LoadStyle.ShowAlways, 35 | ), 36 | enablePullUp: true, 37 | controller: model.refreshController, 38 | onRefresh: model.onRefresh, 39 | onLoading: model.onLoadMore, 40 | child: Container( 41 | child: FollowListWidget(), 42 | ), 43 | ), 44 | ); 45 | }, 46 | ); 47 | } 48 | } 49 | 50 | class FollowListWidget extends StatelessWidget { 51 | @override 52 | Widget build(BuildContext context) { 53 | FollowListModel model = Provider.of(context); 54 | if (model.isInit) { 55 | return LoadingWidget(); 56 | } 57 | return ListView.separated( 58 | shrinkWrap: true, 59 | physics: BouncingScrollPhysics(), 60 | itemBuilder: (context, index) { 61 | return FollowItemDetailsWidget(item: model.dataList[index]); 62 | }, 63 | separatorBuilder: (context, index) { 64 | return Divider( 65 | height: .5, 66 | ); 67 | }, 68 | itemCount: model.dataList.length, 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/pages/author/issue/author_issue_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 3 | import 'package:flutter_eyepetizer/provider/author_issue_model.dart'; 4 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 5 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | import 'issue_item_widget.dart'; 9 | 10 | /// 作者专辑页 11 | class AuthorIssuePage extends StatefulWidget { 12 | final String apiUrl; 13 | 14 | AuthorIssuePage({Key key, this.apiUrl}); 15 | 16 | @override 17 | State createState() => AuthorIssuePageState(); 18 | } 19 | 20 | class AuthorIssuePageState extends State 21 | with AutomaticKeepAliveClientMixin { 22 | @override 23 | Widget build(BuildContext context) { 24 | super.build(context); 25 | return ProviderWidget( 26 | model: AuthorIssueModel(), 27 | onModelInitial: (model) { 28 | model.init(widget.apiUrl); 29 | }, 30 | builder: (context, model, child) { 31 | if (model.isInit) { 32 | return LoadingWidget(); 33 | } 34 | return EasyRefresh.custom( 35 | slivers: [ 36 | SliverToBoxAdapter( 37 | child: Container( 38 | child: AuthorIssuePageWidget(), 39 | ), 40 | ), 41 | ], 42 | ); 43 | }, 44 | ); 45 | } 46 | 47 | @override 48 | bool get wantKeepAlive => true; 49 | } 50 | 51 | class AuthorIssuePageWidget extends StatelessWidget { 52 | @override 53 | Widget build(BuildContext context) { 54 | AuthorIssueModel model = Provider.of(context); 55 | return ListView.separated( 56 | shrinkWrap: true, 57 | physics: BouncingScrollPhysics(), 58 | itemBuilder: (context, index) { 59 | return IssueItemWidget( 60 | item: model.itemList[index], 61 | ); 62 | }, 63 | separatorBuilder: (context, index) { 64 | return Divider( 65 | height: .5, 66 | color: Color(0xFFDDDDDD), 67 | 68 | /// indent: 前间距, endIndent: 后间距 69 | indent: 15, 70 | endIndent: 15, 71 | ); 72 | }, 73 | itemCount: model.itemList.length, 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/pages/author/works/author_works_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 3 | import 'package:flutter_eyepetizer/provider/author_works_page_model.dart'; 4 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 5 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 6 | import 'package:provider/provider.dart'; 7 | import 'works_item_widget.dart'; 8 | 9 | /// 作者作品 10 | class AuthorWorksPage extends StatefulWidget { 11 | final String apiUrl; 12 | 13 | AuthorWorksPage({Key key, this.apiUrl}); 14 | 15 | @override 16 | State createState() => AuthorWorksPageState(); 17 | } 18 | 19 | class AuthorWorksPageState extends State 20 | with AutomaticKeepAliveClientMixin { 21 | @override 22 | Widget build(BuildContext context) { 23 | super.build(context); 24 | return ProviderWidget( 25 | model: AuthorWorksModel(), 26 | onModelInitial: (model) { 27 | model.init(widget.apiUrl); 28 | }, 29 | builder: (context, model, child) { 30 | if (model.isInit) { 31 | return LoadingWidget(); 32 | } 33 | return EasyRefresh.custom( 34 | slivers: [ 35 | SliverToBoxAdapter( 36 | child: Container( 37 | child: AuthorWorksPageWidget(), 38 | ), 39 | ), 40 | ], 41 | ); 42 | }, 43 | ); 44 | } 45 | 46 | @override 47 | bool get wantKeepAlive => true; 48 | } 49 | 50 | class AuthorWorksPageWidget extends StatelessWidget { 51 | @override 52 | Widget build(BuildContext context) { 53 | AuthorWorksModel model = Provider.of(context); 54 | return ListView.separated( 55 | shrinkWrap: true, 56 | physics: BouncingScrollPhysics(), 57 | itemBuilder: (context, index) { 58 | return WorksItemWidget( 59 | item: model.itemList[index], 60 | dataSource: model.list[index], 61 | ); 62 | }, 63 | separatorBuilder: (context, index) { 64 | return Divider( 65 | height: .5, 66 | color: Color(0xFFDDDDDD), 67 | 68 | /// indent: 前间距, endIndent: 后间距 69 | indent: 15, 70 | ); 71 | }, 72 | itemCount: model.itemList.length, 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /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 plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | applicationId "com.flutter.flutter_eyepetizer" 41 | minSdkVersion 16 42 | targetSdkVersion 29 43 | versionCode flutterVersionCode.toInteger() 44 | versionName flutterVersionName 45 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 46 | } 47 | 48 | buildTypes { 49 | release { 50 | debuggable false 51 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 52 | } 53 | } 54 | 55 | android { 56 | lintOptions { 57 | checkReleaseBuilds false 58 | abortOnError false 59 | } 60 | } 61 | 62 | aaptOptions { 63 | cruncherEnabled = false 64 | useNewCruncher = false 65 | } 66 | 67 | } 68 | 69 | flutter { 70 | source '../..' 71 | } 72 | 73 | dependencies { 74 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 75 | testImplementation 'junit:junit:4.13' 76 | androidTestImplementation 'androidx.test:runner:1.2.0' 77 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 78 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Android 10 | 11 | 12 | Class structureJava 13 | 14 | 15 | Code maturityJava 16 | 17 | 18 | ComplianceLintAndroid 19 | 20 | 21 | CorrectnessLintAndroid 22 | 23 | 24 | JUnitJava 25 | 26 | 27 | Java 28 | 29 | 30 | Java 5Java language level migration aidsJava 31 | 32 | 33 | Java 7Java language level migration aidsJava 34 | 35 | 36 | Java 8Java language level migration aidsJava 37 | 38 | 39 | Java language level migration aidsJava 40 | 41 | 42 | JavadocJava 43 | 44 | 45 | LintAndroid 46 | 47 | 48 | Numeric issuesJava 49 | 50 | 51 | PerformanceJava 52 | 53 | 54 | PerformanceLintAndroid 55 | 56 | 57 | TestNGJava 58 | 59 | 60 | Threading issuesJava 61 | 62 | 63 | UsabilityLintAndroid 64 | 65 | 66 | 67 | 68 | Android 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 80 | -------------------------------------------------------------------------------- /lib/router/router_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_eyepetizer/pages/author/author_details_page.dart'; 4 | import 'package:flutter_eyepetizer/pages/bottom_navigator.dart'; 5 | import 'package:flutter_eyepetizer/pages/discovery/category/category_list_page.dart'; 6 | import 'package:flutter_eyepetizer/pages/discovery/follow/follow_list_page.dart'; 7 | import 'package:flutter_eyepetizer/pages/splash/splash_page.dart'; 8 | import 'package:flutter_eyepetizer/pages/video/video_details_page.dart'; 9 | 10 | class RouterManager { 11 | static Router router; 12 | 13 | /// 启动/欢迎页 14 | static const String splash = "/"; 15 | 16 | /// 底部导航 17 | static const String navigator = "/navigator"; 18 | 19 | /// 视频详情 20 | static const String video = "/video"; 21 | 22 | /// 热门分类 23 | static const String category = "/category"; 24 | 25 | /// 推荐关注 26 | static const String follow = "/follow"; 27 | 28 | /// 作者详情 29 | static const String author = "/author"; 30 | 31 | static void configureRouter(Router router) { 32 | router.notFoundHandler = new Handler( 33 | // ignore: missing_return 34 | handlerFunc: (BuildContext context, Map> params) { 35 | print("ROUTE WAS NOT FOUND !!!"); 36 | }); 37 | 38 | router.define(splash, handler: new Handler( 39 | handlerFunc: (BuildContext context, Map> params) { 40 | return SplashPage(); 41 | })); 42 | router.define(navigator, handler: new Handler( 43 | handlerFunc: (BuildContext context, Map> params) { 44 | return BottomNavigator(); 45 | })); 46 | router.define(video, handler: new Handler( 47 | handlerFunc: (BuildContext context, Map> params) { 48 | String itemJson = params['itemJson']?.first; 49 | return VideoDetailsPage( 50 | itemJson: itemJson, 51 | ); 52 | })); 53 | router.define(category, handler: new Handler( 54 | handlerFunc: (BuildContext context, Map> params) { 55 | String itemJson = params['itemJson']?.first; 56 | return CategoryListPage( 57 | itemJson: itemJson, 58 | ); 59 | })); 60 | router.define(follow, handler: new Handler( 61 | handlerFunc: (BuildContext context, Map> params) { 62 | return FollowListPage(); 63 | })); 64 | router.define(author, handler: new Handler( 65 | handlerFunc: (BuildContext context, Map> params) { 66 | String itemJson = params['itemJson']?.first; 67 | return AuthorDetailsPage( 68 | itemJson: itemJson, 69 | ); 70 | })); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/device_info-0.4.0+2/","dependencies":[]},{"name":"flutter_ijkplayer","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/flutter_ijkplayer-0.3.5+1/","dependencies":[]},{"name":"path_provider","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/path_provider-1.6.11/","dependencies":[]},{"name":"shared_preferences","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.8/","dependencies":[]},{"name":"sqflite","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/sqflite-1.3.1/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/device_info-0.4.0+2/","dependencies":[]},{"name":"flutter_ijkplayer","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/flutter_ijkplayer-0.3.5+1/","dependencies":[]},{"name":"path_provider","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/path_provider-1.6.11/","dependencies":[]},{"name":"shared_preferences","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.8/","dependencies":[]},{"name":"sqflite","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/sqflite-1.3.1/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/path_provider_macos-0.0.4+3/","dependencies":[]},{"name":"shared_preferences_macos","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_macos-0.0.1+10/","dependencies":[]},{"name":"sqflite","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/sqflite-1.3.1/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_linux-0.0.2+1/","dependencies":["path_provider_linux"]}],"windows":[],"web":[{"name":"shared_preferences_web","path":"/Users/mac/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences_web-0.1.2+7/","dependencies":[]}]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_ijkplayer","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_linux","shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-08-03 11:22:03.211241","version":"1.20.0-0.0.pre"} -------------------------------------------------------------------------------- /lib/pages/search/search_page_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 4 | import 'package:fluro/fluro.dart'; 5 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 6 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 7 | import 'package:flutter_eyepetizer/util/time_util.dart'; 8 | 9 | class SearchPageItem extends StatelessWidget { 10 | final Item item; 11 | 12 | SearchPageItem({Key key, this.item}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Container( 17 | child: Stack( 18 | alignment: FractionalOffset(0.5, 0.5), 19 | children: [ 20 | GestureDetector( 21 | child: CachedNetworkImage( 22 | height: 220, 23 | width: double.infinity, 24 | fit: BoxFit.cover, 25 | imageUrl: item.data.cover.feed, 26 | errorWidget: (context, url, error) => 27 | Image.asset('images/img_load_fail.png'), 28 | ), 29 | onTap: () { 30 | /// 点击图片跳转至视频详情页 31 | String itemJson = FluroConvertUtils.object2string(this.item); 32 | RouterManager.router.navigateTo( 33 | context, 34 | RouterManager.video + "?itemJson=$itemJson", 35 | transition: TransitionType.inFromRight, 36 | ); 37 | }, 38 | ), 39 | Positioned( 40 | child: Column( 41 | children: [ 42 | Text( 43 | item.data.title, 44 | maxLines: 1, 45 | style: TextStyle( 46 | color: Colors.white, 47 | fontWeight: FontWeight.bold, 48 | fontSize: 14, 49 | ), 50 | ), 51 | Container( 52 | margin: EdgeInsets.only(top: 10), 53 | child: Padding( 54 | padding: EdgeInsets.all(5), 55 | child: Text( 56 | TimeUtil.formatDuration(item.data.duration), 57 | style: TextStyle( 58 | fontSize: 13, 59 | fontWeight: FontWeight.bold, 60 | color: Colors.white, 61 | ), 62 | ), 63 | ), 64 | decoration: BoxDecoration( 65 | color: Colors.black26, 66 | borderRadius: BorderRadius.circular(2), 67 | ), 68 | ), 69 | ], 70 | ), 71 | ), 72 | ], 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - device_info (0.0.1): 3 | - Flutter 4 | - Flutter (1.0.0) 5 | - flutter_ijkplayer (0.0.1): 6 | - Flutter 7 | - FlutterIJK (~> 0.1.0) 8 | - FlutterIJK (0.1.0) 9 | - FMDB (2.7.5): 10 | - FMDB/standard (= 2.7.5) 11 | - FMDB/standard (2.7.5) 12 | - path_provider (0.0.1): 13 | - Flutter 14 | - path_provider_macos (0.0.1): 15 | - Flutter 16 | - shared_preferences (0.0.1): 17 | - Flutter 18 | - shared_preferences_macos (0.0.1): 19 | - Flutter 20 | - shared_preferences_web (0.0.1): 21 | - Flutter 22 | - sqflite (0.0.1): 23 | - Flutter 24 | - FMDB (~> 2.7.2) 25 | 26 | DEPENDENCIES: 27 | - device_info (from `.symlinks/plugins/device_info/ios`) 28 | - Flutter (from `Flutter`) 29 | - flutter_ijkplayer (from `.symlinks/plugins/flutter_ijkplayer/ios`) 30 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 31 | - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) 32 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 33 | - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) 34 | - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) 35 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 36 | 37 | SPEC REPOS: 38 | trunk: 39 | - FlutterIJK 40 | - FMDB 41 | 42 | EXTERNAL SOURCES: 43 | device_info: 44 | :path: ".symlinks/plugins/device_info/ios" 45 | Flutter: 46 | :path: Flutter 47 | flutter_ijkplayer: 48 | :path: ".symlinks/plugins/flutter_ijkplayer/ios" 49 | path_provider: 50 | :path: ".symlinks/plugins/path_provider/ios" 51 | path_provider_macos: 52 | :path: ".symlinks/plugins/path_provider_macos/ios" 53 | shared_preferences: 54 | :path: ".symlinks/plugins/shared_preferences/ios" 55 | shared_preferences_macos: 56 | :path: ".symlinks/plugins/shared_preferences_macos/ios" 57 | shared_preferences_web: 58 | :path: ".symlinks/plugins/shared_preferences_web/ios" 59 | sqflite: 60 | :path: ".symlinks/plugins/sqflite/ios" 61 | 62 | SPEC CHECKSUMS: 63 | device_info: 3ebad48f726348f69abd802f3334a8d1ed795fbd 64 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 65 | flutter_ijkplayer: ef9006131553deb6d8ae6d7fbd3bcbf93b135930 66 | FlutterIJK: 68b00c1d11162b5016f3611853aa699e198bd1d2 67 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 68 | path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d 69 | path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 70 | shared_preferences: 430726339841afefe5142b9c1f50cb6bd7793e01 71 | shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 72 | shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 73 | sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 74 | 75 | PODFILE CHECKSUM: 1d146de45fa5c8e45e26913e853ef93c3de924c7 76 | 77 | COCOAPODS: 1.8.3 78 | -------------------------------------------------------------------------------- /lib/provider/provider_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:provider/provider.dart'; 3 | 4 | /// ProviderWidget 封装 5 | class ProviderWidget extends StatefulWidget { 6 | final T model; 7 | final Widget child; 8 | final Widget Function(BuildContext context, T model, Widget child) builder; 9 | final Function(T) onModelInitial; 10 | 11 | ProviderWidget({ 12 | Key key, 13 | this.model, 14 | this.child, 15 | this.builder, 16 | this.onModelInitial, 17 | }); 18 | 19 | @override 20 | ProviderWidgetState createState() => ProviderWidgetState(); 21 | } 22 | 23 | class ProviderWidgetState 24 | extends State> { 25 | T model; 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | this.model = widget.model; 31 | if (widget.onModelInitial != null) { 32 | widget.onModelInitial(this.model); 33 | } 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return ChangeNotifierProvider( 39 | builder: (_) => this.model, 40 | child: Consumer( 41 | builder: widget.builder, 42 | child: widget.child, 43 | ), 44 | ); 45 | } 46 | } 47 | 48 | class ProviderWidget2 49 | extends StatefulWidget { 50 | final Widget Function(BuildContext context, A model1, B model2, Widget child) 51 | builder; 52 | final A model1; 53 | final B model2; 54 | final Widget child; 55 | final Function(A, B) onModelInitial; 56 | 57 | ProviderWidget2({ 58 | Key key, 59 | this.builder, 60 | this.model1, 61 | this.model2, 62 | this.child, 63 | this.onModelInitial, 64 | }) : super(key: key); 65 | 66 | _ProviderWidgetState2 createState() => _ProviderWidgetState2(); 67 | } 68 | 69 | class _ProviderWidgetState2 70 | extends State> { 71 | A model1; 72 | B model2; 73 | 74 | @override 75 | void initState() { 76 | model1 = widget.model1; 77 | model2 = widget.model2; 78 | 79 | if (widget.onModelInitial != null) { 80 | widget.onModelInitial(model1, model2); 81 | } 82 | 83 | super.initState(); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return MultiProvider( 89 | providers: [ 90 | ChangeNotifierProvider( 91 | builder: (context) => model1, 92 | ), 93 | ChangeNotifierProvider( 94 | builder: (context) => model2, 95 | ) 96 | ], 97 | child: Consumer2( 98 | builder: widget.builder, 99 | child: widget.child, 100 | )); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/provider/author_details_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_eyepetizer/data/entity/author_info_entity.dart'; 7 | import 'package:flutter_eyepetizer/http/http.dart'; 8 | import 'package:flutter_eyepetizer/pages/author/author_details_page.dart'; 9 | import 'package:flutter_eyepetizer/pages/author/issue/author_issue_page.dart'; 10 | import 'package:flutter_eyepetizer/pages/author/main/author_main_page.dart'; 11 | import 'package:flutter_eyepetizer/pages/author/works/author_works_page.dart'; 12 | import 'package:flutter_eyepetizer/util/constant.dart'; 13 | 14 | class AuthorDetailsModel extends ChangeNotifier { 15 | AuthorDetailsPageState vsync; 16 | 17 | AuthorDetailsModel({this.vsync}); 18 | 19 | int id; 20 | bool isInit; 21 | 22 | List tabItems = []; 23 | List pages = []; 24 | List pageList = []; 25 | 26 | AuthorInfoPgcinfo info; 27 | TabController tabController; 28 | 29 | /// 初始化 30 | init(id) async { 31 | this.id = id; 32 | _initPage(true); 33 | await getTabInfo(); 34 | } 35 | 36 | void _initPage(bool isInit) { 37 | this.isInit = isInit; 38 | notifyListeners(); 39 | } 40 | 41 | getTabInfo() async { 42 | try { 43 | var response = await HttpUtil.buildDio().get( 44 | Constant.authorUrl, 45 | queryParameters: { 46 | 'id': id, 47 | "udid": 'd2807c895f0348a180148c9dfa6f2feeac0781b5', 48 | }, 49 | options: Options(headers: httpHeaders), 50 | ); 51 | print( 52 | "request url : ${Constant.authorUrl}?id=$id&udid=d2807c895f0348a180148c9dfa6f2feeac0781b5"); 53 | Map map = json.decode(response.toString()); 54 | var authorInfoEntity = AuthorInfoEntity.fromJson(map); 55 | var tabItemList = authorInfoEntity.tabInfo.tabList; 56 | this.info = authorInfoEntity.pgcInfo; 57 | this.tabItems = tabItemList; 58 | 59 | if (tabItemList.length == 2) { 60 | pages = [ 61 | AuthorMainPage( 62 | apiUrl: tabItemList[0].apiUrl, 63 | ), 64 | AuthorWorksPage( 65 | apiUrl: tabItemList[1].apiUrl, 66 | ) 67 | ]; 68 | } else if (tabItemList.length == 3) { 69 | pages = [ 70 | AuthorMainPage( 71 | apiUrl: tabItemList[0].apiUrl, 72 | ), 73 | AuthorWorksPage( 74 | apiUrl: tabItemList[1].apiUrl, 75 | ), 76 | AuthorIssuePage( 77 | apiUrl: tabItemList[2].apiUrl, 78 | ) 79 | ]; 80 | } 81 | 82 | if (isInit) { 83 | this.tabController = 84 | TabController(length: tabItemList.length, vsync: this.vsync); 85 | } 86 | 87 | _initPage(false); 88 | notifyListeners(); 89 | } catch (e, s) { 90 | print(e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/pages/bottom_navigator.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'discovery/discovery_page.dart'; 6 | import 'home/home_page.dart'; 7 | import 'hot/hot_page.dart'; 8 | import 'mine/mine_page.dart'; 9 | 10 | class BottomNavigator extends StatefulWidget{ 11 | 12 | @override 13 | State createState() => BottomNavigatorState(); 14 | 15 | } 16 | 17 | class BottomNavigatorState extends State{ 18 | 19 | // 记录当前 tab 选择位置 20 | int tabIndex = 0; 21 | var tabImages; 22 | var tabPages; 23 | 24 | final tabTextStyleNormal = TextStyle(color: Colors.black38); 25 | final tabTextStyleSelected = TextStyle(color: Colors.black); 26 | final tabTitles = ['首页精选', '发现', '热门', '我的']; 27 | 28 | var body; 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | tabImages ??= [ 34 | [ 35 | getTabImage('images/ic_home_normal.png'), 36 | getTabImage('images/ic_home_selected.png') 37 | ], 38 | [ 39 | getTabImage('images/ic_discovery_normal.png'), 40 | getTabImage('images/ic_discovery_selected.png') 41 | ], 42 | [ 43 | getTabImage('images/ic_hot_normal.png'), 44 | getTabImage('images/ic_hot_selected.png') 45 | ], 46 | [ 47 | getTabImage('images/ic_mine_normal.png'), 48 | getTabImage('images/ic_mine_selected.png') 49 | ], 50 | ]; 51 | tabPages ??= [HomePage(), DiscoveryPage(), HotPage(), MinePage()]; 52 | } 53 | 54 | Image getTabImage(imagePath) => Image.asset(imagePath, width: 22, height: 22); 55 | 56 | Image getTabIcon(int index) { 57 | if (tabIndex == index) { 58 | return tabImages[index][1]; 59 | } 60 | return tabImages[index][0]; 61 | } 62 | 63 | TextStyle getTabTextStyle(int index) { 64 | if (tabIndex == index) { 65 | return tabTextStyleSelected; 66 | } 67 | return tabTextStyleNormal; 68 | } 69 | 70 | Text getTabTitle(index) => Text( 71 | tabTitles[index], 72 | style: getTabTextStyle(index), 73 | ); 74 | 75 | @override 76 | Widget build(BuildContext context) { 77 | body = IndexedStack( 78 | children: tabPages, 79 | index: tabIndex, 80 | ); 81 | return Scaffold( 82 | body: this.body, 83 | bottomNavigationBar: CupertinoTabBar( 84 | backgroundColor: Color(0xFFFFFFFF), 85 | items: [ 86 | BottomNavigationBarItem(icon: getTabIcon(0), title: getTabTitle(0)), 87 | BottomNavigationBarItem(icon: getTabIcon(1), title: getTabTitle(1)), 88 | BottomNavigationBarItem(icon: getTabIcon(2), title: getTabTitle(2)), 89 | BottomNavigationBarItem(icon: getTabIcon(3), title: getTabTitle(3)), 90 | ], 91 | currentIndex: tabIndex, 92 | onTap: (index) => { 93 | setState(() { 94 | tabIndex = index; 95 | }) 96 | }, 97 | ), 98 | ); 99 | } 100 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/pages/hot/rank_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_eyepetizer/pages/home/home_page_item.dart'; 3 | import 'package:flutter_eyepetizer/provider/rank_page_model.dart'; 4 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 5 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 6 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | /// 热门排行 10 | class RankPage extends StatefulWidget { 11 | final String pageUrl; 12 | 13 | RankPage({Key key, this.pageUrl}) : super(key: key); 14 | 15 | @override 16 | State createState() => RankPageState(); 17 | } 18 | 19 | class RankPageState extends State with AutomaticKeepAliveClientMixin { 20 | @override 21 | Widget build(BuildContext context) { 22 | super.build(context); 23 | return ProviderWidget( 24 | model: RankPageModel(widget.pageUrl), 25 | onModelInitial: (model) { 26 | model.init(); 27 | }, 28 | builder: (context, model, child) { 29 | return Container( 30 | color: Color(0xFFF4F4F4), 31 | child: RefreshConfiguration( 32 | enableLoadingWhenNoData: false, 33 | child: SmartRefresher( 34 | header: WaterDropHeader(), 35 | footer: ClassicFooter( 36 | height: 50, 37 | textStyle: TextStyle( 38 | color: Colors.grey, 39 | fontSize: 12, 40 | ), 41 | noDataText: "——— 我是有底线的 ———", 42 | ), 43 | enablePullUp: true, 44 | controller: model.refreshController, 45 | onRefresh: model.onRefresh, 46 | child: Container( 47 | child: RankPageWidget(), 48 | ), 49 | ), 50 | ) /*EasyRefresh.custom( 51 | enableControlFinishRefresh: true, 52 | header: MyClassicalHeader( 53 | enableInfiniteRefresh: false, 54 | ), 55 | controller: model.controller, 56 | scrollController: model.scrollController, 57 | onRefresh: model.onRefresh, 58 | slivers: [ 59 | SliverToBoxAdapter( 60 | child: Container( 61 | child: RankPageWidget(), 62 | ), 63 | ), 64 | ], 65 | )*/ 66 | , 67 | ); 68 | }, 69 | ); 70 | } 71 | 72 | @override 73 | bool get wantKeepAlive => true; 74 | } 75 | 76 | class RankPageWidget extends StatelessWidget { 77 | @override 78 | Widget build(BuildContext context) { 79 | RankPageModel model = Provider.of(context); 80 | if (model.isInit) { 81 | return LoadingWidget(); 82 | } 83 | return ListView.separated( 84 | shrinkWrap: true, 85 | physics: BouncingScrollPhysics(), 86 | itemBuilder: (context, index) { 87 | return HomePageItem(item: model.dataList[index]); 88 | }, 89 | separatorBuilder: (context, index) { 90 | return Divider( 91 | height: 10, 92 | color: Color(0xFFF4F4F4), 93 | ); 94 | }, 95 | itemCount: model.dataList.length, 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/pages/discovery/follow_item_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 4 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 5 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 6 | 7 | class FollowItemWidget extends StatelessWidget { 8 | final Item item; 9 | 10 | FollowItemWidget({Key key, this.item}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return GestureDetector( 15 | onTap: () { 16 | 17 | }, 18 | child: Container( 19 | color: Colors.white, 20 | padding: EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 10), 21 | child: Row( 22 | mainAxisAlignment: MainAxisAlignment.start, 23 | crossAxisAlignment: CrossAxisAlignment.center, 24 | children: [ 25 | ClipOval( 26 | child: CachedNetworkImage( 27 | width: 40, 28 | height: 40, 29 | imageUrl: item.data.header.icon, 30 | placeholder: (context, url) => CircularProgressIndicator( 31 | strokeWidth: 2.5, 32 | backgroundColor: Colors.deepPurple[600], 33 | ), 34 | ), 35 | ), 36 | Expanded( 37 | flex: 1, 38 | child: Padding( 39 | padding: EdgeInsets.only(left: 10, right: 10), 40 | child: Column( 41 | mainAxisAlignment: MainAxisAlignment.center, 42 | crossAxisAlignment: CrossAxisAlignment.start, 43 | children: [ 44 | Text( 45 | item.data.header.title, 46 | style: TextStyle( 47 | fontSize: 14, 48 | color: Colors.black, 49 | ), 50 | ), 51 | Padding( 52 | padding: EdgeInsets.only(top: 3), 53 | child: Text( 54 | item.data.header.description, 55 | maxLines: 1, 56 | overflow: TextOverflow.ellipsis, 57 | style: TextStyle( 58 | fontSize: 11, 59 | color: Colors.grey, 60 | ), 61 | ), 62 | ) 63 | ], 64 | ), 65 | ), 66 | ), 67 | GestureDetector( 68 | child: Container( 69 | padding: EdgeInsets.all(7), 70 | child: Text( 71 | '+ 关注', 72 | style: TextStyle( 73 | fontSize: 12, 74 | color: Colors.grey, 75 | ), 76 | ), 77 | decoration: BoxDecoration( 78 | color: Color(0xFFF4F4F4), 79 | borderRadius: BorderRadius.circular(2), 80 | ), 81 | ), 82 | onTap: (() { 83 | print('点击关注'); 84 | }), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/pages/author/main/author_main_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 3 | import 'package:flutter_eyepetizer/provider/author_main_page_model.dart'; 4 | import 'package:flutter_eyepetizer/pages/discovery/follow/follow_item_details_widget.dart'; 5 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 6 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | import 'collection_card.dart'; 10 | import 'video_welcom_page.dart'; 11 | 12 | /// 作者首页 13 | class AuthorMainPage extends StatefulWidget { 14 | final String apiUrl; 15 | 16 | AuthorMainPage({Key key, this.apiUrl}); 17 | 18 | @override 19 | State createState() => AuthorMainPageState(); 20 | } 21 | 22 | class AuthorMainPageState extends State 23 | with AutomaticKeepAliveClientMixin { 24 | @override 25 | Widget build(BuildContext context) { 26 | super.build(context); 27 | return ProviderWidget( 28 | model: AuthorMainPageModel(), 29 | onModelInitial: (model) { 30 | model.init(widget.apiUrl); 31 | }, 32 | builder: (context, model, child) { 33 | if (model.isInit) { 34 | return LoadingWidget(); 35 | } 36 | return AuthorMainPageWidget(); 37 | }, 38 | ); 39 | } 40 | 41 | @override 42 | bool get wantKeepAlive => true; 43 | } 44 | 45 | class AuthorMainPageWidget extends StatelessWidget { 46 | @override 47 | Widget build(BuildContext context) { 48 | AuthorMainPageModel model = Provider.of(context); 49 | return ListView.builder( 50 | physics: ClampingScrollPhysics(), 51 | itemBuilder: (context, index) { 52 | var item = model.itemList[index]; 53 | if (item.type == 'videoCollectionOfHorizontalScrollCard') { 54 | return CollectionCard( 55 | item: item, 56 | childIndex: index, 57 | ); 58 | } else if (item.type == 'textHeader') { 59 | return Padding( 60 | padding: EdgeInsets.only( 61 | top: 10, 62 | left: 10, 63 | bottom: 10, 64 | ), 65 | child: Text( 66 | item.data.text, 67 | style: TextStyle( 68 | fontSize: 16, 69 | color: Colors.black54, 70 | fontWeight: FontWeight.bold, 71 | ), 72 | ), 73 | ); 74 | } else if (item.type == 'textFooter') { 75 | return Container( 76 | color: Colors.white, 77 | alignment: Alignment.centerRight, 78 | padding: EdgeInsets.all(10), 79 | child: Text( 80 | '${item.data.text} >', 81 | style: TextStyle( 82 | fontSize: 12, 83 | color: Colors.black, 84 | ), 85 | ), 86 | ); 87 | } else if (item.type == 'video') { 88 | return VideoWelcomePage( 89 | item: item, 90 | ); 91 | } else if (item.type == 'videoCollectionWithBrief') { 92 | return FollowItemDetailsWidget( 93 | item: item, 94 | ); 95 | } 96 | return Text(item.type); 97 | }, 98 | itemCount: model.itemList.length, 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/pages/author/main/video_welcom_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 5 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 6 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 7 | import 'package:flutter_eyepetizer/util/time_util.dart'; 8 | 9 | class VideoWelcomePage extends StatelessWidget { 10 | final Item item; 11 | 12 | VideoWelcomePage({Key key, this.item}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return GestureDetector( 17 | onTap: () { 18 | String itemJson = FluroConvertUtils.object2string(this.item); 19 | RouterManager.router.navigateTo( 20 | context, 21 | RouterManager.video + "?itemJson=$itemJson", 22 | transition: TransitionType.inFromRight, 23 | ); 24 | }, 25 | child: Container( 26 | color: Colors.white, 27 | padding: EdgeInsets.only(left: 10, right: 10), 28 | child: Row( 29 | children: [ 30 | Padding( 31 | padding: EdgeInsets.only(right: 15, top: 10, bottom: 5), 32 | child: Stack( 33 | alignment: FractionalOffset(0.95, 0.90), 34 | children: [ 35 | ClipRRect( 36 | borderRadius: BorderRadius.circular(5), 37 | child: CachedNetworkImage( 38 | fit: BoxFit.cover, 39 | imageUrl: item.data.cover.detail, 40 | width: 135, 41 | height: 80, 42 | ), 43 | ), 44 | Positioned( 45 | child: Container( 46 | child: Padding( 47 | padding: EdgeInsets.all(3), 48 | child: Text( 49 | TimeUtil.formatDuration(item.data.duration), 50 | style: TextStyle( 51 | fontSize: 10, 52 | color: Colors.white, 53 | ), 54 | ), 55 | ), 56 | decoration: BoxDecoration( 57 | color: Colors.black26, 58 | borderRadius: BorderRadius.circular(2), 59 | ), 60 | ), 61 | ) 62 | ], 63 | ), 64 | ), 65 | Expanded( 66 | flex: 1, 67 | child: Column( 68 | crossAxisAlignment: CrossAxisAlignment.start, 69 | children: [ 70 | Text( 71 | item.data.title, 72 | style: TextStyle( 73 | color: Colors.black, 74 | fontSize: 14, 75 | ), 76 | ), 77 | Padding( 78 | padding: EdgeInsets.only(top: 15), 79 | child: Text( 80 | '#${item.data.category} / ${item.data.author.name}', 81 | style: TextStyle( 82 | color: Colors.grey, 83 | fontSize: 12, 84 | ), 85 | ), 86 | ) 87 | ], 88 | ), 89 | ), 90 | ], 91 | ), 92 | ), 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_eyepetizer 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 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.4+1 15 | 16 | environment: 17 | sdk: ">=2.2.2 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | 27 | dev_dependencies: 28 | flutter_test: 29 | sdk: flutter 30 | 31 | # 网络请求 32 | dio: 2.1.13 33 | 34 | # 图片加载 35 | cached_network_image: 2.0.0-rc 36 | 37 | # 工具类 38 | flustars: 0.2.6+1 39 | device_info: 0.4.0+2 40 | 41 | # 视频播放 42 | flutter_ijkplayer: 0.3.5+1 43 | 44 | # 下拉刷新、上拉加载 45 | flutter_easyrefresh: ^2.0.4 46 | pull_to_refresh: ^1.5.7 47 | shimmer: 1.0.0 48 | 49 | # 状态管理 50 | provider: ^3.0.0+1 51 | 52 | # 吐司 53 | oktoast: ^2.1.7 54 | 55 | # 路由管理 56 | fluro: ^1.5.1 57 | 58 | # 日志打印 59 | logger: ^0.7.0+2 60 | 61 | # 屏幕适配 62 | # flutter_screenutil: ^0.5.3 63 | 64 | # For information on the generic Dart part of this file, see the 65 | # following page: https://dart.dev/tools/pub/pubspec 66 | 67 | # The following section is specific to Flutter. 68 | flutter: 69 | 70 | # The following line ensures that the Material Icons font is 71 | # included with your application, so that you can use the icons in 72 | # the material Icons class. 73 | uses-material-design: true 74 | 75 | # To add assets to your application, add an assets section, like this: 76 | # assets: 77 | # - images/a_dot_burr.jpeg 78 | # - images/a_dot_ham.jpeg 79 | 80 | assets: 81 | - images/ 82 | 83 | # An image asset can refer to one or more resolution-specific "variants", see 84 | # https://flutter.dev/assets-and-images/#resolution-aware. 85 | 86 | # For details regarding adding assets from package dependencies, see 87 | # https://flutter.dev/assets-and-images/#from-packages 88 | 89 | # To add custom fonts to your application, add a fonts section here, 90 | # in this "flutter" section. Each entry in this list should have a 91 | # "family" key with the font family name, and a "fonts" key with a 92 | # list giving the asset and other descriptors for the font. For 93 | # example: 94 | # fonts: 95 | # - family: Schyler 96 | # fonts: 97 | # - asset: fonts/Schyler-Regular.ttf 98 | # - asset: fonts/Schyler-Italic.ttf 99 | # style: italic 100 | # - family: Trajan Pro 101 | # fonts: 102 | # - asset: fonts/TrajanPro.ttf 103 | # - asset: fonts/TrajanPro_Bold.ttf 104 | # weight: 700 105 | # 106 | # For details regarding fonts from package dependencies, 107 | # see https://flutter.dev/custom-fonts/#from-packages 108 | -------------------------------------------------------------------------------- /lib/pages/video/video_related_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 5 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 6 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 7 | import 'package:flutter_eyepetizer/util/time_util.dart'; 8 | 9 | class VideoRelatedPage extends StatelessWidget { 10 | final Item item; 11 | final callback; 12 | 13 | VideoRelatedPage({Key key, this.item, this.callback}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return GestureDetector( 18 | onTap: () { 19 | this.callback(); 20 | String itemJson = FluroConvertUtils.object2string(this.item); 21 | RouterManager.router.navigateTo( 22 | context, 23 | RouterManager.video + "?itemJson=$itemJson", 24 | transition: TransitionType.inFromRight, 25 | ); 26 | }, 27 | child: Container( 28 | padding: EdgeInsets.only(left: 15, right: 10), 29 | child: Row( 30 | children: [ 31 | Padding( 32 | padding: EdgeInsets.only(right: 15, top: 5, bottom: 5), 33 | child: Stack( 34 | alignment: FractionalOffset(0.95, 0.90), 35 | children: [ 36 | ClipRRect( 37 | borderRadius: BorderRadius.circular(5), 38 | child: CachedNetworkImage( 39 | fit: BoxFit.cover, 40 | imageUrl: item.data.cover.detail, 41 | width: 135, 42 | height: 80, 43 | ), 44 | ), 45 | Positioned( 46 | child: Container( 47 | child: Padding( 48 | padding: EdgeInsets.all(3), 49 | child: Text( 50 | TimeUtil.formatDuration(item.data.duration), 51 | style: TextStyle( 52 | fontSize: 10, 53 | color: Colors.white, 54 | ), 55 | ), 56 | ), 57 | decoration: BoxDecoration( 58 | color: Colors.black45, 59 | borderRadius: BorderRadius.circular(2), 60 | ), 61 | ), 62 | ) 63 | ], 64 | ), 65 | ), 66 | Expanded( 67 | flex: 1, 68 | child: Column( 69 | crossAxisAlignment: CrossAxisAlignment.start, 70 | children: [ 71 | Text( 72 | item.data.title, 73 | style: TextStyle( 74 | color: Colors.white, 75 | fontSize: 14, 76 | ), 77 | ), 78 | Padding( 79 | padding: EdgeInsets.only(top: 15), 80 | child: Text( 81 | '#${item.data.category} / ${item.data.author.name}', 82 | style: TextStyle( 83 | color: Colors.white, 84 | fontSize: 12, 85 | ), 86 | ), 87 | ) 88 | ], 89 | ), 90 | ), 91 | ], 92 | ), 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '8.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 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |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 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/pages/author/issue/issue_item_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:fluro/fluro.dart'; 4 | import 'package:flutter_eyepetizer/data/entity/issue_entity.dart'; 5 | import 'package:flutter_eyepetizer/pages/discovery/follow/follow_item_list_widget.dart'; 6 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 7 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 8 | 9 | class IssueItemWidget extends StatelessWidget { 10 | final Item item; 11 | 12 | IssueItemWidget({Key key, this.item}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Container( 17 | color: Colors.white, 18 | padding: EdgeInsets.only(left: 15, top: 10, right: 15, bottom: 10), 19 | child: Column( 20 | children: [ 21 | Row( 22 | mainAxisAlignment: MainAxisAlignment.start, 23 | crossAxisAlignment: CrossAxisAlignment.center, 24 | children: [ 25 | GestureDetector( 26 | onTap: () { 27 | String itemJson = FluroConvertUtils.object2string(item); 28 | RouterManager.router.navigateTo( 29 | context, 30 | RouterManager.author + "?itemJson=$itemJson", 31 | transition: TransitionType.inFromRight, 32 | ); 33 | }, 34 | child: ClipOval( 35 | child: CachedNetworkImage( 36 | width: 40, 37 | height: 40, 38 | imageUrl: item.data.header.icon, 39 | placeholder: (context, url) => CircularProgressIndicator( 40 | strokeWidth: 2.5, 41 | backgroundColor: Colors.deepPurple[600], 42 | ), 43 | ), 44 | ), 45 | ), 46 | Expanded( 47 | flex: 1, 48 | child: Padding( 49 | padding: EdgeInsets.only(left: 10, right: 10), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.center, 52 | crossAxisAlignment: CrossAxisAlignment.start, 53 | children: [ 54 | Text( 55 | item.data.header.title, 56 | style: TextStyle( 57 | fontSize: 14, 58 | color: Colors.black, 59 | ), 60 | ), 61 | Padding( 62 | padding: EdgeInsets.only(top: 3), 63 | child: Text( 64 | item.data.header.description, 65 | maxLines: 1, 66 | overflow: TextOverflow.ellipsis, 67 | style: TextStyle( 68 | fontSize: 12, 69 | color: Colors.grey, 70 | ), 71 | ), 72 | ) 73 | ], 74 | ), 75 | ), 76 | ), 77 | ], 78 | ), 79 | Container( 80 | height: 245, 81 | child: ListView.builder( 82 | itemBuilder: (context, index) { 83 | return FollowItemListWidget( 84 | item: item.data.itemList[index], 85 | index: index, 86 | ); 87 | }, 88 | shrinkWrap: true, 89 | scrollDirection: Axis.horizontal, 90 | itemCount: item.data.itemList.length, 91 | ), 92 | ), 93 | ], 94 | ), 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /android/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /lib/pages/discovery/category/category_list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 3 | import 'package:flutter_eyepetizer/router/router_manager.dart'; 4 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 5 | import 'package:flutter_eyepetizer/data/entity/category_entity.dart'; 6 | import 'package:flutter_eyepetizer/pages/discovery/category/category_list_model.dart'; 7 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 8 | import 'package:flutter_eyepetizer/util/fluro_convert_util.dart'; 9 | 10 | import 'category_list_item.dart'; 11 | 12 | /// 分类列表 13 | class CategoryListPage extends StatefulWidget { 14 | final String itemJson; 15 | 16 | CategoryListPage({Key key, this.itemJson}) : super(key: key); 17 | 18 | @override 19 | State createState() => CategoryListPageState(); 20 | } 21 | 22 | class CategoryListPageState extends State { 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | CategoryEntity item = 27 | CategoryEntity.fromJson(FluroConvertUtils.string2map(widget.itemJson)); 28 | 29 | return ProviderWidget( 30 | model: CategoryListModel(item.id), 31 | onModelInitial: (model) { 32 | model.init(); 33 | }, 34 | builder: (context, model, child) { 35 | return MaterialApp( 36 | theme: ThemeData( 37 | brightness: Brightness.light, 38 | primaryColor: 39 | Colors.white, //Changing this will change the color of the TabBar 40 | ), 41 | home: Scaffold( 42 | body: SmartRefresher( 43 | onRefresh: model.onRefresh, 44 | onLoading: model.onLoadMore, 45 | enablePullUp: true, 46 | controller: model.refreshController, 47 | header: MaterialClassicHeader( 48 | distance: 80.0, 49 | backgroundColor: Colors.redAccent, 50 | ), 51 | child: CustomScrollView( 52 | physics: ClampingScrollPhysics(), 53 | slivers: [ 54 | SliverAppBar( 55 | leading: GestureDetector( 56 | child: Icon(Icons.arrow_back), 57 | onTap: () { 58 | RouterManager.router.pop(context); 59 | }, 60 | ), 61 | title: Text(item.name), 62 | expandedHeight: 180.0, 63 | pinned: true, 64 | backgroundColor: Colors.white, 65 | flexibleSpace: FlexibleSpaceBar( 66 | background: Image.network( 67 | item.bgPicture, 68 | fit: BoxFit.cover, 69 | ), 70 | ), 71 | ), 72 | SliverList( 73 | delegate: SliverChildBuilderDelegate( 74 | (context, index) { 75 | var i = index; 76 | i -= 1; 77 | if (i.isOdd) { 78 | i = (i + 1) ~/ 2; 79 | return CategoryListItem( 80 | item: model.dataList[i], 81 | ); 82 | } 83 | i = i ~/ 2; 84 | return Divider( 85 | height: 10, 86 | color: Color(0xFFF4F4F4), 87 | ); 88 | 89 | // return CategoryListItem(item: model.itemList[index]); 90 | }, 91 | childCount: model.dataList.length * 2, 92 | ), 93 | ), 94 | ], 95 | ), 96 | ), 97 | ), 98 | ); 99 | }, 100 | ); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/pages/home/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_eyepetizer/pages/search/search_page.dart'; 3 | import 'package:flutter_eyepetizer/provider/provider_widget.dart'; 4 | import 'package:flutter_eyepetizer/widget/loading_widget.dart'; 5 | import 'package:flutter_eyepetizer/widget/search/search_bar.dart' as search_bar; 6 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | import 'home_page_item.dart'; 10 | import '../../provider/home_page_model.dart'; 11 | import 'time_title_item.dart'; 12 | 13 | /// 首页 14 | class HomePage extends StatefulWidget { 15 | @override 16 | State createState() => HomePageState(); 17 | } 18 | 19 | class HomePageState extends State with AutomaticKeepAliveClientMixin { 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | super.build(context); 24 | return ProviderWidget( 25 | model: HomePageModel(), 26 | onModelInitial: (model) { 27 | model.init(); 28 | }, 29 | builder: (context, model, child) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: Text('每日精选', style: TextStyle(color: Colors.black)), 33 | centerTitle: true, 34 | backgroundColor: Colors.white, 35 | 36 | /// 去除阴影 37 | elevation: 0, 38 | actions: [ 39 | IconButton( 40 | icon: Icon( 41 | Icons.search, 42 | color: Colors.black87, 43 | ), 44 | onPressed: () { 45 | search_bar.showSearch( 46 | context: context, 47 | delegate: SearchBarDelegate(), 48 | ); 49 | }, 50 | ), 51 | ], 52 | ), 53 | body: Container( 54 | color: Color(0xFFF4F4F4), 55 | child: RefreshConfiguration( 56 | enableLoadingWhenNoData: false, 57 | child: SmartRefresher( 58 | header: WaterDropHeader(), 59 | footer: ClassicFooter( 60 | loadStyle: LoadStyle.ShowAlways, 61 | ), 62 | enablePullUp: true, 63 | controller: model.refreshController, 64 | onRefresh: model.onRefresh, 65 | onLoading: model.onLoadMore, 66 | child: Container( 67 | child: HomePageListWidget(), 68 | ), 69 | ), 70 | ), 71 | ), 72 | ); 73 | }, 74 | ); 75 | } 76 | 77 | @override 78 | bool get wantKeepAlive => true; 79 | } 80 | 81 | class HomePageListWidget extends StatelessWidget { 82 | @override 83 | Widget build(BuildContext context) { 84 | HomePageModel model = Provider.of(context); 85 | if (model.isInit) { 86 | return LoadingWidget(); 87 | } 88 | return ListView.separated( 89 | shrinkWrap: true, 90 | physics: BouncingScrollPhysics(), 91 | itemBuilder: (context, index) { 92 | var item = model.dataList[index]; 93 | if (item.type == 'textHeader') { 94 | return TimeTitleItem(timeTitle: item.data.text); 95 | } 96 | return HomePageItem(item: item); 97 | }, 98 | separatorBuilder: (context, index) { 99 | var item = model.dataList[index]; 100 | var itemNext = model.dataList[index + 1]; 101 | if (item.type == 'textHeader' || itemNext.type == 'textHeader') { 102 | return Divider( 103 | height: 0, 104 | color: Color(0xFFF4F4F4), 105 | 106 | /// indent: 前间距, endIndent: 后间距 107 | ); 108 | } 109 | return Divider( 110 | height: 10, 111 | color: Color(0xFFF4F4F4), 112 | 113 | /// indent: 前间距, endIndent: 后间距 114 | ); 115 | }, 116 | itemCount: model.dataList.length, 117 | ); 118 | } 119 | } 120 | --------------------------------------------------------------------------------