├── .gitignore ├── .metadata ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── yourcompany │ │ │ └── doubanmovies │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── flutterKey.jks ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── main.m ├── lib ├── WanAndroid │ ├── Data │ │ ├── data_article_bean.dart │ │ ├── data_banner_bean.dart │ │ ├── data_key_bean.dart │ │ ├── data_navi_bean.dart │ │ ├── data_person_bean.dart │ │ └── data_tree_bean.dart │ ├── Manager │ │ └── manager_article_base.dart │ ├── Progress │ │ ├── painter.dart │ │ ├── painter │ │ │ ├── WriteRoundEye │ │ │ │ ├── Gy2Wht.dart │ │ │ │ ├── GyWriteRoundEye.dart │ │ │ │ ├── WhtWriteRoundEye.dart │ │ │ │ ├── painter_write_round_eye.dart │ │ │ │ └── widget_write_round_eye.dart │ │ │ ├── painter_base.dart │ │ │ ├── painter_circle.dart │ │ │ ├── painter_circle_snapchat.dart │ │ │ ├── painter_line_four.dart │ │ │ ├── painter_progress_text.dart │ │ │ ├── painter_wave.dart │ │ │ └── rainDrop │ │ │ │ ├── painter_rain_drop.dart │ │ │ │ ├── point_rain_drop.dart │ │ │ │ └── widget_rain_drop.dart │ │ ├── painter_factory.dart │ │ ├── progress_manager.dart │ │ ├── rain_drop.dart │ │ └── widget_rain_drop.dart │ ├── article_detail_page.dart │ ├── article_list_content.dart │ ├── article_page.dart │ ├── bloc │ │ ├── AccountBloc.dart │ │ ├── ApplicationBloc.dart │ │ └── bloc_utils.dart │ ├── config.dart │ ├── dialog_utils.dart │ ├── home_page.dart │ ├── icon_font_utils.dart │ ├── knowledge_tree_page.dart │ ├── navigator_router_utils.dart │ ├── person_page.dart │ ├── popular_page.dart │ ├── project_page.dart │ ├── router_anim_utils.dart │ ├── search_page.dart │ ├── sp_utils.dart │ ├── web_page.dart │ └── widget_banner.dart ├── data │ ├── bean_move_detail.dart │ └── bean_move_list.dart ├── db │ └── db_utils.dart ├── main.dart ├── net │ └── http.dart ├── pages │ ├── detail │ │ ├── MovieActors │ │ │ └── MovieActors.dart │ │ ├── MovieChannel │ │ │ └── MovieChannel.dart │ │ ├── MovieComments │ │ │ └── MovieCommentsView.dart │ │ ├── MovieDesc │ │ │ └── MovieDesc.dart │ │ ├── MoviePopularComments │ │ │ └── MoviePopularCommentsView.dart │ │ ├── MovieRating │ │ │ └── MovieRating.dart │ │ ├── MovieTitle │ │ │ └── MovieTitle.dart │ │ └── page_detail.dart │ ├── page_home.dart │ └── views │ │ ├── Chips.dart │ │ ├── ClipImageView.dart │ │ └── StartsView.dart └── res │ └── value_string.dart ├── pubspec.yaml ├── static └── font │ └── iconfont.ttf └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /.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: f37c235c32fc15babe6dc7b7bc2ee4387e5ecf92 8 | channel: beta 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # douban_movies 2 | 3 | A new Flutter application. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.yourcompany.doubanmovies" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | signingConfigs { 45 | debug { 46 | storeFile file("../flutterKey.jks") 47 | storePassword "3250905" 48 | keyAlias "flutter" 49 | keyPassword "3250905" 50 | } 51 | release { 52 | storeFile file("../flutterKey.jks") 53 | storePassword "3250905" 54 | keyAlias "flutter" 55 | keyPassword "3250905" 56 | } 57 | } 58 | 59 | buildTypes { 60 | debug { 61 | signingConfig signingConfigs.debug 62 | } 63 | release { 64 | signingConfig signingConfigs.release 65 | } 66 | } 67 | 68 | } 69 | 70 | flutter { 71 | source '../..' 72 | } 73 | 74 | dependencies { 75 | testImplementation 'junit:junit:4.12' 76 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 77 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 78 | } 79 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/yourcompany/doubanmovies/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.doubanmovies; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/flutterKey.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/android/flutterKey.jks -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/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/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | douban_movies 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/WanAndroid/Data/data_article_bean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | 3 | import 'package:html_unescape/html_unescape.dart'; 4 | 5 | class HomeBean { 6 | int errorCode; 7 | String errorMsg; 8 | HomeListBean data; 9 | 10 | HomeBean.fromParams({ 11 | this.errorCode, 12 | this.errorMsg, 13 | this.data, 14 | }); 15 | 16 | factory HomeBean(jsonStr) => jsonStr == null 17 | ? null 18 | : jsonStr is String 19 | ? new HomeBean.fromJson(json.decode(jsonStr)) 20 | : new HomeBean.fromJson(jsonStr); 21 | 22 | HomeBean.fromJson(jsonRes) { 23 | errorCode = jsonRes['errorCode']; 24 | errorMsg = jsonRes['errorMsg']; 25 | data = jsonRes['data'] == null 26 | ? null 27 | : new HomeListBean.fromJson(jsonRes['data']); 28 | } 29 | 30 | @override 31 | String toString() { 32 | // TODO: implement toString 33 | return super.toString(); 34 | } 35 | } 36 | 37 | class HomeListBean { 38 | int curPage; 39 | int offset; 40 | bool over; 41 | int pageCount; 42 | int size; 43 | int total; 44 | List datas; 45 | 46 | HomeListBean.fromParams( 47 | {this.curPage, 48 | this.offset, 49 | this.over, 50 | this.pageCount, 51 | this.size, 52 | this.total, 53 | this.datas}); 54 | 55 | factory HomeListBean(jsonStr) => jsonStr == null 56 | ? null 57 | : jsonStr is String 58 | ? new HomeListBean.fromJson(json.decode(jsonStr)) 59 | : new HomeListBean.fromJson(jsonStr); 60 | 61 | HomeListBean.fromJson(jsonRes) { 62 | curPage = jsonRes['curPage']; 63 | offset = jsonRes['offset']; 64 | over = jsonRes['over']; 65 | pageCount = jsonRes['pageCount']; 66 | size = jsonRes['size']; 67 | total = jsonRes['total']; 68 | datas = jsonRes['datas'] == null ? null : []; 69 | 70 | for (var data in datas == null ? [] : jsonRes['datas']) { 71 | datas.add(data == null ? null : new Data.fromJson(data)); 72 | } 73 | } 74 | 75 | @override 76 | String toString() { 77 | // TODO: implement toString 78 | return super.toString(); 79 | } 80 | } 81 | 82 | class Data { 83 | String apkLink; 84 | String author; 85 | int chapterId; 86 | String chapterName; 87 | bool collect; 88 | int courseId; 89 | String desc; 90 | String envelopePic; 91 | bool fresh; 92 | int mId; 93 | String link; 94 | String niceDate; 95 | String origin; 96 | String projectLink; 97 | int publishTime; 98 | int superChapterId; 99 | String superChapterName; 100 | List tags; 101 | String title; 102 | int type; 103 | int userId; 104 | int visible; 105 | int zan; 106 | 107 | getTitle() { 108 | var unescape = new HtmlUnescape(); 109 | return unescape.convert(title); 110 | } 111 | 112 | getChapterName() { 113 | var unescape = new HtmlUnescape(); 114 | return unescape.convert(chapterName); 115 | } 116 | 117 | getNiceDate() { 118 | var unescape = new HtmlUnescape(); 119 | return unescape.convert(niceDate); 120 | } 121 | 122 | Data.fromParams({ 123 | this.apkLink, 124 | this.author, 125 | this.chapterId, 126 | this.chapterName, 127 | this.collect, 128 | this.courseId, 129 | this.desc, 130 | this.envelopePic, 131 | this.fresh, 132 | this.mId, 133 | this.link, 134 | this.niceDate, 135 | this.origin, 136 | this.projectLink, 137 | this.publishTime, 138 | this.superChapterId, 139 | this.superChapterName, 140 | this.tags, 141 | this.title, 142 | this.type, 143 | this.userId, 144 | this.visible, 145 | this.zan, 146 | }); 147 | 148 | Data.fromJson(jsonRes) { 149 | apkLink = jsonRes['apkLink']; 150 | author = jsonRes['author']; 151 | chapterId = jsonRes['chapterId']; 152 | chapterName = jsonRes['chapterName']; 153 | collect = jsonRes['collect']; 154 | courseId = jsonRes['courseId']; 155 | desc = jsonRes['desc']; 156 | envelopePic = jsonRes['envelopePic']; 157 | fresh = jsonRes['fresh']; 158 | mId = jsonRes['id']; 159 | link = jsonRes['link']; 160 | niceDate = jsonRes['niceDate']; 161 | origin = jsonRes['origin']; 162 | projectLink = jsonRes['projectLink']; 163 | publishTime = jsonRes['publishTime']; 164 | superChapterId = jsonRes['superChapterId']; 165 | superChapterName = jsonRes['superChapterName']; 166 | tags = jsonRes['tags'] == null ? null : []; 167 | title = jsonRes['title']; 168 | type = jsonRes['type']; 169 | userId = jsonRes['userId']; 170 | visible = jsonRes['visible']; 171 | zan = jsonRes['zan']; 172 | } 173 | 174 | Map toJson() => { 175 | 'apkLink': apkLink, 176 | 'author': author, 177 | 'chapterId': chapterId, 178 | 'chapterName': chapterName, 179 | 'collect': collect, 180 | 'courseId': courseId, 181 | 'desc': desc, 182 | 'envelopePic': envelopePic, 183 | 'fresh': fresh, 184 | 'id': mId, 185 | 'link': link, 186 | 'niceDate': niceDate, 187 | 'origin': origin, 188 | 'projectLink': projectLink, 189 | 'publishTime': publishTime, 190 | 'superChapterId': superChapterId, 191 | 'superChapterName': superChapterName, 192 | 'tags': tags, 193 | 'title': title, 194 | 'type': type, 195 | 'userId': userId, 196 | 'visible': visible, 197 | 'zan': zan, 198 | }; 199 | } 200 | 201 | class Tag { 202 | String name; 203 | String url; 204 | 205 | Tag.fromParams({ 206 | this.name, 207 | this.url, 208 | }); 209 | 210 | Tag.fromJson(jsonRes) { 211 | name = jsonRes['name']; 212 | url = jsonRes['url']; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /lib/WanAndroid/Data/data_banner_bean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | 3 | import 'package:html_unescape/html_unescape.dart'; 4 | 5 | class BannerListBean { 6 | 7 | int errorCode; 8 | String errorMsg; 9 | List data; 10 | 11 | BannerListBean.fromParams({this.errorCode, this.errorMsg, this.data}); 12 | 13 | factory BannerListBean(jsonStr) => jsonStr == null ? null : jsonStr is String ? new BannerListBean.fromJson(json.decode(jsonStr)) : new BannerListBean.fromJson(jsonStr); 14 | 15 | BannerListBean.fromJson(jsonRes) { 16 | errorCode = jsonRes['errorCode']; 17 | errorMsg = jsonRes['errorMsg']; 18 | data = jsonRes['data'] == null ? null : []; 19 | 20 | for (var dataItem in data == null ? [] : jsonRes['data']){ 21 | data.add(dataItem == null ? null : new banner.fromJson(dataItem)); 22 | } 23 | } 24 | 25 | @override 26 | String toString() { 27 | return '{"errorCode": $errorCode,"errorMsg": ${errorMsg != null?'${json.encode(errorMsg)}':'null'},"data": $data}'; 28 | } 29 | } 30 | 31 | class banner { 32 | 33 | int id; 34 | int isVisible; 35 | int order; 36 | int type; 37 | String desc; 38 | String imagePath; 39 | String title; 40 | String url; 41 | 42 | getName(){ 43 | var unescape = new HtmlUnescape(); 44 | return unescape.convert(desc); 45 | } 46 | 47 | banner.fromParams({this.id, this.isVisible, this.order, this.type, this.desc, this.imagePath, this.title, this.url}); 48 | 49 | banner.fromJson(jsonRes) { 50 | id = jsonRes['id']; 51 | isVisible = jsonRes['isVisible']; 52 | order = jsonRes['order']; 53 | type = jsonRes['type']; 54 | desc = jsonRes['desc']; 55 | imagePath = jsonRes['imagePath']; 56 | title = jsonRes['title']; 57 | url = jsonRes['url']; 58 | } 59 | 60 | @override 61 | String toString() { 62 | return '{"id": $id,"isVisible": $isVisible,"order": $order,"type": $type,"desc": ${desc != null?'${json.encode(desc)}':'null'},"imagePath": ${imagePath != null?'${json.encode(imagePath)}':'null'},"title": ${title != null?'${json.encode(title)}':'null'},"url": ${url != null?'${json.encode(url)}':'null'}}'; 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /lib/WanAndroid/Data/data_key_bean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | 3 | import 'package:html_unescape/html_unescape.dart'; 4 | 5 | class KeyBean { 6 | int errorCode; 7 | String errorMsg; 8 | List nodes; 9 | 10 | KeyBean.fromParams({ 11 | this.errorCode, 12 | this.errorMsg, 13 | this.nodes, 14 | }); 15 | 16 | factory KeyBean(jsonStr) => jsonStr == null 17 | ? null 18 | : jsonStr is String 19 | ? new KeyBean.fromJson(json.decode(jsonStr)) 20 | : new KeyBean.fromJson(jsonStr); 21 | 22 | KeyBean.fromJson(jsonRes) { 23 | errorCode = jsonRes['errorCode']; 24 | errorMsg = jsonRes['errorMsg']; 25 | nodes = jsonRes['data'] == null ? null : []; 26 | 27 | for (var node in nodes == null ? [] : jsonRes['data']) { 28 | nodes.add(node == null ? null : new KeyNode.fromJson(node)); 29 | } 30 | } 31 | 32 | @override 33 | String toString() { 34 | // TODO: implement toString 35 | return super.toString(); 36 | } 37 | } 38 | 39 | class KeyNode { 40 | int mid; 41 | String link; 42 | String name; 43 | int order; 44 | int visible; 45 | 46 | getName(){ 47 | var unescape = new HtmlUnescape(); 48 | return unescape.convert(name); 49 | } 50 | 51 | KeyNode.fromParams( 52 | {this.mid, this.link, this.name, this.order, this.visible}); 53 | 54 | factory KeyNode(jsonStr) => jsonStr == null 55 | ? null 56 | : jsonStr is String 57 | ? new KeyNode.fromJson(json.decode(jsonStr)) 58 | : new KeyNode.fromJson(jsonStr); 59 | 60 | KeyNode.fromJson(jsonRes) { 61 | mid = jsonRes['id']; 62 | link = jsonRes['link']; 63 | name = jsonRes['name']; 64 | order = jsonRes['order']; 65 | visible = jsonRes['visible']; 66 | } 67 | 68 | @override 69 | String toString() { 70 | // TODO: implement toString 71 | return super.toString(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/WanAndroid/Data/data_navi_bean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | 3 | import 'package:douban_movies/WanAndroid/Data/data_article_bean.dart'; 4 | import 'package:html_unescape/html_unescape.dart'; 5 | 6 | class NaviBean { 7 | int errorCode; 8 | String errorMsg; 9 | List nodes; 10 | 11 | NaviBean.fromParams({ 12 | this.errorCode, 13 | this.errorMsg, 14 | this.nodes, 15 | }); 16 | 17 | factory NaviBean(jsonStr) => jsonStr == null 18 | ? null 19 | : jsonStr is String 20 | ? new NaviBean.fromJson(json.decode(jsonStr)) 21 | : new NaviBean.fromJson(jsonStr); 22 | 23 | NaviBean.fromJson(jsonRes) { 24 | errorCode = jsonRes['errorCode']; 25 | errorMsg = jsonRes['errorMsg']; 26 | nodes = jsonRes['data'] == null ? null : []; 27 | 28 | for (var node in nodes == null ? [] : jsonRes['data']) { 29 | nodes.add(node == null ? null : new NaviNode.fromJson(node)); 30 | } 31 | } 32 | 33 | @override 34 | String toString() { 35 | // TODO: implement toString 36 | return super.toString(); 37 | } 38 | } 39 | 40 | class NaviNode { 41 | int cid; 42 | String name; 43 | List articles; 44 | 45 | getName(){ 46 | var unescape = new HtmlUnescape(); 47 | return unescape.convert(name); 48 | } 49 | 50 | NaviNode.fromParams({this.cid, this.name, this.articles}); 51 | 52 | factory NaviNode(jsonStr) => jsonStr == null 53 | ? null 54 | : jsonStr is String 55 | ? new NaviNode.fromJson(json.decode(jsonStr)) 56 | : new NaviNode.fromJson(jsonStr); 57 | 58 | NaviNode.fromJson(jsonRes) { 59 | cid = jsonRes['cid']; 60 | name = jsonRes['name']; 61 | articles = jsonRes['articles'] == null ? null : []; 62 | 63 | for (var article in articles == null ? [] : jsonRes['articles']) { 64 | articles.add(article == null ? null : new Data.fromJson(article)); 65 | } 66 | } 67 | 68 | @override 69 | String toString() { 70 | // TODO: implement toString 71 | return super.toString(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/WanAndroid/Data/data_person_bean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | 3 | class PersonBean { 4 | 5 | int errorCode; 6 | String errorMsg; 7 | detail data; 8 | 9 | PersonBean.fromParams({this.errorCode, this.errorMsg, this.data}); 10 | 11 | factory PersonBean(jsonStr) => jsonStr == null ? null : jsonStr is String ? new PersonBean.fromJson(json.decode(jsonStr)) : new PersonBean.fromJson(jsonStr); 12 | 13 | PersonBean.fromJson(jsonRes) { 14 | errorCode = jsonRes['errorCode']; 15 | errorMsg = jsonRes['errorMsg']; 16 | data = jsonRes['data'] == null ? null : new detail.fromJson(jsonRes['data']); 17 | } 18 | 19 | @override 20 | String toString() { 21 | return '{"errorCode": $errorCode,"errorMsg": ${errorMsg != null?'${json.encode(errorMsg)}':'null'},"data": $data}'; 22 | } 23 | } 24 | 25 | class detail { 26 | 27 | int id; 28 | int type; 29 | String email; 30 | String icon; 31 | String password; 32 | String token; 33 | String username; 34 | List chapterTops; 35 | List collectIds; 36 | 37 | detail.fromParams({this.id, this.type, this.email, this.icon, this.password, this.token, this.username, this.chapterTops, this.collectIds}); 38 | 39 | detail.fromJson(jsonRes) { 40 | id = jsonRes['id']; 41 | type = jsonRes['type']; 42 | email = jsonRes['email']; 43 | icon = jsonRes['icon']; 44 | password = jsonRes['password']; 45 | token = jsonRes['token']; 46 | username = jsonRes['username']; 47 | chapterTops = jsonRes['chapterTops'] == null ? null : []; 48 | 49 | for (var chapterTopsItem in chapterTops == null ? [] : jsonRes['chapterTops']){ 50 | chapterTops.add(chapterTopsItem); 51 | } 52 | 53 | collectIds = jsonRes['collectIds'] == null ? null : []; 54 | 55 | for (var collectIdsItem in collectIds == null ? [] : jsonRes['collectIds']){ 56 | collectIds.add(collectIdsItem); 57 | } 58 | } 59 | 60 | @override 61 | String toString() { 62 | return '{"id": $id,"type": $type,"email": ${email != null?'${json.encode(email)}':'null'},"icon": ${icon != null?'${json.encode(icon)}':'null'},"password": ${password != null?'${json.encode(password)}':'null'},"token": ${token != null?'${json.encode(token)}':'null'},"username": ${username != null?'${json.encode(username)}':'null'},"chapterTops": $chapterTops,"collectIds": $collectIds}'; 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /lib/WanAndroid/Data/data_tree_bean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | import 'package:html_unescape/html_unescape.dart'; 3 | 4 | class TreeBean { 5 | int errorCode; 6 | String errorMsg; 7 | List nodes; 8 | 9 | TreeBean.fromParams( 10 | {this.errorCode, this.errorMsg, this.nodes,}); 11 | 12 | factory TreeBean(jsonStr) => 13 | jsonStr == null ? null : jsonStr is String ? new TreeBean 14 | .fromJson(json.decode(jsonStr)) : new TreeBean.fromJson( 15 | jsonStr); 16 | 17 | TreeBean.fromJson(jsonRes) { 18 | errorCode = jsonRes['errorCode']; 19 | errorMsg = jsonRes['errorMsg']; 20 | nodes = jsonRes['data'] == null ? null : []; 21 | 22 | for (var node in nodes == null ? [] : jsonRes['data']) { 23 | nodes.add(node == null ? null : new Node.fromJson(node)); 24 | } 25 | } 26 | 27 | @override 28 | String toString() { 29 | // TODO: implement toString 30 | return super.toString(); 31 | } 32 | } 33 | 34 | class Node { 35 | 36 | int courseId; 37 | int mId; 38 | String name; 39 | int order; 40 | int parentChapterId; 41 | bool userControlSetTop; 42 | int visible; 43 | List childNodes; 44 | 45 | getName(){ 46 | var unescape = new HtmlUnescape(); 47 | return unescape.convert(name); 48 | } 49 | 50 | 51 | Node.fromParams( 52 | {this.courseId, this.mId, this.name, this.order, this.parentChapterId, 53 | this.userControlSetTop, this.visible,this.childNodes}); 54 | 55 | factory Node(jsonStr) => 56 | jsonStr == null ? null : jsonStr is String ? new Node 57 | .fromJson(json.decode(jsonStr)) : new Node.fromJson( 58 | jsonStr); 59 | 60 | Node.fromJson(jsonRes) { 61 | courseId = jsonRes['courseId']; 62 | mId = jsonRes['id']; 63 | name = jsonRes['name']; 64 | order = jsonRes['order']; 65 | parentChapterId = jsonRes['parentChapterId']; 66 | userControlSetTop = jsonRes['userControlSetTop']; 67 | visible = jsonRes['visible']; 68 | childNodes = jsonRes['children'] == null ? null : []; 69 | 70 | for (var childNode in childNodes == null ? [] : jsonRes['children']) { 71 | childNodes.add(childNode == null ? null : new Node.fromJson(childNode)); 72 | } 73 | } 74 | 75 | @override 76 | String toString() { 77 | // TODO: implement toString 78 | return super.toString(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/WanAndroid/Manager/manager_article_base.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Data/data_article_bean.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:douban_movies/WanAndroid/sp_utils.dart'; 4 | import 'dart:convert' show json; 5 | 6 | 7 | class ArticleSaveManager{ 8 | static final String KEY_FAVORITE="key_favorite"; 9 | static final String KEY_HISTORY="key_history"; 10 | 11 | static Future> getSaveList(String key) async{ 12 | List dataList=[]; 13 | String dataListStr=await SpUtils.getString(key, ""); 14 | if(dataListStr==null||dataListStr.isEmpty){ 15 | return dataList; 16 | } 17 | List itemStrList=dataListStr.split("-"); 18 | if(itemStrList==null){ 19 | return dataList; 20 | } 21 | for(int i=0;i _xAnimation; 23 | Animation _yAnimation; 24 | 25 | set XAnimation(Animation value) { 26 | _xAnimation = value; 27 | } 28 | 29 | set YAnimation(Animation value) { 30 | _yAnimation = value; 31 | } 32 | } 33 | 34 | class WavePainter extends BasePainter { 35 | int waveCount; 36 | double waveHeight; 37 | List waveColors; 38 | double circleWidth; 39 | Color circleColor; 40 | Color circleBackgroundColor; 41 | 42 | WavePainter({ 43 | this.waveCount = 1, 44 | this.waveHeight, 45 | this.waveColors, 46 | this.circleColor = Colors.blue, 47 | this.circleBackgroundColor = Colors.white, 48 | this.circleWidth = 5.0}); 49 | 50 | @override 51 | void paint(Canvas canvas, Size size) { 52 | double width = size.width; 53 | double height = size.height; 54 | 55 | if (waveHeight == null) { 56 | waveHeight = height / 8; 57 | } 58 | 59 | if (waveColors == null) { 60 | waveColors = [ 61 | Color.fromARGB( 62 | 100, Colors.blue.red, Colors.blue.green, Colors.blue.blue) 63 | ]; 64 | } 65 | 66 | Offset center = new Offset(width / 2, height / 2); 67 | double xMove = width * _xAnimation.value; 68 | double yAnimValue = 0.0; 69 | if (_yAnimation != null) { 70 | yAnimValue = _yAnimation.value; 71 | } 72 | double yMove = height * (1.0 - yAnimValue); 73 | Offset waveCenter = new Offset(xMove, yMove); 74 | 75 | 76 | var paintCircle = new Paint() 77 | ..color = circleColor 78 | ..style = PaintingStyle.stroke 79 | ..strokeWidth = circleWidth; 80 | 81 | canvas.drawCircle(center, min(width, height) / 2, paintCircle); 82 | 83 | List wavePaths = []; 84 | 85 | for (int i = 0; i < waveCount; i++) { 86 | double direction = pow(-1.0, i); 87 | Path path = new Path() 88 | ..moveTo(waveCenter.dx - width, waveCenter.dy) 89 | ..lineTo(waveCenter.dx - width, center.dy + height / 2) 90 | ..lineTo(waveCenter.dx + width, center.dy + height / 2) 91 | ..lineTo(waveCenter.dx + width, waveCenter.dy) 92 | ..quadraticBezierTo( 93 | waveCenter.dx + width * 3 / 4, 94 | waveCenter.dy + waveHeight * direction, 95 | waveCenter.dx + width / 2, 96 | waveCenter.dy) 97 | ..quadraticBezierTo( 98 | waveCenter.dx + width / 4, 99 | waveCenter.dy - waveHeight * direction, 100 | waveCenter.dx, 101 | waveCenter.dy) 102 | ..quadraticBezierTo( 103 | waveCenter.dx - width / 4, 104 | waveCenter.dy + waveHeight * direction, 105 | waveCenter.dx - width / 2, 106 | waveCenter.dy) 107 | ..quadraticBezierTo( 108 | waveCenter.dx - width * 3 / 4, 109 | waveCenter.dy - waveHeight * direction, 110 | waveCenter.dx - width, 111 | waveCenter.dy) 112 | ..close(); 113 | 114 | wavePaths.add(path); 115 | } 116 | var paint = new Paint() 117 | ..color = circleBackgroundColor 118 | ..style = PaintingStyle.fill; 119 | 120 | canvas.saveLayer( 121 | Rect.fromCircle(center: center, radius: min(width, height) / 2), paint); 122 | canvas.drawCircle(center, min(width, height) / 2, paint); 123 | paint 124 | ..blendMode = BlendMode.srcATop 125 | ..style = PaintingStyle.fill 126 | ..strokeWidth = 2.0; 127 | for (int i = 0; i < wavePaths.length; i++) { 128 | if (waveColors.length >= wavePaths.length) { 129 | paint.color = waveColors[i]; 130 | } else { 131 | paint.color = waveColors[0]; 132 | } 133 | canvas.drawPath(wavePaths[i], paint); 134 | } 135 | paint.blendMode = BlendMode.src; 136 | 137 | canvas.restore(); 138 | 139 | TextPainter tp = TextPainter( 140 | text: TextSpan( 141 | text: '${(yAnimValue * 100.0).toStringAsFixed(0)}%', 142 | style: TextStyle( 143 | fontSize: 60.0, 144 | color: Color.fromARGB( 145 | 100, Colors.blue.red, Colors.blue.green, Colors.blue.blue), 146 | fontWeight: FontWeight.bold, 147 | )), 148 | textDirection: TextDirection.rtl) 149 | ..layout(); 150 | 151 | tp.paint( 152 | canvas, Offset(center.dx - tp.width / 2, center.dy - tp.height / 2)); 153 | } 154 | 155 | @override 156 | bool shouldRepaint(CustomPainter oldDelegate) { 157 | return oldDelegate != this; 158 | } 159 | 160 | } -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/WriteRoundEye/Gy2Wht.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Gy2Wht { 4 | double outRadius = 100.0; 5 | 6 | //中心的半径 7 | double _centerRadius = 15.0; 8 | 9 | int progress; 10 | 11 | //绘制 12 | void drawPonit(Canvas canvas, Size size, int progress) { 13 | Paint _paint = new Paint() 14 | ..color = Colors.black 15 | ..style = PaintingStyle.stroke 16 | ..strokeCap = StrokeCap.round 17 | ..strokeWidth = 3.0; 18 | 19 | double width = size.width; 20 | double height = size.height; 21 | Offset center = new Offset(width / 2, height / 2); 22 | //绘制红色 23 | _paint.color = Colors.red; 24 | _paint.style = PaintingStyle.fill; 25 | canvas.drawCircle(center, outRadius, _paint); 26 | //绘制外圈 27 | _paint.color = Colors.black; 28 | _paint.style = PaintingStyle.stroke; 29 | _paint.strokeCap = StrokeCap.round; 30 | _paint.strokeWidth = 5.0; 31 | canvas.drawCircle(center, outRadius, _paint); 32 | 33 | //内环越来越收拢 34 | double centerRadius; 35 | centerRadius = _centerRadius + (outRadius - _centerRadius) * progress / 360; 36 | 37 | //绘制中心 38 | _paint.style = PaintingStyle.fill; 39 | canvas.drawCircle(center, centerRadius, _paint); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/WriteRoundEye/GyWriteRoundEye.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class GyWriteRoundEye { 6 | double outRadius = 100.0; 7 | double whtRadius = 100.0; 8 | 9 | //圆环的半径 10 | double _initRadius = 60.0; 11 | 12 | //圆环的厚度 13 | double initGap = 10.0; 14 | 15 | //中心的半径 16 | double centerRadius = 15.0; 17 | 18 | //勾玉的大小 19 | double _gyRadius = 10; 20 | int _initCount = 180; 21 | int progress; 22 | 23 | //绘制 24 | void drawPonit(Canvas canvas, Size size, int progress) { 25 | Paint _paint = new Paint() 26 | ..color = Colors.black 27 | ..style = PaintingStyle.stroke 28 | ..strokeCap = StrokeCap.round 29 | ..strokeWidth = 3.0; 30 | 31 | double width = size.width; 32 | double height = size.height; 33 | Offset center = new Offset(width / 2, height / 2); 34 | //绘制红色 35 | _paint.color = Colors.red; 36 | _paint.style = PaintingStyle.fill; 37 | canvas.drawCircle(center, outRadius, _paint); 38 | //绘制外圈 39 | _paint.color = Colors.black; 40 | _paint.style = PaintingStyle.stroke; 41 | _paint.strokeCap = StrokeCap.round; 42 | _paint.strokeWidth = 5.0; 43 | canvas.drawCircle(center, outRadius, _paint); 44 | 45 | //内环越来越收拢 46 | double initRadius, gyRadius, initCount; 47 | initRadius = _initRadius * (360 - progress) / 360; 48 | //勾玉越来越小,缩小的速率毕内环要慢一倍 49 | gyRadius = _gyRadius * (360 - 0.7 * progress) / 360; 50 | //内圈的齿数越来越小 51 | initCount = _initCount * (360 - 0.7 * progress) / 360; 52 | 53 | //绘制内圈 54 | Offset start, end; 55 | _paint.strokeWidth = 1; 56 | for (int i = 0; i < initCount; i++) { 57 | start = Offset(initRadius * sin(2 * pi * (i + 1) / initCount) + center.dx, 58 | initRadius * cos(2 * pi * (i + 1) / initCount) + center.dy); 59 | end = Offset( 60 | (initRadius + initGap) * sin(2 * pi * (i + 1) / initCount) + 61 | center.dx, 62 | (initRadius + initGap) * cos(2 * pi * (i + 1) / initCount) + 63 | center.dy); 64 | canvas.drawLine(start, end, _paint); 65 | } 66 | canvas.save(); 67 | canvas.translate(width / 2, height / 2); 68 | //绘制3个逗号 69 | double gyCenterGap = initRadius; 70 | Offset gyStart; 71 | [1, 2, 3].forEach((a) { 72 | double offsetAngle = 2 * a * pi / 3 + progress * 2 * pi / 360; 73 | 74 | gyStart = new Offset((gyCenterGap - gyRadius) * sin(offsetAngle), 75 | (gyCenterGap - gyRadius) * cos(offsetAngle)); 76 | 77 | _paint.style = PaintingStyle.fill; 78 | 79 | Path gyPath = new Path(); 80 | 81 | Offset p1 = new Offset((gyCenterGap + 3 * gyRadius) * sin(offsetAngle), 82 | (gyCenterGap + 3 * gyRadius) * cos(offsetAngle)); 83 | 84 | Offset p2 = new Offset((gyCenterGap + 2 * gyRadius) * sin(offsetAngle), 85 | (gyCenterGap + 2 * gyRadius) * cos(offsetAngle)); 86 | 87 | gyPath 88 | ..lineTo(gyStart.dx, gyStart.dy) 89 | ..arcToPoint(p1, 90 | radius: Radius.circular(1.5 * gyRadius), 91 | largeArc: true, 92 | clockwise: false) 93 | ..arcToPoint(p2, 94 | radius: Radius.circular(0.5 * gyRadius), 95 | largeArc: true, 96 | clockwise: true) 97 | ..arcToPoint(gyStart, 98 | radius: Radius.circular(1 * gyRadius), 99 | largeArc: true, 100 | clockwise: false); 101 | 102 | canvas.drawPath(gyPath, _paint); 103 | }); 104 | canvas.restore(); 105 | //绘制中心 106 | _paint.style = PaintingStyle.fill; 107 | canvas.drawCircle(center, centerRadius, _paint); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/WriteRoundEye/WhtWriteRoundEye.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class WhtWriteRoundEye { 6 | double outRadius = 100.0; 7 | double _whtRadius = 100.0; 8 | 9 | //圆环的半径 10 | double initRadius = 60.0; 11 | 12 | //圆环的厚度 13 | double initGap = 10.0; 14 | 15 | //中心的半径 16 | double centerRadius = 15.0; 17 | 18 | //勾玉的大小 19 | double gyRadius = 10; 20 | int initCount = 180; 21 | int progress; 22 | 23 | //绘制 24 | void drawPonit(Canvas canvas, Size size, int progress) { 25 | Paint paint = new Paint() 26 | ..color = Colors.black 27 | ..style = PaintingStyle.stroke 28 | ..strokeCap = StrokeCap.round 29 | ..strokeWidth = 3.0; 30 | 31 | double width = size.width; 32 | double height = size.height; 33 | Offset center = new Offset(width / 2, height / 2); 34 | //绘制红色 35 | paint.color = Colors.black; 36 | paint.style = PaintingStyle.fill; 37 | canvas.drawCircle(center, outRadius, paint); 38 | //绘制外圈 39 | paint.color = Colors.black; 40 | paint.style = PaintingStyle.stroke; 41 | paint.strokeCap = StrokeCap.round; 42 | paint.strokeWidth = 5.0; 43 | canvas.drawCircle(center, outRadius, paint); 44 | 45 | canvas.save(); 46 | //先绘制万花筒写轮眼的竖直部分 47 | canvas.translate(width / 2, height / 2); 48 | paint.strokeWidth = 2.0; 49 | 50 | //内环越来越收拢 51 | double whtRadius; 52 | whtRadius = _whtRadius * progress / 360; 53 | 54 | paint.color = Colors.red; 55 | paint.style = PaintingStyle.fill; 56 | 57 | [1, 2, 3].forEach((a) { 58 | double offsetAngle = 2 * a * pi / 3 + progress * 2 * pi / 360; 59 | Offset start = new Offset( 60 | whtRadius * sin(offsetAngle), whtRadius * cos(offsetAngle)); 61 | Offset end = new Offset( 62 | -1 * whtRadius * sin(offsetAngle), -1 * whtRadius * cos(offsetAngle)); 63 | Path path = new Path(); 64 | path 65 | ..moveTo(start.dx, start.dy) 66 | ..arcToPoint(end, 67 | radius: Radius.circular(1.5 * whtRadius), clockwise: false) 68 | ..arcToPoint(start, 69 | radius: Radius.circular(1.5 * whtRadius), clockwise: false); 70 | canvas.drawPath(path, paint); 71 | }); 72 | paint.style = PaintingStyle.stroke; 73 | paint.color = Colors.black; 74 | [1, 2, 3].forEach((a) { 75 | double offsetAngle = 2 * a * pi / 3 + progress * 2 * pi / 360; 76 | Offset start = new Offset( 77 | whtRadius * sin(offsetAngle), whtRadius * cos(offsetAngle)); 78 | Offset end = new Offset( 79 | -1 * whtRadius * sin(offsetAngle), -1 * whtRadius * cos(offsetAngle)); 80 | Path path = new Path(); 81 | 82 | path 83 | ..moveTo(start.dx, start.dy) 84 | ..arcToPoint(end, 85 | radius: Radius.circular(1.5 * whtRadius), clockwise: false) 86 | ..arcToPoint(start, 87 | radius: Radius.circular(1.5 * whtRadius), clockwise: false); 88 | canvas.drawPath(path, paint); 89 | }); 90 | canvas.restore(); 91 | 92 | //绘制中心 93 | paint.style = PaintingStyle.fill; 94 | canvas.drawCircle(center, centerRadius, paint); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/WriteRoundEye/painter_write_round_eye.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/Gy2Wht.dart'; 4 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/GyWriteRoundEye.dart'; 5 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/WhtWriteRoundEye.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class WriteRoundEyePainter extends CustomPainter { 9 | int progress; 10 | GyWriteRoundEye gyWriteRoundEye; 11 | WhtWriteRoundEye whtWriteRoundEye; 12 | Gy2Wht gy2wht; 13 | 14 | WriteRoundEyePainter(this.progress, 15 | {this.gyWriteRoundEye, this.whtWriteRoundEye, this.gy2wht}); 16 | 17 | @override 18 | void paint(Canvas canvas, Size size) { 19 | if ((progress / 360).floor() == 0) { 20 | gyWriteRoundEye.drawPonit(canvas, size, progress % 360); 21 | } else if ((progress / 360).floor() == 1) { 22 | gy2wht.drawPonit(canvas, size, progress % 360); 23 | } else { 24 | whtWriteRoundEye.drawPonit(canvas, size, progress % 360); 25 | } 26 | } 27 | 28 | @override 29 | bool shouldRepaint(CustomPainter oldDelegate) { 30 | return oldDelegate != this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/WriteRoundEye/widget_write_round_eye.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/Gy2Wht.dart'; 2 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/GyWriteRoundEye.dart'; 3 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/WhtWriteRoundEye.dart'; 4 | import 'package:douban_movies/WanAndroid/Progress/painter/WriteRoundEye/painter_write_round_eye.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class TestWidget extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | // TODO: implement build 11 | return Center( 12 | child: Container( 13 | width: 300.0, 14 | height: 300.0, 15 | child: WriteRoundEyeWidget( 16 | width: 300.0, 17 | height: 300.0, 18 | ), 19 | ), 20 | ); 21 | } 22 | } 23 | 24 | class WriteRoundEyeWidget extends StatefulWidget { 25 | final width; 26 | final height; 27 | int progress = 0; 28 | GyWriteRoundEye gyEye = new GyWriteRoundEye(); 29 | WhtWriteRoundEye whtEye = new WhtWriteRoundEye(); 30 | Gy2Wht gy2wht = new Gy2Wht(); 31 | 32 | WriteRoundEyeWidget({Key key, this.width, this.height}) : super(key: key); 33 | 34 | @override 35 | _WriteRoundEyeWidgetState createState() => 36 | new _WriteRoundEyeWidgetState(width, height); 37 | } 38 | 39 | class _WriteRoundEyeWidgetState extends State 40 | with TickerProviderStateMixin { 41 | AnimationController xController; 42 | Animation xAnimation; 43 | double _width = 200; 44 | double _height = 200; 45 | 46 | _WriteRoundEyeWidgetState(double width, double height) { 47 | _width = width ?? _width; 48 | _height = height ?? _height; 49 | } 50 | 51 | @override 52 | void initState() { 53 | // TODO: implement initState 54 | super.initState(); 55 | xController = new AnimationController( 56 | vsync: this, duration: Duration(milliseconds: 3000)); 57 | xAnimation = new Tween(begin: 0.0, end: 1.0).animate(xController); 58 | xAnimation.addListener(_change); 59 | doDelay(xController, 0); 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | if ((widget.progress / 360).floor() == 0) { 65 | widget.progress++; 66 | } else if ((widget.progress / 360).floor() == 1) { 67 | widget.progress += 10; 68 | } else { 69 | widget.progress += 2; 70 | } 71 | if (widget.progress > 1079) { 72 | widget.progress = 1080 - 1; 73 | } 74 | 75 | print("progress=${widget.progress}"); 76 | return Container( 77 | child: new CustomPaint( 78 | painter: WriteRoundEyePainter(widget.progress, 79 | gyWriteRoundEye: widget.gyEye, 80 | whtWriteRoundEye: widget.whtEye, 81 | gy2wht: widget.gy2wht), 82 | ), 83 | ); 84 | } 85 | 86 | void _change() { 87 | setState(() {}); 88 | } 89 | 90 | void doDelay(AnimationController controller, int delay) async { 91 | Future.delayed(Duration(milliseconds: delay), () { 92 | try { 93 | controller..repeat(); 94 | } catch (e) {} 95 | }); 96 | } 97 | 98 | @override 99 | void dispose() { 100 | xController.dispose(); 101 | xAnimation.removeListener(_change); 102 | super.dispose(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/painter_base.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | abstract class BasePainter extends CustomPainter{ 4 | Animation _xAnimation; 5 | Animation _yAnimation; 6 | 7 | set XAnimation(Animation value) { 8 | _xAnimation = value; 9 | } 10 | 11 | set YAnimation(Animation value) { 12 | _yAnimation = value; 13 | } 14 | 15 | Animation get YAnimation => _yAnimation; 16 | 17 | Animation get XAnimation => _xAnimation; 18 | 19 | } -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/painter_circle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_base.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class CirclePainter extends BasePainter { 7 | List waveColors; 8 | double circleWidth; 9 | double circleGap; 10 | Color circleColor; 11 | Color progressColor; 12 | 13 | CirclePainter( 14 | {this.waveColors, 15 | this.circleColor = Colors.blue, 16 | this.progressColor = Colors.blue, 17 | this.circleWidth = 1.0, 18 | this.circleGap = 1.0}); 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | double width = size.width; 23 | double height = size.height; 24 | double radius = min(width, height) / 2 - circleWidth - circleGap; 25 | double scale = YAnimation.value; 26 | double degree = 2 * pi * scale; 27 | Offset center = new Offset(width / 2, height / 2); 28 | 29 | var circlePaint = new Paint() 30 | ..color = circleColor 31 | ..style = PaintingStyle.stroke 32 | ..strokeWidth = circleWidth; 33 | 34 | canvas.drawCircle(center, min(width, height) / 2, circlePaint); 35 | 36 | var progressPaint = new Paint() 37 | ..color = progressColor 38 | ..style = PaintingStyle.fill 39 | ..strokeWidth = circleWidth; 40 | 41 | if (scale == 1) { 42 | canvas.saveLayer( 43 | Rect.fromCircle(center: center, radius: min(width, height) / 2), 44 | progressPaint); 45 | canvas.drawCircle(center, radius, progressPaint); 46 | canvas.restore(); 47 | return; 48 | } 49 | 50 | List wavePaths = []; 51 | 52 | Path path = new Path() 53 | ..moveTo(center.dx, center.dy) 54 | ..lineTo(center.dx, center.dy + radius) 55 | ..arcTo(Rect.fromCircle(center: center, radius: radius), 0, degree, true) 56 | ..lineTo(center.dy, center.dy) 57 | ..close(); 58 | 59 | wavePaths.add(path); 60 | canvas.saveLayer( 61 | Rect.fromCircle(center: center, radius: min(width, height) / 2), 62 | progressPaint); 63 | canvas.drawPath(path, progressPaint); 64 | canvas.restore(); 65 | } 66 | 67 | @override 68 | bool shouldRepaint(CustomPainter oldDelegate) { 69 | return oldDelegate != this; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/painter_circle_snapchat.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_base.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class SnapchatCirclePainter extends BasePainter { 7 | double circleWidth; 8 | double circleGap; 9 | Color circleColor; 10 | Color progressColor; 11 | 12 | SnapchatCirclePainter( 13 | {this.circleColor = Colors.blue, 14 | this.progressColor = Colors.blue, 15 | this.circleWidth = 1.0, 16 | this.circleGap = 1.0}); 17 | 18 | @override 19 | void paint(Canvas canvas, Size size) { 20 | double width = size.width; 21 | double height = size.height; 22 | double radius = min(width, height) / 2 - circleWidth - circleGap; 23 | 24 | double scale = XAnimation.value; 25 | double rotateDegree = 2 * pi * scale; 26 | 27 | Offset center = new Offset(width / 2, height / 2); 28 | 29 | var circlePaint = new Paint() 30 | ..color = circleColor 31 | ..style = PaintingStyle.stroke 32 | ..strokeWidth = circleWidth 33 | ..strokeCap = StrokeCap.round; 34 | 35 | canvas.save(); 36 | // canvas.translate(width / 2, height / 2); 37 | // canvas.rotate(rotateDegree); 38 | 39 | canvas.drawArc( 40 | Rect.fromCircle(center: center, radius: min(width, height) / 2), 41 | 0 + rotateDegree, 42 | 1.5 * pi, 43 | false, 44 | circlePaint); 45 | 46 | canvas.restore(); 47 | 48 | var progressPaint = new Paint() 49 | ..color = progressColor 50 | ..style = PaintingStyle.stroke 51 | ..strokeWidth = circleWidth 52 | ..isAntiAlias = true 53 | ..strokeCap = StrokeCap.round; 54 | 55 | canvas.drawArc(Rect.fromCircle(center: center, radius: radius), 56 | 0 - rotateDegree, 1.5 * pi, false, progressPaint); 57 | // 58 | // List wavePaths = []; 59 | // Path path = new Path() 60 | // ..moveTo(center.dx + radius * cos(rotateDegree), 61 | // center.dy + radius * sin(rotateDegree)) 62 | // ..arcTo(Rect.fromCircle(center: center, radius: radius), 0, 63 | // 1.5 * pi + rotateDegree, false) 64 | // ..close(); 65 | // 66 | // wavePaths.add(path); 67 | // 68 | // canvas.saveLayer( 69 | // Rect.fromCircle(center: center, radius: min(width, height) / 2), 70 | // progressPaint); 71 | // canvas.drawPath(path, progressPaint); 72 | // canvas.restore(); 73 | } 74 | 75 | @override 76 | bool shouldRepaint(CustomPainter oldDelegate) { 77 | return oldDelegate != this; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/painter_line_four.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_base.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class FourLinePainter extends BasePainter { 5 | double gap; 6 | List lineColors; 7 | List baseHightScales; 8 | 9 | FourLinePainter({ 10 | this.gap = 0, 11 | this.lineColors = const [ 12 | Colors.green, 13 | Colors.red, 14 | Colors.blue, 15 | Colors.yellow, 16 | ], 17 | this.baseHightScales = const [ 18 | 0.1, 19 | 0.6, 20 | 0.3, 21 | 0.8, 22 | ], 23 | }); 24 | 25 | @override 26 | void paint(Canvas canvas, Size size) { 27 | double width = size.width; 28 | double height = size.height; 29 | 30 | int count = lineColors.length; 31 | double lineWidth = (width - count * gap) / count; 32 | 33 | var bgPainter = new Paint() 34 | ..color = Colors.grey 35 | ..style = PaintingStyle.fill 36 | ..strokeWidth = lineWidth 37 | ..strokeCap = StrokeCap.round; 38 | 39 | canvas.drawRect( 40 | new Rect.fromPoints(new Offset(0.0, 0.0), new Offset(width, height)), 41 | bgPainter); 42 | 43 | double scale = XAnimation.value; 44 | 45 | for (int i = 0; i < count; i++) { 46 | var painter = new Paint() 47 | ..color = lineColors[i] 48 | ..style = PaintingStyle.stroke 49 | ..strokeWidth = lineWidth 50 | ..strokeCap = StrokeCap.round; 51 | 52 | Offset start = 53 | new Offset(width / (count * 2) + width * i / count, height); 54 | 55 | double lastScale = scale + baseHightScales[i]; 56 | if (lastScale > 2.0) { 57 | lastScale = lastScale - 2; 58 | } else if (lastScale > 1.0) { 59 | lastScale = 2 - lastScale; 60 | } 61 | 62 | Offset end = new Offset( 63 | width / (count * 2) + width * i / count, height - lastScale * height); 64 | canvas.drawLine(start, end, painter); 65 | } 66 | } 67 | 68 | @override 69 | bool shouldRepaint(CustomPainter oldDelegate) { 70 | return oldDelegate != this; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/painter_progress_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_base.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class TextProgressPainter extends BasePainter { 5 | Color progressColor; 6 | TextStyle textStyle; 7 | 8 | TextProgressPainter({this.progressColor, this.textStyle}); 9 | 10 | @override 11 | void paint(Canvas canvas, Size size) { 12 | double width = size.width; 13 | double height = size.height; 14 | 15 | if (textStyle == null) { 16 | textStyle = new TextStyle( 17 | color: Colors.yellow, 18 | // shadows: [ 19 | // Shadow(color: Colors.grey, offset: Offset(5.0, 5.0), blurRadius: 5.0) 20 | // ], 21 | 22 | fontSize: 80.0, 23 | ); 24 | } 25 | TextPainter tp = TextPainter( 26 | text: TextSpan( 27 | text: '${(YAnimation.value * 100.0).toStringAsFixed(0)}%', 28 | style: textStyle), 29 | textDirection: TextDirection.rtl) 30 | ..layout(); 31 | 32 | var baseLineHight = 33 | tp.computeDistanceToActualBaseline(TextBaseline.alphabetic); 34 | 35 | print('baseLine:$baseLineHight\nheight:${tp.height}'); 36 | 37 | double textHeight = tp.height; 38 | 39 | Offset center = new Offset(width / 2, height / 2); 40 | double xMove = width * XAnimation.value; 41 | double yAnimValue = 0.0; 42 | if (YAnimation != null) { 43 | yAnimValue = YAnimation.value; 44 | } 45 | 46 | double yMove = height * (1.0 - yAnimValue); 47 | Offset waveCenter = new Offset(xMove, yMove); 48 | 49 | Path path = new Path() 50 | ..moveTo(0, height / 2) 51 | ..lineTo(width, height / 2) 52 | ..close(); 53 | 54 | var paint = new Paint() 55 | ..color = Colors.black 56 | ..style = PaintingStyle.fill; 57 | 58 | canvas.drawLine(Offset(0, height / 2 + baseLineHight / 2), 59 | Offset(width, height / 2 + baseLineHight / 2), paint); 60 | 61 | // canvas.saveLayer( 62 | // Rect.fromCircle(center: center, radius: min(width, height) / 2), paint); 63 | paint 64 | ..blendMode = BlendMode.srcATop 65 | ..style = PaintingStyle.fill 66 | ..strokeWidth = 2.0 67 | ..color = Colors.black; 68 | tp.paint( 69 | canvas, Offset(center.dx - tp.width / 2, center.dy - tp.height / 2)); 70 | 71 | canvas.drawPath(path, paint); 72 | // canvas.restore(); 73 | } 74 | 75 | @override 76 | bool shouldRepaint(CustomPainter oldDelegate) { 77 | return oldDelegate != this; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/painter_wave.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_base.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class WavePainter extends BasePainter { 7 | int waveCount; 8 | int crestCount; 9 | double waveHeight; 10 | List waveColors; 11 | double circleWidth; 12 | Color circleColor; 13 | Color circleBackgroundColor; 14 | bool showProgressText; 15 | TextStyle textStyle; 16 | 17 | WavePainter( 18 | {this.waveCount = 1, 19 | this.crestCount = 2, 20 | this.waveHeight, 21 | this.waveColors, 22 | this.circleColor = Colors.grey, 23 | this.circleBackgroundColor = Colors.white, 24 | this.circleWidth = 5.0, 25 | this.showProgressText = true, 26 | this.textStyle = const TextStyle( 27 | fontSize: 60.0, 28 | color: Colors.blue, 29 | fontWeight: FontWeight.bold, 30 | shadows: [ 31 | Shadow(color: Colors.grey, offset: Offset(5.0, 5.0), blurRadius: 5.0) 32 | ], 33 | )}); 34 | 35 | @override 36 | void paint(Canvas canvas, Size size) { 37 | double width = size.width; 38 | double height = size.height; 39 | 40 | if (waveHeight == null) { 41 | waveHeight = height / 8; 42 | height = height + waveHeight; 43 | } 44 | 45 | if (waveColors == null) { 46 | waveColors = [ 47 | Color.fromARGB( 48 | 100, Colors.blue.red, Colors.blue.green, Colors.blue.blue) 49 | ]; 50 | } 51 | 52 | Offset center = new Offset(width / 2, height / 2); 53 | double xMove = width * XAnimation.value; 54 | double yAnimValue = 0.0; 55 | if (YAnimation != null) { 56 | yAnimValue = YAnimation.value; 57 | } 58 | double yMove = height * (1.0 - yAnimValue); 59 | Offset waveCenter = new Offset(xMove, yMove); 60 | 61 | var paintCircle = new Paint() 62 | ..color = Colors.grey 63 | ..style = PaintingStyle.fill 64 | ..strokeWidth = circleWidth 65 | ..maskFilter = MaskFilter.blur(BlurStyle.inner, 5.0); 66 | 67 | canvas.drawCircle(center, min(width, height) / 2, paintCircle); 68 | 69 | List wavePaths = []; 70 | 71 | for (int index = 0; index < waveCount; index++) { 72 | double direction = pow(-1.0, index); 73 | Path path = new Path() 74 | ..moveTo(waveCenter.dx - width, waveCenter.dy) 75 | ..lineTo(waveCenter.dx - width, center.dy + height / 2) 76 | ..lineTo(waveCenter.dx + width, center.dy + height / 2) 77 | ..lineTo(waveCenter.dx + width, waveCenter.dy); 78 | 79 | for (int i = 0; i < 2; i++) { 80 | for (int j = 0; j < crestCount; j++) { 81 | double a = pow(-1.0, j); 82 | path 83 | ..quadraticBezierTo( 84 | waveCenter.dx + 85 | width * (1 - i - (1 + 2 * j) / (2 * crestCount)), 86 | waveCenter.dy + waveHeight * a * direction, 87 | waveCenter.dx + 88 | width * (1 - i - (2 + 2 * j) / (2 * crestCount)), 89 | waveCenter.dy); 90 | } 91 | } 92 | 93 | path..close(); 94 | 95 | wavePaths.add(path); 96 | } 97 | var paint = new Paint() 98 | ..color = circleBackgroundColor 99 | ..style = PaintingStyle.fill 100 | ..maskFilter = MaskFilter.blur(BlurStyle.inner, 5.0); 101 | 102 | canvas.saveLayer( 103 | Rect.fromCircle(center: center, radius: min(width, height) / 2), paint); 104 | 105 | canvas.drawCircle(center, min(width, height) / 2, paint); 106 | 107 | paint 108 | ..blendMode = BlendMode.srcATop 109 | ..style = PaintingStyle.fill 110 | ..strokeWidth = 2.0 111 | ..maskFilter = MaskFilter.blur(BlurStyle.inner, 10.0); 112 | 113 | for (int i = 0; i < wavePaths.length; i++) { 114 | if (waveColors.length >= wavePaths.length) { 115 | paint.color = waveColors[i]; 116 | } else { 117 | paint.color = waveColors[0]; 118 | } 119 | canvas.drawPath(wavePaths[i], paint); 120 | } 121 | // paint.blendMode = BlendMode.srcATop; 122 | if (showProgressText) { 123 | TextPainter tp = TextPainter( 124 | text: TextSpan( 125 | text: '${(yAnimValue * 100.0).toStringAsFixed(0)}%', 126 | style: textStyle), 127 | textDirection: TextDirection.rtl) 128 | ..layout(); 129 | 130 | tp.paint( 131 | canvas, Offset(center.dx - tp.width / 2, center.dy - tp.height / 2)); 132 | } 133 | canvas.restore(); 134 | } 135 | 136 | @override 137 | bool shouldRepaint(CustomPainter oldDelegate) { 138 | return oldDelegate != this; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/rainDrop/painter_rain_drop.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter/rainDrop/point_rain_drop.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RainDropPainter extends CustomPainter { 5 | List rainList = List(); 6 | // Paint _paint = new Paint() 7 | // ..style = PaintingStyle.stroke 8 | // ..strokeWidth = 1.0 9 | // ..strokeCap = StrokeCap.round; 10 | 11 | RainDropPainter({@required this.rainList}); 12 | 13 | @override 14 | void paint(Canvas canvas, Size size) { 15 | double width = size.width; 16 | double height = size.height; 17 | 18 | var bgPainter = new Paint() 19 | ..color = Colors.black87 20 | ..style = PaintingStyle.fill; 21 | 22 | //绘制背景 23 | canvas.drawRect( 24 | new Rect.fromPoints(new Offset(0.0, 0.0), new Offset(width, height)), 25 | bgPainter); 26 | 27 | rainList.forEach((rain) { 28 | rain.drawPonit(canvas); 29 | }); 30 | 31 | rainList.removeWhere((rain) { 32 | return !rain.isVaild(); 33 | }); 34 | } 35 | 36 | @override 37 | bool shouldRepaint(CustomPainter oldDelegate) { 38 | return oldDelegate != this; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/rainDrop/point_rain_drop.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | /** 6 | * 定义一个雨点类 7 | * 功能: 8 | * - 在给定位置的时候,绘制对应的雨点 9 | * - 具有自己的动画控制 10 | */ 11 | class RainDropPoint { 12 | Offset offset; 13 | final double MAX_RADIUS = 30.0; 14 | double radius = 0.0; 15 | Paint _paint; 16 | Color _color; 17 | 18 | RainDropPoint({this.offset}) { 19 | Random random = new Random(); 20 | _color = Color.fromARGB( 21 | 255, random.nextInt(255), random.nextInt(255), random.nextInt(255)); 22 | _paint = new Paint() 23 | ..style = PaintingStyle.stroke 24 | ..strokeWidth = 1.0 25 | ..strokeCap = StrokeCap.round; 26 | } 27 | 28 | //绘制 29 | void drawPonit(Canvas canvas) { 30 | int alpha = (255 * (MAX_RADIUS - radius) / MAX_RADIUS).toInt(); 31 | _paint.color = Color.fromARGB(alpha, _color.red, _color.green, _color.blue); 32 | canvas.drawCircle(offset, radius, _paint); 33 | radius += 0.5; 34 | } 35 | 36 | //是否有效,无效则隐藏 37 | bool isVaild() { 38 | return radius < MAX_RADIUS; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter/rainDrop/widget_rain_drop.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:douban_movies/WanAndroid/Progress/painter/rainDrop/painter_rain_drop.dart'; 4 | import 'package:douban_movies/WanAndroid/Progress/painter/rainDrop/point_rain_drop.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class TestWidget extends StatelessWidget { 8 | @override 9 | Widget build(BuildContext context) { 10 | // TODO: implement build 11 | return Center( 12 | child: Container( 13 | width: 300.0, 14 | height: 300.0, 15 | child: RainDropWidget( 16 | width: 300.0, 17 | height: 300.0, 18 | ), 19 | ), 20 | ); 21 | } 22 | } 23 | 24 | class RainDropWidget extends StatefulWidget { 25 | List rainList = List(); 26 | final width; 27 | final height; 28 | 29 | RainDropWidget({Key key, this.width, this.height}) : super(key: key); 30 | 31 | @override 32 | _RainDropWidgetState createState() => new _RainDropWidgetState(width, height); 33 | } 34 | 35 | class _RainDropWidgetState extends State 36 | with TickerProviderStateMixin { 37 | AnimationController xController; 38 | Animation xAnimation; 39 | AnimationController rainController; 40 | Animation rainAnimation; 41 | double _width = 200; 42 | double _height = 200; 43 | 44 | _RainDropWidgetState(double width, double height) { 45 | _width = width ?? _width; 46 | _height = height ?? _height; 47 | } 48 | 49 | @override 50 | void initState() { 51 | // TODO: implement initState 52 | super.initState(); 53 | xController = new AnimationController( 54 | vsync: this, duration: Duration(milliseconds: 3000)); 55 | xAnimation = new Tween(begin: 0.0, end: 1.0).animate(xController); 56 | xAnimation.addListener(_change); 57 | 58 | rainController = new AnimationController( 59 | vsync: this, duration: Duration(milliseconds: 100000)); 60 | rainAnimation = new Tween(begin: 0.0, end: 1.0).animate(rainController); 61 | rainAnimation.addListener(_rainDrop); 62 | doDelay(rainController, 0); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return Container( 68 | child: GestureDetector( 69 | onTapUp: (tapUp) { 70 | RenderBox getBox = context.findRenderObject(); 71 | var localOffset = getBox.globalToLocal(tapUp.globalPosition); 72 | print("w: ${localOffset.dx},h: ${localOffset.dy}"); 73 | var rainDrop = 74 | RainDropPoint(offset: new Offset(localOffset.dx, localOffset.dy)); 75 | addRain(rainDrop); 76 | }, 77 | onHorizontalDragUpdate: (hMove) { 78 | RenderBox getBox = context.findRenderObject(); 79 | var localOffset = getBox.globalToLocal(hMove.globalPosition); 80 | var rainDrop = 81 | RainDropPoint(offset: new Offset(localOffset.dx, localOffset.dy)); 82 | addRain(rainDrop); 83 | }, 84 | onVerticalDragUpdate: (hMove) { 85 | RenderBox getBox = context.findRenderObject(); 86 | var localOffset = getBox.globalToLocal(hMove.globalPosition); 87 | var rainDrop = 88 | RainDropPoint(offset: new Offset(localOffset.dx, localOffset.dy)); 89 | addRain(rainDrop); 90 | }, 91 | child: new CustomPaint( 92 | painter: RainDropPainter(rainList: widget.rainList), 93 | ), 94 | ), 95 | ); 96 | } 97 | 98 | void addRain(RainDropPoint rainDrop) { 99 | widget.rainList.add(rainDrop); 100 | doDelay(xController, 0); 101 | } 102 | 103 | void _change() { 104 | if (widget.rainList.isEmpty) { 105 | print("xController stop!"); 106 | xController.stop(); 107 | } 108 | setState(() {}); 109 | } 110 | 111 | void _rainDrop() { 112 | var random = new Random(); 113 | addRain(RainDropPoint( 114 | offset: Offset( 115 | _width * random.nextDouble(), _height * random.nextDouble()))); 116 | } 117 | 118 | void doDelay(AnimationController controller, int delay) async { 119 | Future.delayed(Duration(milliseconds: delay), () { 120 | try { 121 | controller..repeat(); 122 | } catch (e) {} 123 | }); 124 | } 125 | 126 | @override 127 | void dispose() { 128 | xController.dispose(); 129 | xAnimation.removeListener(_change); 130 | rainController.dispose(); 131 | rainAnimation.removeListener(_rainDrop); 132 | super.dispose(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/painter_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_base.dart'; 2 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_circle_snapchat.dart'; 3 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_line_four.dart'; 4 | import 'package:douban_movies/WanAndroid/Progress/painter/painter_wave.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | abstract class BasePainterFactory { 8 | BasePainter getPainter(); 9 | } 10 | 11 | class WavePainterFactory extends BasePainterFactory { 12 | BasePainter getPainter() { 13 | return WavePainter( 14 | waveCount: 1, 15 | waveColors: [ 16 | Colors.lightBlue, 17 | ], 18 | textStyle: TextStyle( 19 | fontSize: 60.0, 20 | foreground: Paint() 21 | ..color = Colors.lightBlue 22 | ..style = PaintingStyle.fill 23 | ..strokeWidth = 2.0 24 | ..blendMode = BlendMode.difference 25 | ..colorFilter = ColorFilter.mode(Colors.white, BlendMode.exclusion) 26 | ..maskFilter = MaskFilter.blur(BlurStyle.solid, 5.0), 27 | // color: Color.fromARGB( 28 | // 255, Colors.green.red, Colors.green.green, Colors.green.blue), 29 | fontWeight: FontWeight.bold, 30 | // shadows: [ 31 | // Shadow( 32 | // color: Color.fromARGB( 33 | // 255, Colors.grey.red, Colors.grey.green, Colors.grey.blue), 34 | // offset: Offset(5.0, 5.0), 35 | // blurRadius: 5.0) 36 | // ], 37 | ), 38 | ); 39 | } 40 | } 41 | 42 | class SnapchatCirclePainterFactory extends BasePainterFactory { 43 | BasePainter getPainter() { 44 | return SnapchatCirclePainter( 45 | circleGap: 15.0, 46 | circleColor: Colors.pink, 47 | circleWidth: 20.0, 48 | progressColor: Colors.green, 49 | ); 50 | } 51 | } 52 | 53 | class FourLinePainterFactory extends BasePainterFactory { 54 | BasePainter getPainter() { 55 | return FourLinePainter( 56 | gap: 10.0, 57 | lineColors: const [ 58 | Colors.green, 59 | Colors.red, 60 | Colors.blue, 61 | Colors.yellow, 62 | Colors.pinkAccent, 63 | ], 64 | baseHightScales: const [ 65 | 0.1, 66 | 0.6, 67 | 0.3, 68 | 0.8, 69 | 0.7, 70 | ], 71 | ); 72 | } 73 | } 74 | 75 | //class RainDropPainterFactory extends BasePainterFactory { 76 | // BasePainter getPainter(List rainList) { 77 | // return RainDropPainter( 78 | // rainList: rainList, 79 | // ); 80 | // } 81 | //} 82 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/progress_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter_factory.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ProgressManager extends StatefulWidget { 5 | @override 6 | _ProgressManagerState createState() => 7 | new _ProgressManagerState().._factory = WavePainterFactory(); 8 | } 9 | 10 | class _ProgressManagerState extends State 11 | with TickerProviderStateMixin { 12 | AnimationController xController; 13 | AnimationController yController; 14 | Animation xAnimation; 15 | Animation yAnimation; 16 | List _progressList = []; 17 | double curProgress = 0; 18 | BasePainterFactory _factory; 19 | 20 | set painter(BasePainterFactory factory) { 21 | _factory = factory; 22 | } 23 | 24 | setProgress(double progress) { 25 | _progressList.add(progress); 26 | onProgressChange(); 27 | } 28 | 29 | onProgressChange() { 30 | if (_progressList.length > 0) { 31 | if (yController != null && yController.isAnimating) { 32 | return; 33 | } 34 | double nextProgress = _progressList[0]; 35 | _progressList.removeAt(0); 36 | final double begin = curProgress; 37 | yController = new AnimationController( 38 | vsync: this, duration: Duration(milliseconds: 500)); 39 | yAnimation = 40 | new Tween(begin: begin, end: nextProgress).animate(yController); 41 | yAnimation.addListener(_onProgressChange); 42 | yAnimation.addStatusListener(_onProgressStatusChange); 43 | yController.forward(); 44 | } 45 | } 46 | 47 | @override 48 | void initState() { 49 | // TODO: implement initState 50 | super.initState(); 51 | xController = new AnimationController( 52 | vsync: this, duration: Duration(milliseconds: 3000)); 53 | xAnimation = new Tween(begin: 0.0, end: 1.0).animate(xController); 54 | 55 | xAnimation.addListener(_change); 56 | // xAnimation.addStatusListener((status) { 57 | // if (status == AnimationStatus.completed) { 58 | // xController.reverse(); 59 | // } else if (status == AnimationStatus.dismissed) { 60 | // xController.forward(); 61 | // } 62 | // }); 63 | 64 | yController = new AnimationController( 65 | vsync: this, duration: Duration(milliseconds: 500)); 66 | yAnimation = new Tween(begin: 0.0, end: 1.0).animate(yController); 67 | yAnimation.addListener(_onProgressChange); 68 | yAnimation.addStatusListener(_onProgressStatusChange); 69 | 70 | doDelay(xController, 0); 71 | 72 | Future.delayed(Duration(milliseconds: 1000), () { 73 | setProgress(0.8); 74 | }); 75 | Future.delayed(Duration(milliseconds: 2000), () { 76 | setProgress(0.2); 77 | }); 78 | Future.delayed(Duration(milliseconds: 3000), () { 79 | setProgress(1.0); 80 | }); 81 | Future.delayed(Duration(milliseconds: 4000), () { 82 | setProgress(0.5); 83 | }); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | return Center( 89 | child: Container( 90 | width: 200.0, 91 | height: 200.0, 92 | child: new CustomPaint( 93 | painter: _factory.getPainter() 94 | ..XAnimation = xAnimation 95 | ..YAnimation = yAnimation, 96 | size: new Size(200.0, 200.0), 97 | ), 98 | ), 99 | ); 100 | } 101 | 102 | void _change() { 103 | setState(() {}); 104 | } 105 | 106 | void _onProgressChange() { 107 | setState(() { 108 | curProgress = yAnimation.value; 109 | }); 110 | } 111 | 112 | void _onProgressStatusChange(status) { 113 | if (status == AnimationStatus.completed) { 114 | onProgressChange(); 115 | } 116 | } 117 | 118 | void doDelay(AnimationController controller, int delay) async { 119 | Future.delayed(Duration(milliseconds: delay), () { 120 | controller..repeat(); 121 | }); 122 | } 123 | 124 | @override 125 | void dispose() { 126 | xController.dispose(); 127 | yController.dispose(); 128 | xAnimation.removeListener(_change); 129 | yAnimation.removeListener(_onProgressChange); 130 | yAnimation.removeStatusListener(_onProgressStatusChange); 131 | super.dispose(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/rain_drop.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | class RainDropWidget extends StatefulWidget { 5 | RainDropWidget({Key key, this.width, this.height}) : super(key: key); 6 | 7 | final double width; 8 | final double height; 9 | 10 | @override 11 | State createState() { 12 | return RainDropState(width, height); 13 | } 14 | } 15 | 16 | class RainDropState extends State 17 | with TickerProviderStateMixin { 18 | List _rainList; 19 | AnimationController _animation; 20 | double _width = 200; 21 | double _height = 200; 22 | 23 | RainDropState(double width, double height) { 24 | _width = width ?? _width; 25 | _height = height ?? _height; 26 | } 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | _rainList = List(); 32 | _animation = new AnimationController( 33 | // 因为是repeat的,这里的duration其实不care 34 | duration: const Duration(milliseconds: 200), 35 | vsync: this) 36 | ..addListener(() { 37 | if (_rainList.isEmpty) { 38 | _animation.stop(); 39 | } 40 | setState(() {}); 41 | }); 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return Container( 47 | color: Colors.lightBlueAccent, 48 | width: _width, 49 | height: _height, 50 | child: GestureDetector( 51 | onTapUp: (TapUpDetails tapUp) { 52 | RenderBox getBox = context.findRenderObject(); 53 | var localOffset = getBox.globalToLocal(tapUp.globalPosition); 54 | var rainDrop = RainDropDrawer(localOffset.dx, localOffset.dy); 55 | _rainList.add(rainDrop); 56 | _animation.repeat(); 57 | }, 58 | child: CustomPaint( 59 | painter: RainDrop(_rainList), 60 | ), 61 | ), 62 | ); 63 | } 64 | } 65 | 66 | class RainDrop extends CustomPainter { 67 | RainDrop(this.rainList); 68 | 69 | List rainList = List(); 70 | Paint _paint = new Paint()..style = PaintingStyle.stroke; 71 | 72 | @override 73 | void paint(Canvas canvas, Size size) { 74 | rainList.forEach((item) { 75 | item.drawRainDrop(canvas, _paint); 76 | }); 77 | rainList.removeWhere((item) { 78 | return !item.isValid(); 79 | }); 80 | } 81 | 82 | @override 83 | bool shouldRepaint(CustomPainter oldDelegate) { 84 | return true; 85 | } 86 | } 87 | 88 | class RainDropDrawer { 89 | static const double MAX_RADIUS = 30; 90 | double posX; 91 | double posY; 92 | double radius = 5; 93 | 94 | RainDropDrawer(this.posX, this.posY); 95 | 96 | drawRainDrop(Canvas canvas, Paint paint) { 97 | double opt = (MAX_RADIUS - radius) / MAX_RADIUS; 98 | paint.color = Color.fromRGBO(0, 0, 0, opt); 99 | canvas.drawCircle(Offset(posX, posY), radius, paint); 100 | radius += 0.5; 101 | } 102 | 103 | bool isValid() { 104 | return radius < MAX_RADIUS; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/WanAndroid/Progress/widget_rain_drop.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/painter/rainDrop/painter_rain_drop.dart'; 2 | import 'package:douban_movies/WanAndroid/Progress/painter/rainDrop/point_rain_drop.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class RainDropWidget extends StatefulWidget { 6 | List rainList = List(); 7 | final width; 8 | final height; 9 | 10 | RainDropWidget({Key key, this.width, this.height}) : super(key: key); 11 | 12 | @override 13 | _RainDropWidgetState createState() => new _RainDropWidgetState(width, height); 14 | } 15 | 16 | class _RainDropWidgetState extends State 17 | with TickerProviderStateMixin { 18 | AnimationController xController; 19 | Animation xAnimation; 20 | AnimationController rainController; 21 | Animation rainAnimation; 22 | double _width = 200; 23 | double _height = 200; 24 | 25 | _RainDropWidgetState(double width, double height) { 26 | _width = width ?? _width; 27 | _height = height ?? _height; 28 | } 29 | 30 | @override 31 | void initState() { 32 | // TODO: implement initState 33 | super.initState(); 34 | xController = new AnimationController( 35 | vsync: this, duration: Duration(milliseconds: 3000)); 36 | xAnimation = new Tween(begin: 0.0, end: 1.0).animate(xController); 37 | xAnimation.addListener(_change); 38 | 39 | rainController = new AnimationController( 40 | vsync: this, duration: Duration(milliseconds: 100000)); 41 | rainAnimation = new Tween(begin: 0.0, end: 1.0).animate(rainController); 42 | rainAnimation.addListener(_rainDrop); 43 | doDelay(rainController, 0); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return Container( 49 | width: _width, 50 | height: _height, 51 | child: GestureDetector( 52 | onTapUp: (tapUp) { 53 | RenderBox getBox = context.findRenderObject(); 54 | var localOffset = getBox.globalToLocal(tapUp.globalPosition); 55 | var rainDrop = 56 | RainDropPoint(offset: new Offset(localOffset.dx, localOffset.dy)); 57 | addRain(rainDrop); 58 | }, 59 | onHorizontalDragUpdate: (hMove) { 60 | RenderBox getBox = context.findRenderObject(); 61 | var localOffset = getBox.globalToLocal(hMove.globalPosition); 62 | var rainDrop = 63 | RainDropPoint(offset: new Offset(localOffset.dx, localOffset.dy)); 64 | addRain(rainDrop); 65 | }, 66 | onVerticalDragUpdate: (hMove) { 67 | RenderBox getBox = context.findRenderObject(); 68 | var localOffset = getBox.globalToLocal(hMove.globalPosition); 69 | var rainDrop = 70 | RainDropPoint(offset: new Offset(localOffset.dx, localOffset.dy)); 71 | addRain(rainDrop); 72 | }, 73 | child: new CustomPaint( 74 | painter: RainDropPainter(rainList: widget.rainList), 75 | ), 76 | ), 77 | ); 78 | } 79 | 80 | void addRain(RainDropPoint rainDrop) { 81 | widget.rainList.add(rainDrop); 82 | doDelay(xController, 0); 83 | } 84 | 85 | void _change() { 86 | if (widget.rainList.isEmpty) { 87 | print("xController stop!"); 88 | xController.stop(); 89 | } 90 | setState(() {}); 91 | } 92 | 93 | void _rainDrop() { 94 | // var random = new Random(); 95 | // addRain(RainDropPoint( 96 | // offset: Offset( 97 | // _width * random.nextDouble(), _height * random.nextDouble()))); 98 | } 99 | 100 | void doDelay(AnimationController controller, int delay) async { 101 | Future.delayed(Duration(milliseconds: delay), () { 102 | controller..repeat(); 103 | }); 104 | } 105 | 106 | @override 107 | void dispose() { 108 | xController.dispose(); 109 | xAnimation.removeListener(_change); 110 | super.dispose(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/WanAndroid/article_detail_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/web_page.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 4 | import 'package:douban_movies/WanAndroid/Data/data_article_bean.dart'; 5 | 6 | class ArticleDetailPage extends StatelessWidget { 7 | final Data data; 8 | 9 | ArticleDetailPage({this.data}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return WebPage( 14 | title: data.getTitle(), 15 | url: data.link, 16 | ); 17 | // return Container( 18 | // child: new WebviewScaffold( 19 | // url: data.link, 20 | // appBar: new AppBar( 21 | // title:Text(data.getTitle()), 22 | // ), 23 | // withZoom: true, 24 | // withLocalStorage: true, 25 | // hidden: true, 26 | // initialChild: new Center( 27 | // child: new CircularProgressIndicator(), 28 | // ), 29 | // ), 30 | // ); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /lib/WanAndroid/article_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/article_list_content.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | enum ArticleType { 5 | HOME_ARTICLE, 6 | NORMAL_ARTICLE, 7 | PROJECT_ARTICLE, 8 | } 9 | 10 | enum JumpFromType{ 11 | KNOWLEDGE, 12 | OTHER, 13 | } 14 | 15 | class ArticlePage extends StatelessWidget { 16 | final String name; 17 | final int id; 18 | final ArticleType type; 19 | 20 | ArticlePage({this.name, this.id, this.type}); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return new Scaffold( 25 | appBar: new AppBar( 26 | title: Text(name), 27 | ), 28 | body: ArticleListPage( 29 | type: type, 30 | id: id, 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/WanAndroid/bloc/AccountBloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:douban_movies/WanAndroid/sp_utils.dart'; 4 | 5 | import 'bloc_utils.dart'; 6 | 7 | class AccountBloc implements BlocBase { 8 | String _account; 9 | 10 | String get getAccount => _account; 11 | 12 | StreamController _accountController = 13 | StreamController.broadcast(); 14 | 15 | StreamSink get _accountSink => _accountController.sink; 16 | 17 | Stream get accountStream => _accountController.stream; 18 | 19 | StreamController _actionControll = StreamController.broadcast(); 20 | 21 | StreamSink get accountSaveSink => _actionControll.sink; 22 | 23 | AccountBloc() { 24 | _account = ""; 25 | _actionControll.stream.listen(_handleLogic); 26 | initAccount(); 27 | } 28 | 29 | @override 30 | void dispose() { 31 | _accountController.close(); 32 | _actionControll.close(); 33 | } 34 | 35 | void _handleLogic(data) { 36 | _account = data; 37 | _accountSink.add(_account); 38 | SpUtils.save(SpUtils.KEY_ACCOUNT, data); 39 | } 40 | 41 | void initAccount() async { 42 | _account = await SpUtils.get(SpUtils.KEY_ACCOUNT, ""); 43 | accountSaveSink.add(_account); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/WanAndroid/bloc/ApplicationBloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:douban_movies/WanAndroid/sp_utils.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'bloc_utils.dart'; 7 | 8 | class ApplicationBloc implements BlocBase { 9 | ThemeData _theme; 10 | 11 | ThemeData get getTheme => _theme; 12 | 13 | StreamController _themeController = 14 | StreamController < ThemeData 15 | 16 | > 17 | 18 | . 19 | 20 | broadcast(); 21 | 22 | StreamSink get _themeSink => _themeController.sink; 23 | 24 | Stream get themeStream => _themeController.stream; 25 | 26 | StreamController _changeThemeController = StreamController.broadcast(); 27 | 28 | StreamSink get changeThemeSink => _changeThemeController.sink; 29 | 30 | ApplicationBloc() { 31 | _theme = new ThemeData( 32 | primaryColor: Colors.blue, 33 | ); 34 | _changeThemeController.stream.listen(_changeTheme); 35 | initTheme(); 36 | } 37 | 38 | @override 39 | void dispose() { 40 | _themeController.close(); 41 | _changeThemeController.close(); 42 | } 43 | 44 | void _changeTheme(data) { 45 | _theme = data; 46 | _themeSink.add(_theme); 47 | } 48 | 49 | void initTheme() async { 50 | _theme = new ThemeData( 51 | primaryColor: Color( 52 | await SpUtils.get(SpUtils.KEY_PRIMARYCOLOR, Colors.blue.value)), 53 | ); 54 | changeThemeSink.add(_theme); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/WanAndroid/bloc/bloc_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | 4 | abstract class BlocBase { 5 | void dispose(); 6 | } 7 | 8 | class BlocProvider extends StatefulWidget { 9 | final T bloc; 10 | final Widget child; 11 | 12 | BlocProvider({ 13 | Key key, 14 | @required this.child, 15 | @required this.bloc, 16 | }) : super(key: key); 17 | 18 | @override 19 | _BlocProviderState createState() => new _BlocProviderState(); 20 | 21 | static T of(BuildContext context) { 22 | final type = _typeOf>(); 23 | BlocProvider provider = context.ancestorWidgetOfExactType(type); 24 | return provider.bloc; 25 | } 26 | 27 | static Type _typeOf() => T; 28 | } 29 | 30 | class _BlocProviderState extends State> { 31 | @override 32 | Widget build(BuildContext context) { 33 | return widget.child; 34 | } 35 | 36 | @override 37 | void dispose() { 38 | // TODO: implement dispose 39 | widget.bloc.dispose(); 40 | super.dispose(); 41 | } 42 | } 43 | 44 | class IncrementBloc implements BlocBase { 45 | int _counter; 46 | 47 | StreamController _counterController = StreamController.broadcast(); 48 | 49 | StreamSink get _inAdd => _counterController.sink; 50 | 51 | Stream get outCounter => _counterController.stream; 52 | 53 | StreamController _actionControll = StreamController.broadcast(); 54 | 55 | StreamSink get incrementCounter => _actionControll.sink; 56 | 57 | IncrementBloc() { 58 | _counter = 0; 59 | _actionControll.stream.listen(_handleLogic); 60 | } 61 | 62 | @override 63 | void dispose() { 64 | _actionControll.close(); 65 | _counterController.close(); 66 | } 67 | 68 | void _handleLogic(data) { 69 | _counter += 1; 70 | _inAdd.add(_counter); 71 | } 72 | } -------------------------------------------------------------------------------- /lib/WanAndroid/config.dart: -------------------------------------------------------------------------------- 1 | final String URL_HOME_ARTICLE_LIST_part_1 = 2 | 'http://www.wanandroid.com/article/list/'; 3 | final String URL_HOME_ARTICLE_LIST_part_2 = '/json'; 4 | 5 | final String URL_TREE_ARTICLE_LIST_part_1 = 6 | 'http://www.wanandroid.com/article/list/'; 7 | final String URL_TREE_ARTICLE_LIST_part_2 = '/json?cid='; 8 | 9 | final String URL_PROJECT_ARTICLE_LIST_part_1 = 10 | 'http://www.wanandroid.com/project/list/'; 11 | 12 | final String URL_PROJECT_ARTICLE_LIST_part_2 = '/json?cid='; 13 | 14 | final String URL_LOGIN='http://www.wanandroid.com/user/login'; 15 | 16 | final String URL_REGISTER='http://www.wanandroid.com/user/register'; 17 | 18 | final String URL_NAVI_LIST = 'http://www.wanandroid.com/navi/json'; 19 | 20 | final String URL_BANNER_LIST = 'http://www.wanandroid.com/banner/json'; -------------------------------------------------------------------------------- /lib/WanAndroid/dialog_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // ignore: must_be_immutable 4 | class CustomizeDialog extends Dialog { 5 | final Widget widget; 6 | double width; 7 | double height; 8 | bool canceledOnTouchOutSide; 9 | 10 | CustomizeDialog({ 11 | Key key, 12 | @required this.widget, 13 | @required this.width, 14 | @required this.height, 15 | this.canceledOnTouchOutSide, 16 | }) : super(key: key); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return new Material( 21 | //创建透明层 22 | type: MaterialType.transparency, //透明类型 23 | child: GestureDetector( 24 | onTap: () { 25 | //设置弹窗范围外点击是否返回 26 | if (!canceledOnTouchOutSide) { 27 | return; 28 | } 29 | Navigator.pop(context); 30 | }, 31 | child: Container( 32 | color: Colors.transparent, 33 | child: GestureDetector( 34 | onTap: () { 35 | //设置点击弹窗范围内点击无响应 36 | }, 37 | child: new Center( 38 | //保证控件居中效果 39 | child: new SizedBox( 40 | width: width, 41 | height: height, 42 | child: new Container( 43 | decoration: ShapeDecoration( 44 | color: Colors.white, 45 | shape: RoundedRectangleBorder( 46 | borderRadius: BorderRadius.all( 47 | Radius.circular(8.0), 48 | ), 49 | ), 50 | ), 51 | child: widget, 52 | ), 53 | ), 54 | ), 55 | ), 56 | ), 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/WanAndroid/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/Progress/progress_manager.dart'; 2 | import 'package:douban_movies/WanAndroid/Progress/widget_rain_drop.dart'; 3 | import 'package:douban_movies/WanAndroid/article_list_content.dart'; 4 | import 'package:douban_movies/WanAndroid/article_page.dart'; 5 | import 'package:douban_movies/WanAndroid/bloc/AccountBloc.dart'; 6 | import 'package:douban_movies/WanAndroid/bloc/bloc_utils.dart'; 7 | import 'package:douban_movies/WanAndroid/icon_font_utils.dart'; 8 | import 'package:douban_movies/WanAndroid/knowledge_tree_page.dart'; 9 | import 'package:douban_movies/WanAndroid/person_page.dart'; 10 | import 'package:douban_movies/WanAndroid/popular_page.dart'; 11 | import 'package:douban_movies/WanAndroid/project_page.dart'; 12 | import 'package:douban_movies/WanAndroid/search_page.dart'; 13 | import 'package:flutter/material.dart'; 14 | 15 | 16 | class WanAndroidMainPage extends StatelessWidget { 17 | 18 | final ThemeData themeData; 19 | 20 | WanAndroidMainPage({this.themeData}); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | // TODO: implement build 25 | return MaterialApp( 26 | title: '玩安卓', 27 | theme: themeData, 28 | home: new WanAndroidHomePage(), 29 | ); 30 | } 31 | } 32 | 33 | class WanAndroidHomePage extends StatefulWidget { 34 | @override 35 | _WanAndroidHomePageState createState() => _WanAndroidHomePageState(); 36 | } 37 | 38 | class _WanAndroidHomePageState extends State { 39 | final _pages = [ 40 | ArticleListPage( 41 | type: ArticleType.HOME_ARTICLE, 42 | ), 43 | // ProgressManager(), 44 | // RainDropWidget(), 45 | PopularContentView(), 46 | KnowledgeTreePage(), 47 | ProjectTreePage(), 48 | BlocProvider( 49 | child: PersonPage(), 50 | bloc: AccountBloc(), 51 | ), 52 | ]; 53 | final _controller = PageController(initialPage: 0); 54 | int _selectedIndex = 0; 55 | 56 | void onItemTap(int index) { 57 | debugPrint('$index'); 58 | _controller.animateToPage(index, 59 | duration: const Duration(milliseconds: 200), curve: Curves.ease); 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | return new Scaffold( 65 | backgroundColor: Colors.white, 66 | appBar: new AppBar( 67 | elevation: 0.0, 68 | title: new Text('玩安卓'), 69 | actions: [ 70 | IconButton( 71 | icon: Icon( 72 | Icons.search, 73 | color: Colors.black87, 74 | ), 75 | onPressed: () { 76 | showSearch(context: context, delegate: searchBarDelegate()); 77 | }, 78 | ), 79 | ], 80 | ), 81 | body: PageView( 82 | controller: _controller, 83 | scrollDirection: Axis.horizontal, 84 | children: _pages, 85 | onPageChanged: (index) { 86 | if (_selectedIndex != index) { 87 | setState(() { 88 | _selectedIndex = index; 89 | }); 90 | } 91 | }, 92 | ), 93 | bottomNavigationBar: new BottomNavigationBar( 94 | items: [ 95 | new BottomNavigationBarItem( 96 | icon: IconFontUtils.getIcon(0xe639), 97 | title: Text( 98 | '文章', 99 | style: TextStyle( 100 | height: 1.1, 101 | ), 102 | ), 103 | backgroundColor: Colors.blue), 104 | new BottomNavigationBarItem( 105 | icon: IconFontUtils.getIcon(0xe600), 106 | title: Text( 107 | '导航', 108 | style: TextStyle( 109 | height: 1.1, 110 | ), 111 | ), 112 | backgroundColor: Colors.orange, 113 | ), 114 | new BottomNavigationBarItem( 115 | icon: IconFontUtils.getIcon(0xe61f), 116 | title: Text( 117 | '知识树', 118 | style: TextStyle( 119 | height: 1.1, 120 | ), 121 | ), 122 | backgroundColor: Colors.green, 123 | ), 124 | new BottomNavigationBarItem( 125 | icon: IconFontUtils.getIcon(0xe64f), 126 | title: Text( 127 | '项目', 128 | style: TextStyle( 129 | height: 1.1, 130 | ), 131 | ), 132 | backgroundColor: Colors.yellow, 133 | ), 134 | new BottomNavigationBarItem( 135 | icon: IconFontUtils.getIcon(0xe612), 136 | title: Text( 137 | '个人', 138 | style: TextStyle( 139 | height: 1.1, 140 | ), 141 | ), 142 | backgroundColor: Colors.purple, 143 | ), 144 | ], 145 | currentIndex: _selectedIndex, 146 | fixedColor: Colors.blue, 147 | onTap: onItemTap, 148 | type: BottomNavigationBarType.fixed, 149 | ), 150 | ); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /lib/WanAndroid/icon_font_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class IconFontUtils { 4 | static getIcon(int codePoint) { 5 | return Icon(IconData(codePoint, fontFamily: "IconFont")); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/WanAndroid/knowledge_tree_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | 4 | import 'package:dio/dio.dart'; 5 | import 'package:douban_movies/WanAndroid/Data/data_tree_bean.dart'; 6 | import 'package:douban_movies/WanAndroid/article_page.dart'; 7 | import 'package:douban_movies/WanAndroid/navigator_router_utils.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | final String URL_TREE_LIST = 'http://www.wanandroid.com/tree/json'; 11 | 12 | class KnowledgeTreePage extends StatefulWidget { 13 | @override 14 | _KnowledgeTreePageState createState() => new _KnowledgeTreePageState(); 15 | } 16 | 17 | class _KnowledgeTreePageState extends State 18 | with AutomaticKeepAliveClientMixin { 19 | @override 20 | Widget build(BuildContext context) { 21 | // TODO: implement build 22 | return buildFutureBuilder(); 23 | } 24 | 25 | FutureBuilder buildFutureBuilder() { 26 | return new FutureBuilder( 27 | builder: (context, AsyncSnapshot async) { 28 | if (async.connectionState == ConnectionState.active || 29 | async.connectionState == ConnectionState.waiting) { 30 | return new Center( 31 | child: new CircularProgressIndicator(), 32 | ); 33 | } 34 | 35 | if (async.connectionState == ConnectionState.done) { 36 | debugPrint('done'); 37 | if (async.hasError) { 38 | return new Center( 39 | child: new Text('Error:code '), 40 | ); 41 | } else if (async.hasData) { 42 | TreeBean bean = async.data; 43 | return RefreshIndicator( 44 | child: new ContentView(bean.nodes), 45 | onRefresh: () {}, 46 | ); 47 | } 48 | } 49 | }, 50 | future: getData(), 51 | ); 52 | } 53 | 54 | Future getData() async { 55 | debugPrint('getData'); 56 | var dio = new Dio(); 57 | Response response = await dio.get(URL_TREE_LIST); 58 | TreeBean bean = TreeBean.fromJson(response.data); 59 | return bean; 60 | } 61 | 62 | @override 63 | // TODO: implement wantKeepAlive 64 | bool get wantKeepAlive => true; 65 | } 66 | 67 | class ContentView extends StatelessWidget { 68 | final List nodes; 69 | 70 | ContentView(this.nodes); 71 | 72 | void jump2AtriclePage(int id) {} 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | // TODO: implement build 77 | return ListView.builder( 78 | itemBuilder: (context, i) { 79 | return new ContentItemView(nodes[i]); 80 | }, 81 | itemCount: nodes.length, 82 | scrollDirection: Axis.vertical, 83 | ); 84 | } 85 | } 86 | 87 | class ContentItemView extends StatelessWidget { 88 | final Node node; 89 | 90 | ContentItemView(this.node); 91 | 92 | getRandomColor() { 93 | return Color.fromARGB(255, Random.secure().nextInt(255), 94 | Random.secure().nextInt(255), Random.secure().nextInt(255)); 95 | } 96 | 97 | @override 98 | Widget build(BuildContext context) { 99 | // TODO: implement build 100 | return Container( 101 | decoration: BoxDecoration( 102 | border: Border(bottom: BorderSide(color: Colors.grey, width: 0.2)), 103 | ), 104 | padding: EdgeInsets.all(10.0), 105 | child: Column( 106 | children: [ 107 | Container( 108 | alignment: Alignment.centerLeft, 109 | child: Text( 110 | node.getName(), 111 | style: TextStyle( 112 | fontSize: 18.0, 113 | fontWeight: FontWeight.bold, 114 | ), 115 | ), 116 | ), 117 | Container( 118 | margin: EdgeInsets.only( 119 | top: 10.0, 120 | ), 121 | alignment: Alignment.centerLeft, 122 | child: Wrap( 123 | spacing: 8.0, 124 | runSpacing: 8.0, 125 | children: node.childNodes.map((childNode) { 126 | return GestureDetector( 127 | child: new ClipRRect( 128 | child: Container( 129 | padding: EdgeInsets.all(3.0), 130 | child: Text( 131 | childNode.getName(), 132 | style: TextStyle( 133 | color: Colors.white, 134 | shadows: [ 135 | BoxShadow( 136 | color: Colors.grey, offset: Offset(0.2, 0.2)) 137 | ], 138 | ), 139 | ), 140 | color: getRandomColor(), 141 | ), 142 | borderRadius: new BorderRadius.circular(3.0), 143 | ), 144 | onTap: () { 145 | NavigatorRouterUtils.pushToPage( 146 | context, 147 | new ArticlePage( 148 | name: childNode.getName(), 149 | id: childNode.mId, 150 | type: ArticleType.NORMAL_ARTICLE, 151 | )); 152 | }, 153 | ); 154 | }).toList(), 155 | ), 156 | ), 157 | ], 158 | ), 159 | ); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /lib/WanAndroid/navigator_router_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/router_anim_utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | /** 5 | * 跳转管理 6 | */ 7 | class NavigatorRouterUtils { 8 | /** 9 | * 跳转到指定page 10 | */ 11 | static void pushToPage(BuildContext context, Widget page) { 12 | Navigator.of(context).push(new PageRouteBuilder(pageBuilder: 13 | (BuildContext c, Animation animation, 14 | Animation secondartAnimation) { 15 | return page; 16 | }, transitionsBuilder: (BuildContext c, Animation animation, 17 | Animation secondartAnimation, Widget child) { 18 | return RouterAnim.createTransition(animation, child); 19 | })); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/WanAndroid/popular_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:douban_movies/WanAndroid/Data/data_navi_bean.dart'; 5 | import 'package:douban_movies/WanAndroid/Data/data_article_bean.dart'; 6 | import 'package:douban_movies/WanAndroid/article_detail_page.dart'; 7 | import 'package:douban_movies/WanAndroid/navigator_router_utils.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | final String URL_NAVI_LIST = 'http://www.wanandroid.com/navi/json'; 11 | 12 | class PopularContentView extends StatefulWidget { 13 | @override 14 | _PopularContentViewState createState() => _PopularContentViewState(); 15 | } 16 | 17 | class _PopularContentViewState extends State 18 | with AutomaticKeepAliveClientMixin { 19 | @override 20 | Widget build(BuildContext context) { 21 | // TODO: implement build 22 | return buildFutureBuilder(); 23 | } 24 | 25 | FutureBuilder buildFutureBuilder() { 26 | return new FutureBuilder( 27 | builder: (context, AsyncSnapshot async) { 28 | if (async.connectionState == ConnectionState.active || 29 | async.connectionState == ConnectionState.waiting) { 30 | return new Center( 31 | child: new CircularProgressIndicator(), 32 | ); 33 | } 34 | 35 | if (async.connectionState == ConnectionState.done) { 36 | debugPrint('done'); 37 | if (async.hasError) { 38 | return new Center( 39 | child: new Text('Error:code '), 40 | ); 41 | } else if (async.hasData) { 42 | NaviBean bean = async.data; 43 | return new PopularMenuView( 44 | nodes: bean.nodes, 45 | ); 46 | } 47 | } 48 | }, 49 | future: getData(), 50 | ); 51 | } 52 | 53 | Future getData() async { 54 | debugPrint('getData'); 55 | var dio = new Dio(); 56 | Response response = await dio.get(URL_NAVI_LIST); 57 | NaviBean bean = NaviBean.fromJson(response.data); 58 | return bean; 59 | } 60 | 61 | @override 62 | // TODO: implement wantKeepAlive 63 | bool get wantKeepAlive => true; 64 | } 65 | 66 | class PopularMenuView extends StatefulWidget { 67 | final List nodes; 68 | 69 | PopularMenuView({this.nodes}); 70 | 71 | @override 72 | _PopularMenuViewState createState() => _PopularMenuViewState(); 73 | } 74 | 75 | class _PopularMenuViewState extends State { 76 | int _checkedIndex=0; 77 | void Function(int) onMenuChecked; 78 | 79 | @override 80 | void initState() { 81 | // TODO: implement initState 82 | super.initState(); 83 | onMenuChecked=(int index){ 84 | if(_checkedIndex!=index){ 85 | _checkedIndex=index; 86 | setState(() { 87 | 88 | }); 89 | } 90 | }; 91 | } 92 | 93 | 94 | @override 95 | Widget build(BuildContext context) { 96 | // TODO: implement build 97 | return Row( 98 | children: [ 99 | Expanded( 100 | child: ContentLeftListView( 101 | nodes: widget.nodes, 102 | checkedIndex: _checkedIndex, 103 | onMenuCheckedListener: onMenuChecked, 104 | ), 105 | flex: 3, 106 | ), 107 | Expanded( 108 | child: ContentRightListView( 109 | articles:widget.nodes[_checkedIndex].articles, 110 | ), 111 | flex: 8, 112 | ), 113 | ], 114 | ); 115 | } 116 | } 117 | 118 | class ContentLeftListView extends StatelessWidget { 119 | final List nodes; 120 | final int checkedIndex; 121 | final onMenuCheckedListener; 122 | 123 | ContentLeftListView({this.nodes,this.checkedIndex,this.onMenuCheckedListener}); 124 | 125 | @override 126 | Widget build(BuildContext context) { 127 | // TODO: implement build 128 | return ListView.builder( 129 | itemCount: nodes.length, 130 | itemBuilder: (BuildContext context, int i) { 131 | return InkWell( 132 | child: Container( 133 | margin: const EdgeInsets.all(3.0), 134 | padding: const EdgeInsets.only(top: 10.0, bottom: 10.0), 135 | alignment: Alignment.center, 136 | decoration: BoxDecoration( 137 | borderRadius: BorderRadius.circular(5.0), 138 | border: Border.all(color: checkedIndex==i?Colors.blue:Colors.white,width: 1.0), 139 | ), 140 | child: Text( 141 | nodes[i].getName(), 142 | style: TextStyle( 143 | fontSize: 15.0, 144 | color: Colors.black, 145 | ), 146 | ), 147 | ), 148 | onTap: (){ 149 | onMenuCheckedListener(i); 150 | }, 151 | ); 152 | }, 153 | scrollDirection: Axis.vertical, 154 | ); 155 | } 156 | } 157 | 158 | class ContentRightListView extends StatelessWidget { 159 | final List articles; 160 | 161 | 162 | ContentRightListView({this.articles}); 163 | 164 | @override 165 | Widget build(BuildContext context) { 166 | // TODO: implement build 167 | return Container( 168 | margin: EdgeInsets.only( 169 | left: 10.0, 170 | top: 10.0, 171 | ), 172 | alignment: Alignment.topLeft, 173 | child: Wrap( 174 | spacing: 8.0, 175 | runSpacing: 8.0, 176 | children: articles.map((childNode) { 177 | return GestureDetector( 178 | child: new ClipRRect( 179 | child: Container( 180 | padding: EdgeInsets.all(3.0), 181 | child: Text( 182 | childNode.getTitle(), 183 | style: TextStyle( 184 | color: Colors.white, 185 | shadows: [ 186 | BoxShadow( 187 | color: Colors.grey, offset: Offset(0.2, 0.2)) 188 | ], 189 | ), 190 | ), 191 | color: getRandomColor(), 192 | ), 193 | borderRadius: new BorderRadius.circular(3.0), 194 | ), 195 | onTap: () { 196 | NavigatorRouterUtils.pushToPage( 197 | context, 198 | ArticleDetailPage( 199 | data: childNode, 200 | ), 201 | ); 202 | }, 203 | ); 204 | }).toList(), 205 | ), 206 | ); 207 | } 208 | } 209 | 210 | class ContentView extends StatelessWidget { 211 | final List nodes; 212 | 213 | ContentView(this.nodes); 214 | 215 | @override 216 | Widget build(BuildContext context) { 217 | // TODO: implement build 218 | return ListView.builder( 219 | itemCount: nodes.length, 220 | scrollDirection: Axis.vertical, 221 | itemBuilder: (context, i) { 222 | return ContentItemView(nodes[i]); 223 | }); 224 | } 225 | } 226 | 227 | getRandomColor() { 228 | return Color.fromARGB(255, Random.secure().nextInt(255), 229 | Random.secure().nextInt(255), Random.secure().nextInt(255)); 230 | } 231 | 232 | class ContentItemView extends StatelessWidget { 233 | final NaviNode naviNode; 234 | 235 | ContentItemView(this.naviNode); 236 | 237 | 238 | 239 | @override 240 | Widget build(BuildContext context) { 241 | // TODO: implement build 242 | return Card( 243 | elevation: 0.3, 244 | child: Container( 245 | margin: EdgeInsets.all(5.0), 246 | child: Column( 247 | children: [ 248 | Container( 249 | alignment: Alignment.centerLeft, 250 | child: Text( 251 | naviNode.getName(), 252 | style: TextStyle( 253 | fontSize: 18.0, 254 | fontWeight: FontWeight.bold, 255 | ), 256 | ), 257 | ), 258 | Container( 259 | margin: EdgeInsets.only( 260 | top: 10.0, 261 | ), 262 | alignment: Alignment.centerLeft, 263 | child: Wrap( 264 | spacing: 8.0, 265 | runSpacing: 8.0, 266 | children: naviNode.articles.map((childNode) { 267 | return GestureDetector( 268 | child: new ClipRRect( 269 | child: Container( 270 | padding: EdgeInsets.all(3.0), 271 | child: Text( 272 | childNode.getTitle(), 273 | style: TextStyle( 274 | color: Colors.white, 275 | shadows: [ 276 | BoxShadow( 277 | color: Colors.grey, offset: Offset(0.2, 0.2)) 278 | ], 279 | ), 280 | ), 281 | color: getRandomColor(), 282 | ), 283 | borderRadius: new BorderRadius.circular(3.0), 284 | ), 285 | onTap: () { 286 | NavigatorRouterUtils.pushToPage( 287 | context, 288 | ArticleDetailPage( 289 | data: childNode, 290 | ), 291 | ); 292 | }, 293 | ); 294 | }).toList(), 295 | ), 296 | ), 297 | ], 298 | ), 299 | ), 300 | ); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /lib/WanAndroid/project_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:douban_movies/WanAndroid/Data/data_tree_bean.dart'; 3 | import 'package:douban_movies/WanAndroid/article_page.dart'; 4 | import 'package:douban_movies/WanAndroid/navigator_router_utils.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'dart:math'; 7 | 8 | final String URL_TREE_LIST = 'http://www.wanandroid.com/project/tree/json'; 9 | 10 | getRandomColor() { 11 | return Color.fromARGB(255, Random.secure().nextInt(255), 12 | Random.secure().nextInt(255), Random.secure().nextInt(255)); 13 | } 14 | 15 | class ProjectTreePage extends StatefulWidget { 16 | @override 17 | _ProjectTreePageState createState() => new _ProjectTreePageState(); 18 | } 19 | 20 | class _ProjectTreePageState extends State 21 | with AutomaticKeepAliveClientMixin { 22 | @override 23 | Widget build(BuildContext context) { 24 | // TODO: implement build 25 | return buildFutureBuilder(); 26 | } 27 | 28 | FutureBuilder buildFutureBuilder() { 29 | return new FutureBuilder( 30 | builder: (context, AsyncSnapshot async) { 31 | if (async.connectionState == ConnectionState.active || 32 | async.connectionState == ConnectionState.waiting) { 33 | return new Center( 34 | child: new CircularProgressIndicator(), 35 | ); 36 | } 37 | 38 | if (async.connectionState == ConnectionState.done) { 39 | debugPrint('done'); 40 | if (async.hasError) { 41 | return new Center( 42 | child: new Text('Error:code '), 43 | ); 44 | } else if (async.hasData) { 45 | TreeBean bean = async.data; 46 | return RefreshIndicator( 47 | child: new ContentView(bean.nodes), 48 | onRefresh: () {}, 49 | ); 50 | } 51 | } 52 | }, 53 | future: getData(), 54 | ); 55 | } 56 | 57 | Future getData() async { 58 | debugPrint('getData'); 59 | var dio = new Dio(); 60 | Response response = await dio.get(URL_TREE_LIST); 61 | TreeBean bean = TreeBean.fromJson(response.data); 62 | return bean; 63 | } 64 | 65 | @override 66 | // TODO: implement wantKeepAlive 67 | bool get wantKeepAlive => true; 68 | } 69 | 70 | class ContentView extends StatelessWidget { 71 | final List nodes; 72 | 73 | ContentView(this.nodes); 74 | 75 | void jump2AtriclePage(int id) {} 76 | 77 | @override 78 | Widget build(BuildContext context) { 79 | // TODO: implement build 80 | // return ListView.builder( 81 | // itemBuilder: (context, i) { 82 | // return new ContentItemView(nodes[i]); 83 | // }, 84 | // itemCount: nodes.length, 85 | // scrollDirection: Axis.vertical, 86 | // ); 87 | return GridView.count( 88 | crossAxisCount: 2, 89 | padding: const EdgeInsets.all(10.0), 90 | children: nodes.map((node) { 91 | return ProjectItemView( 92 | node: node, 93 | ); 94 | }).toList(), 95 | ); 96 | } 97 | } 98 | 99 | class ProjectItemView extends StatelessWidget { 100 | final Node node; 101 | 102 | ProjectItemView({this.node}); 103 | 104 | @override 105 | Widget build(BuildContext context) { 106 | // TODO: implement build 107 | return Card( 108 | color: getRandomColor(), 109 | shape: new RoundedRectangleBorder( 110 | borderRadius: BorderRadius.only( 111 | topLeft: Radius.circular(5.0), 112 | topRight: Radius.circular(5.0), 113 | bottomLeft: Radius.circular(5.0), 114 | bottomRight: Radius.circular(5.0), 115 | ), 116 | ), 117 | elevation: 2.5, 118 | child: InkWell( 119 | onTap: () { 120 | NavigatorRouterUtils.pushToPage( 121 | context, 122 | new ArticlePage( 123 | name: node.getName(), 124 | id: node.mId, 125 | type: ArticleType.PROJECT_ARTICLE, 126 | )); 127 | }, 128 | child: Container( 129 | decoration: BoxDecoration( 130 | borderRadius: BorderRadius.circular(5.0), 131 | ), 132 | alignment: Alignment.center, 133 | child: Container( 134 | margin: const EdgeInsets.only(left: 10.0, right: 10.0), 135 | child: Text( 136 | node.getName(), 137 | textAlign: TextAlign.center, 138 | style: TextStyle( 139 | fontWeight: FontWeight.bold, 140 | fontSize: 20.0, 141 | color: Colors.white, 142 | shadows: [ 143 | BoxShadow(color: Colors.grey, offset: Offset(0.5, 0.5)), 144 | ], 145 | ), 146 | ), 147 | ), 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | 154 | class ContentItemView extends StatelessWidget { 155 | final Node node; 156 | 157 | ContentItemView(this.node); 158 | 159 | @override 160 | Widget build(BuildContext context) { 161 | // TODO: implement build 162 | return Card( 163 | elevation: 0.3, 164 | child: InkWell( 165 | onTap: () { 166 | NavigatorRouterUtils.pushToPage( 167 | context, 168 | new ArticlePage( 169 | name: node.getName(), 170 | id: node.mId, 171 | type: ArticleType.PROJECT_ARTICLE, 172 | )); 173 | }, 174 | child: Container( 175 | margin: EdgeInsets.all(10.0), 176 | child: Column( 177 | children: [ 178 | Container( 179 | alignment: Alignment.centerLeft, 180 | child: Text( 181 | node.getName(), 182 | style: TextStyle( 183 | fontSize: 18.0, 184 | fontWeight: FontWeight.bold, 185 | ), 186 | ), 187 | ), 188 | ], 189 | ), 190 | ), 191 | ), 192 | ); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /lib/WanAndroid/router_anim_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RouterAnim { 4 | static createTransition(Animation animation, Widget child) { 5 | return SlideTransition( 6 | position: new Tween( 7 | begin: const Offset(1.0, 0.0), 8 | end: const Offset(0.0, 0.0), 9 | ).animate(animation), 10 | child: child, 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/WanAndroid/search_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:douban_movies/WanAndroid/Data/data_article_bean.dart'; 5 | import 'package:douban_movies/WanAndroid/Data/data_key_bean.dart'; 6 | import 'package:douban_movies/WanAndroid/article_list_content.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | import 'article_page.dart'; 10 | 11 | final String URL_HOT_KEY = 'http://www.wanandroid.com/hotkey/json'; 12 | final String URL_SEARCH = 'http://www.wanandroid.com/article/query/0/json'; 13 | 14 | List nodes = []; 15 | 16 | 17 | class searchBarDelegate extends SearchDelegate { 18 | @override 19 | List buildActions(BuildContext context) { 20 | return [ 21 | IconButton( 22 | icon: Icon(Icons.clear), 23 | onPressed: () { 24 | query = ""; 25 | showSuggestions(context); 26 | }, 27 | ), 28 | ]; 29 | } 30 | 31 | @override 32 | Widget buildLeading(BuildContext context) { 33 | return IconButton( 34 | icon: AnimatedIcon( 35 | icon: AnimatedIcons.menu_arrow, progress: transitionAnimation), 36 | onPressed: () => close(context, null), 37 | ); 38 | } 39 | 40 | @override 41 | Widget buildResults(BuildContext context) { 42 | return buildSearchFutureBuilder(query); 43 | } 44 | 45 | @override 46 | Widget buildSuggestions(BuildContext context) { 47 | //如果说已经加载过一次搜索热榜,那么下次就不再重复加载了 48 | if (nodes.length == 0) { 49 | return buildDefaultFutureBuilder(); 50 | } else { 51 | return new SearchDefaultView( 52 | nodes: nodes, 53 | callback: (key) { 54 | query = key; 55 | showResults(context); 56 | }, 57 | ); 58 | } 59 | } 60 | 61 | FutureBuilder buildDefaultFutureBuilder() { 62 | return new FutureBuilder( 63 | builder: (context, AsyncSnapshot async) { 64 | if (async.connectionState == ConnectionState.active || 65 | async.connectionState == ConnectionState.waiting) { 66 | return new Center( 67 | child: new Text('Waitiing...'), 68 | ); 69 | } 70 | 71 | if (async.connectionState == ConnectionState.done) { 72 | debugPrint('done'); 73 | if (async.hasError) { 74 | return new Center( 75 | child: new Text('Error:code '), 76 | ); 77 | } else if (async.hasData) { 78 | KeyBean bean = async.data; 79 | nodes = bean.nodes; 80 | return new SearchDefaultView( 81 | nodes: nodes, 82 | callback: (key) { 83 | query = key; 84 | showResults(context); 85 | }, 86 | ); 87 | } 88 | } 89 | }, 90 | future: getData(), 91 | ); 92 | } 93 | 94 | Future getData() async { 95 | debugPrint('getKeyBeanData'); 96 | var dio = new Dio(); 97 | Response response = await dio.get(URL_HOT_KEY); 98 | KeyBean bean = KeyBean.fromJson(response.data); 99 | return bean; 100 | } 101 | 102 | FutureBuilder buildSearchFutureBuilder(String key) { 103 | return new FutureBuilder( 104 | builder: (context, AsyncSnapshot async) { 105 | if (async.connectionState == ConnectionState.active || 106 | async.connectionState == ConnectionState.waiting) { 107 | return new Center( 108 | child: new CircularProgressIndicator(), 109 | ); 110 | } 111 | 112 | if (async.connectionState == ConnectionState.done) { 113 | debugPrint('done'); 114 | if (async.hasError) { 115 | return new Center( 116 | child: new Text('Error:code '), 117 | ); 118 | } else if (async.hasData) { 119 | HomeBean bean = async.data; 120 | return new ArticleListView( 121 | bean.data.datas, ArticleType.NORMAL_ARTICLE, null); 122 | } 123 | } 124 | }, 125 | future: getSearchData(key), 126 | ); 127 | } 128 | 129 | Future getSearchData(String key) async { 130 | var dio = new Dio(); 131 | Response response = await dio.post( 132 | URL_SEARCH, 133 | options: Options( 134 | contentType: new ContentType('application', 'x-www-form-urlencoded', 135 | charset: 'utf-8'), 136 | ), 137 | data: { 138 | "k": key, 139 | }, 140 | ); 141 | HomeBean bean = HomeBean.fromJson(response.data); 142 | return bean; 143 | } 144 | } 145 | 146 | class SearchDefaultView extends StatelessWidget { 147 | final List nodes; 148 | final callback; 149 | 150 | SearchDefaultView({this.nodes, this.callback}); 151 | 152 | @override 153 | Widget build(BuildContext context) { 154 | // TODO: implement build 155 | return Column( 156 | children: [ 157 | SearchDefaultItemView( 158 | nodes: nodes, 159 | callback: callback, 160 | ), 161 | ], 162 | ); 163 | } 164 | } 165 | 166 | class SearchDefaultItemView extends StatelessWidget { 167 | final List nodes; 168 | final callback; 169 | 170 | SearchDefaultItemView({this.nodes, this.callback}); 171 | 172 | @override 173 | Widget build(BuildContext context) { 174 | return Container( 175 | margin: EdgeInsets.all(10.0), 176 | child: Column( 177 | children: [ 178 | Container( 179 | alignment: Alignment.centerLeft, 180 | child: Text( 181 | '大家都在搜', 182 | style: TextStyle( 183 | fontSize: 18.0, 184 | fontWeight: FontWeight.bold, 185 | ), 186 | ), 187 | ), 188 | Container( 189 | margin: EdgeInsets.only( 190 | top: 10.0, 191 | ), 192 | alignment: Alignment.centerLeft, 193 | child: Wrap( 194 | spacing: 8.0, 195 | runSpacing: 8.0, 196 | children: nodes.map((childNode) { 197 | return GestureDetector( 198 | child: new ClipRRect( 199 | child: Container( 200 | padding: EdgeInsets.all(3.0), 201 | child: Text( 202 | childNode.getName(), 203 | style: TextStyle( 204 | color: Colors.white, 205 | ), 206 | ), 207 | color: Colors.blue, 208 | ), 209 | borderRadius: new BorderRadius.circular(3.0), 210 | ), 211 | onTap: () { 212 | debugPrint('onTap key-> ${childNode.getName()}'); 213 | callback(childNode.getName()); 214 | }, 215 | ); 216 | }).toList(), 217 | ), 218 | ), 219 | ], 220 | ), 221 | ); 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /lib/WanAndroid/sp_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | /* 5 | * 存储数据 6 | */ 7 | class SpUtils { 8 | static final String KEY_ACCOUNT="key_account"; 9 | static final String KEY_PRIMARYCOLOR="key_primarycolor"; 10 | 11 | 12 | static Future save(String key, Object value) async { 13 | SharedPreferences preferences = await SharedPreferences.getInstance(); 14 | if (value is String) { 15 | preferences.setString(key, value); 16 | } else if (value is int) { 17 | preferences.setInt(key, value); 18 | } else if (value is bool) { 19 | preferences.setBool(key, value); 20 | } else if (value is double) { 21 | preferences.setDouble(key, value); 22 | } else if (value is List) { 23 | preferences.setStringList(key, value); 24 | } 25 | } 26 | 27 | static Future get(String key, Object defaultValue) async { 28 | SharedPreferences preferences = await SharedPreferences.getInstance(); 29 | if (defaultValue is String) { 30 | defaultValue = preferences.getString(key); 31 | } else if (defaultValue is int) { 32 | defaultValue = preferences.getInt(key); 33 | } else if (defaultValue is bool) { 34 | defaultValue = preferences.getBool(key); 35 | } else if (defaultValue is double) { 36 | defaultValue = preferences.getDouble(key); 37 | } else if (defaultValue is List) { 38 | defaultValue = preferences.getStringList(key); 39 | } 40 | return defaultValue; 41 | } 42 | 43 | static Future getString(String key, String defaultValue) async { 44 | SharedPreferences preferences = await SharedPreferences.getInstance(); 45 | defaultValue = preferences.getString(key); 46 | return defaultValue; 47 | } 48 | 49 | static Future getInt(String key, Object defaultValue) async { 50 | SharedPreferences preferences = await SharedPreferences.getInstance(); 51 | defaultValue = preferences.getInt(key); 52 | return defaultValue; 53 | } 54 | 55 | static Future getBool(String key, Object defaultValue) async { 56 | SharedPreferences preferences = await SharedPreferences.getInstance(); 57 | defaultValue = preferences.getBool(key); 58 | return defaultValue; 59 | } 60 | 61 | static Future getDouoble(String key, Object defaultValue) async { 62 | SharedPreferences preferences = await SharedPreferences.getInstance(); 63 | defaultValue = preferences.getDouble(key); 64 | return defaultValue; 65 | } 66 | 67 | static Future getStringList(String key, Object defaultValue) async { 68 | SharedPreferences preferences = await SharedPreferences.getInstance(); 69 | defaultValue = preferences.getStringList(key); 70 | return defaultValue; 71 | } 72 | 73 | static Future remove(String key) async { 74 | SharedPreferences preferences = await SharedPreferences.getInstance(); 75 | preferences.remove(key); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/WanAndroid/web_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 3 | import 'package:douban_movies/WanAndroid/Data/data_article_bean.dart'; 4 | import 'package:webview_flutter/webview_flutter.dart'; 5 | 6 | class WebPage extends StatelessWidget { 7 | final String title; 8 | final String url; 9 | 10 | WebPage({this.title,this.url}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: AppBar( 16 | title: Text(title), 17 | ), 18 | body: new WebView( 19 | onWebViewCreated: (WebViewController webViewController) { 20 | // _webViewController = webViewController; 21 | // _webViewController.addListener(() { 22 | // int _scrollY = _webViewController.scrollY.toInt(); 23 | // if (_scrollY < 480 && _isShowFloatBtn) { 24 | // _isShowFloatBtn = false; 25 | // setState(() {}); 26 | // } else if (_scrollY > 480 && !_isShowFloatBtn) { 27 | // _isShowFloatBtn = true; 28 | // setState(() {}); 29 | // } 30 | // }); 31 | }, 32 | initialUrl: url, 33 | javascriptMode: JavascriptMode.unrestricted, 34 | ), 35 | ); 36 | // return Container( 37 | // child: new WebviewScaffold( 38 | // url: url, 39 | // appBar: new AppBar( 40 | // title:Text(title), 41 | // ), 42 | // withZoom: true, 43 | // withLocalStorage: true, 44 | // hidden: true, 45 | // initialChild: new Center( 46 | // child: new CircularProgressIndicator(), 47 | // ), 48 | // ), 49 | // ); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /lib/WanAndroid/widget_banner.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_swiper/flutter_swiper.dart'; 3 | import 'package:dio/dio.dart'; 4 | import 'package:douban_movies/WanAndroid/Data/data_banner_bean.dart'; 5 | import 'package:douban_movies/WanAndroid/config.dart'; 6 | import 'package:transparent_image/transparent_image.dart'; 7 | import 'package:douban_movies/WanAndroid/navigator_router_utils.dart'; 8 | import 'package:douban_movies/WanAndroid/web_page.dart'; 9 | 10 | 11 | BannerListBean bannerListBean; 12 | 13 | class BannerWidget extends StatefulWidget { 14 | @override 15 | _BannerWidgetState createState() => _BannerWidgetState(); 16 | } 17 | 18 | class _BannerWidgetState extends State { 19 | @override 20 | Widget build(BuildContext context) { 21 | //如果banner已经加载过,那么刷新的时候轮播图就不再进行下一次刷新了 22 | if (bannerListBean != null) { 23 | return Swiper( 24 | itemBuilder: (BuildContext context, int index) { 25 | return Container( 26 | padding: const EdgeInsets.only(bottom: 20.0), 27 | child: Card( 28 | shape: const RoundedRectangleBorder( 29 | borderRadius: BorderRadius.all(Radius.circular(6.0)), 30 | ), 31 | elevation: 4.0, 32 | child: ClipRRect( 33 | borderRadius: BorderRadius.circular(6.0), 34 | child: new FadeInImage.memoryNetwork( 35 | fit: BoxFit.fill, 36 | placeholder: kTransparentImage, 37 | image: bannerListBean.data[index].imagePath, 38 | ), 39 | ), 40 | ), 41 | ); 42 | }, 43 | itemCount: bannerListBean.data.length, 44 | viewportFraction: 0.85, 45 | scale: 0.95, 46 | pagination: null, 47 | control: null, 48 | scrollDirection: Axis.horizontal, 49 | autoplay: true, 50 | onTap: (index) { 51 | NavigatorRouterUtils.pushToPage( 52 | context, 53 | new WebPage( 54 | title: bannerListBean.data[index].getName(), 55 | url: bannerListBean.data[index].url, 56 | )); 57 | }, 58 | ); 59 | } else { 60 | return buildFutureBuilder(); 61 | } 62 | } 63 | 64 | FutureBuilder buildFutureBuilder() { 65 | return new FutureBuilder( 66 | builder: (context, AsyncSnapshot async) { 67 | if (async.connectionState == ConnectionState.active || 68 | async.connectionState == ConnectionState.waiting) { 69 | return new Center( 70 | child: new CircularProgressIndicator(), 71 | ); 72 | } 73 | 74 | if (async.connectionState == ConnectionState.done) { 75 | debugPrint('done'); 76 | if (async.hasError) { 77 | return new Center( 78 | child: new Text('Error:code '), 79 | ); 80 | } else if (async.hasData) { 81 | bannerListBean = async.data; 82 | return Swiper( 83 | itemBuilder: (BuildContext context, int index) { 84 | return Container( 85 | padding: const EdgeInsets.only(bottom: 20.0), 86 | child: Card( 87 | shape: const RoundedRectangleBorder( 88 | borderRadius: BorderRadius.all(Radius.circular(6.0)), 89 | ), 90 | elevation: 4.0, 91 | child: ClipRRect( 92 | borderRadius: BorderRadius.circular(6.0), 93 | child: new FadeInImage.memoryNetwork( 94 | fit: BoxFit.fill, 95 | placeholder: kTransparentImage, 96 | image: bannerListBean.data[index].imagePath, 97 | ), 98 | ), 99 | ), 100 | ); 101 | }, 102 | itemCount: bannerListBean.data.length, 103 | viewportFraction: 0.85, 104 | scale: 0.95, 105 | pagination: null, 106 | control: null, 107 | scrollDirection: Axis.horizontal, 108 | autoplay: true, 109 | onTap: (index) { 110 | NavigatorRouterUtils.pushToPage( 111 | context, 112 | new WebPage( 113 | title: bannerListBean.data[index].getName(), 114 | url: bannerListBean.data[index].url, 115 | )); 116 | }, 117 | ); 118 | } 119 | } 120 | }, 121 | future: getData(), 122 | ); 123 | } 124 | 125 | 126 | Future getData() async { 127 | debugPrint('getData'); 128 | var dio = new Dio(); 129 | Response response = await dio.get(URL_BANNER_LIST); 130 | BannerListBean bean = BannerListBean.fromJson(response.data); 131 | return bean; 132 | } 133 | 134 | } -------------------------------------------------------------------------------- /lib/data/bean_move_list.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert' show json; 2 | 3 | class MovieList { 4 | 5 | int count; 6 | int start; 7 | int total; 8 | String title; 9 | List subjects; 10 | 11 | static getDetailDesc(subject detail){ 12 | StringBuffer sb=new StringBuffer(); 13 | for(var i=0;i jsonStr == null ? null : jsonStr is String ? new MovieList.fromJson(json.decode(jsonStr)) : new MovieList.fromJson(jsonStr); 26 | 27 | MovieList.fromJson(jsonRes) { 28 | count = jsonRes['count']; 29 | start = jsonRes['start']; 30 | total = jsonRes['total']; 31 | title = jsonRes['title']; 32 | subjects = jsonRes['subjects'] == null ? null : []; 33 | 34 | for (var subjectsItem in subjects == null ? [] : jsonRes['subjects']){ 35 | subjects.add(subjectsItem == null ? null : new subject.fromJson(subjectsItem)); 36 | } 37 | } 38 | 39 | @override 40 | String toString() { 41 | return '{"count": $count,"start": $start,"total": $total,"title": ${title != null?'${json.encode(title)}':'null'},"subjects": $subjects}'; 42 | } 43 | } 44 | 45 | class subject { 46 | 47 | int collect_count; 48 | bool has_video; 49 | String alt; 50 | String id; 51 | String mainland_pubdate; 52 | String original_title; 53 | String subtype; 54 | String title; 55 | String year; 56 | List casts; 57 | List directors; 58 | List durations; 59 | List genres; 60 | List pubdates; 61 | image images; 62 | rate rating; 63 | 64 | subject.fromParams({this.collect_count, this.has_video, this.alt, this.id, this.mainland_pubdate, this.original_title, this.subtype, this.title, this.year, this.casts, this.directors, this.durations, this.genres, this.pubdates, this.images, this.rating}); 65 | 66 | subject.fromJson(jsonRes) { 67 | collect_count = jsonRes['collect_count']; 68 | has_video = jsonRes['has_video']; 69 | alt = jsonRes['alt']; 70 | id = jsonRes['id']; 71 | mainland_pubdate = jsonRes['mainland_pubdate']; 72 | original_title = jsonRes['original_title']; 73 | subtype = jsonRes['subtype']; 74 | title = jsonRes['title']; 75 | year = jsonRes['year']; 76 | casts = jsonRes['casts'] == null ? null : []; 77 | 78 | for (var castsItem in casts == null ? [] : jsonRes['casts']){ 79 | casts.add(castsItem == null ? null : new cast.fromJson(castsItem)); 80 | } 81 | 82 | directors = jsonRes['directors'] == null ? null : []; 83 | 84 | for (var directorsItem in directors == null ? [] : jsonRes['directors']){ 85 | directors.add(directorsItem == null ? null : new director.fromJson(directorsItem)); 86 | } 87 | 88 | durations = jsonRes['durations'] == null ? null : []; 89 | 90 | for (var durationsItem in durations == null ? [] : jsonRes['durations']){ 91 | durations.add(durationsItem); 92 | } 93 | 94 | genres = jsonRes['genres'] == null ? null : []; 95 | 96 | for (var genresItem in genres == null ? [] : jsonRes['genres']){ 97 | genres.add(genresItem); 98 | } 99 | 100 | pubdates = jsonRes['pubdates'] == null ? null : []; 101 | 102 | for (var pubdatesItem in pubdates == null ? [] : jsonRes['pubdates']){ 103 | pubdates.add(pubdatesItem); 104 | } 105 | 106 | images = jsonRes['images'] == null ? null : new image.fromJson(jsonRes['images']); 107 | rating = jsonRes['rating'] == null ? null : new rate.fromJson(jsonRes['rating']); 108 | } 109 | 110 | @override 111 | String toString() { 112 | return '{"collect_count": $collect_count,"has_video": $has_video,"alt": ${alt != null?'${json.encode(alt)}':'null'},"id": ${id != null?'${json.encode(id)}':'null'},"mainland_pubdate": ${mainland_pubdate != null?'${json.encode(mainland_pubdate)}':'null'},"original_title": ${original_title != null?'${json.encode(original_title)}':'null'},"subtype": ${subtype != null?'${json.encode(subtype)}':'null'},"title": ${title != null?'${json.encode(title)}':'null'},"year": ${year != null?'${json.encode(year)}':'null'},"casts": $casts,"directors": $directors,"durations": $durations,"genres": $genres,"pubdates": $pubdates,"images": $images,"rating": $rating}'; 113 | } 114 | } 115 | 116 | class rate { 117 | 118 | int max; 119 | int min; 120 | var average; 121 | String stars; 122 | detail details; 123 | 124 | rate.fromParams({this.max, this.min, this.average, this.stars, this.details}); 125 | 126 | rate.fromJson(jsonRes) { 127 | max = jsonRes['max']; 128 | min = jsonRes['min']; 129 | average = jsonRes['average']; 130 | stars = jsonRes['stars']; 131 | details = jsonRes['details'] == null ? null : new detail.fromJson(jsonRes['details']); 132 | } 133 | 134 | @override 135 | String toString() { 136 | return '{"max": $max,"min": $min,"average": $average,"stars": ${stars != null?'${json.encode(stars)}':'null'},"details": $details}'; 137 | } 138 | } 139 | 140 | class detail { 141 | 142 | var detial_1; 143 | var detial_2; 144 | var detial_3; 145 | var detial_4; 146 | var detial_5; 147 | 148 | detail.fromParams({this.detial_1, this.detial_2, this.detial_3, this.detial_4, this.detial_5}); 149 | 150 | detail.fromJson(jsonRes) { 151 | detial_1 = jsonRes['1']; 152 | detial_2 = jsonRes['2']; 153 | detial_3 = jsonRes['3']; 154 | detial_4 = jsonRes['4']; 155 | detial_5 = jsonRes['5']; 156 | } 157 | 158 | @override 159 | String toString() { 160 | return '{"1": $detial_1,"2": $detial_2,"3": $detial_3,"4": $detial_4,"5": $detial_5}'; 161 | } 162 | } 163 | 164 | class image { 165 | 166 | String large; 167 | String medium; 168 | String small; 169 | 170 | image.fromParams({this.large, this.medium, this.small}); 171 | 172 | image.fromJson(jsonRes) { 173 | large = jsonRes['large']; 174 | medium = jsonRes['medium']; 175 | small = jsonRes['small']; 176 | } 177 | 178 | @override 179 | String toString() { 180 | return '{"large": ${large != null?'${json.encode(large)}':'null'},"medium": ${medium != null?'${json.encode(medium)}':'null'},"small": ${small != null?'${json.encode(small)}':'null'}}'; 181 | } 182 | } 183 | 184 | class director { 185 | 186 | String alt; 187 | String id; 188 | String name; 189 | String name_en; 190 | avatar avatars; 191 | 192 | director.fromParams({this.alt, this.id, this.name, this.name_en, this.avatars}); 193 | 194 | director.fromJson(jsonRes) { 195 | alt = jsonRes['alt']; 196 | id = jsonRes['id']; 197 | name = jsonRes['name']; 198 | name_en = jsonRes['name_en']; 199 | avatars = jsonRes['avatars'] == null ? null : new avatar.fromJson(jsonRes['avatars']); 200 | } 201 | 202 | @override 203 | String toString() { 204 | return '{"alt": ${alt != null?'${json.encode(alt)}':'null'},"id": ${id != null?'${json.encode(id)}':'null'},"name": ${name != null?'${json.encode(name)}':'null'},"name_en": ${name_en != null?'${json.encode(name_en)}':'null'},"avatars": $avatars}'; 205 | } 206 | } 207 | 208 | class avatar { 209 | 210 | String large; 211 | String medium; 212 | String small; 213 | 214 | avatar.fromParams({this.large, this.medium, this.small}); 215 | 216 | avatar.fromJson(jsonRes) { 217 | large = jsonRes['large']; 218 | medium = jsonRes['medium']; 219 | small = jsonRes['small']; 220 | } 221 | 222 | @override 223 | String toString() { 224 | return '{"large": ${large != null?'${json.encode(large)}':'null'},"medium": ${medium != null?'${json.encode(medium)}':'null'},"small": ${small != null?'${json.encode(small)}':'null'}}'; 225 | } 226 | } 227 | 228 | class cast { 229 | 230 | String alt; 231 | String id; 232 | String name; 233 | String name_en; 234 | avatar avatars; 235 | 236 | cast.fromParams({this.alt, this.id, this.name, this.name_en, this.avatars}); 237 | 238 | cast.fromJson(jsonRes) { 239 | alt = jsonRes['alt']; 240 | id = jsonRes['id']; 241 | name = jsonRes['name']; 242 | name_en = jsonRes['name_en']; 243 | avatars = jsonRes['avatars'] == null ? null : new avatar.fromJson(jsonRes['avatars']); 244 | } 245 | 246 | @override 247 | String toString() { 248 | return '{"alt": ${alt != null?'${json.encode(alt)}':'null'},"id": ${id != null?'${json.encode(id)}':'null'},"name": ${name != null?'${json.encode(name)}':'null'},"name_en": ${name_en != null?'${json.encode(name_en)}':'null'},"avatars": $avatars}'; 249 | } 250 | } 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /lib/db/db_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:sqflite/sqflite.dart'; 3 | import 'dart:io'; 4 | 5 | 6 | 7 | class DbUtils { 8 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:douban_movies/WanAndroid/bloc/ApplicationBloc.dart'; 4 | import 'package:douban_movies/WanAndroid/bloc/bloc_utils.dart'; 5 | import 'WanAndroid/home_page.dart'; 6 | 7 | void main() { 8 | //设置debugPaintSizeEnabled为true来更直观的调试布局问题 9 | debugPaintSizeEnabled = false; 10 | return runApp(BlocProvider( 11 | child: Application(), 12 | bloc: ApplicationBloc(), 13 | )); 14 | } 15 | 16 | class Application extends StatefulWidget { 17 | @override 18 | _ApplicationState createState() => _ApplicationState(); 19 | } 20 | 21 | class _ApplicationState extends State { 22 | @override 23 | Widget build(BuildContext context) { 24 | ApplicationBloc bloc=BlocProvider.of(context); 25 | 26 | return StreamBuilder( 27 | initialData: bloc.getTheme, 28 | stream: bloc.themeStream, 29 | builder: (BuildContext context,AsyncSnapshot snapshot){ 30 | return WanAndroidMainPage( 31 | themeData: snapshot.data, 32 | ); 33 | }, 34 | ); 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/net/http.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class HttpUtils { 4 | static String URL_GET_MOVIE_LIST_IN_THEATERS = 'https://api.douban.com/v2/movie/in_theaters'; 5 | static String URL_GET_MOVIE_LIST_COMING_SOON = 'https://api.douban.com/v2/movie/coming_soon'; 6 | static String URL_GET_MOVIE_LIST_TOP_250 = 'https://api.douban.com/v2/movie/top250'; 7 | static String URL_GET_MOVIE_LIST_WEEKLY = 'https://api.douban.com/v2/movie/weekly'; 8 | static String URL_GET_MOVIE_LIST_US_BOX = 'https://api.douban.com/v2/movie/us_box'; 9 | static String URL_GET_MOVIE_LIST_NEW_MOVIES = 'https://api.douban.com/v2/movie/new_movies'; 10 | static String URL_GET_MOVIE_DETAIL = 'http://api.douban.com/v2/movie/subject/'; 11 | static String URL_API_KEY='0b2bdeda43b5688921839c8ecb20399b'; 12 | static String URL_UDID='dddddddddddddddddddddd'; 13 | 14 | static get(String url, {Map map}) async { 15 | Dio dio = new Dio(); 16 | Response response = await dio.get(url,data: map); 17 | return response.data; 18 | } 19 | } -------------------------------------------------------------------------------- /lib/pages/detail/MovieActors/MovieActors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/data/bean_move_detail.dart'; 3 | import 'package:douban_movies/pages/views/ClipImageView.dart'; 4 | 5 | class MovieActorsView extends StatelessWidget { 6 | 7 | final MovieDetail detail; 8 | 9 | MovieActorsView(this.detail); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | // TODO: implement build 14 | return new Container( 15 | margin: new EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), 16 | child: new Column( 17 | children: [ 18 | new Container( 19 | alignment: Alignment.centerLeft, 20 | child: new Text('影人', 21 | style: new TextStyle( 22 | fontSize: 20.0, 23 | fontWeight: FontWeight.bold, 24 | ), 25 | ), 26 | ), 27 | new Container( 28 | height: 160.0, 29 | margin: EdgeInsets.only(top: 10.0), 30 | child: new Row( 31 | children: [ 32 | getActorsListView(detail.casts), 33 | ], 34 | ), 35 | ), 36 | ], 37 | ), 38 | ); 39 | } 40 | 41 | getActorsListView(List casts) { 42 | return new Expanded( 43 | child: new ListView.builder( 44 | itemBuilder: (c, i) { 45 | return new Container( 46 | width: 85.0, 47 | child: new Column( 48 | children: [ 49 | new ClipImageView( 50 | casts[i].avatars.small, 51 | new BorderRadius.circular(4.0), 52 | ), 53 | new Container( 54 | width: double.infinity, 55 | child: new Text( 56 | '${casts[i].name}', 57 | overflow: TextOverflow.ellipsis, 58 | textAlign: TextAlign.start, 59 | ), 60 | ), 61 | new Container( 62 | width: double.infinity, 63 | child: new Text( 64 | '${casts[i].name_en}', 65 | overflow: TextOverflow.ellipsis, 66 | textAlign: TextAlign.start, 67 | style: TextStyle( 68 | fontSize: 10.0, 69 | color: Colors.grey, 70 | ), 71 | ), 72 | ), 73 | ], 74 | 75 | ), 76 | margin: EdgeInsets.only(right: 10.0), 77 | ); 78 | }, 79 | itemCount: casts.length, 80 | scrollDirection: Axis.horizontal, 81 | ), 82 | ); 83 | } 84 | } -------------------------------------------------------------------------------- /lib/pages/detail/MovieChannel/MovieChannel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/data/bean_move_detail.dart'; 3 | import 'package:douban_movies/pages/views/Chips.dart'; 4 | 5 | class MovieChnnelView extends StatelessWidget { 6 | final MovieDetail detail; 7 | MovieChnnelView(this.detail); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | // TODO: implement build 12 | return new Container( 13 | padding: new EdgeInsets.only(left:10.0,right: 10.0,bottom: 5.0), 14 | child: new Row( 15 | children: [ 16 | getMovieChannelItems(detail), 17 | ], 18 | ), 19 | ); 20 | } 21 | 22 | ///get movie channel items 23 | getMovieChannelItems(MovieDetail detail) { 24 | return new Expanded( 25 | child: new ListView.builder( 26 | scrollDirection: Axis.horizontal, 27 | itemCount: detail.tags.length, 28 | itemBuilder: (context, i) { 29 | if(i==0){ 30 | return new Container( 31 | alignment: Alignment.centerLeft, 32 | padding: new EdgeInsets.only(right:5.0), 33 | child: new Text('所属频道'), 34 | ); 35 | }else { 36 | return new Container( 37 | padding: new EdgeInsets.only(right: 5.0), 38 | child: new Chip( 39 | onDeleted: () { 40 | print('点击了标签!'); 41 | }, 42 | deleteIcon: new Icon(Icons.chevron_right), 43 | label: new Text(detail.tags[i]), 44 | ), 45 | ); 46 | } 47 | }, 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/pages/detail/MovieComments/MovieCommentsView.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/pages/views/ClipImageView.dart'; 3 | import 'package:douban_movies/data/bean_move_detail.dart'; 4 | import 'package:douban_movies/pages/views/StartsView.dart'; 5 | 6 | class MovieCommentsView extends StatelessWidget { 7 | final MovieDetail _detail; 8 | 9 | MovieCommentsView(this._detail); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | // TODO: implement build 14 | return Container( 15 | margin: EdgeInsets.all(10.0), 16 | decoration: new BoxDecoration( 17 | color: Colors.grey, 18 | borderRadius: BorderRadius.circular(5.0), 19 | ), 20 | child: new Column( 21 | children: 22 | getCommentsList(_detail), 23 | ), 24 | ); 25 | } 26 | 27 | getCommentsList(MovieDetail detail) { 28 | if (detail == null) { 29 | return []; 30 | } 31 | List views = []; 32 | views.add(new Container( 33 | width: double.infinity, 34 | margin: EdgeInsets.only(left: 10.0,top: 10.0,bottom: 20.0), 35 | alignment: Alignment.centerLeft, 36 | child: Text( 37 | '短评', 38 | textAlign: TextAlign.start, 39 | style: TextStyle( 40 | fontWeight: FontWeight.bold, 41 | fontSize: 20.0, 42 | color: Colors.white, 43 | ), 44 | ), 45 | )); 46 | for (var i = 0; i < detail.popular_comments.length; i++) { 47 | print(detail.popular_comments[i]); 48 | views.add(new CommentItemView(detail.popular_comments[i], 49 | (i == (detail.popular_comments.length - 1)))); 50 | } 51 | return views; 52 | } 53 | 54 | } 55 | 56 | class CommentItemView extends StatelessWidget { 57 | final popular_comment comment; 58 | final bool isLast; 59 | 60 | CommentItemView(this.comment, this.isLast); 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | // TODO: implement build 65 | return new Container( 66 | margin: EdgeInsets.only(left: 10.0, right: 10.0, bottom: 20.0), 67 | child: Column( 68 | children: [ 69 | Container( 70 | height: 40.0, 71 | child: new Row( 72 | children: [ 73 | new Container( 74 | alignment: Alignment.center, 75 | width: 30.0, 76 | child: ClipImageView( 77 | comment.author.avatar, BorderRadius.circular(15.0)), 78 | ), 79 | new Container(width: 10.0,), 80 | new Expanded( 81 | child: Column( 82 | children: [ 83 | Expanded( 84 | flex: 1, 85 | child: Container( 86 | alignment: Alignment.centerLeft, 87 | width: double.infinity, 88 | child: new Text( 89 | comment.author.name, 90 | textAlign: TextAlign.start, 91 | style: TextStyle( 92 | fontSize: 15.0, 93 | color: Colors.white, 94 | fontWeight: FontWeight.bold, 95 | ), 96 | ), 97 | ), 98 | ), 99 | Expanded( 100 | flex: 1, 101 | child: Container( 102 | alignment: Alignment.centerLeft, 103 | width: double.infinity, 104 | child: new Text( 105 | comment.created_at, 106 | textAlign: TextAlign.start, 107 | style: TextStyle( 108 | fontSize: 12.0, 109 | color: Colors.grey[300], 110 | ), 111 | ), 112 | ), 113 | ), 114 | ], 115 | ) 116 | ), 117 | ], 118 | ), 119 | ), 120 | Container( 121 | margin: EdgeInsets.only(top: 10.0), 122 | width: double.infinity, 123 | child: new Text( 124 | comment.content, 125 | textAlign: TextAlign.start, 126 | style: TextStyle( 127 | color: Colors.white, 128 | ), 129 | ), 130 | ), 131 | Container( 132 | margin: EdgeInsets.only(top: 10.0), 133 | width: double.infinity, 134 | child: Row( 135 | children: [ 136 | Icon(Icons.thumb_up, color: Colors.grey[300], size: 15.0,), 137 | Expanded( 138 | child: Container( 139 | alignment: Alignment.centerLeft, 140 | margin: EdgeInsets.only(left: 5.0), 141 | child: new Text( 142 | '${comment.getUserfulCountDesc()}k', 143 | textAlign: TextAlign.start, 144 | style: TextStyle( 145 | color: Colors.grey[300], 146 | ), 147 | ), 148 | ), 149 | ), 150 | ], 151 | ), 152 | ), 153 | getDivider(isLast), 154 | ], 155 | ), 156 | ); 157 | } 158 | 159 | getDivider(bool isLast) { 160 | if (!isLast) { 161 | return Container( 162 | margin: EdgeInsets.only(top: 20.0), 163 | child: new Divider(color: Colors.white,), 164 | ); 165 | } else { 166 | return new Container(); 167 | } 168 | } 169 | 170 | } -------------------------------------------------------------------------------- /lib/pages/detail/MovieDesc/MovieDesc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:douban_movies/data/bean_move_detail.dart'; 4 | 5 | class MovieDescView extends StatelessWidget { 6 | 7 | final MovieDetail detail; 8 | 9 | MovieDescView(this.detail); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return new Container( 14 | margin: new EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), 15 | child: new Column( 16 | children: [ 17 | new Container( 18 | alignment: Alignment.centerLeft, 19 | child: new Text('简介', 20 | style: new TextStyle( 21 | fontSize: 20.0, 22 | fontWeight: FontWeight.bold, 23 | ), 24 | ), 25 | ), 26 | new Container( 27 | margin: EdgeInsets.only(top: 10.0), 28 | child: new ExpansionText(detail.summary.substring(0, 40), 29 | detail.summary, false), 30 | ), 31 | ], 32 | ), 33 | ); 34 | } 35 | } 36 | 37 | 38 | class ExpansionText extends StatefulWidget { 39 | 40 | final String title; 41 | final String body; 42 | final bool expand; 43 | 44 | ExpansionText(this.title, this.body, this.expand); 45 | 46 | @override 47 | _ExpansionTextState createState() => 48 | _ExpansionTextState(this.title, this.body, this.expand); 49 | 50 | } 51 | 52 | class _ExpansionTextState extends State { 53 | 54 | String _title; 55 | String _body; 56 | bool _expand; 57 | 58 | _ExpansionTextState(String title, String body, bool expand) { 59 | this._title = title; 60 | this._body = body; 61 | this._expand = expand; 62 | } 63 | 64 | 65 | expandText() { 66 | if (_expand) { 67 | _expand = false; 68 | } else { 69 | _expand = true; 70 | } 71 | setState(() { 72 | 73 | }); 74 | } 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | 79 | 80 | return new Container( 81 | width: double.infinity, 82 | alignment: Alignment.centerLeft, 83 | child: new Text.rich(new TextSpan( 84 | children: [ 85 | new TextSpan( 86 | text: _expand ? _body : _title, 87 | recognizer: new TapGestureRecognizer() 88 | ..onTap = () { 89 | expandText(); 90 | }, 91 | ), 92 | getExpandText(_expand), 93 | ], 94 | ),), 95 | ); 96 | } 97 | 98 | getExpandText(bool expand) { 99 | return expand ? new TextSpan() : new TextSpan( 100 | text: '展开', 101 | recognizer: new TapGestureRecognizer() 102 | ..onTap = () { 103 | expandText(); 104 | }, 105 | style: new TextStyle( 106 | decoration: TextDecoration.underline, 107 | color: Colors.lightBlue, 108 | ), 109 | ); 110 | } 111 | } -------------------------------------------------------------------------------- /lib/pages/detail/MoviePopularComments/MoviePopularCommentsView.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/data/bean_move_detail.dart'; 3 | import 'package:douban_movies/pages/views/ClipImageView.dart'; 4 | import 'package:douban_movies/pages/views/StartsView.dart'; 5 | 6 | class MoviePopularCommentsView extends StatelessWidget { 7 | final MovieDetail _detail; 8 | 9 | MoviePopularCommentsView(this._detail); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return _detail.popular_comments != null ? 14 | new Container( 15 | child: Column( 16 | children: [ 17 | new CommentItemView(_detail.popular_comments[0]), 18 | ], 19 | ), 20 | ) : new Container(); 21 | } 22 | 23 | } 24 | 25 | class CommentItemView extends StatelessWidget { 26 | final popular_comment _comment; 27 | 28 | CommentItemView(this._comment); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | // TODO: implement build 33 | return new Container( 34 | child: new Column( 35 | children: [ 36 | new CommentItemTopView(_comment), 37 | new Container( 38 | child: Text( 39 | '${_comment.content}', 40 | ), 41 | ), 42 | // new CommentItemBottomView(_comments), 43 | ], 44 | ), 45 | ); 46 | } 47 | } 48 | 49 | class CommentItemTopView extends StatelessWidget { 50 | final popular_comment _comment; 51 | 52 | CommentItemTopView(this._comment); 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return Container( 57 | child: new Row( 58 | children: [ 59 | new ClipImageView( 60 | _comment.author.avatar, 61 | BorderRadius.circular(4.0), 62 | ), 63 | new Expanded( 64 | child: Container( 65 | child: new Column( 66 | children: [ 67 | new Row( 68 | children: [ 69 | new Container( 70 | child: new Text('${_comment.author.name}'), 71 | ), 72 | new Expanded( 73 | child: new Container( 74 | child: new StarItem( 75 | int.parse(_comment.rating.stars==null?50:_comment.rating.stars), 15.0, 76 | Colors.orange[300], true), 77 | ), 78 | ), 79 | ], 80 | ), 81 | new Container( 82 | child: Text('${_comment.created_at}'), 83 | ), 84 | ], 85 | ), 86 | ) 87 | ), 88 | ], 89 | ), 90 | ); 91 | } 92 | } -------------------------------------------------------------------------------- /lib/pages/detail/MovieRating/MovieRating.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/data/bean_move_detail.dart'; 3 | import 'package:douban_movies/pages/views/StartsView.dart'; 4 | 5 | class MovieRatingView extends StatelessWidget { 6 | final MovieDetail detail; 7 | 8 | MovieRatingView(this.detail); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return new Container( 13 | decoration: new BoxDecoration( 14 | color: Colors.grey[500], 15 | borderRadius: new BorderRadius.all(new Radius.circular(5.0)), 16 | ), 17 | margin: EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0), 18 | width: double.infinity, 19 | height: 160.0, 20 | child: new Column( 21 | children: [ 22 | new Container( 23 | padding: EdgeInsets.only(top: 10.0, left: 10.0), 24 | child: new Text( 25 | '豆瓣评分', 26 | style: new TextStyle( 27 | color: Colors.white, 28 | ), 29 | ), 30 | height: 10.0, 31 | width: double.infinity, 32 | ), 33 | new Container( 34 | height: 110.0, 35 | width: double.infinity, 36 | child: new RateStarsView(detail), 37 | ), 38 | new Container( 39 | margin: EdgeInsets.only(left: 10.0, right: 10.0), 40 | child: new Divider(color: Colors.grey[300],), 41 | ), 42 | new Container( 43 | margin: EdgeInsets.only(right: 10.0), 44 | width: double.infinity, 45 | child: Text('${detail.getCollectPeopleCount()}k人看过 ${detail 46 | .getWishPeopleCount()}k人想看', 47 | style: TextStyle( 48 | color: Colors.grey[300], 49 | fontSize: 12.0, 50 | ), 51 | textAlign: TextAlign.end,), 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class RateStarsView extends StatelessWidget { 60 | final MovieDetail detail; 61 | 62 | RateStarsView(this.detail); 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | // TODO: implement build 67 | return new Row( 68 | children: [ 69 | new Expanded( 70 | flex: 3, 71 | child: new RateStarsLeftView(detail), 72 | ), 73 | new Expanded( 74 | flex: 7, 75 | child: new RateStarsRightView(detail), 76 | ), 77 | ], 78 | ); 79 | } 80 | } 81 | 82 | ///评分区域中间左边的部分 83 | class RateStarsLeftView extends StatelessWidget { 84 | final MovieDetail detail; 85 | 86 | RateStarsLeftView(this.detail); 87 | 88 | @override 89 | Widget build(BuildContext context) { 90 | // TODO: implement build 91 | return new Column( 92 | children: [ 93 | new Expanded( 94 | child: new Container( 95 | child: new Text( 96 | '${detail.rating.average}', 97 | style: new TextStyle( 98 | fontSize: 40.0, 99 | fontWeight: FontWeight.bold, 100 | color: Colors.white, 101 | ), 102 | ), 103 | alignment: Alignment.bottomRight, 104 | )), 105 | new Row( 106 | children: [ 107 | new Expanded(child: new Container()), 108 | new Container( 109 | child: new StarItem(int.parse(detail.rating.stars), 15.0, 110 | Colors.orange[300], true), 111 | padding: EdgeInsets.only(bottom: 20.0), 112 | alignment: Alignment.topRight, 113 | ) 114 | ], 115 | ) 116 | ], 117 | ); 118 | } 119 | } 120 | 121 | ///评分区域中间中部的部分 122 | class RateStarsRightView extends StatelessWidget { 123 | final MovieDetail detail; 124 | 125 | RateStarsRightView(this.detail); 126 | 127 | @override 128 | Widget build(BuildContext context) { 129 | // TODO: implement build 130 | return new Column( 131 | children: [ 132 | new Expanded(child: new Container()), 133 | new Container( 134 | alignment: Alignment.bottomRight, 135 | margin: EdgeInsets.only(left: 5.0), 136 | child: new Column( 137 | children: [ 138 | new StarAndProgressItem(detail.rating, 5), 139 | new StarAndProgressItem(detail.rating, 4), 140 | new StarAndProgressItem(detail.rating, 3), 141 | new StarAndProgressItem(detail.rating, 2), 142 | new StarAndProgressItem(detail.rating, 1), 143 | ], 144 | mainAxisSize: MainAxisSize.min, 145 | ), 146 | ), 147 | new Container( 148 | height: 20.0, 149 | width: double.infinity, 150 | margin: EdgeInsets.only(left: 145.0), 151 | alignment: Alignment.centerLeft, 152 | child: new Text('${detail.rating.details.getDetailTotal()}人评分', 153 | style: TextStyle( 154 | fontSize: 12.0, 155 | color: Colors.grey[300] 156 | ), 157 | textAlign: TextAlign.start, 158 | ), 159 | ), 160 | ], 161 | ); 162 | } 163 | } 164 | 165 | class StarAndProgressItem extends StatelessWidget { 166 | final rate _rate; 167 | final int _starIndex; 168 | 169 | StarAndProgressItem(this._rate, this._starIndex); 170 | 171 | @override 172 | Widget build(BuildContext context) { 173 | return new Row( 174 | children: [ 175 | new Container( 176 | width: 60.0, 177 | alignment: Alignment.centerRight, 178 | child: new Row( 179 | children: [ 180 | new Expanded(child: new Container()), 181 | new Container( 182 | alignment: Alignment.centerRight, 183 | child: new StarItem( 184 | _starIndex * 10, 12.0, Colors.grey[300], false), 185 | ) 186 | ], 187 | ), 188 | ), 189 | new Container( 190 | width: 150.0, 191 | child: new LinearProgressIndicator( 192 | value: _rate.details.getDetailIndexRate(_starIndex), 193 | backgroundColor: Colors.grey[600], 194 | ), 195 | ), 196 | ], 197 | ); 198 | } 199 | } -------------------------------------------------------------------------------- /lib/pages/detail/MovieTitle/MovieTitle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/data/bean_move_detail.dart'; 3 | import 'package:douban_movies/pages/views/ClipImageView.dart'; 4 | 5 | ///Movie Title block 6 | class MovieTitleView extends StatelessWidget { 7 | final MovieDetail detail; 8 | 9 | MovieTitleView(this.detail); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return new Container( 14 | padding: new EdgeInsets.all(10.0), 15 | child: new Row( 16 | children: [ 17 | new Container( 18 | width: 100.0, 19 | child: new ClipImageView( 20 | detail.images.medium, 21 | new BorderRadius.circular(4.0), 22 | ), 23 | ), 24 | new MovieTitleContentItem(detail), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | ///Movie title 右边的内容区域 32 | class MovieTitleContentItem extends StatelessWidget { 33 | final MovieDetail detail; 34 | 35 | MovieTitleContentItem(this.detail); 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return new Expanded( 40 | child: new Container( 41 | padding: new EdgeInsets.only( 42 | left: 10.0, top: 5.0, right: 5.0, bottom: 5.0), 43 | child: new Column( 44 | children: [ 45 | 46 | /// Title 47 | new Container( 48 | width: double.infinity, 49 | child: new Text( 50 | detail.title, 51 | style: new TextStyle( 52 | fontWeight: FontWeight.bold, 53 | fontSize: 20.0, 54 | ), 55 | ), 56 | ), 57 | 58 | /// Title Top Desc 59 | new Container( 60 | margin: new EdgeInsets.only(top: 5.0, right: 5.0), 61 | width: double.infinity, 62 | alignment: Alignment.centerLeft, 63 | child: new Text( 64 | detail.getTitleDescTop(), 65 | style: new TextStyle( 66 | fontWeight: FontWeight.bold, 67 | fontSize: 15.0, 68 | ), 69 | ), 70 | ), 71 | 72 | ///Title Bottom Desc 73 | new Container( 74 | margin: new EdgeInsets.only(top: 5.0, right: 5.0), 75 | width: double.infinity, 76 | alignment: Alignment.centerLeft, 77 | child: new Text( 78 | detail.getTitleDescBottom(), 79 | style: new TextStyle( 80 | fontWeight: FontWeight.bold, 81 | color: Colors.grey, 82 | fontSize: 12.0, 83 | ), 84 | ), 85 | ), 86 | 87 | /// Title Bottom Buttons 88 | getTitleButtons() 89 | ], 90 | ), 91 | ), 92 | ); 93 | } 94 | 95 | ///get Title Block bottom buttons 96 | getTitleButtons() { 97 | return new Container( 98 | margin: new EdgeInsets.only(top: 10.0), 99 | child: new Row( 100 | children: [ 101 | new Expanded( 102 | flex: 1, 103 | child: new RaisedButton.icon( 104 | color: Colors.white, 105 | icon: new Icon( 106 | Icons.favorite_border, 107 | color: Colors.orange[400], 108 | ), 109 | label: new Text( 110 | '想看', 111 | style: new TextStyle(fontWeight: FontWeight.bold), 112 | ), 113 | onPressed: () { 114 | print('点击了按钮!'); 115 | }, 116 | )), 117 | new Padding(padding: new EdgeInsets.all(5.0)), 118 | new Expanded( 119 | flex: 1, 120 | child: new RaisedButton.icon( 121 | color: Colors.white, 122 | icon: new Icon( 123 | Icons.star_border, 124 | color: Colors.orange[400], 125 | ), 126 | label: new Text( 127 | '看过', 128 | style: new TextStyle(fontWeight: FontWeight.bold), 129 | ), 130 | onPressed: () { 131 | print('点击了按钮!'); 132 | }, 133 | )), 134 | ], 135 | ), 136 | ); 137 | } 138 | } -------------------------------------------------------------------------------- /lib/pages/detail/page_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:douban_movies/res/value_string.dart'; 3 | import 'package:douban_movies/pages/page_home.dart'; 4 | import 'package:douban_movies/data/bean_move_list.dart' as MoveList; 5 | import 'package:douban_movies/net/http.dart'; 6 | import 'package:douban_movies/data/bean_move_detail.dart'; 7 | import 'package:douban_movies/pages/views/StartsView.dart'; 8 | import 'package:transparent_image/transparent_image.dart'; 9 | import 'package:douban_movies/pages/detail/MovieTitle/MovieTitle.dart'; 10 | import 'package:douban_movies/pages/detail/MovieRating/MovieRating.dart'; 11 | import 'package:douban_movies/pages/detail/MovieChannel/MovieChannel.dart'; 12 | import 'package:douban_movies/pages/detail/MovieDesc/MovieDesc.dart'; 13 | import 'package:douban_movies/pages/detail/MovieActors/MovieActors.dart'; 14 | import 'package:douban_movies/pages/detail/MovieComments/MovieCommentsView.dart'; 15 | 16 | class DetailPage extends StatelessWidget { 17 | final subjectItem; 18 | 19 | DetailPage(this.subjectItem); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return new Scaffold( 24 | appBar: new AppBar( 25 | title: new Text(subjectItem.getTitle()), 26 | ), 27 | body: new DetailContent(subjectItem.id), 28 | ); 29 | } 30 | } 31 | 32 | class DetailContent extends StatefulWidget { 33 | final String id; 34 | 35 | DetailContent(this.id); 36 | 37 | @override 38 | _DetailContentState createState() => _DetailContentState(id); 39 | } 40 | 41 | class _DetailContentState extends State { 42 | final String id; 43 | MovieDetail detail; 44 | 45 | _DetailContentState(this.id); 46 | 47 | @override 48 | void initState() { 49 | super.initState(); 50 | loadData(); 51 | } 52 | 53 | //获取电影详情 54 | loadData() async { 55 | var datas = await HttpUtils.get( 56 | HttpUtils.URL_GET_MOVIE_DETAIL + id, 57 | map: { 58 | 'apikey': HttpUtils.URL_API_KEY, 59 | 'udid': HttpUtils.URL_UDID, 60 | 'city': '上海', 61 | 'client': '', 62 | }, 63 | ); 64 | detail = new MovieDetail(datas); 65 | setState(() {}); 66 | } 67 | 68 | @override 69 | Widget build(BuildContext context) { 70 | // TODO: implement build 71 | return new Container( 72 | child: detail == null ? new Container() : new MovieDetial(detail), 73 | ); 74 | } 75 | } 76 | 77 | class MovieDetial extends StatelessWidget { 78 | final MovieDetail detail; 79 | 80 | MovieDetial(this.detail); 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | return new ListView( 85 | children: [ 86 | new MovieTitleView(detail), 87 | new MovieRatingView(detail), 88 | new Container( 89 | padding: new EdgeInsets.only(left: 10.0, right: 10.0), 90 | child: new Divider(color: Colors.grey,), 91 | ), 92 | new Container( 93 | height: 50.0, 94 | child: new MovieChnnelView(detail), 95 | ), 96 | new MovieDescView(detail), 97 | new MovieActorsView(detail), 98 | new MovieCommentsView(detail), 99 | // new Expanded(child: new Container(), 100 | // ), 101 | ], 102 | ); 103 | } 104 | } 105 | 106 | 107 | -------------------------------------------------------------------------------- /lib/pages/page_home.dart: -------------------------------------------------------------------------------- 1 | import 'package:douban_movies/WanAndroid/navigator_router_utils.dart'; 2 | import 'package:douban_movies/data/bean_move_list.dart'; 3 | import 'package:douban_movies/net/http.dart'; 4 | import 'package:douban_movies/pages/detail/page_detail.dart'; 5 | import 'package:douban_movies/pages/views/ClipImageView.dart'; 6 | import 'package:douban_movies/res/value_string.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:transparent_image/transparent_image.dart'; 9 | 10 | import 'views/StartsView.dart'; 11 | 12 | final List tabs = [ 13 | new Tab(text: '正在热映'), 14 | new Tab(text: '即将上映'), 15 | new Tab(text: 'Top250'), 16 | // new Tab(text: '口碑榜'), 17 | // new Tab(text: '北美票房榜'), 18 | new Tab(text: '新片榜'), 19 | ]; 20 | 21 | class HomePage extends StatelessWidget { 22 | @override 23 | Widget build(BuildContext context) { 24 | return new MaterialApp( 25 | title: AppStrings.AppName, 26 | home: new DefaultTabController( 27 | length: tabs.length, 28 | child: new Scaffold( 29 | appBar: new AppBar( 30 | title: new Text(AppStrings.HomePageAppBarTitle), 31 | bottom: new TabBar( 32 | tabs: tabs, 33 | isScrollable: true, 34 | ), 35 | ), 36 | body: new TabBarView( 37 | children: tabs.map((Tab tab) { 38 | return HomeContent(tab); 39 | }).toList(), 40 | ), 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | 47 | /** 48 | * 创建显示在首页的内容 49 | */ 50 | class HomeContent extends StatefulWidget { 51 | final Tab _tab; 52 | 53 | HomeContent(this._tab); 54 | 55 | @override 56 | _HomeContentState createState() => _HomeContentState(_tab); 57 | } 58 | 59 | class _HomeContentState extends State 60 | with AutomaticKeepAliveClientMixin { 61 | final Tab _tab; 62 | List _subjects = new List(); 63 | ScrollController _scrollController = new ScrollController(); 64 | final GlobalKey _refreshIndicaterState = 65 | GlobalKey(); 66 | int start = 0; 67 | int limit = 25; 68 | int page = 0; 69 | 70 | _HomeContentState(this._tab); 71 | 72 | @override 73 | void initState() { 74 | super.initState(); 75 | _scrollController.addListener(() { 76 | if (_scrollController.position.pixels == 77 | _scrollController.position.maxScrollExtent) { 78 | _getMore(); 79 | } 80 | }); 81 | _loadData(); 82 | new Future.delayed(const Duration(seconds: 0), () { 83 | _refreshIndicaterState.currentState.show().then((e) {}); 84 | return true; 85 | }); 86 | } 87 | 88 | //获取电影列表 89 | Future _loadData() async { 90 | page = 0; 91 | String url = HttpUtils.URL_GET_MOVIE_LIST_IN_THEATERS; 92 | Map map = { 93 | 'apikey': HttpUtils.URL_API_KEY, 94 | 'udid': HttpUtils.URL_UDID, 95 | 'city': '上海', 96 | 'client': '', 97 | 'start': start + page * limit, 98 | 'count': limit 99 | }; 100 | switch (_tab.text) { 101 | case '正在热映': 102 | url = HttpUtils.URL_GET_MOVIE_LIST_IN_THEATERS; 103 | break; 104 | case '即将上映': 105 | url = HttpUtils.URL_GET_MOVIE_LIST_COMING_SOON; 106 | break; 107 | case 'Top250': 108 | url = HttpUtils.URL_GET_MOVIE_LIST_TOP_250; 109 | break; 110 | case '口碑榜': 111 | url = HttpUtils.URL_GET_MOVIE_LIST_WEEKLY; 112 | map = { 113 | 'apikey': HttpUtils.URL_API_KEY, 114 | 'udid': '', 115 | 'city': '上海', 116 | 'client': '', 117 | }; 118 | break; 119 | case '北美票房榜': 120 | url = HttpUtils.URL_GET_MOVIE_LIST_US_BOX; 121 | map = { 122 | 'apikey': HttpUtils.URL_API_KEY, 123 | 'udid': '', 124 | 'client': '', 125 | }; 126 | break; 127 | case '新片榜': 128 | url = HttpUtils.URL_GET_MOVIE_LIST_NEW_MOVIES; 129 | map = { 130 | 'apikey': HttpUtils.URL_API_KEY, 131 | 'udid': '', 132 | 'client': '', 133 | }; 134 | break; 135 | } 136 | var datas = await HttpUtils.get( 137 | url, 138 | map: map, 139 | ); 140 | MovieList moveList = new MovieList(datas); 141 | _subjects = moveList.subjects; 142 | setState(() {}); 143 | } 144 | 145 | @override 146 | Widget build(BuildContext context) { 147 | return new RefreshIndicator( 148 | key: _refreshIndicaterState, 149 | child: new Container( 150 | child: new ListView.builder( 151 | // physics: new AlwaysScrollableScrollPhysics(), 152 | controller: _scrollController, 153 | itemCount: _subjects.length, 154 | itemBuilder: (context, i) { 155 | if (i.isOdd) return new Divider(); 156 | int index = i ~/ 2; 157 | return new Container( 158 | margin: EdgeInsets.only(top: i == 0 ? 10.0 : 0), 159 | child: new MoveItem(_subjects[index]), 160 | ); 161 | }, 162 | ), 163 | ), 164 | onRefresh: _loadData, 165 | ); 166 | } 167 | 168 | void _getMore() async { 169 | page++; 170 | print('$page'); 171 | String url = HttpUtils.URL_GET_MOVIE_LIST_IN_THEATERS; 172 | bool canGetMore = true; 173 | switch (_tab.text) { 174 | case '正在热映': 175 | url = HttpUtils.URL_GET_MOVIE_LIST_IN_THEATERS; 176 | break; 177 | case '即将上映': 178 | url = HttpUtils.URL_GET_MOVIE_LIST_COMING_SOON; 179 | break; 180 | case 'Top250': 181 | url = HttpUtils.URL_GET_MOVIE_LIST_TOP_250; 182 | break; 183 | case '口碑榜': 184 | canGetMore = false; 185 | url = HttpUtils.URL_GET_MOVIE_LIST_WEEKLY; 186 | break; 187 | case '北美票房榜': 188 | canGetMore = false; 189 | url = HttpUtils.URL_GET_MOVIE_LIST_US_BOX; 190 | break; 191 | case '新片榜': 192 | canGetMore = false; 193 | url = HttpUtils.URL_GET_MOVIE_LIST_NEW_MOVIES; 194 | break; 195 | } 196 | if (canGetMore) { 197 | var datas = await HttpUtils.get(url, map: { 198 | 'apikey': HttpUtils.URL_API_KEY, 199 | 'udid': HttpUtils.URL_UDID, 200 | 'city': '上海', 201 | 'client': '', 202 | 'start': start + page * limit, 203 | 'count': limit 204 | }); 205 | MovieList moveList = new MovieList(datas); 206 | _subjects.addAll(moveList.subjects); 207 | } 208 | setState(() {}); 209 | } 210 | 211 | @override 212 | // TODO: implement wantKeepAlive 213 | bool get wantKeepAlive => true; 214 | } 215 | 216 | class MoveItem extends StatelessWidget { 217 | final subject subjectData; 218 | 219 | MoveItem(this.subjectData); 220 | 221 | @override 222 | Widget build(BuildContext context) { 223 | return new GestureDetector( 224 | onTap: () { 225 | NavigatorRouterUtils.pushToPage(context, new DetailPage(subjectData)); 226 | }, 227 | child: new Row( 228 | children: [ 229 | new Container( 230 | width: 110.0, 231 | padding: EdgeInsets.only(left: 10.0), 232 | //使用图片渐入框架 233 | child: ClipImageView( 234 | subjectData.images.medium, new BorderRadius.circular(4.0)), 235 | ), 236 | new Expanded( 237 | child: new Container( 238 | height: 150.0, 239 | padding: EdgeInsets.all(10.0), 240 | child: new Column( 241 | children: [ 242 | new Container( 243 | width: double.infinity, 244 | child: new Text( 245 | subjectData.title, 246 | style: new TextStyle( 247 | fontSize: 18.0, 248 | fontWeight: FontWeight.bold, 249 | ), 250 | textAlign: TextAlign.left, 251 | ), 252 | ), 253 | getRatingItem(subjectData), 254 | new Container( 255 | margin: new EdgeInsets.only(top: 10.0), 256 | width: double.infinity, 257 | child: new Text( 258 | '${MovieList.getDetailDesc(subjectData)}', 259 | textAlign: TextAlign.left, 260 | style: TextStyle( 261 | color: Colors.grey, 262 | ), 263 | ), 264 | ), 265 | // new Expanded( 266 | // child: new Container( 267 | // padding: new EdgeInsets.only(top:10.0), 268 | // child: new CastsView(subjectData.casts), 269 | // ), 270 | // ), 271 | ], 272 | ), 273 | ), 274 | ), 275 | ], 276 | ), 277 | ); 278 | } 279 | 280 | getRatingItem(subject subjectData) { 281 | return int.parse(subjectData.rating.stars) == 0 282 | ? new Container( 283 | width: double.infinity, 284 | margin: new EdgeInsets.only(top: 10.0), 285 | child: Text( 286 | '暂无评分', 287 | textAlign: TextAlign.start, 288 | style: TextStyle( 289 | color: Colors.grey, 290 | fontSize: 12.0, 291 | ), 292 | ), 293 | ) 294 | : new Container( 295 | margin: new EdgeInsets.only(top: 10.0), 296 | child: new Row( 297 | children: [ 298 | new StarItem(int.parse(subjectData.rating.stars), 15.0, 299 | Colors.orange[300], true), 300 | new Container( 301 | margin: EdgeInsets.only(left: 5.0), 302 | child: new Text( 303 | '${subjectData.rating.average}', 304 | style: TextStyle( 305 | fontSize: 12.0, 306 | ), 307 | ), 308 | ) 309 | ], 310 | ), 311 | ); 312 | } 313 | } 314 | 315 | class CastsView extends StatelessWidget { 316 | final List casts; 317 | 318 | CastsView(this.casts); 319 | 320 | @override 321 | Widget build(BuildContext context) { 322 | // TODO: implement build 323 | return casts == null 324 | ? new Container() 325 | : new ListView.builder( 326 | scrollDirection: Axis.horizontal, 327 | itemCount: casts.length, 328 | itemBuilder: (context, i) { 329 | return new Column( 330 | children: [ 331 | new Container( 332 | padding: new EdgeInsets.only( 333 | left: i != 0 ? 10.0 : 0.0, top: 5.0), 334 | child: new ClipOval( 335 | child: new FadeInImage.memoryNetwork( 336 | placeholder: kTransparentImage, 337 | fit: BoxFit.fitWidth, 338 | image: casts[i].avatars.small, 339 | height: 45.0, 340 | width: 45.0, 341 | ), 342 | ), 343 | ), 344 | ], 345 | ); 346 | }, 347 | ); 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /lib/pages/views/Chips.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | class _ChipsTile extends StatelessWidget { 3 | const _ChipsTile({ 4 | Key key, 5 | this.label, 6 | this.children, 7 | }) : super(key: key); 8 | 9 | final String label; 10 | final List children; 11 | 12 | // Wraps a list of chips into a ListTile for display as a section in the demo. 13 | @override 14 | Widget build(BuildContext context) { 15 | final List cardChildren = [ 16 | Container( 17 | padding: const EdgeInsets.only(top: 16.0, bottom: 4.0), 18 | alignment: Alignment.center, 19 | child: Text(label, textAlign: TextAlign.start), 20 | ), 21 | ]; 22 | if (children.isNotEmpty) { 23 | cardChildren.add(Wrap( 24 | children: children.map((Widget chip) { 25 | return Padding( 26 | padding: const EdgeInsets.all(2.0), 27 | child: chip, 28 | ); 29 | }).toList())); 30 | } else { 31 | final TextStyle textStyle = Theme.of(context).textTheme.caption.copyWith(fontStyle: FontStyle.italic); 32 | cardChildren.add( 33 | Semantics( 34 | container: true, 35 | child: Container( 36 | alignment: Alignment.center, 37 | constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0), 38 | padding: const EdgeInsets.all(8.0), 39 | child: Text('None', style: textStyle), 40 | ), 41 | )); 42 | } 43 | 44 | return Card( 45 | semanticContainer: false, 46 | child: Column( 47 | mainAxisSize: MainAxisSize.min, 48 | children: cardChildren, 49 | ) 50 | ); 51 | } 52 | } -------------------------------------------------------------------------------- /lib/pages/views/ClipImageView.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:transparent_image/transparent_image.dart'; 3 | 4 | class ClipImageView extends StatelessWidget { 5 | final String imgUrl; 6 | final BorderRadius borderRadius; 7 | 8 | ClipImageView(this.imgUrl, this.borderRadius); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | // TODO: implement build 13 | return new ClipRRect( 14 | child: new FadeInImage.memoryNetwork( 15 | fit: BoxFit.fitWidth, 16 | placeholder: kTransparentImage, 17 | image: imgUrl, 18 | ), 19 | borderRadius: borderRadius, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/pages/views/StartsView.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class StarItem extends StatelessWidget { 4 | final int _startCount; 5 | final double _starSize; 6 | final Color _starColor; 7 | final bool _hasBorderStar; 8 | 9 | StarItem( 10 | this._startCount, this._starSize, this._starColor, this._hasBorderStar); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return getStarView(_startCount, _starSize, _starColor, _hasBorderStar); 15 | } 16 | } 17 | 18 | getStarView(int starts, double starSize, Color starColor, bool hasBorderStar) { 19 | //获取半实心Start的数量 20 | int halfStarCount = starts % 10 == 0 ? 0 : 1; 21 | //获取实心Start的数量 22 | int fullStartCount = starts ~/ 10; 23 | 24 | List starList = []; 25 | 26 | for (var i = 0; i < fullStartCount; i++) { 27 | starList.add(new Icon( 28 | Icons.star, 29 | color: starColor, 30 | size: starSize, 31 | )); 32 | } 33 | 34 | if (halfStarCount != 0) { 35 | starList.add(new Icon( 36 | Icons.star_half, 37 | color: starColor, 38 | size: starSize, 39 | )); 40 | } 41 | 42 | if (hasBorderStar) { 43 | int borderStartCount=halfStarCount!=0?4 - fullStartCount:5-fullStartCount; 44 | for (var i = 0; i < borderStartCount; i++) { 45 | starList.add(new Icon( 46 | Icons.star_border, 47 | color: starColor, 48 | size: starSize, 49 | )); 50 | } 51 | } 52 | 53 | return new Row( 54 | children: starList, 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /lib/res/value_string.dart: -------------------------------------------------------------------------------- 1 | class AppStrings { 2 | static String AppName = '豆瓣电影'; 3 | static String HomePageAppBarTitle = '豆瓣电影'; 4 | static String DetailPageAppBarTitle = '电影详情'; 5 | } -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: douban_movies 2 | description: A new Flutter application. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.0.0-dev.68.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^0.1.2 22 | dio: ^1.0.6 23 | cached_network_image: ^0.4.1+1 24 | transparent_image: ^0.1.0 25 | flutter_webview_plugin: ^0.3.0+2 26 | html_unescape: ^1.0.1+2 27 | sqflite: ^0.13.0+1 28 | shared_preferences: ^0.4.3 29 | flutter_swiper: ^1.1.4 30 | webview_flutter: ^0.3.3+1 31 | 32 | 33 | dev_dependencies: 34 | flutter_test: 35 | sdk: flutter 36 | 37 | 38 | # For information on the generic Dart part of this file, see the 39 | # following page: https://www.dartlang.org/tools/pub/pubspec 40 | 41 | # The following section is specific to Flutter. 42 | flutter: 43 | 44 | # The following line ensures that the Material Icons font is 45 | # included with your application, so that you can use the icons in 46 | # the material Icons class. 47 | uses-material-design: true 48 | 49 | # To add assets to your application, add an assets section, like this: 50 | # assets: 51 | # - images/a_dot_burr.jpeg 52 | # - images/a_dot_ham.jpeg 53 | 54 | # An image asset can refer to one or more resolution-specific "variants", see 55 | # https://flutter.io/assets-and-images/#resolution-aware. 56 | 57 | # For details regarding adding assets from package dependencies, see 58 | # https://flutter.io/assets-and-images/#from-packages 59 | 60 | # To add custom fonts to your application, add a fonts section here, 61 | # in this "flutter" section. Each entry in this list should have a 62 | # "family" key with the font family name, and a "fonts" key with a 63 | # list giving the asset and other descriptors for the font. For 64 | # example: 65 | fonts: 66 | - family: IconFont 67 | fonts: 68 | - asset: static/font/iconfont.ttf 69 | # - asset: fonts/Schyler-Italic.ttf 70 | # style: italic 71 | # - family: Trajan Pro 72 | # fonts: 73 | # - asset: fonts/TrajanPro.ttf 74 | # - asset: fonts/TrajanPro_Bold.ttf 75 | # weight: 700 76 | # 77 | # For details regarding fonts from package dependencies, 78 | # see https://flutter.io/custom-fonts/#from-packages 79 | -------------------------------------------------------------------------------- /static/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhgd3250905/WANAndroid_Proj/a34763eb11f342ee3042bfaa2197a71b25fe5804/static/font/iconfont.ttf -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:douban_movies/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | --------------------------------------------------------------------------------