wangbc 4 éve
szülő
commit
6218770ed4
100 módosított fájl, 3079 hozzáadás és 1 törlés
  1. 14 0
      .flutter-plugins
  2. 0 0
      .flutter-plugins-dependencies
  3. 42 0
      CHANGELOG.md
  4. 24 1
      README.md
  5. BIN
      ScreenShots/Screenshot_20200604-134616_One UI Home.jpg
  6. BIN
      ScreenShots/Screenshot_20200604-134628.jpg
  7. BIN
      ScreenShots/Screenshot_20200604-134646.jpg
  8. BIN
      ScreenShots/Screenshot_20200604-134654.jpg
  9. BIN
      ScreenShots/Screenshot_20200604-134700.jpg
  10. BIN
      ScreenShots/Screenshot_20200604-134713.jpg
  11. BIN
      ScreenShots/Screenshot_20200604-134724.jpg
  12. BIN
      ScreenShots/Screenshot_20200604-134737.jpg
  13. BIN
      ScreenShots/Screenshot_20200604-134741.jpg
  14. BIN
      android/.DS_Store
  15. BIN
      android/.gradle/5.4.1/executionHistory/executionHistory.bin
  16. BIN
      android/.gradle/5.4.1/executionHistory/executionHistory.lock
  17. BIN
      android/.gradle/5.4.1/fileChanges/last-build.bin
  18. BIN
      android/.gradle/5.4.1/fileContent/fileContent.lock
  19. BIN
      android/.gradle/5.4.1/fileHashes/fileHashes.bin
  20. BIN
      android/.gradle/5.4.1/fileHashes/fileHashes.lock
  21. BIN
      android/.gradle/5.4.1/fileHashes/resourceHashesCache.bin
  22. 0 0
      android/.gradle/5.4.1/gc.properties
  23. BIN
      android/.gradle/5.4.1/javaCompile/classAnalysis.bin
  24. BIN
      android/.gradle/5.4.1/javaCompile/jarAnalysis.bin
  25. BIN
      android/.gradle/5.4.1/javaCompile/javaCompile.lock
  26. BIN
      android/.gradle/5.4.1/javaCompile/taskHistory.bin
  27. BIN
      android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
  28. 2 0
      android/.gradle/buildOutputCleanup/cache.properties
  29. BIN
      android/.gradle/buildOutputCleanup/outputFiles.bin
  30. 0 0
      android/.gradle/vcs-1/gc.properties
  31. 17 0
      android/.project
  32. 80 0
      android/android.iml
  33. 82 0
      android/android3.iml
  34. BIN
      android/app/.DS_Store
  35. 6 0
      android/app/.classpath
  36. 23 0
      android/app/.project
  37. 2 0
      android/app/.settings/org.eclipse.buildship.core.prefs
  38. 171 0
      android/app/app.iml
  39. 80 0
      android/app/build.gradle
  40. BIN
      android/app/src/.DS_Store
  41. 6 0
      android/app/src/debug/AndroidManifest.xml
  42. BIN
      android/app/src/main/.DS_Store
  43. 27 0
      android/app/src/main/AndroidManifest.xml
  44. 41 0
      android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
  45. 13 0
      android/app/src/main/java/vn/hunghd/flutterdownload/MainActivity.java
  46. 13 0
      android/app/src/main/java/vn/hunghd/flutterdownload/MyApplication.java
  47. 12 0
      android/app/src/main/res/drawable/launch_background.xml
  48. BIN
      android/app/src/main/res/mipmap-hdpi/movie.png
  49. BIN
      android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  50. BIN
      android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  51. BIN
      android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  52. BIN
      android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  53. 8 0
      android/app/src/main/res/values/styles.xml
  54. 31 0
      android/build.gradle
  55. 4 0
      android/gradle.properties
  56. BIN
      android/gradle/wrapper/gradle-wrapper.jar
  57. 6 0
      android/gradle/wrapper/gradle-wrapper.properties
  58. 160 0
      android/gradlew
  59. 90 0
      android/gradlew.bat
  60. 4 0
      android/key.properties
  61. 4 0
      android/local.properties
  62. 27 0
      android/movie_android.iml
  63. 15 0
      android/settings.gradle
  64. BIN
      images/loading.jpg
  65. BIN
      images/search.png
  66. BIN
      images/setting.png
  67. BIN
      key.jks
  68. BIN
      lib/.DS_Store
  69. 55 0
      lib/http/request.dart
  70. 183 0
      lib/http/reqularDetail.dart
  71. 59 0
      lib/http/reqularList.dart
  72. 51 0
      lib/http/reqularSearch.dart
  73. 44 0
      lib/main.dart
  74. 16 0
      lib/model/refreshModel.dart
  75. 33 0
      lib/model/urlModel.dart
  76. 47 0
      lib/model/video.dart
  77. 45 0
      lib/model/videoDetail.dart
  78. 18 0
      lib/providers/providerManager.dart
  79. BIN
      lib/screen/.DS_Store
  80. 157 0
      lib/screen/detail.dart
  81. 59 0
      lib/screen/home.dart
  82. 84 0
      lib/screen/loading.dart
  83. 62 0
      lib/screen/page/changLog.dart
  84. 148 0
      lib/screen/page/favorite.dart
  85. 45 0
      lib/screen/page/search.dart
  86. 81 0
      lib/screen/page/setting.dart
  87. 22 0
      lib/screen/widget/bottomClipper.dart
  88. 110 0
      lib/screen/widget/fijkPlayer.dart
  89. 114 0
      lib/screen/widget/searchBottomWidget.dart
  90. 103 0
      lib/screen/widget/searchHistoricalWidget.dart
  91. 86 0
      lib/screen/widget/searchResult.dart
  92. 136 0
      lib/services/favoriteData.dart
  93. 120 0
      lib/services/searchData.dart
  94. 4 0
      lib/services/type.dart
  95. 15 0
      lib/utils/appcolor.dart
  96. 42 0
      lib/utils/convert.dart
  97. 13 0
      lib/utils/movieApi.dart
  98. 37 0
      lib/utils/platform.dart
  99. 42 0
      lib/utils/router.dart
  100. 44 0
      lib/utils/screen.dart

+ 14 - 0
.flutter-plugins

@@ -0,0 +1,14 @@
+# This is a generated file; do not edit or check into version control.
+device_info=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\device_info-0.4.2+4\\
+fijkplayer=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\fijkplayer-0.8.4\\
+open_file=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\open_file-2.1.1\\
+package_info=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\package_info-0.4.0+13\\
+path_provider=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\path_provider-1.5.1\\
+permission_handler=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\permission_handler-4.0.0\\
+shared_preferences=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\shared_preferences-0.5.7+3\\
+shared_preferences_macos=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\shared_preferences_macos-0.0.1+3\\
+shared_preferences_web=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\shared_preferences_web-0.1.2+2\\
+sqflite=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\sqflite-1.2.0\\
+url_launcher=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\url_launcher-5.4.1\\
+url_launcher_macos=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\url_launcher_macos-0.0.1+2\\
+url_launcher_web=D:\\flutter\\.pub-cache\\hosted\\pub.flutter-io.cn\\url_launcher_web-0.1.0+2\\

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
.flutter-plugins-dependencies


+ 42 - 0
CHANGELOG.md

@@ -0,0 +1,42 @@
+> Powered By 王宝臣
+
+# 最近更新
+
+## 0.1.6 (2020-3-2)
+
+- 调整界面 UI
+- 优化播放器卡顿问题
+- 添加播放已观看按钮变灰功能
+- 添加搜索选项(pujw)
+
+## 0.1.5 (2020-01-16)
+
+- 添加收藏按钮(孙伟赫)
+
+## 0.1.4 (2020-01-15)
+
+- 优化播放器 UI(现播放器控制按钮与播放时间分离)(刘伟)
+- 添加播放上一个按钮
+- 添加自动切换下一集(孙伟赫)
+
+## 0.1.3 (2020-01-14)
+
+- 播放视频时,自动横屏
+- 播放下一个按钮移至右下角
+
+## 0.1.2 (2020-01-12)
+
+- 添加播放下一个视频的按钮(左下角)
+- 显示集数
+
+## 0.1.1 (2020-01-11)
+
+- 添加更新日志
+- 优化视频播放器
+- 打开视频后默认横屏
+- 打开视频后保持屏幕常亮
+- 添加手势,快进、快退,音量、亮度控制
+
+## 0.1.0
+
+- 初始发布

+ 24 - 1
README.md

@@ -1,3 +1,26 @@
 # video
 
-flutter 编写的视频软件
+用 Flutter 框架编写的安卓端电影 APP
+
+## 说明
+
+1.本 APP 用 Dart 语言编写。  
+2.本 APP 通过分析解析网页代码获取播放链接。
+
+## 截图
+
+![首页](ScreenShots/Screenshot_20200604-134628.jpg)
+
+![搜索](ScreenShots/Screenshot_20200604-134646.jpg)
+
+![搜索详情](ScreenShots/Screenshot_20200604-134654.jpg)
+
+![详情](ScreenShots/Screenshot_20200604-134700.jpg)
+
+![详情](ScreenShots/Screenshot_20200604-134713.jpg)
+
+![播放](ScreenShots/Screenshot_20200604-134724.jpg)
+
+![收藏](ScreenShots/Screenshot_20200604-134737.jpg)
+
+![设置](ScreenShots/Screenshot_20200604-134741.jpg)

BIN
ScreenShots/Screenshot_20200604-134616_One UI Home.jpg


BIN
ScreenShots/Screenshot_20200604-134628.jpg


BIN
ScreenShots/Screenshot_20200604-134646.jpg


BIN
ScreenShots/Screenshot_20200604-134654.jpg


BIN
ScreenShots/Screenshot_20200604-134700.jpg


BIN
ScreenShots/Screenshot_20200604-134713.jpg


BIN
ScreenShots/Screenshot_20200604-134724.jpg


BIN
ScreenShots/Screenshot_20200604-134737.jpg


BIN
ScreenShots/Screenshot_20200604-134741.jpg


BIN
android/.DS_Store


BIN
android/.gradle/5.4.1/executionHistory/executionHistory.bin


BIN
android/.gradle/5.4.1/executionHistory/executionHistory.lock


BIN
android/.gradle/5.4.1/fileChanges/last-build.bin


BIN
android/.gradle/5.4.1/fileContent/fileContent.lock


BIN
android/.gradle/5.4.1/fileHashes/fileHashes.bin


BIN
android/.gradle/5.4.1/fileHashes/fileHashes.lock


BIN
android/.gradle/5.4.1/fileHashes/resourceHashesCache.bin


+ 0 - 0
android/.gradle/5.4.1/gc.properties


BIN
android/.gradle/5.4.1/javaCompile/classAnalysis.bin


BIN
android/.gradle/5.4.1/javaCompile/jarAnalysis.bin


BIN
android/.gradle/5.4.1/javaCompile/javaCompile.lock


BIN
android/.gradle/5.4.1/javaCompile/taskHistory.bin


BIN
android/.gradle/buildOutputCleanup/buildOutputCleanup.lock


+ 2 - 0
android/.gradle/buildOutputCleanup/cache.properties

@@ -0,0 +1,2 @@
+#Sat Apr 11 15:13:33 CST 2020
+gradle.version=5.4.1

BIN
android/.gradle/buildOutputCleanup/outputFiles.bin


+ 0 - 0
android/.gradle/vcs-1/gc.properties


+ 17 - 0
android/.project

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>android</name>
+	<comment>Project android created by Buildship.</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
+	</natures>
+</projectDescription>

+ 80 - 0
android/android.iml

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="classes3" level="project" />
+    <orderEntry type="library" name="classes4" level="project" />
+    <orderEntry type="library" name="debug5" level="project" />
+    <orderEntry type="library" name="classes11" level="project" />
+    <orderEntry type="library" name="classes20" level="project" />
+    <orderEntry type="library" name="classes15" level="project" />
+    <orderEntry type="library" name="debug1" level="project" />
+    <orderEntry type="library" name="debug8" level="project" />
+    <orderEntry type="library" name="release4" level="project" />
+    <orderEntry type="library" name="release9" level="project" />
+    <orderEntry type="library" name="debug6" level="project" />
+    <orderEntry type="library" name="release" level="project" />
+    <orderEntry type="library" name="classes14" level="project" />
+    <orderEntry type="library" name="classes16" level="project" />
+    <orderEntry type="library" name="classes2" level="project" />
+    <orderEntry type="library" name="classes9" level="project" />
+    <orderEntry type="library" name="debug9" level="project" />
+    <orderEntry type="library" name="classes18" level="project" />
+    <orderEntry type="library" name="classes1" level="project" />
+    <orderEntry type="library" name="release6" level="project" />
+    <orderEntry type="library" name="debug7" level="project" />
+    <orderEntry type="library" name="classes13" level="project" />
+    <orderEntry type="library" name="debug11" level="project" />
+    <orderEntry type="library" name="release11" level="project" />
+    <orderEntry type="library" name="release10" level="project" />
+    <orderEntry type="library" name="classes22" level="project" />
+    <orderEntry type="library" name="debug12" level="project" />
+    <orderEntry type="library" name="release8" level="project" />
+    <orderEntry type="library" name="classes21" level="project" />
+    <orderEntry type="library" name="release3" level="project" />
+    <orderEntry type="library" name="debug3" level="project" />
+    <orderEntry type="library" name="classes5" level="project" />
+    <orderEntry type="library" name="classes6" level="project" />
+    <orderEntry type="library" name="release5" level="project" />
+    <orderEntry type="library" name="classes8" level="project" />
+    <orderEntry type="library" name="classes" level="project" />
+    <orderEntry type="library" name="classes10" level="project" />
+    <orderEntry type="library" name="debug2" level="project" />
+    <orderEntry type="module" module-name="generated9" />
+    <orderEntry type="module" module-name="release4" />
+    <orderEntry type="module" module-name="generated3" />
+    <orderEntry type="module" module-name="debug11" />
+    <orderEntry type="module" module-name="release5" />
+    <orderEntry type="module" module-name="debug6" />
+    <orderEntry type="module" module-name="generated10" />
+    <orderEntry type="module" module-name="generated" />
+    <orderEntry type="module" module-name="release2" />
+    <orderEntry type="module" module-name="debug7" />
+    <orderEntry type="module" module-name="release7" />
+    <orderEntry type="module" module-name="debug" />
+    <orderEntry type="module" module-name="generated6" />
+    <orderEntry type="module" module-name="debug5" />
+    <orderEntry type="module" module-name="generated2" />
+    <orderEntry type="module" module-name="debug8" />
+    <orderEntry type="module" module-name="generated8" />
+    <orderEntry type="module" module-name="release1" />
+    <orderEntry type="module" module-name="release11" />
+    <orderEntry type="module" module-name="generated5" />
+    <orderEntry type="module" module-name="not_namespaced_r_class_sources" />
+    <orderEntry type="module" module-name="debug4" />
+    <orderEntry type="module" module-name="release8" />
+    <orderEntry type="module" module-name="generated12" />
+    <orderEntry type="module" module-name="debug1" />
+    <orderEntry type="module" module-name="generated11" />
+    <orderEntry type="module" module-name="release" />
+    <orderEntry type="module" module-name="release9" />
+    <orderEntry type="module" module-name="release3" />
+    <orderEntry type="module" module-name="debug9" />
+    <orderEntry type="module" module-name="debug2" />
+  </component>
+</module>

+ 82 - 0
android/android3.iml

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android" name="Android">
+      <configuration />
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="R2" level="project" />
+    <orderEntry type="library" name="R8" level="project" />
+    <orderEntry type="library" name="classes16" level="project" />
+    <orderEntry type="library" name="R3" level="project" />
+    <orderEntry type="library" name="R16" level="project" />
+    <orderEntry type="library" name="classes9" level="project" />
+    <orderEntry type="library" name="R4" level="project" />
+    <orderEntry type="library" name="release" level="project" />
+    <orderEntry type="library" name="release7" level="project" />
+    <orderEntry type="library" name="classes1" level="project" />
+    <orderEntry type="library" name="release6" level="project" />
+    <orderEntry type="library" name="classes11" level="project" />
+    <orderEntry type="library" name="R" level="project" />
+    <orderEntry type="library" name="R5" level="project" />
+    <orderEntry type="library" name="R15" level="project" />
+    <orderEntry type="library" name="release1" level="project" />
+    <orderEntry type="library" name="release13" level="project" />
+    <orderEntry type="library" name="classes10" level="project" />
+    <orderEntry type="library" name="classes8" level="project" />
+    <orderEntry type="library" name="release9" level="project" />
+    <orderEntry type="library" name="release16" level="project" />
+    <orderEntry type="library" name="release14" level="project" />
+    <orderEntry type="library" name="R6" level="project" />
+    <orderEntry type="library" name="classes2" level="project" />
+    <orderEntry type="library" name="classes4" level="project" />
+    <orderEntry type="library" name="release11" level="project" />
+    <orderEntry type="library" name="classes12" level="project" />
+    <orderEntry type="library" name="release12" level="project" />
+    <orderEntry type="library" name="release15" level="project" />
+    <orderEntry type="library" name="classes13" level="project" />
+    <orderEntry type="library" name="R7" level="project" />
+    <orderEntry type="library" name="R10" level="project" />
+    <orderEntry type="library" name="release4" level="project" />
+    <orderEntry type="library" name="classes3" level="project" />
+    <orderEntry type="library" name="R1" level="project" />
+    <orderEntry type="library" name="classes14" level="project" />
+    <orderEntry type="module" module-name="android2" />
+    <orderEntry type="module" module-name="main4" />
+    <orderEntry type="module" module-name="url_launcher" />
+    <orderEntry type="module" module-name="package_info" />
+    <orderEntry type="module" module-name="screen" />
+    <orderEntry type="module" module-name="path_provider" />
+    <orderEntry type="module" module-name="test1" />
+    <orderEntry type="module" module-name="main" />
+    <orderEntry type="module" module-name="test2" />
+    <orderEntry type="module" module-name="androidTest" />
+    <orderEntry type="module" module-name="android4" />
+    <orderEntry type="module" module-name="open_file" />
+    <orderEntry type="module" module-name="android8" />
+    <orderEntry type="module" module-name="android6" />
+    <orderEntry type="module" module-name="auto_orientation" />
+    <orderEntry type="module" module-name="android7" />
+    <orderEntry type="module" module-name="permission_handler" />
+    <orderEntry type="module" module-name="shared_preferences" />
+    <orderEntry type="module" module-name="main6" />
+    <orderEntry type="module" module-name="android11" />
+    <orderEntry type="module" module-name="video_player" />
+    <orderEntry type="module" module-name="android1" />
+    <orderEntry type="module" module-name="androidTest2" />
+    <orderEntry type="module" module-name="android10" />
+    <orderEntry type="module" module-name="test" />
+    <orderEntry type="module" module-name="sqflite" />
+    <orderEntry type="module" module-name="multi_image_picker" />
+    <orderEntry type="module" module-name="device_info" />
+    <orderEntry type="module" module-name="android5" />
+    <orderEntry type="module" module-name="not_namespaced_r_class_sources" />
+  </component>
+</module>

BIN
android/app/.DS_Store


+ 6 - 0
android/app/.classpath

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
+	<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
+	<classpathentry kind="output" path="bin/default"/>
+</classpath>

+ 23 - 0
android/app/.project

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>app</name>
+	<comment>Project app created by Buildship.</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
+	</natures>
+</projectDescription>

+ 2 - 0
android/app/.settings/org.eclipse.buildship.core.prefs

@@ -0,0 +1,2 @@
+connection.project.dir=..
+eclipse.preferences.version=1

+ 171 - 0
android/app/app.iml

@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android-gradle" name="Android-Gradle">
+      <configuration>
+        <option name="GRADLE_PROJECT_PATH" value=":app" />
+        <option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.2.1" />
+        <option name="LAST_KNOWN_AGP_VERSION" value="3.2.1" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="SELECTED_BUILD_VARIANT" value="debug" />
+        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+        <afterSyncTasks>
+          <task>generateDebugSources</task>
+        </afterSyncTasks>
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res;file://$MODULE_DIR$/../../build/app/generated/res/rs/debug;file://$MODULE_DIR$/../../build/app/generated/res/resValues/debug" />
+        <option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+      </configuration>
+    </facet>
+    <facet type="kotlin-language" name="Kotlin">
+      <configuration version="3" platform="JVM 1.6" allPlatforms="JVM [1.6]" useProjectSettings="false" pureKotlinSourceFolders="D:\android\movie\android\app\src\debug\kotlin;D:\android\movie\android\app\src\debug\java;D:\android\movie\android\app\src\profileUnitTest\kotlin;D:\android\movie\android\app\src\release\kotlin;D:\android\movie\android\app\src\release\java;D:\android\movie\android\app\src\debugAndroidTest\kotlin;D:\android\movie\android\app\src\profile\kotlin;D:\android\movie\android\app\src\profile\java;D:\android\movie\android\app\src\debugUnitTest\kotlin;D:\android\movie\android\app\src\releaseUnitTest\kotlin">
+        <compilerSettings />
+        <compilerArguments>
+          <option name="destination" value="$MODULE_DIR$/../../build/app/tmp/kotlin-classes/debug" />
+          <option name="classpath" value="$USER_HOME$/.gradle/caches/transforms-1/files-1.1/flutter.jar/2d2e190dd2b96c2f2949ef98756ede21/jetified-flutter.jar;D:/android/movie/build/app/intermediates/flutter/debug/libs.jar;C:/Users/wangbc/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.2.71/4ce93f539e2133f172f1167291a911f83400a5d0/kotlin-stdlib-jdk7-1.2.71.jar;D:/android/movie/build/sqflite/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/auto_orientation/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/fijkplayer/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/open_file/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/flutter_downloader/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/photo_manager/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/url_launcher/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/path_provider/intermediates/intermediate-jars/debug/classes.jar;D:/android/movie/build/permission_handler/intermediates/intermediate-jars/debug/classes.jar;C:/Users/wangbc/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.2.71/d9717625bb3c731561251f8dd2c67a1011d6764c/kotlin-stdlib-1.2.71.jar;C:/Users/wangbc/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.2.71/ba18ca1aa0e40eb6f1865b324af2f4cbb691c1ec/kotlin-stdlib-common-1.2.71.jar;C:/Users/wangbc/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar;D:/sdk/platforms/android-28/android.jar" />
+          <option name="noStdlib" value="true" />
+          <option name="noReflect" value="true" />
+          <option name="moduleName" value="app_debug" />
+          <option name="languageVersion" value="1.2" />
+          <option name="apiVersion" value="1.2" />
+          <option name="pluginOptions">
+            <array />
+          </option>
+          <option name="pluginClasspaths">
+            <array />
+          </option>
+          <option name="coroutinesState" value="warn" />
+          <option name="errors">
+            <ArgumentParseErrors />
+          </option>
+        </compilerArguments>
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
+    <output url="file://$MODULE_DIR$/../../build/app/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
+    <output-test url="file://$MODULE_DIR$/../../build/app/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/kotlin" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/res/resValues/androidTest/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/res/resValues/androidTest/debug" type="java-test-resource" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/res/resValues/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/res/resValues/debug" type="java-resource" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/res/rs/androidTest/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/res/rs/androidTest/debug" type="java-test-resource" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/res/rs/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/res/rs/debug" type="java-resource" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/aidl/androidTest/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/aidl/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/aidl/debug" isTestSource="false" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/apt/androidTest/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/apt/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/apt/debug" isTestSource="false" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/apt/test/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/apt/test/debug" isTestSource="true" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/buildConfig/androidTest/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/buildConfig/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/rs/androidTest/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
+    </content>
+    <content url="file://$MODULE_DIR$/../../build/app/generated/source/rs/debug">
+      <sourceFolder url="file://$MODULE_DIR$/../../build/app/generated/source/rs/debug" isTestSource="false" generated="true" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="TEST" name="Gradle: androidx.annotation:annotation:1.0.0-alpha1@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: net.sf.kxml:kxml2:2.3.0@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: com.squareup:javawriter:2.1.1@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: javax.inject:javax.inject:1@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-library:1.3@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3@jar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: androidx.test.espresso:espresso-core:3.1.0-alpha3@aar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: androidx.test:runner:1.1.0-alpha3@aar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: androidx.test:monitor:1.1.0-alpha3@aar" level="project" />
+    <orderEntry type="library" scope="TEST" name="Gradle: androidx.test.espresso:espresso-idling-resource:3.1.0-alpha3@aar" level="project" />
+    <orderEntry type="library" name="Gradle: __local_aars__:D./flutter/bin/cache/artifacts/engine/android-arm64/flutter.jar:unspecified@jar" level="project" />
+    <orderEntry type="library" name="Gradle: __local_aars__:D./android/movie/build/app/intermediates/flutter/debug/libs.jar:unspecified@jar" level="project" />
+    <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71@jar" level="project" />
+    <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib:1.2.71@jar" level="project" />
+    <orderEntry type="library" name="Gradle: org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71@jar" level="project" />
+    <orderEntry type="library" name="Gradle: org.jetbrains:annotations:13.0@jar" level="project" />
+    <orderEntry type="module" module-name="sqflite" />
+    <orderEntry type="module" module-name="auto_orientation" />
+    <orderEntry type="module" module-name="fijkplayer" />
+    <orderEntry type="module" module-name="open_file" />
+    <orderEntry type="module" module-name="flutter_downloader" />
+    <orderEntry type="module" module-name="photo_manager" />
+    <orderEntry type="module" module-name="url_launcher" />
+    <orderEntry type="module" module-name="path_provider" />
+    <orderEntry type="module" module-name="permission_handler" />
+  </component>
+</module>

+ 80 - 0
android/app/build.gradle

@@ -0,0 +1,80 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withReader('UTF-8') { reader ->
+        localProperties.load(reader)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+    flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+    flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+def keystorePropertiesFile = rootProject.file("key.properties")
+def keystoreProperties = new Properties()
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
+android {
+    compileSdkVersion 29
+
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
+    }
+
+    lintOptions {
+        disable 'InvalidPackage'
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId "com.example.movie"
+        minSdkVersion 19
+        targetSdkVersion 29
+        versionCode flutterVersionCode.toInteger()
+        versionName flutterVersionName
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    signingConfigs {
+        release {
+            keyAlias keystoreProperties['keyAlias']
+            keyPassword keystoreProperties['keyPassword']
+            storeFile file(keystoreProperties['storeFile'])
+            storePassword keystoreProperties['storePassword']
+        }
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig signingConfigs.debug
+        }
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+}

BIN
android/app/src/.DS_Store


+ 6 - 0
android/app/src/debug/AndroidManifest.xml

@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="vn.hunghd.flutterdownload">
+	<!-- Flutter needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+	<uses-permission android:name="android.permission.INTERNET"/>
+</manifest>

BIN
android/app/src/main/.DS_Store


+ 27 - 0
android/app/src/main/AndroidManifest.xml

@@ -0,0 +1,27 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="vn.hunghd.flutterdownload">
+
+	<!-- new -->
+	<uses-permission android:name="android.permission.INTERNET"/>
+	<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+	<!-- new -->
+
+	<!-- io.flutter.app.FlutterApplication is an android.app.Application that
+         calls FlutterMain.startInitialization(this); in its onCreate method.
+         In most cases you can leave this as-is, but you if you want to provide
+         additional functionality it is fine to subclass or reimplement
+         FlutterApplication and put your custom class here. -->
+	<application android:name=".MyApplication" android:label="电影" android:icon="@mipmap/movie" android:usesCleartextTraffic="true">
+		<activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
+			<!-- This keeps the window background of the activity showing
+                 until Flutter renders its first frame. It can be removed if
+                 there is no splash screen (such as the default splash screen
+                 defined in @style/LaunchTheme). -->
+			<meta-data android:name="io.flutter.app.android.SplashScreenUntilFirstFrame" android:value="true" />
+			<intent-filter>
+				<action android:name="android.intent.action.MAIN"/>
+				<category android:name="android.intent.category.LAUNCHER"/>
+			</intent-filter>
+		</activity>
+
+	</application>
+</manifest>

+ 41 - 0
android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java

@@ -0,0 +1,41 @@
+package io.flutter.plugins;
+
+import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.plugins.deviceinfo.DeviceInfoPlugin;
+import com.befovy.fijkplayer.FijkPlugin;
+import com.crazecoder.openfile.OpenFilePlugin;
+import io.flutter.plugins.packageinfo.PackageInfoPlugin;
+import io.flutter.plugins.pathprovider.PathProviderPlugin;
+import com.baseflow.permissionhandler.PermissionHandlerPlugin;
+import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin;
+import com.tekartik.sqflite.SqflitePlugin;
+import io.flutter.plugins.urllauncher.UrlLauncherPlugin;
+
+/**
+ * Generated file. Do not edit.
+ */
+public final class GeneratedPluginRegistrant {
+  public static void registerWith(PluginRegistry registry) {
+    if (alreadyRegisteredWith(registry)) {
+      return;
+    }
+    DeviceInfoPlugin.registerWith(registry.registrarFor("io.flutter.plugins.deviceinfo.DeviceInfoPlugin"));
+    FijkPlugin.registerWith(registry.registrarFor("com.befovy.fijkplayer.FijkPlugin"));
+    OpenFilePlugin.registerWith(registry.registrarFor("com.crazecoder.openfile.OpenFilePlugin"));
+    PackageInfoPlugin.registerWith(registry.registrarFor("io.flutter.plugins.packageinfo.PackageInfoPlugin"));
+    PathProviderPlugin.registerWith(registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
+    PermissionHandlerPlugin.registerWith(registry.registrarFor("com.baseflow.permissionhandler.PermissionHandlerPlugin"));
+    SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin"));
+    SqflitePlugin.registerWith(registry.registrarFor("com.tekartik.sqflite.SqflitePlugin"));
+    UrlLauncherPlugin.registerWith(registry.registrarFor("io.flutter.plugins.urllauncher.UrlLauncherPlugin"));
+  }
+
+  private static boolean alreadyRegisteredWith(PluginRegistry registry) {
+    final String key = GeneratedPluginRegistrant.class.getCanonicalName();
+    if (registry.hasPlugin(key)) {
+      return true;
+    }
+    registry.registrarFor(key);
+    return false;
+  }
+}

+ 13 - 0
android/app/src/main/java/vn/hunghd/flutterdownload/MainActivity.java

@@ -0,0 +1,13 @@
+package vn.hunghd.flutterdownload;
+
+import android.os.Bundle;
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class MainActivity extends FlutterActivity {
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    GeneratedPluginRegistrant.registerWith(this);
+  }
+}

+ 13 - 0
android/app/src/main/java/vn/hunghd/flutterdownload/MyApplication.java

@@ -0,0 +1,13 @@
+package vn.hunghd.flutterdownload;
+
+import io.flutter.app.FlutterApplication;
+import io.flutter.plugin.common.PluginRegistry;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class MyApplication extends FlutterApplication implements PluginRegistry.PluginRegistrantCallback {
+    @Override
+    public void registerWith(PluginRegistry registry) {
+        GeneratedPluginRegistrant.registerWith(registry);
+//        FlutterDownloaderPlugin.registerWith(registry.registrarFor("vn.hunghd.flutterdownloader.FlutterDownloaderPlugin"));
+    }
+}

+ 12 - 0
android/app/src/main/res/drawable/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:color/white" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

BIN
android/app/src/main/res/mipmap-hdpi/movie.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 8 - 0
android/app/src/main/res/values/styles.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             Flutter draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+</resources>

+ 31 - 0
android/build.gradle

@@ -0,0 +1,31 @@
+buildscript {
+    ext.kotlin_version = '1.3.0'
+    repositories {
+        google()
+        jcenter()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.3.1'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 4 - 0
android/gradle.properties

@@ -0,0 +1,4 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
+android.enableR8=true

BIN
android/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

+ 160 - 0
android/gradlew

@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 90 - 0
android/gradlew.bat

@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 4 - 0
android/key.properties

@@ -0,0 +1,4 @@
+storePassword=168281
+keyPassword=168281
+keyAlias=key
+storeFile=/Users/nuc/Desktop/video/key.jks

+ 4 - 0
android/local.properties

@@ -0,0 +1,4 @@
+sdk.dir=D:\\sdk
+flutter.sdk=D:\\flutter
+flutter.buildMode=release
+flutter.versionName=0.1.5

+ 27 - 0
android/movie_android.iml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/app/src/main/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/app/src/main/res" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/app/src/main/assets" />
+        <option name="LIBS_FOLDER_RELATIVE_PATH" value="/app/src/main/libs" />
+        <option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/app/src/main/proguard_logs" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/app/src/main/kotlin" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Flutter for Android" level="project" />
+    <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
+  </component>
+</module>

+ 15 - 0
android/settings.gradle

@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+}
+
+plugins.each { name, path ->
+    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+    include ":$name"
+    project(":$name").projectDir = pluginDirectory
+}

BIN
images/loading.jpg


BIN
images/search.png


BIN
images/setting.png


BIN
key.jks


BIN
lib/.DS_Store


+ 55 - 0
lib/http/request.dart

@@ -0,0 +1,55 @@
+import 'dart:convert';
+import 'package:dio/dio.dart';
+
+import 'package:flutter/services.dart';
+
+class Request {
+  static const String baseUrl = 'http://192.168.4.32/';
+
+  static Dio createDio() {
+    return Dio();
+  }
+
+  static Future<dynamic> mock({String action, Map params}) async {
+    var responseStr = await rootBundle.loadString('mock/$action.json');
+    var responseJson = json.decode(responseStr);
+    return responseJson['data'];
+  }
+
+  static Future<dynamic> get({String action, Map params}) async {
+    return Request.mock(action: action, params: params);
+  }
+
+  static Future<dynamic> getByHttpClient(String url) async {
+    var dio = new Dio();
+    dio.options.baseUrl = "https://baidu.com";
+    // dio.interceptors.add(CacheInterceptor());
+    Response<String> response = await dio.get(url);
+    String responseBody = response.data;
+    return json.decode(responseBody);
+  }
+
+  static Future<dynamic> getByHttpHtml(String url) async {
+    var dio = new Dio();
+    dio.options.baseUrl = "https://baidu.com";
+    // dio.interceptors.add(CacheInterceptor());
+    Response<String> response = await dio.get(url);
+    String responseBody = response.data;
+    return responseBody;
+  }
+
+  static Future<dynamic> getByDio({String url, Map params}) async {
+    var dio = Request.createDio();
+    Response<Map> response = await dio.get("/get_home_page/section_data");
+    var data = response.data;
+    return data;
+  }
+
+  static Future<dynamic> post({String url, Map<String, dynamic> data}) async {
+    var dio = new Dio();
+    dio.options.baseUrl = "https://baidu.com";
+    Response<String> response = await dio.post(url, queryParameters: data);
+    String responseBody = response.data;
+    return responseBody;
+  }
+}

+ 183 - 0
lib/http/reqularDetail.dart

@@ -0,0 +1,183 @@
+import 'package:video/model/videoDetail.dart';
+
+import 'request.dart';
+
+class RegularDetail {
+  static getHttpDetail(String url) async {
+    var responseBody = await Request.getByHttpHtml(url);
+    VideoDetail recommends = getRecommend(responseBody);
+    return recommends;
+  }
+
+  static VideoDetail getRecommend(String responseBody) {
+    RegExp regHeader =
+        new RegExp(r'class="vodBox">[\s\S]*?</div>[\s\S]*?</div>');
+    RegExp regInfo = new RegExp(r'class="vodinfobox">[\s\S]*?</div>');
+    RegExp regPlayInfo = new RegExp(r'id="play_1">[\s\S]*?</div>');
+    RegExp regDownloadInfo = new RegExp(r'id="down_1">[\s\S]*?</div>');
+
+    Iterable<Match> matchesHeader = regHeader.allMatches(responseBody);
+    Iterable<Match> matchesInfo = regInfo.allMatches(responseBody);
+    Iterable<Match> matchesPlayInfo = regPlayInfo.allMatches(responseBody);
+    Iterable<Match> matchesDownloadInfo =
+        regDownloadInfo.allMatches(responseBody);
+
+    VideoDetail listHeader = new VideoDetail();
+    VideoDetail listInfo = new VideoDetail();
+    VideoDetail listPlayInfo = new VideoDetail();
+    VideoDetail listDownloadinfo = new VideoDetail();
+    listPlayInfo.videoUrl = [];
+
+    VideoDetail videoDetail;
+
+    for (Match m in matchesHeader) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      String match = m.group(0);
+      //在此匹配
+      listHeader = getHeaderItems(match);
+    }
+
+    for (Match m in matchesInfo) {
+      String match = m.group(0);
+      //在此匹配
+      listInfo = getInfoItems(match);
+    }
+
+    for (Match m in matchesPlayInfo) {
+      String match = m.group(0);
+      //在此匹配
+      listPlayInfo.videoUrl = getPlayInfoItems(match);
+    }
+
+    for (Match m in matchesDownloadInfo) {
+      String match = m.group(0);
+
+      listDownloadinfo.downloadUrl = getDownloadItems(match);
+    }
+
+    videoDetail = new VideoDetail(
+      title: listHeader.title,
+      imageUrl: listHeader.imageUrl,
+      score: listHeader.score,
+      starring: listInfo.starring,
+      releaseTime: listInfo.releaseTime,
+      description: listInfo.description,
+      videoUrl: listPlayInfo.videoUrl,
+      downloadUrl: listDownloadinfo.downloadUrl,
+    );
+
+    return videoDetail;
+  }
+
+  static VideoDetail getHeaderItems(String match) {
+    RegExp reg = new RegExp(
+        r'class="vodBox">[\s\S]*?src="(.*?)"[\s\S]*?h2>(.*?)</h2>[\s\S]*?<label>(.*?)</label>[\s\S]*?</div>');
+    Iterable<Match> matches = reg.allMatches(match);
+    String url = "";
+    String title = "";
+    String score = "";
+    VideoDetail video;
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      for (int i = 0; i < m.groupCount + 1; i++) {
+        String match = m.group(i);
+        switch (i) {
+          case 1:
+            url = match;
+            break;
+          case 2:
+            title = match;
+            break;
+          case 3:
+            score = match;
+            break;
+        }
+      }
+    }
+
+    video = new VideoDetail(
+      imageUrl: url,
+      title: title,
+      score: score,
+    );
+    return video;
+  }
+
+  static VideoDetail getInfoItems(String match) {
+    RegExp reg = new RegExp(r'li[\s\S]*?>(.*?)<span>(.*?)</span>[\s\S]*?</li>');
+    Iterable<Match> matches = reg.allMatches(match);
+    String starring = "";
+    String releaseTime = "";
+    String str = "";
+    String description = "";
+    VideoDetail video;
+
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      for (int i = 0; i < m.groupCount + 1; i++) {
+        String match = m.group(i);
+        switch (i) {
+          case 1:
+            str = match;
+            break;
+          case 2:
+            str += match;
+            break;
+        }
+      }
+
+      if (str.contains("主演")) {
+        starring = str;
+      } else if (str.contains("上映")) {
+        releaseTime = str;
+      }
+    }
+
+    RegExp regDescription =
+        new RegExp(r'class="more" txt="(.*?)"[\s\S]*?>[\s\S]*?</span>');
+    Iterable<Match> matcheDescription = regDescription.allMatches(match);
+    for (Match m in matcheDescription) {
+      description = m.group(1);
+    }
+
+    video = new VideoDetail(
+      starring: starring,
+      releaseTime: releaseTime,
+      description: description,
+    );
+    return video;
+  }
+
+  static List<String> getPlayInfoItems(String match) {
+    RegExp reg = new RegExp(r'value="(.*?)"[\s\S]*?checked="" />');
+    Iterable<Match> matches = reg.allMatches(match);
+    List<String> videoUrl = [];
+
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      if (m.group(1).contains("http")) {
+        videoUrl.add(m.group(1));
+      }
+    }
+    return videoUrl;
+  }
+
+  static List<String> getDownloadItems(String match) {
+    RegExp reg = new RegExp(r'value="(.*?)"[\s\S]*?checked="" />');
+    Iterable<Match> matches = reg.allMatches(match);
+    List<String> videoUrl = [];
+
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      if (m.group(1).contains("http")) {
+        videoUrl.add(m.group(1));
+      }
+    }
+    return videoUrl;
+  }
+}

+ 59 - 0
lib/http/reqularList.dart

@@ -0,0 +1,59 @@
+import 'package:video/model/video.dart';
+
+import 'request.dart';
+
+class Regular {
+  static getHttp(String url) async {
+    var responseBody = await Request.getByHttpHtml(url);
+    List<Video> recommends = getRecommend(responseBody);
+    return recommends;
+  }
+
+  static List<Video> getRecommend(String responseBody) {
+    RegExp reg = new RegExp(r'class="xing_vb">[\s\S]*?</div');
+    Iterable<Match> matches = reg.allMatches(responseBody);
+    List<Video> list = [];
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      String match = m.group(0);
+      //在此匹配
+      list = getItems(match);
+    }
+    return list;
+  }
+
+  static List<Video> getItems(String match) {
+    RegExp reg = new RegExp(
+        r'class="xing_vb4">[\s\S]*?href="(.*?)"[\s\S]*?target="_blank">(.*?)</a[\s\S]*?class="xing_vb5">(.*?)</span[\s\S]*?class="xing_vb6">(.*?)</span[\s\S]*?/li>');
+    Iterable<Match> matches = reg.allMatches(match);
+    List<Video> list = [];
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      String url = "";
+      String title = "";
+
+      String time = "";
+      for (int i = 0; i < m.groupCount + 1; i++) {
+        String match = m.group(i);
+        switch (i) {
+          case 1:
+            url = match;
+            break;
+          case 2:
+            title = match;
+            break;
+          case 3:
+            break;
+          case 4:
+            time = match;
+            break;
+        }
+      }
+      Video video = new Video(url: url, title: title, time: time);
+      list.add(video);
+    }
+    return list;
+  }
+}

+ 51 - 0
lib/http/reqularSearch.dart

@@ -0,0 +1,51 @@
+import 'package:video/model/video.dart';
+
+class RegularSearch {
+  static List<Video> getRecommend(String responseBody) {
+    RegExp reg = new RegExp(r'class="xing_vb">[\s\S]*?</div');
+    Iterable<Match> matches = reg.allMatches(responseBody);
+    List<Video> list = [];
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      String match = m.group(0);
+      //在此匹配
+      list = getItems(match);
+    }
+    return list;
+  }
+
+  static List<Video> getItems(String match) {
+    RegExp reg = new RegExp(
+        r'class="xing_vb4">[\s\S]*?href="(.*?)"[\s\S]*?target="_blank">(.*?)<span[\s\S]*?class="xing_vb5">(.*?)</span[\s\S]*?class="xing_vb6">(.*?)</span[\s\S]*?/li>');
+    Iterable<Match> matches = reg.allMatches(match);
+    List<Video> list = [];
+    for (Match m in matches) {
+      //groupCount返回正则表达式的分组数
+      //由于group(0)保存了匹配信息,因此字符串的总长度为:分组数+1
+      String url = "";
+      String title = "";
+
+      String time = "";
+      for (int i = 0; i < m.groupCount + 1; i++) {
+        String match = m.group(i);
+        switch (i) {
+          case 1:
+            url = match;
+            break;
+          case 2:
+            title = match;
+            break;
+          case 3:
+            break;
+          case 4:
+            time = match;
+            break;
+        }
+      }
+      Video video = new Video(url: url, title: title, time: time);
+      list.add(video);
+    }
+    return list;
+  }
+}

+ 44 - 0
lib/main.dart

@@ -0,0 +1,44 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:provider/provider.dart';
+import 'package:video/model/refreshModel.dart';
+import 'package:video/model/urlModel.dart';
+import 'package:video/providers/providerManager.dart';
+
+import 'package:video/utils/router.dart' as router;
+
+import 'screen/loading.dart';
+
+void main() {
+  runApp(MyApp());
+  if (Platform.isAndroid) {
+    // 以下两行 设置android状态栏为透明的沉浸。写在组件渲染之后,是为了在渲染后进行set赋值,覆盖状态栏,写在渲染之前MaterialApp组件会覆盖掉这个值。
+    SystemUiOverlayStyle systemUiOverlayStyle =
+        SystemUiOverlayStyle(statusBarColor: Colors.transparent);
+    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
+  }
+}
+
+class MyApp extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return MultiProvider(
+      providers: providers, //监听共享实例
+      child: Consumer2<UrlInfo, Refresh>(
+        builder: (context, themeModel, localeModel, child) {
+          return MaterialApp(
+            debugShowCheckedModeBanner: false,
+            title: '电影',
+            theme: ThemeData(
+              primarySwatch: Colors.blueGrey,
+            ),
+            onGenerateRoute: router.generateRoute,
+            home: new LoadingPage(),
+          );
+        },
+      ),
+    );
+  }
+}

+ 16 - 0
lib/model/refreshModel.dart

@@ -0,0 +1,16 @@
+import 'package:flutter/material.dart';
+
+class Refresh with ChangeNotifier {
+  bool _isRefresh = false;
+
+  bool get isRefresh => _isRefresh;
+
+  changeRefresh() {
+    _isRefresh = !isRefresh;
+    notifyListeners();
+  }
+
+  init() {
+    _isRefresh = false;
+  }
+}

+ 33 - 0
lib/model/urlModel.dart

@@ -0,0 +1,33 @@
+import 'package:flutter/material.dart';
+import 'package:video/model/video.dart';
+
+class UrlInfo with ChangeNotifier {
+  int _urlIndex = -1;
+  String _url = "";
+  Video _video = Video();
+  bool _isAdd = false;
+  int _change = 0;
+
+  String get url => _url;
+  int get urlIndex => _urlIndex;
+  Video get video => _video;
+  bool get isAdd => _isAdd;
+  int get change => _change;
+
+  changIndex(int currIndex) {
+    _urlIndex = currIndex;
+    notifyListeners();
+  }
+
+  changUrl(String url) {
+    _url = url;
+    notifyListeners();
+  }
+
+  changVideo(Video video, bool isAdd) {
+    _video = video;
+    _isAdd = isAdd;
+    _change = _change + 1;
+    notifyListeners();
+  }
+}

+ 47 - 0
lib/model/video.dart

@@ -0,0 +1,47 @@
+class Video {
+  int id;
+  String url;
+  String title;
+  String time;
+  String type;
+  String imageUrl;
+
+  Video({
+    this.id,
+    this.url,
+    this.title,
+    this.time,
+    this.type,
+    this.imageUrl,
+  });
+
+  Video.fromJson(Map<String, dynamic> json) {
+    id = json['id'];
+    url = json['url'];
+    title = json['title'];
+    type = json['type'];
+    time = json['time'];
+    imageUrl = json['imageUrl'];
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['id'] = this.id;
+    data['url'] = this.url;
+    data['title'] = this.title;
+    data['type'] = this.type;
+    data['time'] = this.time;
+    data['imageUrl'] = this.imageUrl;
+
+    return data;
+  }
+
+  Video.fromSql(Map<String, dynamic> json) {
+    id = json['id'];
+    url = json['url'];
+    title = json['title'];
+    type = json['type'];
+    time = json['time'];
+    imageUrl = json['imageUrl'];
+  }
+}

+ 45 - 0
lib/model/videoDetail.dart

@@ -0,0 +1,45 @@
+class VideoDetail {
+  String title;
+  String imageUrl;
+  String starring;
+  String releaseTime;
+  String score;
+  String description;
+  List<String> videoUrl;
+  List<String> downloadUrl;
+
+  VideoDetail({
+    this.title,
+    this.imageUrl,
+    this.starring,
+    this.releaseTime,
+    this.score,
+    this.description,
+    this.videoUrl,
+    this.downloadUrl,
+  });
+
+  VideoDetail.fromJson(Map<String, dynamic> json) {
+    title = json['title'];
+    imageUrl = json['imageUrl'];
+    starring = json['starring'];
+    releaseTime = json['releaseTime'];
+    score = json['score'];
+    description = json['description'];
+    videoUrl = json['videoUrl']?.cast<String>();
+    videoUrl = json['downloadUrl']?.cast<String>();
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['title'] = this.title;
+    data['imageUrl'] = this.imageUrl;
+    data['starring'] = this.starring;
+    data['releaseTime'] = this.releaseTime;
+    data['score'] = this.score;
+    data['description'] = this.description;
+    data['videoUrl'] = this.videoUrl;
+    data['downloadUrl'] = this.videoUrl;
+    return data;
+  }
+}

+ 18 - 0
lib/providers/providerManager.dart

@@ -0,0 +1,18 @@
+import 'package:provider/provider.dart';
+import 'package:provider/single_child_widget.dart';
+import 'package:video/model/refreshModel.dart';
+import 'package:video/model/urlModel.dart';
+
+List<SingleChildWidget> providers = [
+  ...independentServices,
+];
+
+//独立的model
+List<SingleChildWidget> independentServices = [
+  ChangeNotifierProvider<UrlInfo>(
+    create: (context) => UrlInfo(),
+  ),
+  ChangeNotifierProvider<Refresh>(
+    create: (context) => Refresh(),
+  ),
+];

BIN
lib/screen/.DS_Store


+ 157 - 0
lib/screen/detail.dart

@@ -0,0 +1,157 @@
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter_spinkit/flutter_spinkit.dart';
+
+import 'package:flutter/material.dart';
+
+import 'package:palette_generator/palette_generator.dart';
+import 'package:provider/provider.dart';
+import 'package:video/http/reqularDetail.dart';
+import 'package:video/model/refreshModel.dart';
+import 'package:video/model/urlModel.dart';
+import 'package:video/model/video.dart';
+import 'package:video/model/videoDetail.dart';
+import 'package:video/screen/widget/fijkPlayer.dart';
+import 'package:video/utils/appcolor.dart';
+import 'package:video/utils/movieApi.dart';
+import 'package:video/widget/detail/header.dart';
+import 'package:video/widget/detail/playList.dart';
+import 'package:video/widget/detail/storyLine.dart';
+
+class MovieDetail extends StatefulWidget {
+  final Video video;
+  final String url;
+  MovieDetail({Key key, this.url, this.video}) : super(key: key);
+
+  @override
+  _MovieDetailState createState() => _MovieDetailState();
+}
+
+class _MovieDetailState extends State<MovieDetail> {
+  VideoDetail _data;
+  bool isSummaryUnfold = false;
+  Color pageColor = AppColor.white;
+  ScrollController scrollController = ScrollController();
+  int count = 1;
+  int urlIndex = -1;
+  bool init = true;
+  Video get video => widget.video;
+  String get url => widget.url;
+
+  @override
+  void initState() {
+    super.initState();
+    Provider.of<Refresh>(context, listen: false).init();
+
+    this.fetchData();
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    final _urlIndex = Provider.of<UrlInfo>(context).urlIndex;
+
+    /// 更改视频播放链接DMEO
+    setState(() {
+      urlIndex = _urlIndex;
+    });
+  }
+
+  changeSummaryMaxLines() {
+    setState(() {
+      isSummaryUnfold = !isSummaryUnfold;
+    });
+  }
+
+  Future<void> fetchData() async {
+    VideoDetail res =
+        await RegularDetail.getHttpDetail(MovieApi.videoApi() + url);
+    PaletteGenerator paletteGenerator =
+        await PaletteGenerator.fromImageProvider(
+      CachedNetworkImageProvider(res.imageUrl),
+    );
+
+    setState(() {
+      _data = res;
+
+      if (paletteGenerator.darkVibrantColor != null) {
+        pageColor = paletteGenerator.darkVibrantColor.color;
+      } else {
+        pageColor = Color(0xff35374c);
+      }
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return _data == null
+        ? Scaffold(
+            backgroundColor: Colors.blueGrey,
+            appBar: AppBar(
+              backgroundColor: Colors.blueGrey,
+            ),
+            body: SpinKitWave(
+              color: Colors.white,
+              type: SpinKitWaveType.start,
+              size: 40,
+            ),
+          )
+        : WillPopScope(
+            onWillPop: _onWillPop,
+            child: Scaffold(
+              appBar: AppBar(
+                title: Text(_data.title),
+                backgroundColor: pageColor,
+              ),
+              body: Stack(
+                children: <Widget>[
+                  Container(
+                    color: pageColor,
+                    child: ListView(
+                      children: <Widget>[
+                        Container(
+                          padding: const EdgeInsets.fromLTRB(15, 10, 15, 5),
+                          child: urlIndex == -1 && init
+                              ? MovieDetailHeader(
+                                  videoDetail: _data,
+                                  coverColor: pageColor,
+                                  video: video,
+                                )
+                              : FijkPlayerPage(
+                                  url: _data.videoUrl,
+                                  title: _data.title,
+                                ),
+                        ),
+                        Padding(
+                          padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
+                          child: StoryLine(
+                            storyline: _data.description.toString(),
+                            isUnfold: isSummaryUnfold,
+                            onPressed: changeSummaryMaxLines,
+                          ),
+                        ),
+                        Padding(
+                          padding: const EdgeInsets.fromLTRB(25, 5, 25, 10),
+                          child: PlayList(
+                            title: _data.title,
+                            label: '播放源',
+                            data: _data.videoUrl,
+                            coverColor: pageColor,
+                          ),
+                        ),
+                      ],
+                    ),
+                  ),
+                ],
+              ),
+            ),
+          );
+  }
+
+  // 拦截返回键
+  Future<bool> _onWillPop() async {
+    Provider.of<UrlInfo>(context, listen: false).changIndex(-1);
+
+    init = false;
+    return true;
+  }
+}

+ 59 - 0
lib/screen/home.dart

@@ -0,0 +1,59 @@
+import 'package:fancy_bottom_navigation/fancy_bottom_navigation.dart';
+import 'package:flutter/material.dart';
+import 'package:video/screen/page/favorite.dart';
+import 'package:video/screen/page/search.dart';
+import 'package:video/screen/page/setting.dart';
+
+class Home extends StatefulWidget {
+  Home({Key key}) : super(key: key);
+
+  _HomeState createState() => _HomeState();
+}
+
+class _HomeState extends State<Home> {
+  GlobalKey bottomNavigationKey = GlobalKey();
+  int currentIndex = 1;
+  List<TabData> tabData = [
+    TabData(iconData: Icons.search, title: "搜索", onclick: () {}),
+    TabData(iconData: Icons.favorite, title: "收藏", onclick: () {}),
+    TabData(iconData: Icons.settings, title: "设置", onclick: () {}),
+  ];
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: Colors.white,
+      body: Container(
+        child: _getPage(currentIndex),
+      ),
+      bottomNavigationBar: FancyBottomNavigation(
+        tabs: tabData,
+        initialSelection: 1,
+        key: bottomNavigationKey,
+        onTabChangedListener: (position) {
+          setState(() {
+            currentIndex = position;
+          });
+        },
+      ),
+    );
+  }
+}
+
+_getPage(int currentIndex) {
+  switch (currentIndex) {
+    case 0:
+      return Search();
+      break;
+    case 1:
+      return Favorite();
+      break;
+    default:
+      return SettingPage();
+  }
+}

+ 84 - 0
lib/screen/loading.dart

@@ -0,0 +1,84 @@
+import 'package:flutter/material.dart';
+
+import 'package:palette_generator/palette_generator.dart';
+import 'package:permission_handler/permission_handler.dart';
+import 'package:video/services/favoriteData.dart';
+import 'package:video/services/searchData.dart';
+
+import 'package:video/utils/appcolor.dart';
+
+class LoadingPage extends StatefulWidget {
+  @override
+  _LoadingPageState createState() => _LoadingPageState();
+}
+
+class _LoadingPageState extends State<LoadingPage> {
+  Color pageColor = AppColor.white;
+
+  @override
+  void initState() {
+    super.initState();
+    new Future.delayed(
+      Duration(seconds: 3),
+      () {
+        _getHasSkip();
+      },
+    );
+    this.fetchData();
+    this._checkPermission();
+  }
+
+  void _getHasSkip() {
+    Navigator.of(context).pushReplacementNamed('home');
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      color: pageColor,
+      child: Center(
+        child: Stack(
+          children: <Widget>[
+            Image.asset(
+              "images/loading.jpg",
+              fit: BoxFit.fill,
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+
+  Future<void> fetchData() async {
+    PaletteGenerator paletteGenerator =
+        await PaletteGenerator.fromImageProvider(
+            AssetImage('images/loading.jpg'));
+
+    setState(() {
+      if (paletteGenerator.darkVibrantColor != null) {
+        pageColor = paletteGenerator.darkVibrantColor.color;
+      } else {
+        pageColor = Color(0xff35374c);
+      }
+    });
+
+    FavoriteData().database;
+    SearchData().database;
+  }
+
+  Future<bool> _checkPermission() async {
+    PermissionStatus permission = await PermissionHandler()
+        .checkPermissionStatus(PermissionGroup.storage);
+    if (permission != PermissionStatus.granted) {
+      Map<PermissionGroup, PermissionStatus> permissions =
+          await PermissionHandler()
+              .requestPermissions([PermissionGroup.storage]);
+      if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
+        return true;
+      }
+    } else {
+      return true;
+    }
+    return false;
+  }
+}

+ 62 - 0
lib/screen/page/changLog.dart

@@ -0,0 +1,62 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import 'package:package_info/package_info.dart';
+
+class ChangeLogPage extends StatefulWidget {
+  @override
+  _ChangeLogPageState createState() => _ChangeLogPageState();
+}
+
+class _ChangeLogPageState extends State<ChangeLogPage> {
+  ValueNotifier versionNotifier;
+
+  @override
+  void initState() {
+    versionNotifier = ValueNotifier<String>('');
+    PackageInfo.fromPlatform().then((packageInfo) {
+      versionNotifier.value =
+          '${packageInfo.version}(${packageInfo.buildNumber})';
+    });
+    super.initState();
+  }
+
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(title: Text('更新日志')),
+      body: SafeArea(
+        child: Stack(children: <Widget>[
+          Padding(
+            padding: const EdgeInsets.only(bottom: 75),
+            child: ChangeLogView(),
+          ),
+        ]),
+      ),
+    );
+  }
+}
+
+class ChangeLogView extends StatefulWidget {
+  @override
+  _ChangeLogViewState createState() => _ChangeLogViewState();
+}
+
+class _ChangeLogViewState extends State<ChangeLogView> {
+  String _changelog = "";
+
+  @override
+  void initState() {
+    rootBundle.loadString("CHANGELOG.md").then((data) {
+      setState(() {
+        _changelog = data;
+      });
+    });
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Markdown(data: _changelog);
+  }
+}

+ 148 - 0
lib/screen/page/favorite.dart

@@ -0,0 +1,148 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_easyrefresh/ball_pulse_footer.dart';
+import 'package:flutter_easyrefresh/ball_pulse_header.dart';
+import 'package:flutter_easyrefresh/easy_refresh.dart';
+import 'package:flutter_slidable/flutter_slidable.dart';
+import 'package:provider/provider.dart';
+import 'package:video/model/urlModel.dart';
+import 'package:video/model/video.dart';
+import 'package:video/services/favoriteData.dart';
+
+import 'package:video/services/type.dart';
+import 'package:video/utils/screen.dart';
+import 'package:video/widget/favoriteCard.dart';
+
+class Favorite extends StatefulWidget {
+  Favorite({Key key}) : super(key: key);
+
+  _FavoriteState createState() => _FavoriteState();
+}
+
+class _FavoriteState extends State<Favorite>
+    with AutomaticKeepAliveClientMixin {
+  SlidableController slidableController = SlidableController();
+  int page = 1;
+  int _change = 0;
+
+  List<Video> _movieList = [];
+  ScrollController _scrollController = ScrollController();
+
+  @override
+  bool get wantKeepAlive => true;
+
+  @override
+  void initState() {
+    super.initState();
+    _scrollController.addListener(() {
+      if (_scrollController.position.pixels ==
+          _scrollController.position.maxScrollExtent) {
+        query();
+      }
+    });
+    this.query();
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    final isAdd = Provider.of<UrlInfo>(context).isAdd;
+    final video = Provider.of<UrlInfo>(context).video;
+    final change = Provider.of<UrlInfo>(context).change;
+
+    if (_change != change) {
+      _change = change;
+      if (isAdd) {
+        setState(() {
+          _movieList.add(video);
+        });
+      } else {
+        setState(() {
+          _movieList.remove(video);
+        });
+      }
+    }
+  }
+
+  query() async {
+    List<Video> res = await FavoriteData().getVideoList(DataType().favorite);
+    setState(() {
+      _movieList = res == null ? [] : res;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    super.build(context);
+    return Scaffold(
+      appBar: PreferredSize(
+        child: AppBar(
+          title: Text(
+            "长按删除",
+            style: TextStyle(fontSize: 14),
+          ),
+          actions: <Widget>[
+            IconButton(
+                icon: Icon(
+                  Icons.delete,
+                  size: 20,
+                ),
+                onPressed: () async {
+                  await FavoriteData().deleteVideoList(DataType().favorite);
+
+                  setState(() {
+                    _movieList.removeRange(0, _movieList.length);
+                  });
+                })
+          ],
+        ),
+        preferredSize: Size.fromHeight(Screen.height * 0.05),
+      ),
+      body: _movieList.length == 0
+          ? Container(
+              color: Colors.white,
+              child: Center(
+                child: Text("您没有收藏视频,转至搜索......"),
+              ))
+          : Container(
+              color: Colors.white,
+              padding: EdgeInsets.fromLTRB(10, 5, 10, 0),
+              child: EasyRefresh(
+                header: BallPulseHeader(
+                  color: Colors.white,
+                ),
+                footer: BallPulseFooter(
+                  color: Colors.white,
+                ),
+                onRefresh: () async {
+                  await this.query();
+                },
+                onLoad: () async {
+                  await this.query();
+                },
+                child: GridView.custom(
+                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+                    crossAxisCount: 3,
+                    mainAxisSpacing: 10.0,
+                    crossAxisSpacing: 10.0,
+                    childAspectRatio: 0.57,
+                  ),
+                  childrenDelegate:
+                      SliverChildBuilderDelegate((context, index) {
+                    return InkWell(
+                      child: FavoriteCard(video: _movieList[index]),
+                      onLongPress: () async {
+                        await FavoriteData().deleteVideo(
+                            _movieList[index].url, DataType().favorite);
+
+                        setState(() {
+                          _movieList.remove(_movieList[index]);
+                        });
+                      },
+                    );
+                  }, childCount: _movieList.length),
+                ),
+              ),
+            ),
+    );
+  }
+}

+ 45 - 0
lib/screen/page/search.dart

@@ -0,0 +1,45 @@
+import 'package:beauty_textfield/beauty_textfield.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class Search extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return AnnotatedRegion<SystemUiOverlayStyle>(
+      value: SystemUiOverlayStyle.dark,
+      child: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            Image.asset(
+              "images/search.png",
+              height: 60,
+              width: 60,
+            ),
+            SizedBox(
+              height: 40,
+            ),
+            InkWell(
+              child: BeautyTextfield(
+                width: double.maxFinite,
+                height: 50,
+                duration: Duration(milliseconds: 300),
+                inputType: TextInputType.text,
+                prefixIcon: Icon(Icons.search),
+                accentColor: Colors.black,
+                backgroundColor: Colors.white,
+                cornerRadius: BorderRadius.all(Radius.circular(30)),
+                enabled: false,
+                placeholder: "",
+                textColor: Colors.black,
+              ),
+              onTap: () {
+                Navigator.pushNamed(context, "search");
+              },
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 81 - 0
lib/screen/page/setting.dart

@@ -0,0 +1,81 @@
+import 'package:flutter/material.dart';
+import 'package:video/screen/widget/bottomClipper.dart';
+
+class SettingPage extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return CustomScrollView(
+      slivers: <Widget>[
+        SliverAppBar(
+          backgroundColor: Theme.of(context).scaffoldBackgroundColor,
+          expandedHeight: 200 + MediaQuery.of(context).padding.top,
+          flexibleSpace: UserHeaderWidget(),
+          pinned: false,
+        ),
+        UserListWidget(),
+      ],
+    );
+  }
+}
+
+class UserHeaderWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    return ClipPath(
+      clipper: BottomClipper(),
+      child: Container(
+        color: Theme.of(context).primaryColor.withAlpha(200),
+        padding: EdgeInsets.only(top: 10),
+        child: Column(
+            mainAxisAlignment: MainAxisAlignment.center,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: <Widget>[
+              Hero(
+                tag: 'loginLogo',
+                child: ClipOval(
+                  child: Image.asset(
+                    "images/setting.png",
+                    fit: BoxFit.cover,
+                    width: 140,
+                    height: 140,
+                    // color: Theme.of(context).accentColor.withAlpha(200),
+                    colorBlendMode: BlendMode.colorDodge,
+                  ),
+                ),
+              ),
+              SizedBox(
+                height: 20,
+              ),
+            ]),
+      ),
+    );
+  }
+}
+
+class UserListWidget extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    var iconColor = Theme.of(context).accentColor;
+    return ListTileTheme(
+      contentPadding: const EdgeInsets.symmetric(horizontal: 30),
+      child: SliverList(
+        delegate: SliverChildListDelegate([
+          ListTile(
+            title: Text("更新日志"),
+            onTap: () {
+              Navigator.of(context).pushNamed("changLog");
+            },
+            leading: Icon(
+              Icons.change_history,
+              color: iconColor,
+            ),
+            trailing: Icon(Icons.chevron_right),
+          ),
+          SizedBox(
+            height: 30,
+          )
+        ]),
+      ),
+    );
+  }
+}

+ 22 - 0
lib/screen/widget/bottomClipper.dart

@@ -0,0 +1,22 @@
+import 'package:flutter/material.dart';
+
+class BottomClipper extends CustomClipper<Path> {
+  @override
+  getClip(Size size) {
+    var path = Path();
+    path.lineTo(0, 0);
+    path.lineTo(0, size.height - 50);
+
+    var p1 = Offset(size.width / 2, size.height);
+    var p2 = Offset(size.width, size.height - 50);
+    path.quadraticBezierTo(p1.dx, p1.dy, p2.dx, p2.dy);
+    path.lineTo(size.width, size.height - 50);
+    path.lineTo(size.width, 0);
+    return path;
+  }
+
+  @override
+  bool shouldReclip(CustomClipper oldClipper) {
+    return false;
+  }
+}

+ 110 - 0
lib/screen/widget/fijkPlayer.dart

@@ -0,0 +1,110 @@
+import 'package:fijkplayer/fijkplayer.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import 'package:video/model/urlModel.dart';
+import 'package:video/utils/screen.dart';
+
+class FijkPlayerPage extends StatefulWidget {
+  FijkPlayerPage({
+    @required this.url,
+    this.title,
+  });
+
+  final String title;
+  final List<String> url;
+
+  @override
+  _FijkPlayerState createState() => _FijkPlayerState();
+}
+
+class _FijkPlayerState extends State<FijkPlayerPage>
+    with WidgetsBindingObserver {
+  final FijkPlayer player = FijkPlayer();
+  bool init = true;
+  int urlIndex = -1;
+  String videoUrl = "";
+
+  @override
+  void didChangeAppLifecycleState(AppLifecycleState state) {
+    ///通过state判断App前后台切换
+    if (state == AppLifecycleState.paused) {
+      player.pause();
+    } else if (state == AppLifecycleState.resumed) {
+      player.start();
+    }
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    final _urlIndex = Provider.of<UrlInfo>(context).urlIndex;
+
+    if (_urlIndex != -1 && _urlIndex != urlIndex) {
+      urlIndex = _urlIndex;
+
+      videoUrl = urlList[_urlIndex];
+      startPlay();
+    }
+  }
+
+  @override
+  void dispose() {
+    WidgetsBinding.instance.removeObserver(this);
+    player.release();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    player.addListener(_fijkValueListener);
+    WidgetsBinding.instance.addObserver(this);
+    startPlay(init: true);
+    super.initState();
+  }
+
+  String get title => widget.title;
+
+  List<String> get urlList => widget.url;
+
+  void startPlay({bool init = false}) async {
+    if (init) {
+      await player.setOption(FijkOption.hostCategory, "request-screen-on", 1);
+      await player.setOption(FijkOption.hostCategory, "request-audio-focus", 1);
+      await player.setDataSource(urlList[0], autoPlay: true);
+    } else {
+      await player.reset();
+
+      await player.setDataSource(
+        videoUrl,
+        autoPlay: true,
+      );
+    }
+  }
+
+  void _fijkValueListener() {
+    FijkValue value = player.value;
+
+    if (value.completed) {
+      if (widget.url.length > urlIndex + 1) {
+        Provider.of<UrlInfo>(context).changIndex(urlIndex + 1);
+      } else {
+        Provider.of<UrlInfo>(context).changIndex(-1);
+      }
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      height: 218.0 + Screen.topSafeHeight,
+      width: Screen.width,
+      child: FijkView(
+        fit: FijkFit.ar16_9,
+        color: Colors.black,
+        player: player,
+        panelBuilder: fijkPanel2Builder(),
+      ),
+    );
+  }
+}

+ 114 - 0
lib/screen/widget/searchBottomWidget.dart

@@ -0,0 +1,114 @@
+import 'package:beauty_textfield/beauty_textfield.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:video/model/video.dart';
+import 'package:video/screen/widget/searchHistoricalWidget.dart';
+import 'package:video/services/searchData.dart';
+
+class SearchBottom extends StatefulWidget {
+  SearchBottom();
+
+  @override
+  SearchBottomState createState() => SearchBottomState();
+}
+
+class SearchBottomState extends State<SearchBottom> {
+  List<Video> videos = List<Video>();
+  List<SearchModel> searchData;
+  int length = 0;
+  String str;
+
+  @override
+  void initState() {
+    super.initState();
+    getData();
+  }
+
+  Future<void> getData() async {
+    final _searchData = await SearchData().getSearchList();
+    setState(() {
+      searchData = _searchData == null ? [] : _searchData;
+      length = _searchData == null ? 0 : _searchData.length;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return AnnotatedRegion<SystemUiOverlayStyle>(
+      value: SystemUiOverlayStyle.dark,
+      child: Scaffold(
+        backgroundColor: Colors.white,
+        body: Column(
+          mainAxisAlignment: MainAxisAlignment.end,
+          children: <Widget>[
+            Expanded(
+              child: length == 0
+                  ? Container()
+                  : SearchHistorical(
+                      searchData: searchData,
+                    ),
+            ),
+            Container(
+              decoration: BoxDecoration(
+                color: Colors.white,
+                boxShadow: <BoxShadow>[
+                  BoxShadow(
+                    color: Colors.black,
+                    blurRadius: 2,
+                  ),
+                ],
+              ),
+              child: Row(
+                children: <Widget>[
+                  BeautyTextfield(
+                    width: 330,
+                    height: 50,
+                    prefixIcon: Icon(
+                      Icons.search,
+                      color: Colors.white,
+                    ),
+                    inputType: TextInputType.text,
+                    backgroundColor: Colors.white,
+                    cornerRadius: BorderRadius.all(Radius.circular(30)),
+                    textColor: Colors.black,
+                    autofocus: true,
+                    placeholder: "",
+                    accentColor: Colors.blueGrey,
+                    onSubmitted: (data) async {
+                      Navigator.pushNamed(context, 'searchResult',
+                          arguments: {"query": data});
+
+                      await SearchData()
+                          .insertSearch(new SearchModel(title: data));
+
+                      if (searchData
+                              .where((item) => item.title == data)
+                              .length ==
+                          0) {
+                        searchData.add(new SearchModel(title: data));
+                      }
+                    },
+                    onChanged: (_str) {
+                      str = _str;
+                    },
+                  ),
+                  InkWell(
+                    child: Text(
+                      "搜索",
+                      style:
+                          TextStyle(fontSize: 16, fontWeight: FontWeight.w400),
+                    ),
+                    onTap: () {
+                      Navigator.pushNamed(context, 'searchResult',
+                          arguments: {"query": str});
+                    },
+                  ),
+                ],
+              ),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 103 - 0
lib/screen/widget/searchHistoricalWidget.dart

@@ -0,0 +1,103 @@
+import 'package:flutter/material.dart';
+import 'package:video/services/searchData.dart';
+
+class SearchHistorical extends StatefulWidget {
+  final List<SearchModel> searchData;
+  SearchHistorical({this.searchData});
+
+  SearchHistoricalState createState() => SearchHistoricalState();
+}
+
+class SearchHistoricalState extends State<SearchHistorical> {
+  List<SearchModel> get searchData => widget.searchData;
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      child: Column(
+        mainAxisAlignment: MainAxisAlignment.end,
+        children: <Widget>[
+          searchData.length == 0
+              ? Container()
+              : Container(
+                  decoration: BoxDecoration(
+                    color: Colors.white,
+                    boxShadow: <BoxShadow>[
+                      BoxShadow(
+                        color: Colors.blueGrey,
+                        blurRadius: 1,
+                      ),
+                    ],
+                  ),
+                  child: Row(
+                    children: <Widget>[
+                      Expanded(
+                        child: Container(
+                          padding: EdgeInsets.only(left: 10),
+                          child: Text(
+                            '长按单个删除',
+                            style: TextStyle(
+                              fontSize: 14.0,
+                              fontWeight: FontWeight.bold,
+                            ),
+                          ),
+                        ),
+                      ),
+                      Container(
+                        alignment: Alignment.centerRight,
+                        child: IconButton(
+                          icon: Icon(
+                            Icons.delete,
+                            size: 18,
+                          ),
+                          onPressed: () async {
+                            await SearchData().deleteSearchList();
+                            setState(() {
+                              searchData.removeRange(0, searchData.length);
+                            });
+                          },
+                        ),
+                      ),
+                    ],
+                  ),
+                ),
+          Container(
+            margin: EdgeInsets.fromLTRB(10, 10, 10, 30),
+            alignment: Alignment.centerLeft,
+            child: Wrap(
+              spacing: 8.0,
+              runSpacing: 8.0,
+              children: searchData.map((childNode) {
+                return GestureDetector(
+                  child: ClipRRect(
+                    child: Container(
+                      padding: EdgeInsets.fromLTRB(10, 3, 10, 3),
+                      child: Text(
+                        childNode.title,
+                        style: TextStyle(
+                          color: Colors.white,
+                        ),
+                      ),
+                      color: Colors.blueGrey,
+                    ),
+                    borderRadius: new BorderRadius.circular(30),
+                  ),
+                  onTap: () {
+                    Navigator.pushNamed(context, 'searchResult',
+                        arguments: {"query": childNode.title});
+                  },
+                  onLongPress: () async {
+                    await SearchData().deleteSearch(childNode.title);
+                    setState(() {
+                      searchData.remove(childNode);
+                    });
+                  },
+                );
+              }).toList(),
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 86 - 0
lib/screen/widget/searchResult.dart

@@ -0,0 +1,86 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_easyrefresh/ball_pulse_footer.dart';
+import 'package:flutter_easyrefresh/ball_pulse_header.dart';
+import 'package:flutter_easyrefresh/easy_refresh.dart';
+import 'package:video/http/request.dart';
+import 'package:video/http/reqularSearch.dart';
+import 'package:video/model/video.dart';
+import 'package:video/utils/movieApi.dart';
+import 'package:video/utils/screen.dart';
+import 'package:video/widget/easyCard.dart';
+
+class SearchResult extends StatefulWidget {
+  final String query;
+  SearchResult({this.query});
+
+  @override
+  SearchResultState createState() => SearchResultState();
+}
+
+class SearchResultState extends State<SearchResult> {
+  String get query => widget.query;
+  List<Video> movieList = [];
+  int page = 1;
+
+  @override
+  void initState() {
+    super.initState();
+
+    _getMovieList(query);
+  }
+
+  void _getMovieList(name) async {
+    var responseBody = await Request.post(
+      url: MovieApi.searchApt(),
+      data: {"wd": "$name", "submit": "search"},
+    );
+
+    final _movieList = RegularSearch.getRecommend(responseBody);
+
+    setState(() {
+      movieList = _movieList;
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: Colors.blueGrey,
+      appBar: PreferredSize(
+        child: AppBar(
+          title: Text(
+            query,
+            style: TextStyle(fontSize: 16),
+          ),
+          centerTitle: true,
+        ),
+        preferredSize: Size.fromHeight(Screen.height * 0.05),
+      ),
+      body: EasyRefresh.custom(
+        header: BallPulseHeader(
+          color: Colors.white,
+        ),
+        footer: BallPulseFooter(
+          color: Colors.white,
+        ),
+        slivers: <Widget>[
+          SliverList(
+            delegate: SliverChildBuilderDelegate(
+              (context, index) => EasyCard(
+                prefixBadge: Colors.black38,
+                title: movieList[index].title,
+                suffixIcon: Icons.chevron_right,
+                suffixIconColor: Colors.black38,
+                url: movieList[index].url,
+                time: movieList[index].time,
+                timeColor: Colors.black,
+                video: movieList[index],
+              ),
+              childCount: movieList.length,
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+}

+ 136 - 0
lib/services/favoriteData.dart

@@ -0,0 +1,136 @@
+import 'package:sqflite/sqflite.dart';
+import 'dart:async';
+import 'dart:io';
+import 'package:path_provider/path_provider.dart';
+import 'package:video/model/video.dart';
+
+class FavoriteData {
+  static FavoriteData _databaseHelper;
+  static Database _database;
+  final String tableVideo = 'TableVideo';
+  final String columnId = 'id';
+  final String url = 'url';
+  final String title = 'title';
+  final String time = 'time';
+  final String type = 'type';
+  final String imageUrl = 'imageUrl';
+
+  FavoriteData._creatInstance();
+
+  factory FavoriteData() {
+    if (_databaseHelper == null) {
+      _databaseHelper = FavoriteData._creatInstance();
+    }
+
+    return _databaseHelper;
+  }
+
+  Future<Database> initializeDatabase() async {
+    Directory directory = await getApplicationDocumentsDirectory();
+    String path = directory.path + 'favorite.db';
+
+    var employeeDatabase =
+        await openDatabase(path, version: 1, onCreate: _creatDB);
+    return employeeDatabase;
+  }
+
+  void _creatDB(Database db, int newVersion) async {
+    await db.execute(
+      'CREATE TABLE $tableVideo($columnId INTEGER PRIMARY KEY AUTOINCREMENT, $url TEXT, $title TEXT,  $time TEXT, $type TEXT, $imageUrl)',
+    );
+  }
+
+  Future<Database> get database async {
+    if (_database == null) {
+      _database = await initializeDatabase();
+    }
+
+    return _database;
+  }
+
+  Future<List<Map<String, dynamic>>> getVideoMapList(String dataType) async {
+    Database db = await this.database;
+
+    var result = await db.rawQuery(
+        "SELECT * FROM $tableVideo where $type = '$dataType' order by $columnId ASC");
+    return result;
+  }
+
+  // Insert
+
+  Future<int> insertVideo(Video video) async {
+    Database db = await this.database;
+
+    final videoList = await getVideoList(video.type);
+    if (videoList.where((element) => element.url == video.url).length > 0) {
+      return 0;
+    } else {
+      var result = await db.insert(tableVideo, video.toJson());
+      return result;
+    }
+  }
+
+  // Update
+
+  // Future<int> updateVideo(Video video, String dataType) async {
+  //   Database db = await this.database;
+  //   var result = await db.update(tableVideo, video.toJson(),
+  //       where: '$columnId = ?', whereArgs: [video.id]);
+  //   return result;
+  // }
+
+  // Delete
+
+  Future<int> deleteVideo(String videoUrl, String dataType) async {
+    var db = await this.database;
+    int result = await db.rawDelete(
+        "DELETE FROM $tableVideo WHERE $url = '$videoUrl' and $type = '$dataType'");
+    return result;
+  }
+
+  Future<int> deleteVideoList(String dataType) async {
+    var db = await this.database;
+    int result =
+        await db.rawDelete("DELETE FROM $tableVideo WHERE $type = '$dataType'");
+    return result;
+  }
+
+  //Total Count
+
+  // Future<int> getCount() async {
+  //   var db = await this.database;
+  //   List<Map<String, dynamic>> x =
+  //       await db.rawQuery('SELECT COUNT (*) from $tableVideo');
+  //   int result = Sqflite.firstIntValue(x);
+  //   return result;
+  // }
+
+  // IsExit
+
+  Future<bool> isExit(String videoUrl, String dataType) async {
+    var db = await this.database;
+    List<Map<String, dynamic>> x = await db.rawQuery(
+        "SELECT COUNT (*) from $tableVideo WHERE $url = '$videoUrl' and $type = '$dataType'");
+    int result = Sqflite.firstIntValue(x);
+    if (result == 0) {
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  Future<List<Video>> getVideoList(String type) async {
+    var videoMapList =
+        await getVideoMapList(type); // Get 'Map List' from database
+    int count =
+        videoMapList.length; // Count the number of map entries in db table
+
+    List<Video> videoList = List<Video>();
+    // For loop to create a 'Note List' from a 'Map List'
+    for (int i = 0; i < count; i++) {
+      videoList.add(Video.fromJson(videoMapList[i]));
+    }
+
+    return videoList;
+  }
+}

+ 120 - 0
lib/services/searchData.dart

@@ -0,0 +1,120 @@
+import 'package:sqflite/sqflite.dart';
+import 'dart:async';
+import 'dart:io';
+import 'package:path_provider/path_provider.dart';
+
+class SearchData {
+  static SearchData _databaseHelper;
+  static Database _database;
+  final String tableSearch = 'TableSearch';
+  final String columnId = 'id';
+  final String title = 'title';
+
+  SearchData._creatInstance();
+
+  factory SearchData() {
+    if (_databaseHelper == null) {
+      _databaseHelper = SearchData._creatInstance();
+    }
+
+    return _databaseHelper;
+  }
+
+  Future<Database> initializeDatabase() async {
+    Directory directory = await getApplicationDocumentsDirectory();
+    String path = directory.path + 'search.db';
+
+    var employeeDatabase =
+        await openDatabase(path, version: 1, onCreate: _creatDB);
+    return employeeDatabase;
+  }
+
+  void _creatDB(Database db, int newVersion) async {
+    await db.execute(
+      'CREATE TABLE $tableSearch($columnId INTEGER PRIMARY KEY AUTOINCREMENT, $title TEXT)',
+    );
+  }
+
+  Future<Database> get database async {
+    if (_database == null) {
+      _database = await initializeDatabase();
+    }
+
+    return _database;
+  }
+
+  Future<List<SearchModel>> getSearchList() async {
+    Database db = await this.database;
+
+    var result =
+        await db.rawQuery("SELECT * FROM $tableSearch order by $columnId ASC");
+
+    if (result == null || result.length == 0) {
+      return null;
+    }
+
+    List<SearchModel> titles = List<SearchModel>();
+
+    result.forEach((item) => titles.add(SearchModel.fromJson(item)));
+
+    return titles.take(20).toList();
+  }
+
+  // Insert
+
+  Future<int> insertSearch(SearchModel searchModel) async {
+    Database db = await this.database;
+
+    final searchList = await getSearchList();
+    if (searchList != null &&
+        searchList
+                .where((element) => element.title == searchModel.title)
+                .length >
+            0) {
+      return 0;
+    } else {
+      var result = await db.insert(tableSearch, searchModel.toJson());
+      return result;
+    }
+  }
+
+  // Delete
+
+  Future<int> deleteSearch(String titleSearch) async {
+    var db = await this.database;
+    int result = await db
+        .rawDelete("DELETE FROM $tableSearch WHERE $title = '$titleSearch'");
+    return result;
+  }
+
+  Future<int> deleteSearchList() async {
+    var db = await this.database;
+    int result = await db.rawDelete("DELETE FROM $tableSearch");
+    return result;
+  }
+}
+
+class SearchModel {
+  int id;
+  String title;
+
+  SearchModel({this.id, this.title});
+
+  SearchModel.fromJson(Map<String, dynamic> json) {
+    id = json['id'];
+    title = json['title'];
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['id'] = this.id;
+    data['title'] = this.title;
+
+    return data;
+  }
+
+  SearchModel.fromSql(Map<String, dynamic> json) {
+    id = json['id'];
+    title = json['title'];
+  }
+}

+ 4 - 0
lib/services/type.dart

@@ -0,0 +1,4 @@
+class DataType {
+  String favorite = "favorite";
+  String search = "search";
+}

+ 15 - 0
lib/utils/appcolor.dart

@@ -0,0 +1,15 @@
+import 'package:flutter/material.dart';
+
+class AppColor {
+  static Color primary = Color(0xFF5C5C5C);
+  static Color secondary = Color(0xFF51DEC6);
+  static Color red = Color(0xFFFF2B45);
+  static Color orange = Color(0xFFF67264);
+  static Color white = Color(0xFFFFFFFF);
+  static Color paper = Color(0xFFF5F5F5);
+  static Color lightGrey = Color(0xFFDDDDDD);
+  static Color darkGrey = Color(0xFF333333);
+  static Color grey = Color(0xFF888888);
+  static Color blue = Color(0xFF3688FF);
+  static Color golden = Color(0xff8B7961);
+}

+ 42 - 0
lib/utils/convert.dart

@@ -0,0 +1,42 @@
+import 'dart:typed_data';
+
+class Convert {
+  // String (Dart uses UTF-16) to bytes
+  static stringToUint8(String string) async {
+    var list = new List<int>();
+    string.runes.forEach((rune) {
+      if (rune >= 0x10000) {
+        rune -= 0x10000;
+        int firstWord = (rune >> 10) + 0xD800;
+        list.add(firstWord >> 8);
+        list.add(firstWord & 0xFF);
+        int secondWord = (rune & 0x3FF) + 0xDC00;
+        list.add(secondWord >> 8);
+        list.add(secondWord & 0xFF);
+      } else {
+        list.add(rune >> 8);
+        list.add(rune & 0xFF);
+      }
+    });
+    Uint8List bytes = Uint8List.fromList(list);
+    return bytes;
+  }
+
+  // Bytes to UTF-16 string
+  static uint8ToString(Uint8List bytes) async {
+    StringBuffer buffer = new StringBuffer();
+    for (int i = 0; i < bytes.length;) {
+      int firstWord = (bytes[i] << 8) + bytes[i + 1];
+      if (0xD800 <= firstWord && firstWord <= 0xDBFF) {
+        int secondWord = (bytes[i + 2] << 8) + bytes[i + 3];
+        buffer.writeCharCode(
+            ((firstWord - 0xD800) << 10) + (secondWord - 0xDC00) + 0x10000);
+        i += 4;
+      } else {
+        buffer.writeCharCode(firstWord);
+        i += 2;
+      }
+    }
+    return buffer.toString();
+  }
+}

+ 13 - 0
lib/utils/movieApi.dart

@@ -0,0 +1,13 @@
+class MovieApi {
+  static videoApi() {
+    return "http://www.zuidazy5.com";
+  }
+
+  static movieApi(int page) {
+    return "http://www.zuidazy5.com/?m=vod-index-pg-$page.html";
+  }
+
+  static searchApt() {
+    return "http://www.zuidazy5.com/index.php?m=vod-search";
+  }
+}

+ 37 - 0
lib/utils/platform.dart

@@ -0,0 +1,37 @@
+import 'dart:io';
+import 'dart:async';
+
+import 'package:device_info/device_info.dart';
+import 'package:package_info/package_info.dart';
+
+export 'dart:io';
+
+/// 是否是生产环境
+const bool inProduction = const bool.fromEnvironment("dart.vm.product");
+
+class PlatformUtils {
+  static Future<PackageInfo> getAppPackageInfo() {
+    return PackageInfo.fromPlatform();
+  }
+
+  static Future<String> getAppVersion() async {
+    PackageInfo packageInfo = await PackageInfo.fromPlatform();
+    return packageInfo.version;
+  }
+
+  static Future<String> getBuildNum() async {
+    PackageInfo packageInfo = await PackageInfo.fromPlatform();
+    return packageInfo.buildNumber;
+  }
+
+  static Future getDeviceInfo() async {
+    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
+    if (Platform.isAndroid) {
+      return await deviceInfo.androidInfo;
+    } else if (Platform.isIOS) {
+      return await deviceInfo.iosInfo;
+    } else {
+      return null;
+    }
+  }
+}

+ 42 - 0
lib/utils/router.dart

@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+import 'package:video/screen/detail.dart';
+import 'package:video/screen/home.dart';
+import 'package:video/screen/page/changLog.dart';
+import 'package:video/screen/widget/searchBottomWidget.dart';
+import 'package:video/screen/widget/searchResult.dart';
+
+Route<dynamic> generateRoute(RouteSettings settings) {
+  switch (settings.name) {
+    case 'home':
+      return MaterialPageRoute(
+        builder: (context) => Home(),
+      );
+    case 'detail':
+      var arguments = settings.arguments as Map;
+      return MaterialPageRoute(
+        builder: (context) => MovieDetail(
+          url: arguments["url"],
+          video: arguments["video"],
+        ),
+      );
+    case 'search':
+      return MaterialPageRoute(
+        builder: (context) => SearchBottom(),
+      );
+    case 'searchResult':
+      var arguments = settings.arguments as Map;
+      return MaterialPageRoute(
+        builder: (context) => SearchResult(
+          query: arguments["query"],
+        ),
+      );
+    case 'changLog':
+      return MaterialPageRoute(
+        builder: (context) => ChangeLogPage(),
+      );
+    default:
+      return MaterialPageRoute(
+        builder: (context) => Home(),
+      );
+  }
+}

+ 44 - 0
lib/utils/screen.dart

@@ -0,0 +1,44 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'dart:ui' as ui show window;
+
+class Screen {
+  static double get width {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.size.width;
+  }
+
+  static double get height {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.size.height;
+  }
+
+  static double get scale {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.devicePixelRatio;
+  }
+
+  static double get textScaleFactor {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.textScaleFactor;
+  }
+
+  static double get navigationBarHeight {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.padding.top + kToolbarHeight;
+  }
+
+  static double get topSafeHeight {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.padding.top;
+  }
+
+  static double get bottomSafeHeight {
+    MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
+    return mediaQuery.padding.bottom;
+  }
+
+  static updateStatusBarStyle(SystemUiOverlayStyle style) {
+    SystemChrome.setSystemUIOverlayStyle(style);
+  }
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott