├── .flutter-plugins-dependencies ├── .gitignore ├── .metadata ├── README.md ├── android ├── .project ├── .settings │ └── org.eclipse.buildship.core.prefs ├── app │ ├── .classpath │ ├── .project │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── douyin_demo │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── settings_aar.gradle ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── GoogleService-Info .plist │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── detector_painters.dart ├── images │ ├── pingguo.jpeg │ ├── sky.jpg │ ├── temple.jpg │ ├── waterdrop.jpg │ ├── whitehouse.jpg │ ├── woman.jpg │ ├── woman2.jpg │ └── zhifei.jpg ├── main.dart ├── models │ ├── PostsModel.dart │ └── PostsModel.g.dart ├── pages │ ├── CameraPage │ │ └── CameraMain.dart │ ├── FaceDetect │ │ └── FaceDetection.dart │ ├── RecommendPage │ │ ├── BottomSheet.dart │ │ └── FriendList.dart │ ├── loadData │ │ └── loadData.dart │ ├── sameCity │ │ └── SameCityPage.dart │ └── selfHome │ │ └── HomePage.dart ├── providers │ ├── AtUserProvider.dart │ ├── CameraProvider.dart │ ├── PostsGalleryProvider.dart │ └── RecommendProvider.dart ├── utils.dart └── widgets │ ├── BottomBar.dart │ ├── FavAnimation.dart │ └── WebRequest.dart ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"camera","dependencies":[]},{"name":"firebase_ml_vision","dependencies":[]},{"name":"image_gallery_saver","dependencies":[]},{"name":"image_picker","dependencies":[]},{"name":"image_picker_saver","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"shared_preferences","dependencies":[]},{"name":"video_player","dependencies":[]}]} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/Flutter/flutter_export_environment.sh 65 | **/ios/ServiceDefinitions.json 66 | **/ios/Runner/GeneratedPluginRegistrant.* 67 | 68 | # Exceptions to above rules. 69 | !**/ios/**/default.mode1v3 70 | !**/ios/**/default.mode2v3 71 | !**/ios/**/default.pbxuser 72 | !**/ios/**/default.perspectivev3 73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 74 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # douyin_demo 2 | 3 | This is a Flutter project to copy Dou Yin (which named Tik Tok overseas). 4 | 这是一个利用Flutter复制抖音(海外叫做TikTok)的小项目。 5 | 6 | # Demo for the recommend page. 推荐页面效果图 7 | ![image](https://pic4.zhimg.com/80/v2-27a3d4e7af10b0193848725c1885f10f_hd.jpg) 8 | 9 | 10 | 11 | ## Getting Started 12 | 13 | [Need to Know First]
14 | 【写在前面】
15 | 16 | This project is just for tutorial purpose, NO BUSINESS USING IS ALLOWED.
17 | 本项目只作教材使用,不允许有任何商业用途 18 | 19 | We are going to continously update the tutorial video synchronously on Bilibili.
If you want to learn more about Flutter and not just stop your pace at the elementary stage, this is a good choice for you to follow.(Oversea users? Don't worry, maybe we would update the same video on youtube, but you need to learn more about chinese first...)
20 | 我们将持续在Bilibili更新有关Flutter的教程视频,初步设想把抖音的大部分内容复制下来吧。。
如果你想学习更多与Flutter相关的内容,而不是听了一些基础课程就难以迈出下一步,那就来关注我们吧~括号里的我就不翻译了,反正呢。。youtube的事情咱也就瞎说的。 21 | 22 | # Bilibili space (Bilibili课程地址) 23 | 24 |

https://space.bilibili.com/283403747

25 | 26 | ## make a recommendPage for TikTok within 1 hour(1小时做个抖音推荐页)

27 | https://www.bilibili.com/video/av68733100 28 | ![image](https://i2.hdslb.com/bfs/archive/f9579c518e74ba9df4797e67565072aec62c0972.jpg) 29 |



30 | ## decorate the recommendPage to have a nice look (精修推荐页)

31 | https://www.bilibili.com/video/av68815634 32 | ![image](https://i2.hdslb.com/bfs/archive/cbad241917eaf3ca2c1003d6247579e57a23aebc.jpg) 33 | 34 | continously updating (持续更新中...) 35 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.douyin_demo" 42 | minSdkVersion 21 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.0' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 67 | implementation 'com.google.firebase:firebase-analytics:17.2.0' 68 | api 'com.google.firebase:firebase-ml-vision-image-label-model:17.0.2' 69 | api 'com.google.firebase:firebase-ml-vision-face-model:17.0.2' 70 | } 71 | 72 | apply plugin: 'com.google.gms.google-services' 73 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "416855912244", 4 | "firebase_url": "https://douyindemo.firebaseio.com", 5 | "project_id": "douyindemo", 6 | "storage_bucket": "douyindemo.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:416855912244:android:e38e3a5b3b0c678b2dff61", 12 | "android_client_info": { 13 | "package_name": "com.example.douyin_demo" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "416855912244-5avobokp2nrhk8bgrt0sodtjggorhr9k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyCtYf-GF_H8wDLD116rsudevTOkgzOaNEg" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "416855912244-5avobokp2nrhk8bgrt0sodtjggorhr9k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "416855912244-nqmupjevgtbf7ifcjfv42uocjdv5c77f.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "com.example.douyinDemo" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/douyin_demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.douyin_demo 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.app.FlutterActivity 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | GeneratedPluginRegistrant.registerWith(this) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.2.71' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.2.0' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | subprojects { 31 | project.configurations.all { 32 | resolutionStrategy.eachDependency { details -> 33 | if (details.requested.group == 'androidx.exifinterface' 34 | && !details.requested.name.contains('multidex') ) { 35 | details.useVersion "1.0.0" 36 | } 37 | } 38 | } 39 | } 40 | 41 | subprojects { 42 | project.configurations.all { 43 | resolutionStrategy.eachDependency { details -> 44 | if (details.requested.group == 'androidx.core' 45 | && !details.requested.name.contains('multidex') ) { 46 | details.useVersion "1.0.2" 47 | } 48 | } 49 | } 50 | } 51 | 52 | task clean(type: Delete) { 53 | delete rootProject.buildDir 54 | } 55 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | 5 | android.enableR8=true 6 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Using a CDN with CocoaPods 1.7.2 or later can save a lot of time on pod installation, but it's experimental rather than the default. 2 | # source 'https://cdn.cocoapods.org/' 3 | 4 | # Uncomment this line to define a global platform for your project 5 | # platform :ios, '9.0' 6 | 7 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 8 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 9 | 10 | project 'Runner', { 11 | 'Debug' => :debug, 12 | 'Profile' => :release, 13 | 'Release' => :release, 14 | } 15 | 16 | pod 'Firebase/Analytics' 17 | 18 | def parse_KV_file(file, separator='=') 19 | file_abs_path = File.expand_path(file) 20 | if !File.exists? file_abs_path 21 | return []; 22 | end 23 | pods_ary = [] 24 | skip_line_start_symbols = ["#", "/"] 25 | File.foreach(file_abs_path) { |line| 26 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 27 | plugin = line.split(pattern=separator) 28 | if plugin.length == 2 29 | podname = plugin[0].strip() 30 | path = plugin[1].strip() 31 | podpath = File.expand_path("#{path}", file_abs_path) 32 | pods_ary.push({:name => podname, :path => podpath}); 33 | else 34 | puts "Invalid plugin specification: #{line}" 35 | end 36 | } 37 | return pods_ary 38 | end 39 | 40 | target 'Runner' do 41 | 42 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 43 | # referring to absolute paths on developers' machines. 44 | system('rm -rf .symlinks') 45 | system('mkdir -p .symlinks/plugins') 46 | 47 | # Flutter Pods 48 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 49 | if generated_xcode_build_settings.empty? 50 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first." 51 | end 52 | generated_xcode_build_settings.map { |p| 53 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 54 | symlink = File.join('.symlinks', 'flutter') 55 | File.symlink(File.dirname(p[:path]), symlink) 56 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 57 | end 58 | } 59 | 60 | # Plugin Pods 61 | plugin_pods = parse_KV_file('../.flutter-plugins') 62 | plugin_pods.map { |p| 63 | symlink = File.join('.symlinks', 'plugins', p[:name]) 64 | File.symlink(p[:path], symlink) 65 | pod p[:name], :path => File.join(symlink, 'ios') 66 | } 67 | end 68 | 69 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 70 | install! 'cocoapods', :disable_input_output_paths => true 71 | 72 | post_install do |installer| 73 | installer.pods_project.targets.each do |target| 74 | target.build_configurations.each do |config| 75 | config.build_settings['ENABLE_BITCODE'] = 'NO' 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - camera (0.0.1): 3 | - Flutter 4 | - Firebase/Analytics (6.11.0): 5 | - Firebase/Core 6 | - Firebase/Core (6.11.0): 7 | - Firebase/CoreOnly 8 | - FirebaseAnalytics (= 6.1.3) 9 | - Firebase/CoreOnly (6.11.0): 10 | - FirebaseCore (= 6.3.2) 11 | - FirebaseAnalytics (6.1.3): 12 | - FirebaseCore (~> 6.3) 13 | - FirebaseInstanceID (~> 4.2) 14 | - GoogleAppMeasurement (= 6.1.3) 15 | - GoogleUtilities/AppDelegateSwizzler (~> 6.0) 16 | - GoogleUtilities/MethodSwizzler (~> 6.0) 17 | - GoogleUtilities/Network (~> 6.0) 18 | - "GoogleUtilities/NSData+zlib (~> 6.0)" 19 | - nanopb (~> 0.3.901) 20 | - FirebaseCore (6.3.2): 21 | - FirebaseCoreDiagnostics (~> 1.0) 22 | - FirebaseCoreDiagnosticsInterop (~> 1.0) 23 | - GoogleUtilities/Environment (~> 6.2) 24 | - GoogleUtilities/Logger (~> 6.2) 25 | - FirebaseCoreDiagnostics (1.1.1): 26 | - FirebaseCoreDiagnosticsInterop (~> 1.0) 27 | - GoogleDataTransportCCTSupport (~> 1.0) 28 | - GoogleUtilities/Environment (~> 6.2) 29 | - GoogleUtilities/Logger (~> 6.2) 30 | - nanopb (~> 0.3.901) 31 | - FirebaseCoreDiagnosticsInterop (1.0.0) 32 | - FirebaseInstanceID (4.2.6): 33 | - FirebaseCore (~> 6.0) 34 | - GoogleUtilities/Environment (~> 6.0) 35 | - GoogleUtilities/UserDefaults (~> 6.0) 36 | - Flutter (1.0.0) 37 | - GoogleAppMeasurement (6.1.3): 38 | - GoogleUtilities/AppDelegateSwizzler (~> 6.0) 39 | - GoogleUtilities/MethodSwizzler (~> 6.0) 40 | - GoogleUtilities/Network (~> 6.0) 41 | - "GoogleUtilities/NSData+zlib (~> 6.0)" 42 | - nanopb (~> 0.3.901) 43 | - GoogleDataTransport (3.0.1) 44 | - GoogleDataTransportCCTSupport (1.2.1): 45 | - GoogleDataTransport (~> 3.0) 46 | - nanopb (~> 0.3.901) 47 | - GoogleUtilities/AppDelegateSwizzler (6.3.1): 48 | - GoogleUtilities/Environment 49 | - GoogleUtilities/Logger 50 | - GoogleUtilities/Network 51 | - GoogleUtilities/Environment (6.3.1) 52 | - GoogleUtilities/Logger (6.3.1): 53 | - GoogleUtilities/Environment 54 | - GoogleUtilities/MethodSwizzler (6.3.1): 55 | - GoogleUtilities/Logger 56 | - GoogleUtilities/Network (6.3.1): 57 | - GoogleUtilities/Logger 58 | - "GoogleUtilities/NSData+zlib" 59 | - GoogleUtilities/Reachability 60 | - "GoogleUtilities/NSData+zlib (6.3.1)" 61 | - GoogleUtilities/Reachability (6.3.1): 62 | - GoogleUtilities/Logger 63 | - GoogleUtilities/UserDefaults (6.3.1): 64 | - GoogleUtilities/Logger 65 | - image_gallery_saver (0.0.1): 66 | - Flutter 67 | - image_picker_saver (0.0.1): 68 | - Flutter 69 | - nanopb (0.3.904): 70 | - nanopb/decode (= 0.3.904) 71 | - nanopb/encode (= 0.3.904) 72 | - nanopb/decode (0.3.904) 73 | - nanopb/encode (0.3.904) 74 | - path_provider (0.0.1): 75 | - Flutter 76 | - shared_preferences (0.0.1): 77 | - Flutter 78 | - video_player (0.0.1): 79 | - Flutter 80 | 81 | DEPENDENCIES: 82 | - camera (from `.symlinks/plugins/camera/ios`) 83 | - Firebase/Analytics 84 | - Flutter (from `.symlinks/flutter/ios`) 85 | - image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`) 86 | - image_picker_saver (from `.symlinks/plugins/image_picker_saver/ios`) 87 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 88 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 89 | - video_player (from `.symlinks/plugins/video_player/ios`) 90 | 91 | SPEC REPOS: 92 | trunk: 93 | - Firebase 94 | - FirebaseAnalytics 95 | - FirebaseCore 96 | - FirebaseCoreDiagnostics 97 | - FirebaseCoreDiagnosticsInterop 98 | - FirebaseInstanceID 99 | - GoogleAppMeasurement 100 | - GoogleDataTransport 101 | - GoogleDataTransportCCTSupport 102 | - GoogleUtilities 103 | - nanopb 104 | 105 | EXTERNAL SOURCES: 106 | camera: 107 | :path: ".symlinks/plugins/camera/ios" 108 | Flutter: 109 | :path: ".symlinks/flutter/ios" 110 | image_gallery_saver: 111 | :path: ".symlinks/plugins/image_gallery_saver/ios" 112 | image_picker_saver: 113 | :path: ".symlinks/plugins/image_picker_saver/ios" 114 | path_provider: 115 | :path: ".symlinks/plugins/path_provider/ios" 116 | shared_preferences: 117 | :path: ".symlinks/plugins/shared_preferences/ios" 118 | video_player: 119 | :path: ".symlinks/plugins/video_player/ios" 120 | 121 | SPEC CHECKSUMS: 122 | camera: 38cc83ae9a5667bb5a71c7d9edaf60a91920fd4e 123 | Firebase: bc9cfc7a96c73268656d5aaab453ff1b4b530e0e 124 | FirebaseAnalytics: 0e3ecff2c5d86070f7d4325e21f1edabfbd558dc 125 | FirebaseCore: beeff42c07c30ea94702471d99db2089b594fbbd 126 | FirebaseCoreDiagnostics: af29e43048607588c050889d19204f4d7b758c9f 127 | FirebaseCoreDiagnosticsInterop: 6829da2b8d1fc795ff1bd99df751d3788035d2cb 128 | FirebaseInstanceID: d0eafcd8bdbd3447cd694594734078c3e3e77d8b 129 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 130 | GoogleAppMeasurement: 434cc7be25e71dc04b8d0e3079125127b330e84a 131 | GoogleDataTransport: 166f9b9f82cbf60a204e8fe2daa9db3e3ec1fb15 132 | GoogleDataTransportCCTSupport: f6ab1962e9dc05ab1fb938b795e5b310209edeec 133 | GoogleUtilities: f895fde57977df4e0233edda0dbeac490e3703b6 134 | image_gallery_saver: 73b3cd8ad9c950c739878af9c311744d1bf5405d 135 | image_picker_saver: 4f28bd70e1efdca68ad88beab0f11d22cffe04f6 136 | nanopb: 06f6030d554e6473f5e172460173fcf80f5548f4 137 | path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d 138 | shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523 139 | video_player: 3964090a33353060ed7f58aa6427c7b4b208ec21 140 | 141 | PODFILE CHECKSUM: 4f0d2701c4ba58cfe1b30e95dda891f7c336a7fe 142 | 143 | COCOAPODS: 1.8.3 144 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 362629AB2361F7C300E3C8A6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 362629AA2361F7C300E3C8A6 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 604C5850D878DB6F044AEF77 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 722C26CDBD3C591472F82773 /* Pods_Runner.framework */; }; 16 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 17 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 18 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 19 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 34 | ); 35 | name = "Embed Frameworks"; 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 1D316F3E638415790BE1439B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 44 | 362629AA2361F7C300E3C8A6 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "../../../../Downloads/GoogleService-Info.plist"; sourceTree = ""; }; 45 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 46 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 47 | 4019BE41A61CCF7EF6BB2649 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 48 | 54C773278ECE4CC8A8870A9C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 49 | 722C26CDBD3C591472F82773 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 51 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 53 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 54 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 55 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 56 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 58 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 59 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 60 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 69 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 70 | 604C5850D878DB6F044AEF77 /* Pods_Runner.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 8423D0A36C799C525D07A0EF /* Frameworks */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 722C26CDBD3C591472F82773 /* Pods_Runner.framework */, 81 | ); 82 | name = Frameworks; 83 | sourceTree = ""; 84 | }; 85 | 9740EEB11CF90186004384FC /* Flutter */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3B80C3931E831B6300D905FE /* App.framework */, 89 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 90 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 91 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 92 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 93 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 94 | ); 95 | name = Flutter; 96 | sourceTree = ""; 97 | }; 98 | 97C146E51CF9000F007C117D = { 99 | isa = PBXGroup; 100 | children = ( 101 | 9740EEB11CF90186004384FC /* Flutter */, 102 | 97C146F01CF9000F007C117D /* Runner */, 103 | 97C146EF1CF9000F007C117D /* Products */, 104 | CDEBBB0FDF15C16076542746 /* Pods */, 105 | 8423D0A36C799C525D07A0EF /* Frameworks */, 106 | ); 107 | sourceTree = ""; 108 | }; 109 | 97C146EF1CF9000F007C117D /* Products */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 97C146EE1CF9000F007C117D /* Runner.app */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 97C146F01CF9000F007C117D /* Runner */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 362629AA2361F7C300E3C8A6 /* GoogleService-Info.plist */, 121 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 122 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 123 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 124 | 97C147021CF9000F007C117D /* Info.plist */, 125 | 97C146F11CF9000F007C117D /* Supporting Files */, 126 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 127 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 128 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 129 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 130 | ); 131 | path = Runner; 132 | sourceTree = ""; 133 | }; 134 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | CDEBBB0FDF15C16076542746 /* Pods */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 4019BE41A61CCF7EF6BB2649 /* Pods-Runner.debug.xcconfig */, 145 | 1D316F3E638415790BE1439B /* Pods-Runner.release.xcconfig */, 146 | 54C773278ECE4CC8A8870A9C /* Pods-Runner.profile.xcconfig */, 147 | ); 148 | path = Pods; 149 | sourceTree = ""; 150 | }; 151 | /* End PBXGroup section */ 152 | 153 | /* Begin PBXNativeTarget section */ 154 | 97C146ED1CF9000F007C117D /* Runner */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 157 | buildPhases = ( 158 | E7D294A0D6B8678EA525B583 /* [CP] Check Pods Manifest.lock */, 159 | 9740EEB61CF901F6004384FC /* Run Script */, 160 | 97C146EA1CF9000F007C117D /* Sources */, 161 | 97C146EB1CF9000F007C117D /* Frameworks */, 162 | 97C146EC1CF9000F007C117D /* Resources */, 163 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 164 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 165 | E25D9A001ACCDFBD9456B371 /* [CP] Embed Pods Frameworks */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = Runner; 172 | productName = Runner; 173 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 174 | productType = "com.apple.product-type.application"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | 97C146E61CF9000F007C117D /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastUpgradeCheck = 1020; 183 | ORGANIZATIONNAME = "The Chromium Authors"; 184 | TargetAttributes = { 185 | 97C146ED1CF9000F007C117D = { 186 | CreatedOnToolsVersion = 7.3.1; 187 | DevelopmentTeam = 7DFM6K6KA8; 188 | LastSwiftMigration = 0910; 189 | }; 190 | }; 191 | }; 192 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 193 | compatibilityVersion = "Xcode 3.2"; 194 | developmentRegion = en; 195 | hasScannedForEncodings = 0; 196 | knownRegions = ( 197 | en, 198 | Base, 199 | ); 200 | mainGroup = 97C146E51CF9000F007C117D; 201 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 202 | projectDirPath = ""; 203 | projectRoot = ""; 204 | targets = ( 205 | 97C146ED1CF9000F007C117D /* Runner */, 206 | ); 207 | }; 208 | /* End PBXProject section */ 209 | 210 | /* Begin PBXResourcesBuildPhase section */ 211 | 97C146EC1CF9000F007C117D /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 216 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 217 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 218 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 219 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 220 | 362629AB2361F7C300E3C8A6 /* GoogleService-Info.plist in Resources */, 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXResourcesBuildPhase section */ 225 | 226 | /* Begin PBXShellScriptBuildPhase section */ 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 240 | }; 241 | 9740EEB61CF901F6004384FC /* Run Script */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputPaths = ( 247 | ); 248 | name = "Run Script"; 249 | outputPaths = ( 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | shellPath = /bin/sh; 253 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 254 | }; 255 | E25D9A001ACCDFBD9456B371 /* [CP] Embed Pods Frameworks */ = { 256 | isa = PBXShellScriptBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | ); 260 | inputPaths = ( 261 | ); 262 | name = "[CP] Embed Pods Frameworks"; 263 | outputPaths = ( 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | shellPath = /bin/sh; 267 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 268 | showEnvVarsInLog = 0; 269 | }; 270 | E7D294A0D6B8678EA525B583 /* [CP] Check Pods Manifest.lock */ = { 271 | isa = PBXShellScriptBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | ); 275 | inputFileListPaths = ( 276 | ); 277 | inputPaths = ( 278 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 279 | "${PODS_ROOT}/Manifest.lock", 280 | ); 281 | name = "[CP] Check Pods Manifest.lock"; 282 | outputFileListPaths = ( 283 | ); 284 | outputPaths = ( 285 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | shellPath = /bin/sh; 289 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 290 | showEnvVarsInLog = 0; 291 | }; 292 | /* End PBXShellScriptBuildPhase section */ 293 | 294 | /* Begin PBXSourcesBuildPhase section */ 295 | 97C146EA1CF9000F007C117D /* Sources */ = { 296 | isa = PBXSourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 300 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 301 | ); 302 | runOnlyForDeploymentPostprocessing = 0; 303 | }; 304 | /* End PBXSourcesBuildPhase section */ 305 | 306 | /* Begin PBXVariantGroup section */ 307 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 308 | isa = PBXVariantGroup; 309 | children = ( 310 | 97C146FB1CF9000F007C117D /* Base */, 311 | ); 312 | name = Main.storyboard; 313 | sourceTree = ""; 314 | }; 315 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 316 | isa = PBXVariantGroup; 317 | children = ( 318 | 97C147001CF9000F007C117D /* Base */, 319 | ); 320 | name = LaunchScreen.storyboard; 321 | sourceTree = ""; 322 | }; 323 | /* End PBXVariantGroup section */ 324 | 325 | /* Begin XCBuildConfiguration section */ 326 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | ALWAYS_SEARCH_USER_PATHS = NO; 330 | CLANG_ANALYZER_NONNULL = YES; 331 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 332 | CLANG_CXX_LIBRARY = "libc++"; 333 | CLANG_ENABLE_MODULES = YES; 334 | CLANG_ENABLE_OBJC_ARC = YES; 335 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 336 | CLANG_WARN_BOOL_CONVERSION = YES; 337 | CLANG_WARN_COMMA = YES; 338 | CLANG_WARN_CONSTANT_CONVERSION = YES; 339 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 340 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 341 | CLANG_WARN_EMPTY_BODY = YES; 342 | CLANG_WARN_ENUM_CONVERSION = YES; 343 | CLANG_WARN_INFINITE_RECURSION = YES; 344 | CLANG_WARN_INT_CONVERSION = YES; 345 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 347 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 348 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 349 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 350 | CLANG_WARN_STRICT_PROTOTYPES = YES; 351 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 352 | CLANG_WARN_UNREACHABLE_CODE = YES; 353 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 354 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 355 | COPY_PHASE_STRIP = NO; 356 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 357 | ENABLE_NS_ASSERTIONS = NO; 358 | ENABLE_STRICT_OBJC_MSGSEND = YES; 359 | GCC_C_LANGUAGE_STANDARD = gnu99; 360 | GCC_NO_COMMON_BLOCKS = YES; 361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 362 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 363 | GCC_WARN_UNDECLARED_SELECTOR = YES; 364 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 365 | GCC_WARN_UNUSED_FUNCTION = YES; 366 | GCC_WARN_UNUSED_VARIABLE = YES; 367 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 368 | MTL_ENABLE_DEBUG_INFO = NO; 369 | SDKROOT = iphoneos; 370 | TARGETED_DEVICE_FAMILY = "1,2"; 371 | VALIDATE_PRODUCT = YES; 372 | }; 373 | name = Profile; 374 | }; 375 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 376 | isa = XCBuildConfiguration; 377 | baseConfigurationReference = 54C773278ECE4CC8A8870A9C /* Pods-Runner.profile.xcconfig */; 378 | buildSettings = { 379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 380 | CLANG_ENABLE_MODULES = YES; 381 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 382 | DEVELOPMENT_TEAM = 7DFM6K6KA8; 383 | ENABLE_BITCODE = NO; 384 | FLUTTER_ROOT = $HOME/development/flutter; 385 | FRAMEWORK_SEARCH_PATHS = ( 386 | "$(inherited)", 387 | "$(PROJECT_DIR)/Flutter", 388 | ); 389 | INFOPLIST_FILE = Runner/Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 391 | LIBRARY_SEARCH_PATHS = ( 392 | "$(inherited)", 393 | "$(PROJECT_DIR)/Flutter", 394 | ); 395 | PODS_BUILD_DIR = "$(inherited)"; 396 | PRODUCT_BUNDLE_IDENTIFIER = com.example.douyinDemo; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 399 | SWIFT_VERSION = 4.0; 400 | VERSIONING_SYSTEM = "apple-generic"; 401 | }; 402 | name = Profile; 403 | }; 404 | 97C147031CF9000F007C117D /* Debug */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_ANALYZER_NONNULL = YES; 409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 410 | CLANG_CXX_LIBRARY = "libc++"; 411 | CLANG_ENABLE_MODULES = YES; 412 | CLANG_ENABLE_OBJC_ARC = YES; 413 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 414 | CLANG_WARN_BOOL_CONVERSION = YES; 415 | CLANG_WARN_COMMA = YES; 416 | CLANG_WARN_CONSTANT_CONVERSION = YES; 417 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 418 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 419 | CLANG_WARN_EMPTY_BODY = YES; 420 | CLANG_WARN_ENUM_CONVERSION = YES; 421 | CLANG_WARN_INFINITE_RECURSION = YES; 422 | CLANG_WARN_INT_CONVERSION = YES; 423 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 424 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 425 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 426 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 427 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 428 | CLANG_WARN_STRICT_PROTOTYPES = YES; 429 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 430 | CLANG_WARN_UNREACHABLE_CODE = YES; 431 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 432 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 433 | COPY_PHASE_STRIP = NO; 434 | DEBUG_INFORMATION_FORMAT = dwarf; 435 | ENABLE_STRICT_OBJC_MSGSEND = YES; 436 | ENABLE_TESTABILITY = YES; 437 | GCC_C_LANGUAGE_STANDARD = gnu99; 438 | GCC_DYNAMIC_NO_PIC = NO; 439 | GCC_NO_COMMON_BLOCKS = YES; 440 | GCC_OPTIMIZATION_LEVEL = 0; 441 | GCC_PREPROCESSOR_DEFINITIONS = ( 442 | "DEBUG=1", 443 | "$(inherited)", 444 | ); 445 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 446 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 447 | GCC_WARN_UNDECLARED_SELECTOR = YES; 448 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 449 | GCC_WARN_UNUSED_FUNCTION = YES; 450 | GCC_WARN_UNUSED_VARIABLE = YES; 451 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 452 | MTL_ENABLE_DEBUG_INFO = YES; 453 | ONLY_ACTIVE_ARCH = YES; 454 | SDKROOT = iphoneos; 455 | TARGETED_DEVICE_FAMILY = "1,2"; 456 | }; 457 | name = Debug; 458 | }; 459 | 97C147041CF9000F007C117D /* Release */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | ALWAYS_SEARCH_USER_PATHS = NO; 463 | CLANG_ANALYZER_NONNULL = YES; 464 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 465 | CLANG_CXX_LIBRARY = "libc++"; 466 | CLANG_ENABLE_MODULES = YES; 467 | CLANG_ENABLE_OBJC_ARC = YES; 468 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 469 | CLANG_WARN_BOOL_CONVERSION = YES; 470 | CLANG_WARN_COMMA = YES; 471 | CLANG_WARN_CONSTANT_CONVERSION = YES; 472 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 473 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 474 | CLANG_WARN_EMPTY_BODY = YES; 475 | CLANG_WARN_ENUM_CONVERSION = YES; 476 | CLANG_WARN_INFINITE_RECURSION = YES; 477 | CLANG_WARN_INT_CONVERSION = YES; 478 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 479 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 480 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 481 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 482 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 483 | CLANG_WARN_STRICT_PROTOTYPES = YES; 484 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 485 | CLANG_WARN_UNREACHABLE_CODE = YES; 486 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 487 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 488 | COPY_PHASE_STRIP = NO; 489 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 490 | ENABLE_NS_ASSERTIONS = NO; 491 | ENABLE_STRICT_OBJC_MSGSEND = YES; 492 | GCC_C_LANGUAGE_STANDARD = gnu99; 493 | GCC_NO_COMMON_BLOCKS = YES; 494 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 495 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 496 | GCC_WARN_UNDECLARED_SELECTOR = YES; 497 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 498 | GCC_WARN_UNUSED_FUNCTION = YES; 499 | GCC_WARN_UNUSED_VARIABLE = YES; 500 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 501 | MTL_ENABLE_DEBUG_INFO = NO; 502 | SDKROOT = iphoneos; 503 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 504 | TARGETED_DEVICE_FAMILY = "1,2"; 505 | VALIDATE_PRODUCT = YES; 506 | }; 507 | name = Release; 508 | }; 509 | 97C147061CF9000F007C117D /* Debug */ = { 510 | isa = XCBuildConfiguration; 511 | baseConfigurationReference = 4019BE41A61CCF7EF6BB2649 /* Pods-Runner.debug.xcconfig */; 512 | buildSettings = { 513 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 514 | CLANG_ENABLE_MODULES = YES; 515 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 516 | DEVELOPMENT_TEAM = 7DFM6K6KA8; 517 | ENABLE_BITCODE = NO; 518 | FLUTTER_ROOT = $HOME/development/flutter; 519 | FRAMEWORK_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "$(PROJECT_DIR)/Flutter", 522 | ); 523 | INFOPLIST_FILE = Runner/Info.plist; 524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 525 | LIBRARY_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "$(PROJECT_DIR)/Flutter", 528 | ); 529 | PODS_BUILD_DIR = "$(inherited)"; 530 | PRODUCT_BUNDLE_IDENTIFIER = com.example.douyinDemo; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 533 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 534 | SWIFT_VERSION = 4.0; 535 | VERSIONING_SYSTEM = "apple-generic"; 536 | }; 537 | name = Debug; 538 | }; 539 | 97C147071CF9000F007C117D /* Release */ = { 540 | isa = XCBuildConfiguration; 541 | baseConfigurationReference = 1D316F3E638415790BE1439B /* Pods-Runner.release.xcconfig */; 542 | buildSettings = { 543 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 544 | CLANG_ENABLE_MODULES = YES; 545 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 546 | DEVELOPMENT_TEAM = 7DFM6K6KA8; 547 | ENABLE_BITCODE = NO; 548 | FLUTTER_ROOT = $HOME/development/flutter; 549 | FRAMEWORK_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "$(PROJECT_DIR)/Flutter", 552 | ); 553 | INFOPLIST_FILE = Runner/Info.plist; 554 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 555 | LIBRARY_SEARCH_PATHS = ( 556 | "$(inherited)", 557 | "$(PROJECT_DIR)/Flutter", 558 | ); 559 | PODS_BUILD_DIR = "$(inherited)"; 560 | PRODUCT_BUNDLE_IDENTIFIER = com.example.douyinDemo; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 563 | SWIFT_VERSION = 4.0; 564 | VERSIONING_SYSTEM = "apple-generic"; 565 | }; 566 | name = Release; 567 | }; 568 | /* End XCBuildConfiguration section */ 569 | 570 | /* Begin XCConfigurationList section */ 571 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 97C147031CF9000F007C117D /* Debug */, 575 | 97C147041CF9000F007C117D /* Release */, 576 | 249021D3217E4FDB00AE95B9 /* Profile */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 97C147061CF9000F007C117D /* Debug */, 585 | 97C147071CF9000F007C117D /* Release */, 586 | 249021D4217E4FDB00AE95B9 /* Profile */, 587 | ); 588 | defaultConfigurationIsVisible = 0; 589 | defaultConfigurationName = Release; 590 | }; 591 | /* End XCConfigurationList section */ 592 | }; 593 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 594 | } 595 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import Firebase 4 | // 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | FirebaseApp.configure() 12 | GeneratedPluginRegistrant.register(with: self) 13 | 14 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 15 | } 16 | } 17 | 18 | 19 | //@UIApplicationMain 20 | //class AppDelegate: UIResponder, UIApplicationDelegate { 21 | // 22 | // var window: UIWindow? 23 | // 24 | // func application(_ application: UIApplication, 25 | // didFinishLaunchingWithOptions launchOptions: 26 | // [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 27 | // FirebaseApp.configure() 28 | // 29 | // return true 30 | // } 31 | //} 32 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info .plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 416855912244-nqmupjevgtbf7ifcjfv42uocjdv5c77f.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.416855912244-nqmupjevgtbf7ifcjfv42uocjdv5c77f 9 | API_KEY 10 | AIzaSyCSAI7AIcmi8285VKOBPKnLjh0rH0WRRQ4 11 | GCM_SENDER_ID 12 | 416855912244 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.example.douyinDemo 17 | PROJECT_ID 18 | douyindemo 19 | STORAGE_BUCKET 20 | douyindemo.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:416855912244:ios:966e409b00d2d6762dff61 33 | DATABASE_URL 34 | https://douyindemo.firebaseio.com 35 | 36 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | douyin_demo 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | douyinDemo 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 0.01 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | NSCameraUsageDescription 47 | 系统将使用您的相机进行图片拍摄,视频拍摄等 48 | NSMicrophoneUsageDescription 49 | 系统将使用您的麦克风进行音频录制 50 | NSPhotoLibraryUsageDescription 51 | 我们将存储照片至您的相册以及从相册获取图片信息 52 | NSAppTransportSecurity 53 | 54 | NSAllowsArbitraryLoads 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/detector_painters.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Chromium Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | import 'dart:ui' as ui; 6 | 7 | import 'package:firebase_ml_vision/firebase_ml_vision.dart'; 8 | import 'package:flutter/foundation.dart'; 9 | import 'package:flutter/material.dart'; 10 | 11 | enum Detector { barcode, face, label, cloudLabel, text } 12 | 13 | class BarcodeDetectorPainter extends CustomPainter { 14 | BarcodeDetectorPainter(this.imageSize, this.barcodes); 15 | 16 | final Size imageSize; 17 | final List barcodes; 18 | 19 | @override 20 | void paint(Canvas canvas, Size size) { 21 | final Paint paint = Paint() 22 | ..style = PaintingStyle.stroke 23 | ..strokeWidth = 2.0; 24 | 25 | for (Barcode barcode in barcodes) { 26 | paint.color = Colors.green; 27 | canvas.drawRect( 28 | _scaleRect( 29 | rect: barcode.boundingBox, 30 | imageSize: imageSize, 31 | widgetSize: size, 32 | ), 33 | paint, 34 | ); 35 | } 36 | } 37 | 38 | @override 39 | bool shouldRepaint(BarcodeDetectorPainter oldDelegate) { 40 | return oldDelegate.imageSize != imageSize || 41 | oldDelegate.barcodes != barcodes; 42 | } 43 | } 44 | 45 | class FaceDetectorPainter extends CustomPainter { 46 | FaceDetectorPainter(this.imageSize, this.faces); 47 | 48 | final Size imageSize; 49 | final List faces; 50 | 51 | @override 52 | void paint(Canvas canvas, Size size) { 53 | final Paint paint = Paint() 54 | ..style = PaintingStyle.stroke 55 | ..strokeWidth = 2.0 56 | ..color = Colors.red; 57 | 58 | for (Face face in faces) { 59 | canvas.drawRect( 60 | _scaleRect( 61 | rect: face.boundingBox, 62 | imageSize: imageSize, 63 | widgetSize: size, 64 | ), 65 | paint, 66 | ); 67 | } 68 | } 69 | 70 | @override 71 | bool shouldRepaint(FaceDetectorPainter oldDelegate) { 72 | return oldDelegate.imageSize != imageSize || oldDelegate.faces != faces; 73 | } 74 | } 75 | 76 | class LabelDetectorPainter extends CustomPainter { 77 | LabelDetectorPainter(this.imageSize, this.labels); 78 | 79 | final Size imageSize; 80 | final List labels; 81 | 82 | @override 83 | void paint(Canvas canvas, Size size) { 84 | final ui.ParagraphBuilder builder = ui.ParagraphBuilder( 85 | ui.ParagraphStyle( 86 | textAlign: TextAlign.left, 87 | fontSize: 23.0, 88 | textDirection: TextDirection.ltr), 89 | ); 90 | 91 | builder.pushStyle(ui.TextStyle(color: Colors.green)); 92 | // for (Label label in labels) { 93 | // builder.addText('Label: ${label.label}, ' 94 | // 'Confidence: ${label.confidence.toStringAsFixed(2)}\n'); 95 | // } 96 | builder.pop(); 97 | 98 | canvas.drawParagraph( 99 | builder.build() 100 | ..layout(ui.ParagraphConstraints( 101 | width: size.width, 102 | )), 103 | const Offset(0.0, 0.0), 104 | ); 105 | } 106 | 107 | @override 108 | bool shouldRepaint(LabelDetectorPainter oldDelegate) { 109 | return oldDelegate.imageSize != imageSize || oldDelegate.labels != labels; 110 | } 111 | } 112 | 113 | // Paints rectangles around all the text in the image. 114 | class TextDetectorPainter extends CustomPainter { 115 | TextDetectorPainter(this.imageSize, this.visionText); 116 | 117 | final Size imageSize; 118 | final VisionText visionText; 119 | 120 | @override 121 | void paint(Canvas canvas, Size size) { 122 | final Paint paint = Paint() 123 | ..style = PaintingStyle.stroke 124 | ..strokeWidth = 2.0; 125 | 126 | Rect _getRect(TextContainer container) { 127 | return _scaleRect( 128 | rect: container.boundingBox, 129 | imageSize: imageSize, 130 | widgetSize: size, 131 | ); 132 | } 133 | 134 | for (TextBlock block in visionText.blocks) { 135 | for (TextLine line in block.lines) { 136 | for (TextElement element in line.elements) { 137 | paint.color = Colors.green; 138 | canvas.drawRect(_getRect(element), paint); 139 | } 140 | 141 | paint.color = Colors.yellow; 142 | canvas.drawRect(_getRect(line), paint); 143 | } 144 | 145 | paint.color = Colors.red; 146 | canvas.drawRect(_getRect(block), paint); 147 | } 148 | } 149 | 150 | @override 151 | bool shouldRepaint(TextDetectorPainter oldDelegate) { 152 | return oldDelegate.imageSize != imageSize || 153 | oldDelegate.visionText != visionText; 154 | } 155 | } 156 | 157 | Rect _scaleRect({ 158 | @required Rect rect, 159 | @required Size imageSize, 160 | @required Size widgetSize, 161 | }) { 162 | final double scaleX = widgetSize.width / imageSize.width; 163 | final double scaleY = widgetSize.height / imageSize.height; 164 | 165 | return Rect.fromLTRB( 166 | rect.left.toDouble() * scaleX, 167 | rect.top.toDouble() * scaleY, 168 | rect.right.toDouble() * scaleX, 169 | rect.bottom.toDouble() * scaleY, 170 | ); 171 | } -------------------------------------------------------------------------------- /lib/images/pingguo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/pingguo.jpeg -------------------------------------------------------------------------------- /lib/images/sky.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/sky.jpg -------------------------------------------------------------------------------- /lib/images/temple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/temple.jpg -------------------------------------------------------------------------------- /lib/images/waterdrop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/waterdrop.jpg -------------------------------------------------------------------------------- /lib/images/whitehouse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/whitehouse.jpg -------------------------------------------------------------------------------- /lib/images/woman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/woman.jpg -------------------------------------------------------------------------------- /lib/images/woman2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/woman2.jpg -------------------------------------------------------------------------------- /lib/images/zhifei.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todother/flutter_douyin_demo/948d51ccb6f238411c6fe29b2ed46a0d59fb4429/lib/images/zhifei.jpg -------------------------------------------------------------------------------- /lib/models/PostsModel.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | part 'PostsModel.g.dart'; 3 | 4 | @JsonSerializable() 5 | class PostsModel extends Object { 6 | String postsID; 7 | String postsContent; 8 | String postsMaker; 9 | DateTime postsMakeDate; 10 | int postsPicCount; 11 | int postsReaded; 12 | int postsLoved; 13 | String postsPics; 14 | String makerName; 15 | String makerID; 16 | String picsSimpPath; 17 | String picsPath; 18 | String whenPosts; 19 | String makerPhoto; 20 | double latitude; 21 | double longitude; 22 | String postsLocation; 23 | String postsType; 24 | int postsStatus; 25 | int ifOfficial; 26 | int ifLY; 27 | double picsRate; 28 | bool ifUserLoved; 29 | 30 | PostsModel(this.postsID,this.postsContent,this.postsMaker,this.postsMakeDate,this.postsPicCount,this.postsReaded,this.postsLoved,this.postsPics,this.ifLY 31 | ,this.ifOfficial,this.ifUserLoved,this.latitude,this.longitude,this.makerID,this.makerName,this.makerPhoto,this.picsPath,this.picsRate,this.picsSimpPath,this.postsLocation,this.postsStatus 32 | ,this.postsType,this.whenPosts); 33 | 34 | 35 | factory PostsModel.fromJson(Map json) => _$PostsModelFromJson(json); 36 | Map toJson() => _$PostsModelToJson(this); 37 | } -------------------------------------------------------------------------------- /lib/models/PostsModel.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'PostsModel.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | PostsModel _$PostsModelFromJson(Map json) { 10 | return PostsModel( 11 | json['postsID'] as String, 12 | json['postsContent'] as String, 13 | json['postsMaker'] as String, 14 | json['postsMakeDate'] == null 15 | ? null 16 | : DateTime.parse(json['postsMakeDate'] as String), 17 | json['postsPicCount'] as int, 18 | json['postsReaded'] as int, 19 | json['postsLoved'] as int, 20 | json['postsPics'] as String, 21 | json['ifLY'] as int, 22 | json['ifOfficial'] as int, 23 | json['ifUserLoved'] as bool, 24 | (json['latitude'] as num)?.toDouble(), 25 | (json['longitude'] as num)?.toDouble(), 26 | json['makerID'] as String, 27 | json['makerName'] as String, 28 | json['makerPhoto'] as String, 29 | json['picsPath'] as String, 30 | (json['picsRate'] as num)?.toDouble(), 31 | json['picsSimpPath'] as String, 32 | json['postsLocation'] as String, 33 | json['postsStatus'] as int, 34 | json['postsType'] as String, 35 | json['whenPosts'] as String, 36 | ); 37 | } 38 | 39 | Map _$PostsModelToJson(PostsModel instance) => 40 | { 41 | 'postsID': instance.postsID, 42 | 'postsContent': instance.postsContent, 43 | 'postsMaker': instance.postsMaker, 44 | 'postsMakeDate': instance.postsMakeDate?.toIso8601String(), 45 | 'postsPicCount': instance.postsPicCount, 46 | 'postsReaded': instance.postsReaded, 47 | 'postsLoved': instance.postsLoved, 48 | 'postsPics': instance.postsPics, 49 | 'makerName': instance.makerName, 50 | 'makerID': instance.makerID, 51 | 'picsSimpPath': instance.picsSimpPath, 52 | 'picsPath': instance.picsPath, 53 | 'whenPosts': instance.whenPosts, 54 | 'makerPhoto': instance.makerPhoto, 55 | 'latitude': instance.latitude, 56 | 'longitude': instance.longitude, 57 | 'postsLocation': instance.postsLocation, 58 | 'postsType': instance.postsType, 59 | 'postsStatus': instance.postsStatus, 60 | 'ifOfficial': instance.ifOfficial, 61 | 'ifLY': instance.ifLY, 62 | 'picsRate': instance.picsRate, 63 | 'ifUserLoved': instance.ifUserLoved, 64 | }; 65 | -------------------------------------------------------------------------------- /lib/pages/CameraPage/CameraMain.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | 4 | import 'package:camera/camera.dart'; 5 | import 'package:camera/new/src/support_android/camera.dart'; 6 | import 'package:douyin_demo/providers/CameraProvider.dart'; 7 | import 'package:firebase_ml_vision/firebase_ml_vision.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:image_gallery_saver/image_gallery_saver.dart'; 10 | import 'package:provider/provider.dart'; 11 | import 'package:path/path.dart' as p; 12 | import 'package:image_picker_saver/image_picker_saver.dart'; 13 | 14 | class CameraPage extends StatelessWidget { 15 | const CameraPage({Key key}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | backgroundColor: Theme.of(context).primaryColor, 21 | body: CameraMain( 22 | rpx: MediaQuery.of(context).size.width / 750, 23 | ), 24 | bottomNavigationBar: BottomAppBar(), 25 | ); 26 | } 27 | } 28 | 29 | class CameraMain extends StatefulWidget { 30 | CameraMain({Key key, @required this.rpx}) : super(key: key); 31 | final double rpx; 32 | @override 33 | _CameraMainState createState() => _CameraMainState(); 34 | } 35 | 36 | class _CameraMainState extends State { 37 | CameraProvider provider; 38 | double rpx; 39 | double toTop; 40 | double outBox; 41 | double innerBox; 42 | CameraController _controller; 43 | bool findFace = false; 44 | var cameras; 45 | @override 46 | void initState() { 47 | super.initState(); 48 | 49 | // provider = Provider.of(context); 50 | // getCameras(); 51 | rpx = widget.rpx; 52 | toTop = 100 * rpx; 53 | outBox = 170 * rpx; 54 | innerBox = 130 * rpx; 55 | } 56 | 57 | getCameras() async { 58 | cameras = await availableCameras(); 59 | _controller = CameraController(cameras[1], ResolutionPreset.medium); 60 | 61 | _controller.initialize().then((_) { 62 | if (!mounted) { 63 | return; 64 | } 65 | _controller.startImageStream((CameraImage availableImage) { 66 | _controller.stopImageStream(); 67 | _scanFrame(availableImage); 68 | }); 69 | 70 | setState(() {}); 71 | }); 72 | } 73 | 74 | void _scanFrame(CameraImage availableImage) async { 75 | 76 | final FirebaseVisionImageMetadata metadata = FirebaseVisionImageMetadata( 77 | rawFormat: availableImage.format.raw, 78 | size: Size( 79 | availableImage.width.toDouble(), availableImage.height.toDouble()), 80 | planeData: availableImage.planes 81 | .map((currentPlane) => FirebaseVisionImagePlaneMetadata( 82 | bytesPerRow: currentPlane.bytesPerRow, 83 | height: currentPlane.height, 84 | width: currentPlane.width)) 85 | .toList(), 86 | rotation: ImageRotation.rotation90); 87 | final FirebaseVisionImage visionImage = 88 | FirebaseVisionImage.fromBytes(availableImage.planes[0].bytes, metadata); 89 | final FaceDetector detector = FirebaseVision.instance.faceDetector(); 90 | final List faces = await detector.processImage(visionImage); 91 | 92 | if (faces.length > 0) { 93 | setState(() { 94 | findFace = true; 95 | _controller.startImageStream((CameraImage availableImage) { 96 | _controller.stopImageStream(); 97 | _scanFrame(availableImage); 98 | }); 99 | }); 100 | } else { 101 | setState(() { 102 | findFace = false; 103 | _controller.startImageStream((CameraImage availableImage) { 104 | _controller.stopImageStream(); 105 | _scanFrame(availableImage); 106 | }); 107 | }); 108 | } 109 | 110 | // for (TextBlock block in visionText.blocks) { 111 | 112 | // // final Rectangle boundingBox = block.boundingBox; 113 | // // final List> cornerPoints = block.cornerPoints; 114 | // print(block.text); 115 | // final List languages = block.recognizedLanguages; 116 | 117 | // for (TextLine line in block.lines) { 118 | // // Same getters as TextBlock 119 | // print(line.text); 120 | // for (TextElement element in line.elements) { 121 | // // Same getters as TextBlock 122 | // print(element.text); 123 | // } 124 | // } 125 | // } 126 | } 127 | 128 | @override 129 | void dispose() { 130 | // TODO: implement dispose 131 | _controller.dispose(); 132 | super.dispose(); 133 | } 134 | 135 | @override 136 | Widget build(BuildContext context) { 137 | provider = Provider.of(context); 138 | _controller=provider.cameraController; 139 | if (provider == null || _controller == null) { 140 | return Container( 141 | child: Center(child: CircularProgressIndicator()), 142 | ); 143 | } 144 | 145 | bool ifMakeVideo = provider.ifMakeVideo; 146 | if (_controller == null || _controller?.value == null) { 147 | return Container( 148 | child: Center(child: CircularProgressIndicator()), 149 | ); 150 | } 151 | final size = MediaQuery.of(context).size; 152 | return _controller.value.isInitialized 153 | ? Stack(children: [ 154 | // Camera.open(cameraId), 155 | findFace? Positioned( 156 | top: 0, 157 | left: 0, 158 | child: Container(width: 100,height: 100,color: Colors.red,) 159 | ):Container(), 160 | ClipRect( 161 | child: Transform.scale( 162 | scale: _controller.value.aspectRatio / size.aspectRatio, 163 | child: Center( 164 | child: AspectRatio( 165 | aspectRatio: _controller.value.aspectRatio, 166 | child: CameraPreview(_controller), 167 | ), 168 | ), 169 | )), 170 | Positioned( 171 | //顶部关闭按钮 172 | top: toTop, 173 | left: 30 * rpx, 174 | child: IconButton( 175 | icon: Icon( 176 | Icons.close, 177 | color: Colors.white, 178 | size: 60 * rpx, 179 | ), 180 | onPressed: () { 181 | Navigator.pop(context); 182 | }, 183 | ), 184 | ), 185 | Positioned( 186 | //选择音乐 187 | top: toTop, 188 | left: 250 * rpx, 189 | child: Container( 190 | width: 250 * rpx, 191 | child: FlatButton( 192 | onPressed: () {}, 193 | child: Row( 194 | children: [ 195 | Icon( 196 | Icons.music_note, 197 | color: Colors.white, 198 | ), 199 | SizedBox( 200 | width: 10 * rpx, 201 | ), 202 | Text( 203 | "选择音乐", 204 | style: TextStyle(color: Colors.white), 205 | ), 206 | ], 207 | ), 208 | ), 209 | ), 210 | ), 211 | Positioned( 212 | //拍照按钮 213 | bottom: 140 * rpx, 214 | // left: (750*rpx-outBox)/2, 215 | child: Container( 216 | width: 750 * rpx, 217 | child: Row( 218 | mainAxisAlignment: MainAxisAlignment.spaceAround, 219 | children: [ 220 | ifMakeVideo 221 | ? Container( 222 | width: 80 * rpx, 223 | ) 224 | : IconWithText( 225 | icon: Icon( 226 | Icons.search, 227 | color: Colors.white, 228 | ), 229 | text: "道具"), 230 | ifMakeVideo 231 | ? AnimVideoButton( 232 | rpx: rpx, 233 | outWidth: outBox, 234 | innerWidth: innerBox - 30 * rpx, 235 | provider: provider, 236 | ) 237 | : CircleTakePhoto( 238 | outBox: outBox, 239 | innerBox: innerBox, 240 | ), 241 | ifMakeVideo 242 | ? IconButton( 243 | padding: EdgeInsets.all(0), 244 | icon: Icon( 245 | Icons.check_circle, 246 | color: Color.fromARGB(255, 219, 48, 85), 247 | size: 80 * rpx, 248 | ), 249 | onPressed: () async { 250 | provider.cameraController 251 | .stopVideoRecording(); 252 | await ImageGallerySaver.saveFile( 253 | provider.fileName); 254 | File(provider.fileName).delete(); 255 | }, 256 | ) 257 | : IconWithText( 258 | icon: Icon( 259 | Icons.search, 260 | color: Colors.white, 261 | ), 262 | text: "道具"), 263 | ])), 264 | ), 265 | Positioned( 266 | bottom: 40 * rpx, 267 | child: ScrollBottomBar( 268 | rpx: rpx, 269 | ), 270 | ), 271 | Positioned( 272 | right: 30 * rpx, 273 | top: 80 * rpx, 274 | child: IconButton( 275 | icon: Icon(Icons.camera_front), 276 | onPressed: () { 277 | provider.changeCamera(); 278 | }), 279 | ) 280 | ]) 281 | : Container(); 282 | } 283 | } 284 | 285 | // class CameraMain extends StatelessWidget { 286 | // const CameraMain({Key key}) : super(key: key); 287 | 288 | // @override 289 | // Widget build(BuildContext context) { 290 | // CameraProvider provider = Provider.of(context); 291 | // if (provider == null || provider.cameraController == null) { 292 | // return Container( 293 | // child: Center(child: CircularProgressIndicator()), 294 | // ); 295 | // } 296 | // double rpx = MediaQuery.of(context).size.width / 750; 297 | // double toTop = 100 * rpx; 298 | // double outBox = 170 * rpx; 299 | // double innerBox = 130 * rpx; 300 | // CameraController _controller = provider.cameraController; 301 | // var cameras = provider.cameras; 302 | 303 | // bool ifMakeVideo = provider.ifMakeVideo; 304 | // if (_controller == null || _controller?.value == null) { 305 | // return Container( 306 | // child: Center(child: CircularProgressIndicator()), 307 | // ); 308 | // } 309 | // final size = MediaQuery.of(context).size; 310 | // return _controller.value.isInitialized 311 | // ? Stack(children: [ 312 | // // Camera.open(cameraId), 313 | 314 | // ClipRect( 315 | // child: Transform.scale( 316 | // scale: _controller.value.aspectRatio / size.aspectRatio, 317 | // child: Center( 318 | // child: AspectRatio( 319 | // aspectRatio: _controller.value.aspectRatio, 320 | // child: CameraPreview(_controller), 321 | // ), 322 | // ), 323 | // )), 324 | // Positioned( 325 | // //顶部关闭按钮 326 | // top: toTop, 327 | // left: 30 * rpx, 328 | // child: IconButton( 329 | // icon: Icon( 330 | // Icons.close, 331 | // color: Colors.white, 332 | // size: 60 * rpx, 333 | // ), 334 | // onPressed: () { 335 | // Navigator.pop(context); 336 | // }, 337 | // ), 338 | // ), 339 | // Positioned( 340 | // //选择音乐 341 | // top: toTop, 342 | // left: 250 * rpx, 343 | // child: Container( 344 | // width: 250 * rpx, 345 | // child: FlatButton( 346 | // onPressed: () {}, 347 | // child: Row( 348 | // children: [ 349 | // Icon( 350 | // Icons.music_note, 351 | // color: Colors.white, 352 | // ), 353 | // SizedBox( 354 | // width: 10 * rpx, 355 | // ), 356 | // Text( 357 | // "选择音乐", 358 | // style: TextStyle(color: Colors.white), 359 | // ), 360 | // ], 361 | // ), 362 | // ), 363 | // ), 364 | // ), 365 | // Positioned( 366 | // //拍照按钮 367 | // bottom: 140 * rpx, 368 | // // left: (750*rpx-outBox)/2, 369 | // child: Container( 370 | // width: 750 * rpx, 371 | // child: Row( 372 | // mainAxisAlignment: MainAxisAlignment.spaceAround, 373 | // children: [ 374 | // ifMakeVideo 375 | // ? Container( 376 | // width: 80 * rpx, 377 | // ) 378 | // : IconWithText( 379 | // icon: Icon( 380 | // Icons.search, 381 | // color: Colors.white, 382 | // ), 383 | // text: "道具"), 384 | // ifMakeVideo 385 | // ? AnimVideoButton( 386 | // rpx: rpx, 387 | // outWidth: outBox, 388 | // innerWidth: innerBox - 30 * rpx, 389 | // provider: provider, 390 | // ) 391 | // : CircleTakePhoto( 392 | // outBox: outBox, 393 | // innerBox: innerBox, 394 | // ), 395 | // ifMakeVideo 396 | // ? IconButton( 397 | // padding: EdgeInsets.all(0), 398 | // icon: Icon( 399 | // Icons.check_circle, 400 | // color: Color.fromARGB(255, 219, 48, 85), 401 | // size: 80 * rpx, 402 | // ), 403 | // onPressed: () async { 404 | // provider.cameraController 405 | // .stopVideoRecording(); 406 | // await ImageGallerySaver.saveFile( 407 | // provider.fileName); 408 | // File(provider.fileName).delete(); 409 | // }, 410 | // ) 411 | // : IconWithText( 412 | // icon: Icon( 413 | // Icons.search, 414 | // color: Colors.white, 415 | // ), 416 | // text: "道具"), 417 | // ])), 418 | // ), 419 | // Positioned( 420 | // bottom: 40 * rpx, 421 | // child: ScrollBottomBar( 422 | // rpx: rpx, 423 | // ), 424 | // ), 425 | // Positioned( 426 | // right: 30 * rpx, 427 | // top: 80 * rpx, 428 | // child: IconButton( 429 | // icon: Icon(Icons.camera_front), 430 | // onPressed: () { 431 | // provider.changeCamera(); 432 | // }), 433 | // ) 434 | // ]) 435 | // : Container(); 436 | // } 437 | // } 438 | 439 | class AnimVideoButton extends StatefulWidget { 440 | AnimVideoButton( 441 | {Key key, 442 | @required this.outWidth, 443 | @required this.innerWidth, 444 | @required this.rpx, 445 | @required this.provider}) 446 | : super(key: key); 447 | final double outWidth; 448 | final double innerWidth; 449 | final double rpx; 450 | final CameraProvider provider; 451 | _AnimVideoButtonState createState() => _AnimVideoButtonState(); 452 | } 453 | 454 | class _AnimVideoButtonState extends State 455 | with TickerProviderStateMixin { 456 | Animation animation; 457 | AnimationController controller; 458 | double outWidth; 459 | double innerWidth; 460 | double outBorder; 461 | double rpx; 462 | double maxBorder; 463 | bool ifRecording; 464 | CameraProvider provider; 465 | double curBorder; 466 | @override 467 | void dispose() { 468 | // TODO: implement dispose 469 | controller.dispose(); 470 | super.dispose(); 471 | } 472 | 473 | @override 474 | void initState() { 475 | super.initState(); 476 | ifRecording = true; 477 | provider = widget.provider; 478 | outWidth = widget.outWidth; 479 | innerWidth = widget.innerWidth; 480 | rpx = widget.rpx; 481 | outBorder = 5 * rpx; 482 | maxBorder = (outWidth - innerWidth) / 2 - 10 * rpx; 483 | curBorder = outBorder; 484 | controller = 485 | AnimationController(duration: Duration(milliseconds: 500), vsync: this); 486 | animation = 487 | Tween(begin: outBorder, end: maxBorder).animate(controller) 488 | ..addListener(() { 489 | setState(() { 490 | curBorder = animation.value; 491 | }); 492 | }); 493 | controller.repeat(reverse: true); 494 | } 495 | 496 | pauseRecording() { 497 | // provider.cameraController.pauseVideoRecording(); 498 | controller.stop(); 499 | provider.cameraController.pauseVideoRecording(); 500 | setState(() { 501 | ifRecording = false; 502 | }); 503 | } 504 | 505 | resumeRecording() { 506 | // provider.cameraController.resumeVideoRecording(); 507 | controller.repeat(reverse: true); 508 | provider.cameraController.resumeVideoRecording(); 509 | setState(() { 510 | ifRecording = true; 511 | }); 512 | } 513 | 514 | @override 515 | Widget build(BuildContext context) { 516 | return Container( 517 | width: outWidth, 518 | height: outWidth, 519 | decoration: BoxDecoration( 520 | shape: BoxShape.circle, 521 | color: Colors.transparent, 522 | border: Border.all( 523 | width: curBorder, color: Color.fromARGB(128, 219, 48, 85))), 524 | child: Container( 525 | child: !ifRecording 526 | ? IconButton( 527 | padding: EdgeInsets.all(0), 528 | icon: Icon( 529 | Icons.play_arrow, 530 | size: innerWidth, 531 | color: Color.fromARGB(255, 219, 48, 85), 532 | ), 533 | onPressed: () { 534 | resumeRecording(); 535 | }, 536 | ) 537 | : IconButton( 538 | padding: EdgeInsets.all(0), 539 | icon: Icon( 540 | Icons.pause, 541 | size: innerWidth, 542 | color: Color.fromARGB(255, 219, 48, 85), 543 | ), 544 | onPressed: () { 545 | pauseRecording(); 546 | }, 547 | ), 548 | ), 549 | ); 550 | } 551 | } 552 | 553 | class ScrollBottomBar extends StatefulWidget { 554 | ScrollBottomBar({Key key, @required this.rpx}) : super(key: key); 555 | final double rpx; 556 | _ScrollBottomBarState createState() => _ScrollBottomBarState(); 557 | } 558 | 559 | class _ScrollBottomBarState extends State { 560 | double rpx; 561 | double eachWidth; 562 | double eachSide; 563 | List items; 564 | ScrollController controller; 565 | double startX = 0; 566 | double finalX = 0; 567 | double minValue; 568 | double maxValue; 569 | double curX; 570 | int curIndex; 571 | 572 | @override 573 | void initState() { 574 | super.initState(); 575 | rpx = widget.rpx; 576 | eachWidth = 130 * rpx; 577 | eachSide = (750 - eachWidth / rpx) / 2 * rpx; 578 | curIndex = 2; 579 | minValue = 0; 580 | 581 | items = [ 582 | '拍照', 583 | '拍15秒', 584 | '拍60秒', 585 | '影集', 586 | '开直播', 587 | ]; 588 | maxValue = (items.length - 1) * eachWidth; 589 | curX = curIndex * eachWidth; 590 | controller = ScrollController(initialScrollOffset: curX); 591 | } 592 | 593 | moveToItem(index) { 594 | curX = index * eachWidth; 595 | controller.animateTo(curX, 596 | duration: Duration(milliseconds: 200), curve: Curves.linear); 597 | setState(() { 598 | curX = curX; 599 | curIndex = index; 600 | }); 601 | } 602 | 603 | @override 604 | Widget build(BuildContext context) { 605 | return Column(children: [ 606 | Listener( 607 | onPointerDown: (result) { 608 | setState(() { 609 | startX = result.position.dx; 610 | }); 611 | }, 612 | onPointerMove: (result) { 613 | double moveValue = result.position.dx; 614 | double moved = startX - moveValue; 615 | // curX+moved 616 | double afterMoved = min(max(curX + moved, minValue), maxValue); 617 | setState(() { 618 | curX = afterMoved; 619 | startX = result.position.dx; 620 | }); 621 | }, 622 | onPointerUp: (result) { 623 | int index = 0; 624 | double finalPosition = curX - eachWidth / 2; 625 | index = (finalPosition / eachWidth).ceil(); 626 | moveToItem(index); 627 | }, 628 | child: Container( 629 | width: 750 * rpx, 630 | height: 100 * rpx, 631 | child: SingleChildScrollView( 632 | scrollDirection: Axis.horizontal, 633 | controller: controller, 634 | child: Container( 635 | child: Row( 636 | mainAxisSize: MainAxisSize.min, 637 | children: [ 638 | SizedBox( 639 | width: eachSide, 640 | ), 641 | Row( 642 | children: List.generate(items.length, (index) { 643 | return Container( 644 | width: eachWidth, 645 | child: FlatButton( 646 | child: Text( 647 | items[index], 648 | style: TextStyle( 649 | color: curIndex == index 650 | ? Colors.white 651 | : Colors.white.withOpacity(0.5)), 652 | ), 653 | padding: EdgeInsets.all(0), 654 | onPressed: () { 655 | moveToItem(index); 656 | }, 657 | ), 658 | ); 659 | })), 660 | SizedBox( 661 | width: eachSide, 662 | ), 663 | ], 664 | ), 665 | ), 666 | )), 667 | ), 668 | Center( 669 | child: Container( 670 | decoration: 671 | BoxDecoration(shape: BoxShape.circle, color: Colors.white), 672 | width: 8 * rpx, 673 | height: 8 * rpx, 674 | ), 675 | ) 676 | ]); 677 | } 678 | } 679 | 680 | class CircleTakePhoto extends StatelessWidget { 681 | const CircleTakePhoto( 682 | {Key key, @required this.outBox, @required this.innerBox}) 683 | : super(key: key); 684 | final double outBox; 685 | final double innerBox; 686 | @override 687 | Widget build(BuildContext context) { 688 | double rpx = MediaQuery.of(context).size.width / 750; 689 | CameraProvider provider = Provider.of(context); 690 | 691 | // double outBox=160*rpx; 692 | // double innerBox=130*rpx; 693 | return Container( 694 | width: outBox, 695 | height: outBox, 696 | padding: EdgeInsets.all(10 * rpx), 697 | decoration: BoxDecoration( 698 | color: Colors.transparent, 699 | borderRadius: BorderRadius.circular(90 * rpx), 700 | border: Border.all( 701 | width: 10 * rpx, color: Color.fromARGB(128, 219, 48, 85)), 702 | ), 703 | child: FlatButton( 704 | padding: EdgeInsets.all(0), 705 | onPressed: () async { 706 | // provider.changeFileName('png'); 707 | // print(provider.fileName); 708 | // await provider.cameraController 709 | // .takePicture(provider.fileName) 710 | // .then((_) { 711 | // // Navigator.push(context, MaterialPageRoute(fullscreenDialog: true,builder: (_){ 712 | // // return Image.file(File(provider.fileName) ); 713 | // // })); 714 | // ImagePickerSaver.saveFile( 715 | // fileData: File(provider.fileName).readAsBytesSync()); 716 | // }); 717 | provider.changeFileName('mp4'); 718 | provider.cameraController.startVideoRecording(provider.fileName); 719 | provider.changePhotoWidget(); 720 | }, 721 | child: Container( 722 | width: innerBox, 723 | height: innerBox, 724 | alignment: Alignment.center, 725 | decoration: BoxDecoration( 726 | color: Color.fromARGB(255, 219, 48, 85), 727 | borderRadius: BorderRadius.circular(75 * rpx)), 728 | )), 729 | ); 730 | } 731 | } 732 | 733 | class IconWithText extends StatelessWidget { 734 | const IconWithText({Key key, @required this.icon, @required this.text}) 735 | : super(key: key); 736 | final Icon icon; 737 | final String text; 738 | @override 739 | Widget build(BuildContext context) { 740 | return Column( 741 | children: [ 742 | icon, 743 | Text( 744 | text, 745 | style: TextStyle(color: Colors.white), 746 | ) 747 | ], 748 | ); 749 | } 750 | } 751 | -------------------------------------------------------------------------------- /lib/pages/FaceDetect/FaceDetection.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:firebase_ml_vision/firebase_ml_vision.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:image_picker/image_picker.dart'; 6 | 7 | 8 | class FaceDetectionView extends StatefulWidget { 9 | FaceDetectionView({Key key}) : super(key: key); 10 | 11 | @override 12 | _FaceDetectionViewState createState() => _FaceDetectionViewState(); 13 | } 14 | 15 | class _FaceDetectionViewState extends State { 16 | File filePath; 17 | @override 18 | void initState() { 19 | super.initState(); 20 | 21 | } 22 | chooseImage() async { 23 | filePath=await ImagePicker.pickImage(source: ImageSource.gallery,imageQuality: 100,maxWidth: MediaQuery.of(context).size.width); 24 | var availableImage=filePath; 25 | // final FirebaseVisionImageMetadata metadata = FirebaseVisionImageMetadata( 26 | // rawFormat: availableImage.format.raw, 27 | // size: Size( 28 | // availableImage.width.toDouble(), availableImage.height.toDouble()), 29 | // planeData: availableImage.planes 30 | // .map((currentPlane) => FirebaseVisionImagePlaneMetadata( 31 | // bytesPerRow: currentPlane.bytesPerRow, 32 | // height: currentPlane.height, 33 | // width: currentPlane.width)) 34 | // .toList(), 35 | // rotation: ImageRotation.rotation90); 36 | final FirebaseVisionImage visionImage = 37 | FirebaseVisionImage.fromFile(filePath); 38 | final FaceDetector detector = FirebaseVision.instance.faceDetector(); 39 | final List faces = await detector.processImage(visionImage); 40 | 41 | print(faces[0].boundingBox); 42 | 43 | setState(() { 44 | filePath=filePath; 45 | }); 46 | } 47 | @override 48 | Widget build(BuildContext context) { 49 | return Stack( 50 | children: [ 51 | RaisedButton(child: Text("选择图片"),onPressed: (){chooseImage();},), 52 | filePath==null?Container(): 53 | Container( 54 | child: Image.file(filePath,fit: BoxFit.fitWidth,), 55 | ) 56 | ] 57 | ); 58 | } 59 | } 60 | 61 | class FaceMain extends StatelessWidget { 62 | const FaceMain({Key key}) : super(key: key); 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return Scaffold( 67 | body: FaceDetectionView(), 68 | ); 69 | } 70 | } -------------------------------------------------------------------------------- /lib/pages/RecommendPage/BottomSheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:douyin_demo/pages/RecommendPage/FriendList.dart'; 2 | import 'package:douyin_demo/providers/AtUserProvider.dart'; 3 | import 'package:douyin_demo/providers/RecommendProvider.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class ReplyFullList extends StatelessWidget { 8 | const ReplyFullList({Key key,this.pCtx}) : super(key: key); 9 | final BuildContext pCtx; 10 | @override 11 | Widget build(BuildContext context) { 12 | double rpx = MediaQuery.of(context).size.width / 750; 13 | RecommendProvider provider = Provider.of(pCtx); 14 | Reply reply = provider.reply; 15 | List replies = List(); 16 | 17 | replies.add(reply); 18 | replies.add(reply); 19 | replies.add(reply); 20 | ScrollController controller = ScrollController(); 21 | return Scaffold( 22 | appBar: PreferredSize( 23 | preferredSize: Size.fromHeight(80*rpx), 24 | child: AppBar( 25 | leading: Container(), 26 | elevation:0, 27 | backgroundColor: Colors.grey[50], 28 | actions: [ 29 | 30 | IconButton( 31 | icon: Icon( 32 | Icons.close, 33 | color: Colors.black, 34 | ), 35 | onPressed: () { 36 | Navigator.pop(context); 37 | }, 38 | ) 39 | ], 40 | title: Text( 41 | "10条评论", 42 | style: TextStyle(color: Colors.grey[700],fontSize: 25*rpx), 43 | ), 44 | // elevation: 1, 45 | ) 46 | ), 47 | bottomNavigationBar: SafeArea( 48 | child: BottomReplyBar(pCtx: pCtx,), 49 | ), 50 | body: SingleChildScrollView( 51 | controller: controller, 52 | child: Container( 53 | child: genReplyList(replies, controller), 54 | ))); 55 | } 56 | } 57 | 58 | class ReplyList extends StatelessWidget { 59 | const ReplyList({Key key, this.reply, this.controller}) : super(key: key); 60 | final Reply reply; 61 | final ScrollController controller; 62 | @override 63 | Widget build(BuildContext context) { 64 | double rpx = MediaQuery.of(context).size.width / 750; 65 | List replies = List(); 66 | replies.add(reply); 67 | replies.add(reply); 68 | replies.add(reply); 69 | // RecommendProvider provider=Provider.of(context); 70 | return Container( 71 | child: Column( 72 | mainAxisSize: MainAxisSize.min, 73 | children: [ 74 | Row( 75 | children: [ 76 | Container( 77 | width: 100 * rpx, 78 | height: 100 * rpx, 79 | padding: EdgeInsets.all(10 * rpx), 80 | child: CircleAvatar( 81 | backgroundImage: NetworkImage("${reply.replyMakerAvatar}"), 82 | ), 83 | ), 84 | Container( 85 | width: 550 * rpx, 86 | child: ListTile( 87 | title: Text("${reply.replyMakerName}"), 88 | subtitle: Text( 89 | "${reply.replyContent}", 90 | maxLines: 2, 91 | overflow: TextOverflow.ellipsis, 92 | ), 93 | ), 94 | ), 95 | Container( 96 | width: 100 * rpx, 97 | child: IconButton( 98 | onPressed: () {}, 99 | icon: Icon( 100 | Icons.favorite, 101 | color: Colors.grey[300], 102 | ), 103 | ), 104 | ) 105 | ], 106 | ), 107 | genAfterReplyList(replies, controller) 108 | ], 109 | ), 110 | ); 111 | } 112 | } 113 | 114 | class AfterReply extends StatelessWidget { 115 | const AfterReply({Key key, this.afterReply}) : super(key: key); 116 | final Reply afterReply; 117 | @override 118 | Widget build(BuildContext context) { 119 | double rpx = MediaQuery.of(context).size.width / 750; 120 | return Container( 121 | child: Column( 122 | mainAxisSize: MainAxisSize.min, 123 | children: [ 124 | Row( 125 | children: [ 126 | Container( 127 | width: 100 * rpx, 128 | ), 129 | Container( 130 | width: 550 * rpx, 131 | child: Row( 132 | crossAxisAlignment: CrossAxisAlignment.start, 133 | children: [ 134 | Container( 135 | width: 70 * rpx, 136 | height: 70 * rpx, 137 | margin: EdgeInsets.only(top: 15 * rpx), 138 | padding: EdgeInsets.all(10 * rpx), 139 | child: CircleAvatar( 140 | backgroundImage: 141 | NetworkImage("${afterReply.replyMakerAvatar}"), 142 | ), 143 | ), 144 | Container( 145 | width: 480 * rpx, 146 | child: ListTile( 147 | title: Text("${afterReply.replyMakerName}"), 148 | subtitle: RichText( 149 | text: TextSpan( 150 | text: "${afterReply.replyContent}", 151 | style: TextStyle(color: Colors.grey[500]), 152 | children: [ 153 | TextSpan(text: " ${afterReply.whenReplied}") 154 | ]), 155 | ), 156 | 157 | // Text( 158 | // "${afterReply.replyContent}", 159 | // maxLines: 2, 160 | // overflow: TextOverflow.ellipsis, 161 | // ), 162 | ), 163 | ) 164 | ], 165 | ), 166 | ), 167 | Container( 168 | width: 100 * rpx, 169 | child: IconButton( 170 | onPressed: () {}, 171 | icon: Icon( 172 | Icons.favorite, 173 | color: Colors.grey[300], 174 | ), 175 | ), 176 | ) 177 | ], 178 | ) 179 | ], 180 | ), 181 | ); 182 | } 183 | } 184 | 185 | genReplyList(List replies, ScrollController controller) { 186 | return ListView.builder( 187 | shrinkWrap: true, 188 | controller: controller, 189 | itemCount: replies.length, 190 | itemBuilder: (context, index) { 191 | return ReplyList( 192 | reply: replies[index], 193 | controller: controller, 194 | ); 195 | }, 196 | ); 197 | } 198 | 199 | genAfterReplyList(List replies, ScrollController controller) { 200 | return ListView.builder( 201 | shrinkWrap: true, 202 | controller: controller, 203 | itemCount: replies.length <= 2 ? replies.length : 2, 204 | itemBuilder: (context, index) { 205 | return AfterReply( 206 | afterReply: replies[index], 207 | ); 208 | }, 209 | ); 210 | } 211 | 212 | class BottomReplyBar extends StatelessWidget { 213 | const BottomReplyBar({Key key,this.pCtx}) : super(key: key); 214 | final BuildContext pCtx; 215 | @override 216 | Widget build(BuildContext context) { 217 | TextEditingController _controller=TextEditingController(); 218 | double toBottom=MediaQuery.of(context).viewInsets.bottom; 219 | double rpx=MediaQuery.of(context).size.width/750; 220 | return Container( 221 | padding: EdgeInsets.only(bottom: toBottom), 222 | decoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.grey[200],width: 1))), 223 | child: Row(children: [ 224 | Expanded( 225 | child: Container( 226 | padding: EdgeInsets.only(left: 30*rpx), 227 | // width: 600*rspx, 228 | child: TextField(controller: _controller,decoration: InputDecoration(hintText: "留下你的精彩评论",border: InputBorder.none),), 229 | ) 230 | ), 231 | IconButton(icon: Icon(Icons.email,color: Colors.grey[500],size: 50*rpx,),onPressed: (){showAtFriendPage(pCtx);},), 232 | IconButton(icon: Icon(Icons.face,color: Colors.grey[500],size: 50*rpx),onPressed: (){},), 233 | SizedBox(width: 20*rpx,) 234 | ],), 235 | ); 236 | } 237 | } 238 | 239 | showAtFriendPage(BuildContext context){ 240 | Navigator.of(context).push(new MaterialPageRoute( 241 | builder: (BuildContext context) { 242 | return MultiProvider( 243 | providers: [ChangeNotifierProvider(builder:(context)=>AtUserProvider())], 244 | child: AtFriendPage() 245 | ); 246 | }, 247 | fullscreenDialog: true 248 | )); 249 | } 250 | -------------------------------------------------------------------------------- /lib/pages/RecommendPage/FriendList.dart: -------------------------------------------------------------------------------- 1 | import 'package:douyin_demo/providers/AtUserProvider.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:provider/provider.dart'; 4 | import 'package:sticky_headers/sticky_headers.dart'; 5 | 6 | class AtFriendPage extends StatelessWidget { 7 | const AtFriendPage({Key key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | ScrollController controller = ScrollController(); 12 | double rpx = MediaQuery.of(context).size.width / 750; 13 | AtUserProvider provider = Provider.of(context); 14 | // AtUserProvider provider = Provider.of(context); 15 | 16 | List groupList = provider.groupList; 17 | return provider != null 18 | ? Scaffold( 19 | backgroundColor: Color(0xff121319), 20 | appBar: AppBar( 21 | backgroundColor: Color(0xff121319), 22 | leading: Container( 23 | width: 80 * rpx, 24 | child: IconButton( 25 | icon: Icon(Icons.close), 26 | onPressed: () { 27 | Navigator.pop(context); 28 | }, 29 | )), 30 | title: Text("@好友"), 31 | bottom: PreferredSize( 32 | preferredSize: Size.fromHeight(80 * rpx), 33 | child: Container( 34 | margin: EdgeInsets.symmetric(horizontal: 30 * rpx), 35 | decoration: BoxDecoration(color: Color(0xff2a2b33)), 36 | padding: EdgeInsets.symmetric(horizontal: 20 * rpx), 37 | child: TextField( 38 | decoration: InputDecoration( 39 | icon: Icon( 40 | Icons.search, 41 | color: Colors.grey[500], 42 | ), 43 | hintText: "搜索用户备注或名字", 44 | hintStyle: TextStyle( 45 | color: Colors.grey[500], 46 | )), 47 | ), 48 | ), 49 | )), 50 | body: ListView.builder( 51 | shrinkWrap: true, 52 | controller: controller, 53 | itemCount: groupList.length, 54 | itemBuilder: (BuildContext context, int index) { 55 | return StickyHeader( 56 | header: Container( 57 | width: MediaQuery.of(context).size.width, 58 | padding:EdgeInsets.only(left: 20*rpx) , 59 | height: 50, 60 | decoration: BoxDecoration(color: Color(0xff121319)), 61 | alignment: Alignment.centerLeft, 62 | child: Text( 63 | groupList[index].toString(), 64 | style: TextStyle(color: Colors.white, fontSize: 35*rpx), 65 | ), 66 | ), 67 | content: genContentList( 68 | provider.result[provider.groupList[index]], 69 | context, 70 | controller), 71 | ); 72 | }, 73 | ), 74 | ) 75 | : Scaffold(); 76 | } 77 | } 78 | 79 | genUserList(context, controller) { 80 | AtUserProvider provider = Provider.of(context); 81 | 82 | List groupList = provider.groupList; 83 | return ListView.builder( 84 | shrinkWrap: true, 85 | controller: controller, 86 | itemCount: groupList.length, 87 | itemBuilder: (BuildContext context, int index) { 88 | return StickyHeader( 89 | header: Container( 90 | width: MediaQuery.of(context).size.width, 91 | height: 50, 92 | decoration: BoxDecoration(color: Colors.black), 93 | child: Text( 94 | groupList[index].toString(), 95 | style: TextStyle(color: Colors.white, fontSize: 20), 96 | ), 97 | ), 98 | content: genContentList( 99 | provider.result[provider.groupList[index]], context, controller), 100 | ); 101 | }, 102 | ); 103 | } 104 | 105 | genContentList( 106 | List friends, BuildContext context, ScrollController controller) { 107 | double rpx = MediaQuery.of(context).size.width / 750; 108 | return ListView.builder( 109 | shrinkWrap: true, 110 | controller: controller, 111 | itemCount: friends.length, 112 | itemBuilder: (BuildContext context, int index) { 113 | return Container( 114 | decoration: BoxDecoration(color: Color(0xff121319)), 115 | height: 130 * rpx, 116 | child: Row( 117 | children: [ 118 | Container( 119 | padding: EdgeInsets.all(15 * rpx), 120 | width: 100 * rpx, 121 | height: 100 * rpx, 122 | child: CircleAvatar( 123 | backgroundImage: NetworkImage(friends[index]["avatarUrl"]), 124 | ), 125 | ), 126 | Container( 127 | width: 450 * rpx, 128 | child: friends[index]["desc"].toString().length > 0 129 | ? Column( 130 | crossAxisAlignment: CrossAxisAlignment.start, 131 | mainAxisAlignment: MainAxisAlignment.center, 132 | children: [ 133 | Container( 134 | child: Text( 135 | friends[index]["userName"], 136 | style: TextStyle( 137 | fontSize: 32 * rpx, color: Colors.white), 138 | maxLines: 1, 139 | overflow: TextOverflow.ellipsis, 140 | ), 141 | ), 142 | Container( 143 | child: Text( 144 | friends[index]["desc"], 145 | style: TextStyle(color: Colors.grey[500]), 146 | maxLines: 1, 147 | overflow: TextOverflow.ellipsis, 148 | ), 149 | ), 150 | ], 151 | ) 152 | : Container( 153 | child: Text( 154 | friends[index]["userName"], 155 | style: TextStyle( 156 | fontSize: 32 * rpx, color: Colors.white), 157 | maxLines: 1, 158 | overflow: TextOverflow.ellipsis, 159 | ), 160 | ), 161 | ), 162 | Container( 163 | width: 200 * rpx, 164 | child: Icon( 165 | Icons.search, 166 | color: Colors.grey[500], 167 | ), 168 | ) 169 | ], 170 | )); 171 | }, 172 | ); 173 | } 174 | -------------------------------------------------------------------------------- /lib/pages/loadData/loadData.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadDataDemo extends StatelessWidget { 4 | const LoadDataDemo({Key key}) : super(key: key); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | appBar: AppBar( 10 | title: Text("获取数据"), 11 | centerTitle: true, 12 | ), 13 | body: RefreshPage()); 14 | } 15 | } 16 | 17 | class RefreshPage extends StatefulWidget { 18 | RefreshPage({Key key}) : super(key: key); 19 | 20 | @override 21 | _RefreshPageState createState() => _RefreshPageState(); 22 | } 23 | 24 | class _RefreshPageState extends State { 25 | List data = List(); 26 | ScrollController controller; 27 | bool ifLoading = false; 28 | @override 29 | void initState() { 30 | super.initState(); 31 | controller = ScrollController(); 32 | List.generate(30, (i) => data.add("item ${i + 1}")); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Stack(children: [ 38 | RefreshIndicator( 39 | onRefresh: () { 40 | return Future.delayed(Duration(seconds: 1), () { 41 | data=List(); 42 | List.generate(30, (i) => data.add("item ${i + 1}")); 43 | setState(() { 44 | data = data; 45 | }); 46 | }); 47 | }, 48 | child: NotificationListener( 49 | onNotification: (scroll) { 50 | if (!ifLoading && 51 | scroll.metrics.maxScrollExtent <= controller.offset + 200) { 52 | 53 | setState(() { 54 | ifLoading = true; 55 | }); 56 | List.generate(30, (i) { 57 | data.add("item ${data.length + 1}"); 58 | }); 59 | // setState(() { 60 | // data = data; 61 | // ifLoading = false; 62 | // }); 63 | Future.delayed(Duration(seconds: 2), () { 64 | setState(() { 65 | data = data; 66 | ifLoading = false; 67 | }); 68 | }); 69 | } 70 | // return; 71 | }, 72 | child: ListView.builder( 73 | controller: controller, 74 | shrinkWrap: true, 75 | itemCount: data.length, 76 | itemBuilder: (context, index) { 77 | return Container( 78 | height: 80, 79 | child: Text(data[index]), 80 | ); 81 | }, 82 | )), 83 | ), 84 | ifLoading 85 | ? Center( 86 | child: CircularProgressIndicator(), 87 | ) 88 | : Container() 89 | ]); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/pages/sameCity/SameCityPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:douyin_demo/models/PostsModel.dart'; 2 | import 'package:douyin_demo/providers/PostsGalleryProvider.dart'; 3 | import 'package:douyin_demo/widgets/BottomBar.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:provider/provider.dart'; 6 | 7 | class SameCityMain extends StatelessWidget { 8 | const SameCityMain({Key key, this.selIndex}) : super(key: key); 9 | final int selIndex; 10 | @override 11 | Widget build(BuildContext context) { 12 | PostsGalleryProvider provider = 13 | Provider.of(context); //null 14 | double rpx = MediaQuery.of(context).size.width / 750; 15 | ScrollController controller = ScrollController(); 16 | return provider == null || provider.model1 == null 17 | ? Scaffold( 18 | // body: Loading(), 19 | ) 20 | : Scaffold( 21 | backgroundColor: Theme.of(context).primaryColor, 22 | appBar: AppBar( 23 | title: Text("同城"), 24 | ), 25 | bottomNavigationBar: Container( 26 | decoration: BoxDecoration(color: Colors.black), 27 | child: SafeArea(child: BtmBar(selectIndex: selIndex))), 28 | body: Column( 29 | mainAxisSize: MainAxisSize.min, 30 | children: [ 31 | Container( 32 | height: 120 * rpx, 33 | padding: EdgeInsets.all(20 * rpx), 34 | child: Row( 35 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 36 | children: [ 37 | Row( 38 | children: [ 39 | Icon( 40 | Icons.near_me, 41 | color: Colors.grey[400], 42 | ), 43 | Text( 44 | "自动定位 :上海", 45 | style: TextStyle(color: Colors.grey[400]), 46 | ), 47 | ], 48 | ), 49 | Row( 50 | children: [ 51 | Text("切换", 52 | style: TextStyle(color: Colors.grey[400])), 53 | Icon(Icons.arrow_right, color: Colors.grey[400]) 54 | ], 55 | ), 56 | ], 57 | )), 58 | Expanded( 59 | child: SingleChildScrollView( 60 | controller: controller, 61 | child: Container( 62 | padding: EdgeInsets.symmetric(horizontal: 10 * rpx), 63 | child: Row( 64 | crossAxisAlignment: CrossAxisAlignment.start, 65 | children: [ 66 | Flexible( 67 | flex: 1, 68 | child: WaterFallList( 69 | dataList: provider.model1, 70 | controller: controller, 71 | )), 72 | Flexible( 73 | flex: 1, 74 | child: WaterFallList( 75 | dataList: provider.model2, 76 | controller: controller), 77 | ), 78 | ], 79 | )))) 80 | ], 81 | ), 82 | ); 83 | } 84 | } 85 | 86 | class WaterFallList extends StatelessWidget { 87 | const WaterFallList({Key key, this.dataList, this.controller}) 88 | : super(key: key); 89 | final List dataList; 90 | final ScrollController controller; 91 | @override 92 | Widget build(BuildContext context) { 93 | 94 | double rpx = MediaQuery.of(context).size.width / 750; 95 | double outPadding=10*rpx; 96 | double eachSide=2*rpx; 97 | return ListView.builder( 98 | controller: controller, 99 | shrinkWrap: true, 100 | itemCount: dataList.length, 101 | itemBuilder: (context, index) { 102 | PostsModel curPosts = dataList[index]; 103 | return Container( 104 | margin: EdgeInsets.only(bottom: 10*rpx), 105 | child: Column( 106 | mainAxisSize: MainAxisSize.min, 107 | children: [ 108 | Stack( 109 | children: [ 110 | Container( 111 | width: 345 * rpx, 112 | padding: EdgeInsets.symmetric(horizontal: eachSide), 113 | height: 345 * curPosts.picsRate * rpx, 114 | child: Image.network( 115 | "https://www.guojio.com/" + curPosts.postsPics, 116 | fit: BoxFit.fitWidth, 117 | )), 118 | Positioned( 119 | bottom: 0, 120 | child: Container( 121 | width: 345 * rpx, 122 | height: 60 * rpx, 123 | padding: EdgeInsets.all(eachSide+10*rpx), 124 | child: Row( 125 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 126 | children: [ 127 | Row( 128 | children: [ 129 | Icon(Icons.near_me,color: Colors.grey[400],size: 32*rpx,), 130 | Text("1km",style: TextStyle(color:Colors.grey[400],fontSize: 26*rpx),), 131 | ], 132 | ), 133 | Container( 134 | width: 40 * rpx, 135 | height: 40 * rpx, 136 | child: CircleAvatar( 137 | backgroundImage: NetworkImage( 138 | curPosts.makerPhoto, 139 | ))) 140 | ], 141 | )), 142 | ) 143 | ], 144 | ), 145 | Container( 146 | padding: EdgeInsets.all(10*rpx), 147 | child: Text( 148 | curPosts.postsContent, 149 | maxLines: 3, 150 | overflow: TextOverflow.ellipsis, 151 | style: TextStyle(color: Colors.white,fontSize: 26*rpx), 152 | ) 153 | ) 154 | ], 155 | ) 156 | ); 157 | }, 158 | ); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/pages/selfHome/HomePage.dart: -------------------------------------------------------------------------------- 1 | import 'package:after_layout/after_layout.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class SelfHomePage extends StatelessWidget { 5 | const SelfHomePage({Key key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | double rpx = MediaQuery.of(context).size.width / 750; 10 | return Scaffold( 11 | body: Container( 12 | child: HomeMain( 13 | rpx: rpx, 14 | ), 15 | ), 16 | ); 17 | } 18 | } 19 | 20 | class HomeMain extends StatefulWidget { 21 | HomeMain({Key key, @required this.rpx}) : super(key: key); 22 | final double rpx; 23 | _HomeMainState createState() => _HomeMainState(); 24 | } 25 | 26 | class _HomeMainState extends State with TickerProviderStateMixin { 27 | double extraPicHeight = 0; 28 | BoxFit fitType; 29 | double prev_dy; 30 | double rpx; 31 | AnimationController animationController; 32 | Animation anim; 33 | TabController tabController; 34 | double expanedHeight=300; 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | tabController=TabController(vsync: this,length: 3); 40 | prev_dy = 0; 41 | fitType = BoxFit.fitWidth; 42 | animationController = 43 | AnimationController(vsync: this, duration: Duration(milliseconds: 300)); 44 | anim = Tween(begin: 0.0, end: 0.0).animate(animationController); 45 | } 46 | 47 | updatePicHeight(changed) { 48 | if (prev_dy == 0) { 49 | prev_dy = changed; 50 | } 51 | extraPicHeight += changed - prev_dy; 52 | if (extraPicHeight >= 200 * widget.rpx) { 53 | fitType = BoxFit.fitHeight; 54 | } else { 55 | fitType = BoxFit.fitWidth; 56 | } 57 | setState(() { 58 | prev_dy = changed; 59 | extraPicHeight = extraPicHeight; 60 | fitType = fitType; 61 | }); 62 | } 63 | 64 | updateExpandedHeight(height){ 65 | setState(() { 66 | expanedHeight=height; 67 | }); 68 | } 69 | 70 | runAnimate() { 71 | setState(() { 72 | anim = Tween(begin: extraPicHeight, end: 0.0).animate(animationController) 73 | ..addListener(() { 74 | if (extraPicHeight >= widget.rpx * 200) { 75 | fitType = BoxFit.fitHeight; 76 | } else { 77 | fitType = BoxFit.fitWidth; 78 | } 79 | setState(() { 80 | extraPicHeight = anim.value; 81 | fitType = fitType; 82 | }); 83 | }); 84 | prev_dy = 0; 85 | }); 86 | } 87 | 88 | @override 89 | Widget build(BuildContext context) { 90 | double rpx = MediaQuery.of(context).size.width / 750; 91 | return Listener( 92 | onPointerMove: (result) { 93 | updatePicHeight(result.position.dy); 94 | }, 95 | onPointerUp: (_) { 96 | runAnimate(); 97 | animationController.forward(from: 0); 98 | }, 99 | child: CustomScrollView( 100 | physics: ClampingScrollPhysics(), 101 | slivers: [ 102 | SliverAppBar( 103 | pinned: true, 104 | floating: true, 105 | actions: [ 106 | IconButton( 107 | icon: Icon(Icons.search), 108 | onPressed: () {}, 109 | ), 110 | IconButton( 111 | icon: Icon(Icons.more_vert), 112 | onPressed: () {}, 113 | ), 114 | ], 115 | leading: IconButton( 116 | icon: Icon(Icons.arrow_back), 117 | onPressed: () {}, 118 | ), 119 | bottom: PreferredSize( 120 | preferredSize: Size.fromHeight(50), child:TabBar(controller: tabController,tabs: [ 121 | Text("作品 91"), 122 | Text("动态 91"), 123 | Text("喜欢 91"), 124 | ],)), 125 | // expandedHeight: 510 * rpx + extraPicHeight, 126 | expandedHeight: expanedHeight+extraPicHeight, 127 | flexibleSpace: Container( 128 | child: TopBarWithCallback( 129 | extraPicHeight: extraPicHeight, 130 | fitType: fitType, 131 | updateHeight: updateExpandedHeight, 132 | ), 133 | ), 134 | ), 135 | SliverList( 136 | delegate: SliverChildBuilderDelegate((context, index) { 137 | return Container( 138 | height: 30, 139 | alignment: Alignment.centerLeft, 140 | color: Colors.blueAccent, 141 | child: Text("This is itm $index"), 142 | margin: EdgeInsets.symmetric( 143 | horizontal: 20 * rpx, vertical: 10 * rpx), 144 | ); 145 | }, childCount: 80), 146 | ) 147 | ], 148 | )); 149 | } 150 | } 151 | 152 | class TopBarWithCallback extends StatefulWidget { 153 | TopBarWithCallback({Key key,@required this.extraPicHeight, @required this.fitType, @required this.updateHeight}) : super(key: key); 154 | final double extraPicHeight; 155 | final BoxFit fitType; 156 | final Function(double) updateHeight; 157 | _TopBarWithCallbackState createState() => _TopBarWithCallbackState(); 158 | } 159 | 160 | class _TopBarWithCallbackState extends State with AfterLayoutMixin { 161 | @override 162 | Widget build(BuildContext context) { 163 | return Container( 164 | child: SliverTopBar(extraPicHeight: widget.extraPicHeight,fitType: widget.fitType,), 165 | ); 166 | } 167 | 168 | @override 169 | void afterFirstLayout(BuildContext context) { 170 | RenderBox box=context.findRenderObject(); 171 | double height=box.getMaxIntrinsicHeight(MediaQuery.of(context).size.width); 172 | widget.updateHeight(height); 173 | } 174 | } 175 | 176 | class SliverTopBar extends StatelessWidget { 177 | const SliverTopBar( 178 | {Key key, @required this.extraPicHeight, @required this.fitType}) 179 | : super(key: key); 180 | final double extraPicHeight; 181 | final BoxFit fitType; 182 | @override 183 | Widget build(BuildContext context) { 184 | double rpx = MediaQuery.of(context).size.width / 750; 185 | return Stack( 186 | children: [ 187 | Column( 188 | mainAxisSize: MainAxisSize.min, 189 | children: [ 190 | Image.asset( 191 | "lib/images/temple.jpg", 192 | width: 750 * rpx, 193 | height: 300 * rpx + extraPicHeight, 194 | fit: fitType, 195 | ), 196 | Container( 197 | padding: EdgeInsets.only(top: 20 * rpx), 198 | height: 120 * rpx, 199 | child: Row( 200 | mainAxisAlignment: MainAxisAlignment.end, 201 | children: [ 202 | Container( 203 | height: 80 * rpx, 204 | width: 330 * rpx, 205 | child: RaisedButton( 206 | color: Color(0xffdc3254), 207 | child: Text( 208 | "+关注", 209 | style: TextStyle( 210 | fontSize: 30 * rpx, 211 | color: Colors.white, 212 | letterSpacing: 3 * rpx), 213 | ), 214 | onPressed: () {}, 215 | )), 216 | SizedBox( 217 | width: 10 * rpx, 218 | ), 219 | Container( 220 | decoration: BoxDecoration( 221 | borderRadius: BorderRadius.circular(2), 222 | color: Color(0xff3b3c49), 223 | ), 224 | height: 80 * rpx, 225 | child: IconButton( 226 | icon: Center( 227 | child: Icon( 228 | Icons.arrow_drop_down, 229 | color: Colors.white, 230 | size: 50 * rpx, 231 | )), 232 | onPressed: () {}, 233 | ), 234 | ), 235 | SizedBox( 236 | width: 30 * rpx, 237 | ) 238 | ], 239 | ), 240 | ), 241 | SizedBox( 242 | height: 100 * rpx, 243 | ), 244 | Container( 245 | width: 750 * rpx, 246 | padding: EdgeInsets.symmetric(horizontal: 30 * rpx), 247 | child: Column( 248 | crossAxisAlignment: CrossAxisAlignment.start, 249 | children: [ 250 | Text( 251 | "马友发", 252 | style: TextStyle( 253 | fontSize: 55 * rpx, 254 | color: Colors.white, 255 | fontWeight: FontWeight.bold), 256 | ), 257 | Text("抖音号:1234567", 258 | style: TextStyle( 259 | fontSize: 27 * rpx, 260 | color: Colors.white, 261 | )), 262 | SizedBox( 263 | height: 15 * rpx, 264 | ) 265 | ], 266 | )), 267 | Padding( 268 | padding: EdgeInsets.symmetric(horizontal: 20 * rpx), 269 | child: Divider( 270 | color: Colors.grey[700], 271 | ), 272 | ), 273 | Container( 274 | padding: EdgeInsets.symmetric(horizontal: 20 * rpx), 275 | child: Row( 276 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 277 | children: [ 278 | Row( 279 | children: [ 280 | Icon( 281 | Icons.shop, 282 | color: Color(0xffeacd3f), 283 | ), 284 | Text( 285 | "商品橱窗", 286 | style: TextStyle(color: Color(0xffeacd3f)), 287 | ) 288 | ], 289 | ), 290 | Icon(Icons.keyboard_arrow_right, color: Color(0xffeacd3f)) 291 | ], 292 | )), 293 | Padding( 294 | padding: EdgeInsets.symmetric(horizontal: 20 * rpx), 295 | child: Divider( 296 | color: Colors.grey[700], 297 | ), 298 | ), 299 | Container( 300 | padding: EdgeInsets.symmetric(horizontal: 20 * rpx), 301 | height: 100 * rpx, 302 | width: 750 * rpx, 303 | child: Text( 304 | "爱神的箭发;黑色大力开发哈的\n阿萨德饭还是电话费拉开始的计划发", 305 | style: TextStyle(color: Colors.white, fontSize: 30 * rpx), 306 | ), 307 | ), 308 | Container( 309 | padding: EdgeInsets.symmetric(horizontal: 20*rpx,vertical: 10*rpx), 310 | child: Row( 311 | children: [ 312 | Tag(text:"深圳" ,), 313 | Tag(text: "世界之窗",), 314 | Tag(text: "深圳大学",) 315 | ], 316 | ), 317 | ), 318 | Container(padding: EdgeInsets.symmetric(horizontal: 20*rpx,vertical: 30*rpx),child: Row(children: [ 319 | NumWithDesc(numm: "100.2w",desc: "获赞",), 320 | NumWithDesc(numm: "15",desc: "关注",), 321 | NumWithDesc(numm: "10.8w",desc: "粉丝",), 322 | ],),) 323 | ], 324 | ), 325 | Positioned( 326 | top: 250 * rpx + extraPicHeight, 327 | left: 30 * rpx, 328 | child: Container( 329 | decoration: BoxDecoration( 330 | color: Theme.of(context).primaryColor, 331 | borderRadius: BorderRadius.circular(220 * rpx)), 332 | width: 220 * rpx, 333 | height: 220 * rpx, 334 | padding: EdgeInsets.all(10 * rpx), 335 | child: CircleAvatar( 336 | backgroundImage: NetworkImage( 337 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg"))), 338 | ) 339 | ], 340 | ); 341 | } 342 | } 343 | 344 | class Tag extends StatelessWidget { 345 | const Tag({Key key, @required this.text}) : super(key: key); 346 | final String text; 347 | @override 348 | Widget build(BuildContext context) { 349 | double rpx = MediaQuery.of(context).size.width / 750; 350 | return Container( 351 | child: Text( 352 | text, 353 | style: TextStyle(fontSize: 26 * rpx,color: Color(0xff64626e)), 354 | ), 355 | color: Color(0xff3b3c49), 356 | padding: EdgeInsets.all(10 * rpx), 357 | margin: EdgeInsets.only(right: 10*rpx), 358 | ); 359 | } 360 | } 361 | 362 | class NumWithDesc extends StatelessWidget { 363 | const NumWithDesc({Key key,@required this.numm,@required this.desc}) : super(key: key); 364 | final String numm; 365 | final String desc; 366 | @override 367 | Widget build(BuildContext context) { 368 | 369 | double rpx=MediaQuery.of(context).size.width/750; 370 | double textSize=35*rpx; 371 | return Padding( 372 | padding: EdgeInsets.only(right: 20*rpx), 373 | child: Row( 374 | children: [ 375 | Text(numm,style: TextStyle(fontSize: textSize,color: Colors.white,fontWeight: FontWeight.bold),), 376 | SizedBox(width: 10*rpx,), 377 | Text(desc,style: TextStyle(fontSize: textSize,color: Color(0xff3b3c49))) 378 | ], 379 | ) 380 | ); 381 | } 382 | } 383 | 384 | // import 'package:flutter/material.dart'; 385 | 386 | // class SelfHomePage extends StatelessWidget { 387 | // const SelfHomePage({Key key}) : super(key: key); 388 | 389 | // @override 390 | // Widget build(BuildContext context) { 391 | // return Scaffold( 392 | // body: CustomScrollView( 393 | // physics: ClampingScrollPhysics(), 394 | // slivers: [ 395 | // SliverAppBar( 396 | // leading: IconButton( 397 | // icon: Icon(Icons.arrow_back), 398 | // onPressed: () {}, 399 | // ), 400 | // floating: true, 401 | // pinned: false, 402 | // snap: true, 403 | // expandedHeight: 250, 404 | // flexibleSpace: FlexibleSpaceBar( 405 | // title: Text("This is Sliver App Bar"), 406 | // background: Image.asset("lib/images/temple.jpg",height: 250,fit: BoxFit.fitWidth,), 407 | // ), 408 | // ), 409 | // SliverList(delegate: SliverChildBuilderDelegate((context,index){ 410 | // return Container(child: Text("This is item $index",style: TextStyle(fontSize: 20),),color: Colors.redAccent,); 411 | // },)) 412 | // ], 413 | // ), 414 | // ); 415 | // } 416 | // } 417 | -------------------------------------------------------------------------------- /lib/providers/CameraProvider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:firebase_ml_vision/firebase_ml_vision.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:camera/camera.dart'; 6 | 7 | import 'package:path/path.dart' as p; 8 | import 'package:path_provider/path_provider.dart'; 9 | import 'package:uuid/uuid.dart'; 10 | 11 | class CameraProvider extends State 12 | with ChangeNotifier, TickerProviderStateMixin { 13 | CameraController cameraController; 14 | TabController tabController; 15 | List cameras; 16 | int curCamera = 1; 17 | String appFolder = ""; 18 | String fileName; 19 | Widget photoButton; 20 | bool ifMakeVideo = false; 21 | FaceDetector faceDetector; 22 | 23 | CameraProvider() { 24 | tabController = TabController(length: 6, vsync: this); 25 | getCameras(); 26 | } 27 | 28 | changeFileName(afterFix) { 29 | String id = Uuid().v4().toString(); 30 | fileName = p.join(appFolder, '$id.$afterFix'); 31 | notifyListeners(); 32 | } 33 | 34 | captureFrame() { 35 | 36 | cameraController.startImageStream((CameraImage image) { 37 | cameraController.stopImageStream(); 38 | detectImage(image); 39 | captureFrame(); 40 | }); 41 | } 42 | 43 | detectImage(image) async { 44 | final List faces = await faceDetector.processImage( 45 | FirebaseVisionImage.fromBytes(image.planes[0].bytes, null)); 46 | print(faces); 47 | if (faces.length > 0) { 48 | print(faces[0].headEulerAngleY); 49 | } 50 | } 51 | 52 | getCameras() async { 53 | Directory appDocDir = await getApplicationDocumentsDirectory(); 54 | if (!Directory(appDocDir.path).existsSync()) { 55 | appDocDir.createSync(); 56 | } 57 | appFolder = appDocDir.path; 58 | cameras = await availableCameras(); 59 | cameraController = 60 | CameraController(cameras[curCamera], ResolutionPreset.high); 61 | 62 | try { 63 | await cameraController.initialize(); 64 | } catch (e) { 65 | print(e); 66 | } 67 | 68 | // cameraController.startImageStream(onAvailable); 69 | 70 | notifyListeners(); 71 | // cameraController.initialize().then((_) { 72 | 73 | // cameraController.prepareForVideoRecording(); 74 | // faceDetector = FirebaseVision.instance.faceDetector(); 75 | // // captureFrame(); 76 | // notifyListeners(); 77 | // }); 78 | } 79 | 80 | dispose() { 81 | cameraController.dispose(); 82 | super.dispose(); 83 | } 84 | 85 | changeCamera() { 86 | if (curCamera == 0) { 87 | curCamera = 1; 88 | } else { 89 | curCamera = 0; 90 | } 91 | cameraController = 92 | CameraController(cameras[curCamera], ResolutionPreset.max); 93 | cameraController.initialize().then((_) { 94 | notifyListeners(); 95 | }); 96 | } 97 | 98 | changePhotoWidget() { 99 | ifMakeVideo = !ifMakeVideo; 100 | notifyListeners(); 101 | } 102 | 103 | @override 104 | Widget build(BuildContext context) { 105 | // TODO: implement build 106 | return null; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/providers/PostsGalleryProvider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:douyin_demo/models/PostsModel.dart'; 5 | import 'package:douyin_demo/widgets/WebRequest.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:http/http.dart' as http; 8 | import 'package:provider/provider.dart'; 9 | 10 | 11 | class PostsGalleryProvider with ChangeNotifier { 12 | List model1 = List(); 13 | List model2 = List(); 14 | double _len1 = 0; 15 | double _len2 = 0; 16 | List posts = List(); 17 | 18 | PostsGalleryProvider() { 19 | getPosts(0, 0); 20 | // notifyListeners(); 21 | } 22 | 23 | dispose(){ 24 | super.dispose(); 25 | } 26 | 27 | Future getPosts(orderType, ifRefresh) async { 28 | Uri url = await WebRequest().generate('posts/getPosts', { 29 | "openId": "ol_BV4zcyVJaOBtOTD5AfpkFERww", 30 | "dataFrom": "0", 31 | "count": "30", 32 | "refreshTime": DateTime.now().toString(), 33 | "currentSel": "1", 34 | "ulo": "0", 35 | "ula": "0" 36 | }); 37 | 38 | var response = await http.get(url); 39 | //.then((response) { 40 | // var post = json.decode(response.body)["result"]; 41 | setGalleryModel(response.body); 42 | notifyListeners(); 43 | } 44 | 45 | setGalleryModel(String items) { 46 | var result = List(); 47 | 48 | var posts = json.decode(items)["result"]; 49 | // result.add(posts); 50 | 51 | for (var item in posts) { 52 | result.add(PostsModel.fromJson(item)); 53 | } 54 | for (var item in result) { 55 | if (_len1 <= _len2) { 56 | item.makerName=""; 57 | item.picsPath=""; 58 | item.postsLocation=""; 59 | item.postsReaded=0; 60 | model1.add(item); 61 | _len1 += item.picsRate; 62 | } else { 63 | model2.add(item); 64 | _len2 += item.picsRate; 65 | } 66 | } 67 | // notifyListeners(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/providers/RecommendProvider.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/foundation.dart'; 2 | import 'package:provider/provider.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class RecommendProvider extends State 6 | with ChangeNotifier, TickerProviderStateMixin { 7 | bool ifShowBottom = true; 8 | 9 | double screenHeight; 10 | List infos = List(); 11 | List followed = List(); 12 | TabController controller; 13 | MainInfo mainInfo; 14 | 15 | Reply reply; 16 | 17 | RecommendProvider() { 18 | controller = TabController(vsync: this, length: 2); 19 | mainInfo = MainInfo( 20 | avatarUrl: 21 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 22 | content: 23 | "奥斯卡答复哈士大夫哈师大发输电和健康阿萨德鸿福路口氨基酸的鸿福路口啊,奥斯卡答复哈士大夫哈师大发输电和健康阿萨德鸿福路口氨基酸的鸿福路口啊", 24 | favCount: 109, 25 | replyCount: 212, 26 | shareCount: 317, 27 | userName: "马有发", 28 | videoPath: "lib/images/pingguo.jpeg", 29 | desc: "马友发做的一个有意思的模拟抖音的小App,用的Flutter哦~", 30 | ifFaved: false); 31 | 32 | reply = Reply( 33 | ifFaved: true, 34 | afterReplies: List(), 35 | replyContent: "真可爱,真好看,真厉害~真可爱,真好看,真厉害~", 36 | replyMakerAvatar: 37 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 38 | replyMakerName: "ABC", 39 | whenReplied: "3小时前"); 40 | 41 | infos.add(MainInfo( 42 | avatarUrl: 43 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 44 | content: "弗兰克斯代理费绿色出行女郎自行车卡水电费卡;双列的会计法第十六届法律深刻江东父老;看氨基酸的;联发科", 45 | favCount: 219, 46 | replyCount: 329, 47 | shareCount: 1222, 48 | userName: "范德彪", 49 | videoPath: "lib/images/sky.jpg", 50 | desc: "这个天空的图有点好看", 51 | ifFaved: true)); 52 | infos.add(MainInfo( 53 | avatarUrl: 54 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 55 | content: "弗兰克斯代理费绿色出行女郎自行车卡水电费卡;双列的会计法第十六届法律深刻江东父老;看氨基酸的;联发科", 56 | favCount: 119, 57 | replyCount: 189, 58 | shareCount: 262, 59 | userName: "马大帅", 60 | videoPath: "lib/images/temple.jpg", 61 | desc: "我喜欢拜佛", 62 | ifFaved: true)); 63 | 64 | infos.add(MainInfo( 65 | avatarUrl: 66 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 67 | content: "弗兰克斯代理费绿色出行女郎自行车卡水电费卡;双列的会计法第十六届法律深刻江东父老;看氨基酸的;联发科", 68 | favCount: 98, 69 | replyCount: 222, 70 | shareCount: 1983, 71 | userName: "ABC", 72 | videoPath: "lib/images/woman.jpg", 73 | desc: "黑色女人有黑色的美", 74 | ifFaved: true)); 75 | 76 | followed.add(MainInfo( 77 | avatarUrl: 78 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 79 | content: "弗兰克斯代理费绿色出行女郎自行车卡水电费卡;双列的会计法第十六届法律深刻江东父老;看氨基酸的;联发科", 80 | favCount: 219, 81 | replyCount: 329, 82 | shareCount: 1222, 83 | userName: "范德彪", 84 | videoPath: "lib/images/whitehouse.jpg", 85 | desc: "这个天空的图有点好看", 86 | ifFaved: true)); 87 | followed.add(MainInfo( 88 | avatarUrl: 89 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 90 | content: "弗兰克斯代理费绿色出行女郎自行车卡水电费卡;双列的会计法第十六届法律深刻江东父老;看氨基酸的;联发科", 91 | favCount: 119, 92 | replyCount: 189, 93 | shareCount: 262, 94 | userName: "马大帅", 95 | videoPath: "lib/images/waterdrop.jpg", 96 | desc: "我喜欢拜佛", 97 | ifFaved: true)); 98 | 99 | followed.add(MainInfo( 100 | avatarUrl: 101 | "https://pic2.zhimg.com/v2-a88cd7618933272ca681f86398e6240d_xll.jpg", 102 | content: "弗兰克斯代理费绿色出行女郎自行车卡水电费卡;双列的会计法第十六届法律深刻江东父老;看氨基酸的;联发科", 103 | favCount: 98, 104 | replyCount: 222, 105 | shareCount: 1983, 106 | userName: "ABC", 107 | videoPath: "lib/images/woman2.jpg", 108 | desc: "黑色女人有黑色的美", 109 | ifFaved: true)); 110 | } 111 | 112 | setScreenHeight(height) { 113 | screenHeight = height; 114 | notifyListeners(); 115 | } 116 | 117 | setTabController(ctrl) { 118 | controller = ctrl; 119 | // notifyListeners(); 120 | } 121 | 122 | hideBottomBar() { 123 | ifShowBottom = false; 124 | notifyListeners(); 125 | } 126 | 127 | tapFav() { 128 | mainInfo.ifFaved = !mainInfo.ifFaved; 129 | if (mainInfo.ifFaved) { 130 | mainInfo.favCount += 1; 131 | } else { 132 | mainInfo.favCount -= 1; 133 | } 134 | notifyListeners(); 135 | } 136 | 137 | @override 138 | Widget build(BuildContext context) { 139 | // TODO: implement build 140 | return null; 141 | } 142 | } 143 | 144 | class MainInfo { 145 | String avatarUrl; 146 | String userName; 147 | String content; 148 | int favCount; 149 | int replyCount; 150 | int shareCount; 151 | String videoPath; 152 | String desc; 153 | bool ifFaved; 154 | 155 | MainInfo( 156 | {this.avatarUrl, 157 | this.content, 158 | this.favCount, 159 | this.replyCount, 160 | this.shareCount, 161 | this.userName, 162 | this.videoPath, 163 | this.desc, 164 | this.ifFaved}); 165 | } 166 | 167 | class Reply { 168 | String replyMakerName; 169 | String replyMakerAvatar; 170 | String replyContent; 171 | String whenReplied; 172 | bool ifFaved; 173 | List afterReplies; 174 | 175 | Reply( 176 | {this.ifFaved, 177 | this.afterReplies, 178 | this.replyContent, 179 | this.replyMakerAvatar, 180 | this.replyMakerName, 181 | this.whenReplied}); 182 | } 183 | -------------------------------------------------------------------------------- /lib/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:typed_data'; 3 | import 'dart:ui'; 4 | 5 | import 'package:camera/camera.dart'; 6 | import 'package:firebase_ml_vision/firebase_ml_vision.dart'; 7 | import 'package:flutter/foundation.dart'; 8 | 9 | typedef HandleDetection = Future Function(FirebaseVisionImage image); 10 | 11 | Future getCamera(CameraLensDirection dir) async { 12 | return await availableCameras().then( 13 | (List cameras) => cameras.firstWhere( 14 | (CameraDescription camera) => camera.lensDirection == dir, 15 | ), 16 | ); 17 | } 18 | 19 | Uint8List concatenatePlanes(List planes) { 20 | final WriteBuffer allBytes = WriteBuffer(); 21 | planes.forEach((Plane plane) => allBytes.putUint8List(plane.bytes)); 22 | return allBytes.done().buffer.asUint8List(); 23 | } 24 | 25 | FirebaseVisionImageMetadata buildMetaData( 26 | CameraImage image, 27 | ImageRotation rotation, 28 | ) { 29 | return FirebaseVisionImageMetadata( 30 | rawFormat: image.format.raw, 31 | size: Size(image.width.toDouble(), image.height.toDouble()), 32 | rotation: rotation, 33 | planeData: image.planes.map( 34 | (Plane plane) { 35 | return FirebaseVisionImagePlaneMetadata( 36 | bytesPerRow: plane.bytesPerRow, 37 | height: plane.height, 38 | width: plane.width, 39 | ); 40 | }, 41 | ).toList(), 42 | ); 43 | } 44 | 45 | Future detect( 46 | CameraImage image, 47 | HandleDetection handleDetection, 48 | ImageRotation rotation, 49 | ) async { 50 | return handleDetection( 51 | FirebaseVisionImage.fromBytes( 52 | concatenatePlanes(image.planes), 53 | buildMetaData(image, rotation), 54 | ), 55 | ); 56 | } 57 | 58 | ImageRotation rotationIntToImageRotation(int rotation) { 59 | switch (rotation) { 60 | case 0: 61 | return ImageRotation.rotation0; 62 | case 90: 63 | return ImageRotation.rotation90; 64 | case 180: 65 | return ImageRotation.rotation180; 66 | default: 67 | assert(rotation == 270); 68 | return ImageRotation.rotation270; 69 | } 70 | } -------------------------------------------------------------------------------- /lib/widgets/BottomBar.dart: -------------------------------------------------------------------------------- 1 | import 'package:douyin_demo/main.dart'; 2 | import 'package:douyin_demo/pages/CameraPage/CameraMain.dart'; 3 | import 'package:douyin_demo/pages/FaceDetect/FaceDetection.dart'; 4 | import 'package:douyin_demo/pages/loadData/loadData.dart'; 5 | import 'package:douyin_demo/pages/sameCity/SameCityPage.dart'; 6 | import 'package:douyin_demo/pages/selfHome/HomePage.dart'; 7 | import 'package:douyin_demo/providers/CameraProvider.dart'; 8 | import 'package:douyin_demo/providers/PostsGalleryProvider.dart'; 9 | import 'package:douyin_demo/providers/RecommendProvider.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:provider/provider.dart'; 12 | 13 | // class BtmBar extends StatelessWidget { 14 | // const BtmBar({Key key}) : super(key: key); 15 | 16 | // @override 17 | // Widget build(BuildContext context) { 18 | // // RecommendProvider provider = Provider.of(context); 19 | // return Container( 20 | // child: Row( 21 | // mainAxisAlignment: MainAxisAlignment.spaceAround, 22 | // children: [ 23 | // getBtmTextWidget("首页", true), 24 | // getBtmTextWidget("同城", false), 25 | // AddIcon(), 26 | // getBtmTextWidget("消息", false), 27 | // getBtmTextWidget("我", false), 28 | // ], 29 | // ), 30 | // ); 31 | // } 32 | // } 33 | 34 | class BtmBar extends StatefulWidget { 35 | BtmBar({Key key, this.selectIndex}) : super(key: key); 36 | final int selectIndex; 37 | 38 | _BtmBarState createState() => _BtmBarState(); 39 | } 40 | 41 | class _BtmBarState extends State { 42 | List selected = List(); 43 | List selectItems = List(); 44 | @override 45 | void initState() { 46 | super.initState(); 47 | for (var i = 0; i < 4; i++) { 48 | selected.add(false); 49 | } 50 | selected[widget.selectIndex] = true; 51 | } 52 | 53 | @override 54 | void dispose() { 55 | // _controller.dispose(); 56 | super.dispose(); 57 | } 58 | 59 | tapItem(index) { 60 | // selected=List(); 61 | // for (var i = 0; i < 4; i++) { 62 | // selected.add(false); 63 | // } 64 | // selected[index]=true; 65 | // setState(() { 66 | 67 | // selected=selected; 68 | // }); 69 | switch (index) { 70 | // case 0: 71 | // Navigator.pushAndRemoveUntil( 72 | // context, 73 | // MaterialPageRoute( 74 | // builder: (context) => MyHomePage( 75 | // selIndex: index, 76 | // )), 77 | // ModalRoute.withName("/Home")); 78 | // break; 79 | case 1: 80 | Navigator.pushAndRemoveUntil( 81 | context, 82 | MaterialPageRoute( 83 | builder: (context) => MultiProvider( 84 | providers: [ 85 | ChangeNotifierProvider( 86 | builder: (context) => PostsGalleryProvider(), 87 | ) 88 | ], 89 | child: SameCityMain( 90 | selIndex: index, 91 | ))), 92 | ModalRoute.withName("/sameCity")); 93 | break; 94 | case 2: 95 | Navigator.pushAndRemoveUntil( 96 | context, 97 | MaterialPageRoute( 98 | builder: (context) => SelfHomePage()), 99 | ModalRoute.withName("/selfHome")); 100 | break; 101 | case 3: 102 | Navigator.of(context).push(new MaterialPageRoute( 103 | builder: (BuildContext context) { 104 | return MultiProvider( 105 | providers: [ 106 | ChangeNotifierProvider( 107 | builder: (_) => CameraProvider(), 108 | ) 109 | ], 110 | child: CameraPage( 111 | // rpx: MediaQuery.of(context).size.width / 750, 112 | ) 113 | ); 114 | }, 115 | fullscreenDialog: true 116 | )); 117 | break; 118 | default: 119 | break; 120 | } 121 | } 122 | 123 | @override 124 | Widget build(BuildContext context) { 125 | // RecommendProvider provider = Provider.of(context); 126 | double rpx = MediaQuery.of(context).size.width / 750; 127 | return Container( 128 | child: Row( 129 | mainAxisAlignment: MainAxisAlignment.spaceAround, 130 | children: [ 131 | Expanded( 132 | flex: 1, 133 | child: getBtmTextWidget("首页", selected[0], () { 134 | tapItem(0); 135 | }, rpx)), 136 | Expanded( 137 | flex: 1, 138 | child: getBtmTextWidget("同城", selected[1], () { 139 | tapItem(1); 140 | }, rpx)), 141 | Expanded(flex: 1, child: AddIcon(tapItem:(){ tapItem(3); },)), 142 | Expanded( 143 | flex: 1, 144 | child: getBtmTextWidget("消息", selected[2], () { 145 | tapItem(2); 146 | }, rpx)), 147 | Expanded( 148 | flex: 1, 149 | child: getBtmTextWidget("我", selected[3], () { 150 | tapItem(3); 151 | }, rpx)), 152 | ], 153 | ), 154 | ); 155 | } 156 | } 157 | 158 | getBtmTextWidget(String content, bool ifSelected, tapFunc, double rpx) { 159 | return FlatButton( 160 | onPressed: () { 161 | tapFunc(); 162 | }, 163 | child: Text("$content", 164 | style: ifSelected 165 | ? TextStyle( 166 | fontSize: 30 * rpx, 167 | color: Colors.white, 168 | fontWeight: FontWeight.w900) 169 | : TextStyle( 170 | fontSize: 30 * rpx, 171 | color: Colors.grey[600], 172 | fontWeight: FontWeight.w900))); 173 | } 174 | 175 | class AddIcon extends StatelessWidget { 176 | const AddIcon({Key key,@required this.tapItem}) : super(key: key); 177 | final VoidCallback tapItem; 178 | @override 179 | Widget build(BuildContext context) { 180 | double rpx = MediaQuery.of(context).size.width / 750; 181 | double iconHeight = 55 * rpx; 182 | double totalWidth = 90 * rpx; 183 | double eachSide = 5 * rpx; 184 | return Container( 185 | // decoration: BoxDecoration(), 186 | padding: EdgeInsets.symmetric(horizontal: 30 * rpx), 187 | height: iconHeight, 188 | width: 150 * rpx, 189 | child: FlatButton( 190 | padding: EdgeInsets.all(0), 191 | onPressed: (){tapItem();}, 192 | child: Stack( 193 | children: [ 194 | Positioned( 195 | height: iconHeight, 196 | width: totalWidth - eachSide, 197 | child: Container( 198 | decoration: BoxDecoration( 199 | color: Colors.cyan, borderRadius: BorderRadius.circular(10)), 200 | ), 201 | ), 202 | Positioned( 203 | height: iconHeight, 204 | width: totalWidth - eachSide, 205 | right: 0, 206 | child: Container( 207 | decoration: BoxDecoration( 208 | color: Colors.redAccent, 209 | borderRadius: BorderRadius.circular(10)), 210 | ), 211 | ), 212 | Positioned( 213 | height: iconHeight, 214 | width: totalWidth - eachSide * 2, 215 | right: eachSide, 216 | child: Container( 217 | decoration: BoxDecoration( 218 | color: Colors.white, borderRadius: BorderRadius.circular(10)), 219 | child: Icon(Icons.add), 220 | ), 221 | ), 222 | ], 223 | ) 224 | ), 225 | ); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /lib/widgets/FavAnimation.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter/material.dart'; 2 | 3 | // class AnimateFav extends StatefulWidget { 4 | // AnimateFav({Key key, this.size}) : super(key: key); 5 | // final double size; 6 | 7 | // _AnimateFavState createState() => _AnimateFavState(); 8 | // } 9 | 10 | // class _AnimateFavState extends State 11 | // with TickerProviderStateMixin { 12 | // AnimationController _controller_1; 13 | // AnimationController _controller_2; 14 | // Animation _animation_1; 15 | // Animation _animation_2; 16 | // Animation curAnimation; 17 | // double rpx; 18 | // @override 19 | // void initState() { 20 | // super.initState(); 21 | 22 | // _controller_1 = 23 | // AnimationController(vsync: this, duration: Duration(milliseconds: 300)); 24 | // _controller_2 = 25 | // AnimationController(vsync: this, duration: Duration(milliseconds: 200)); 26 | // _animation_1 = Tween(begin: 0.0, end: 1.3).animate(_controller_1) 27 | // ..addStatusListener((status) { 28 | // if (status == AnimationStatus.completed) { 29 | // _controller_2.forward(from: 0); 30 | // setState(() { 31 | // curAnimation = _animation_2; 32 | // }); 33 | // } 34 | // }) 35 | // ..addListener(() { 36 | // setState(() {}); 37 | // }); 38 | // _animation_2 = Tween(begin: 1.3, end: 1.0).animate(_controller_2) 39 | // ..addListener(() { 40 | // setState(() {}); 41 | // }); 42 | // curAnimation = _animation_1; 43 | // _controller_1.forward(from: 0); 44 | // } 45 | 46 | // @override 47 | // Widget build(BuildContext context) { 48 | // rpx = MediaQuery.of(context).size.width / 750; 49 | // return Center( 50 | // child: Icon( 51 | // Icons.favorite, 52 | // size: widget.size * curAnimation.value, 53 | // color: Colors.redAccent, 54 | // )); 55 | // } 56 | // } 57 | 58 | // class AnimatedUnFav extends StatefulWidget { 59 | // AnimatedUnFav({Key key,@required this.size}) : super(key: key); 60 | // final double size; 61 | // _AnimatedUnFavState createState() => _AnimatedUnFavState(); 62 | // } 63 | 64 | // class _AnimatedUnFavState extends State with TickerProviderStateMixin { 65 | // AnimationController _controller_1; 66 | // AnimationController _controller_2; 67 | // Animation _animation_1; 68 | // Animation _animation_2; 69 | // Animation curAnimation; 70 | // Color curColor; 71 | // @override 72 | // void initState() { 73 | // super.initState(); 74 | // curColor=Colors.redAccent; 75 | // _controller_1 = 76 | // AnimationController(vsync: this, duration: Duration(milliseconds: 100)); 77 | // _controller_2 = 78 | // AnimationController(vsync: this, duration: Duration(milliseconds: 100)); 79 | // _animation_1 = Tween(begin: 1.0, end: 1.2).animate(_controller_1) 80 | // ..addStatusListener((status) { 81 | // if (status == AnimationStatus.completed) { 82 | // _controller_2.forward(from: 0); 83 | // setState(() { 84 | // curAnimation = _animation_2; 85 | // curColor=Colors.grey[100]; 86 | // }); 87 | // } 88 | // }) 89 | // ..addListener(() { 90 | // setState(() {}); 91 | // }); 92 | // _animation_2 = Tween(begin: 1.2, end: 1.0).animate(_controller_2) 93 | // ..addListener(() { 94 | // setState(() {}); 95 | // }); 96 | // curAnimation = _animation_1; 97 | // _controller_1.forward(from: 0); 98 | // } 99 | 100 | // @override 101 | // Widget build(BuildContext context) { 102 | // // rpx = MediaQuery.of(context).size.width / 750; 103 | // return Center( 104 | // child: Icon( 105 | // Icons.favorite, 106 | // size: widget.size * curAnimation.value, 107 | // color: curColor, 108 | // )); 109 | // } 110 | // } 111 | 112 | import 'dart:async'; 113 | 114 | import 'package:douyin_demo/providers/RecommendProvider.dart'; 115 | import 'package:flutter/material.dart'; 116 | // import 'package:provider/provider.dart'; 117 | 118 | // class AnimatePositiveIcon extends StatefulWidget { 119 | // AnimatePositiveIcon({Key key, @required this.size, this.callback}) 120 | // : super(key: key); 121 | // final double size; 122 | // final VoidCallback callback; 123 | // _AnimatePositiveIconState createState() => _AnimatePositiveIconState(); 124 | // } 125 | 126 | // class _AnimatePositiveIconState extends State 127 | // with TickerProviderStateMixin { 128 | // AnimationController _controller1; 129 | // AnimationController _controller2; 130 | // AnimationController _controller3; 131 | 132 | // Animation _animation1; 133 | // Animation _animation2; 134 | // Animation _animation3; 135 | 136 | // Animation curAnimation; 137 | 138 | // Color curColor; 139 | 140 | // @override 141 | // void initState() { 142 | // super.initState(); 143 | // _controller1 = 144 | // AnimationController(vsync: this, duration: Duration(milliseconds: 150)); 145 | // _controller2 = 146 | // AnimationController(vsync: this, duration: Duration(milliseconds: 200)); 147 | // _controller3 = 148 | // AnimationController(vsync: this, duration: Duration(milliseconds: 60)); 149 | 150 | // curColor = Colors.grey[100]; 151 | // _animation1 = Tween(begin: 1.0, end: 0.0).animate(_controller1) 152 | // ..addListener(() { 153 | // setState(() {}); 154 | // }) 155 | // ..addStatusListener((status) { 156 | // if (status == AnimationStatus.completed) { 157 | // _controller2.forward(from: 0); 158 | // curAnimation = _animation2; 159 | // curColor = Colors.redAccent; 160 | // } 161 | // }); 162 | 163 | // _animation2 = Tween(begin: 0.0, end: 1.2).animate(_controller2) 164 | // ..addListener(() { 165 | // setState(() {}); 166 | // }) 167 | // ..addStatusListener((status) { 168 | // if (status == AnimationStatus.completed) { 169 | // _controller3.forward(from: 0); 170 | // curAnimation = _animation3; 171 | // } 172 | // }); 173 | 174 | // _animation3 = Tween(begin: 1.2, end: 1.0).animate(_controller3) 175 | // ..addListener(() { 176 | // setState(() {}); 177 | // }) 178 | // ..addStatusListener((status) { 179 | // if (status == AnimationStatus.completed && widget.callback != null) { 180 | // widget.callback(); 181 | // } 182 | // }); 183 | // // _controller1.forward(from: 0).then((_) { 184 | // // _controller2.forward(from: 0).then((_){ 185 | // // _controller3.forward(from: 0); 186 | // // }); 187 | // // }); 188 | // curAnimation = _animation1; 189 | // _controller1.forward(from: 0); 190 | // } 191 | 192 | // @override 193 | // Widget build(BuildContext context) { 194 | // return Container( 195 | // child: Icon( 196 | // Icons.favorite, 197 | // size: widget.size * curAnimation.value, 198 | // color: curColor, 199 | // ), 200 | // ); 201 | // } 202 | // } 203 | 204 | // class AnimateNegtiveIcon extends StatefulWidget { 205 | // AnimateNegtiveIcon( 206 | // {Key key, 207 | // @required this.size, 208 | // @required this.icon, 209 | // @required this.startColor, 210 | // @required this.endColor, 211 | // this.callback}) 212 | // : super(key: key); 213 | // final double size; 214 | // final IconData icon; 215 | // final VoidCallback callback; 216 | // final Color startColor; 217 | // final Color endColor; 218 | // _AnimateNegtiveIconState createState() => _AnimateNegtiveIconState(); 219 | // } 220 | 221 | // class _AnimateNegtiveIconState extends State 222 | // with TickerProviderStateMixin { 223 | // AnimationController _controller1; 224 | // AnimationController _controller2; 225 | 226 | // Animation _animation1; 227 | // Animation _animation2; 228 | 229 | // Animation curAnimation; 230 | 231 | // Color curColor; 232 | 233 | // @override 234 | // void initState() { 235 | // super.initState(); 236 | // _controller1 = 237 | // AnimationController(vsync: this, duration: Duration(milliseconds: 200)); 238 | // _controller2 = 239 | // AnimationController(vsync: this, duration: Duration(milliseconds: 200)); 240 | 241 | // curColor = widget.startColor; 242 | // _animation1 = Tween(begin: 1.0, end: 1.2).animate(_controller1) 243 | // ..addListener(() { 244 | // setState(() {}); 245 | // }) 246 | // ..addStatusListener((status) { 247 | // if (status == AnimationStatus.completed) { 248 | // _controller2.forward(from: 0); 249 | // curAnimation = _animation2; 250 | // curColor = widget.endColor; 251 | // } 252 | // }); 253 | 254 | // _animation2 = Tween(begin: 1.2, end: 1.0).animate(_controller2) 255 | // ..addListener(() { 256 | // setState(() {}); 257 | // }) 258 | // ..addStatusListener((status) { 259 | // if (status == AnimationStatus.completed && widget.callback != null) { 260 | // Timer(Duration(milliseconds: 100), () { 261 | // widget.callback(); 262 | // }); 263 | // } 264 | // }); 265 | // // _controller1.forward(from: 0).then((_) { 266 | // // _controller2.forward(from: 0).then((_){ 267 | // // _controller3.forward(from: 0); 268 | // // }); 269 | // // }); 270 | // curAnimation = _animation1; 271 | // } 272 | 273 | // @override 274 | // Widget build(BuildContext context) { 275 | // return IconButton( 276 | // padding: EdgeInsets.all(0), 277 | // onPressed: () { 278 | // _controller1.forward(from: 0); 279 | // }, 280 | // icon: Icon( 281 | // widget.icon, 282 | // semanticLabel: "label", 283 | // size: widget.size * curAnimation.value, 284 | // color: curColor, 285 | // )); 286 | // } 287 | // } 288 | 289 | class AnimatedIconWidget extends StatefulWidget { 290 | AnimatedIconWidget( 291 | {Key key, 292 | @required this.animationList, 293 | @required this.icon, 294 | @required this.size, 295 | this.callback, 296 | this.callbackDelay, 297 | this.provider}) 298 | : super(key: key); 299 | final List animationList; 300 | final IconData icon; 301 | final VoidCallback callback; 302 | final double size; 303 | final RecommendProvider provider; 304 | final Duration callbackDelay; 305 | _AnimatedIconWidgetState createState() => _AnimatedIconWidgetState(); 306 | } 307 | 308 | class _AnimatedIconWidgetState extends State 309 | with TickerProviderStateMixin { 310 | List anis = List(); 311 | List controllers = List(); 312 | List> animations = List>(); 313 | Animation curAnim; 314 | Color curColor; 315 | int curIndex = 0; 316 | List ifAdded = List(); 317 | double curSize; 318 | bool ifInit = true; 319 | 320 | loopAnimation(index) { 321 | curColor = curColor == null ? anis.first.color : curColor; 322 | if (index < controllers.length - 1) { 323 | if (!ifAdded[index]) { 324 | animations[index] = animations[index] 325 | ..addStatusListener((status) { 326 | if (status == AnimationStatus.completed) { 327 | curAnim = animations[index + 1]; 328 | curColor = anis[index + 1].color; 329 | 330 | controllers[index + 1].forward(from: 0); 331 | loopAnimation(index + 1); 332 | } 333 | }) 334 | ..addListener(() { 335 | setState(() {}); 336 | }); 337 | ifAdded[index] = true; 338 | } 339 | } else if (index == controllers.length - 1) { 340 | if (!ifAdded[index]) { 341 | animations[index] = animations[index] 342 | ..addStatusListener((status) { 343 | curColor = anis[index].color; 344 | if (status == AnimationStatus.completed) { 345 | if (widget.callback != null) { 346 | if (widget.callbackDelay == null) { 347 | widget.callback(); 348 | } else { 349 | Timer(widget.callbackDelay, () { 350 | widget.callback(); 351 | }); 352 | } 353 | } 354 | } 355 | }) 356 | ..addListener(() { 357 | setState(() {}); 358 | }); 359 | ifAdded[index] = true; 360 | } 361 | } 362 | } 363 | 364 | @override 365 | void initState() { 366 | super.initState(); 367 | anis = widget.animationList; 368 | 369 | List.generate(anis.length, (index) { 370 | var curAni = anis[index]; 371 | ifAdded.add(false); 372 | controllers.add( 373 | AnimationController(vsync: this, duration: anis[index].duration)); 374 | animations.add(Tween(begin: curAni.start, end: curAni.end) 375 | .animate(controllers[index])); 376 | }); 377 | curAnim = animations.first; 378 | loopAnimation(0); 379 | } 380 | 381 | @override 382 | Widget build(BuildContext context) { 383 | return IconButton( 384 | padding: EdgeInsets.all(0), 385 | onPressed: () { 386 | controllers.first.forward(from: 0); 387 | }, 388 | icon: Icon( 389 | widget.icon, 390 | size: widget.size * curAnim.value, 391 | color: curColor, 392 | ), 393 | ); 394 | } 395 | } 396 | 397 | class IconAnimationStage { 398 | double start; 399 | double end; 400 | Color color; 401 | Duration duration; 402 | 403 | IconAnimationStage({this.color, this.duration, this.end, this.start}); 404 | } 405 | -------------------------------------------------------------------------------- /lib/widgets/WebRequest.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | class WebRequest extends Object { 6 | // 7 | bool ifPrd; 8 | bool ifIos; 9 | Future generate(String path, Map params) async { 10 | final prefs = await SharedPreferences.getInstance(); 11 | String hosts; 12 | String scheme; 13 | int ports; 14 | 15 | if (prefs.getBool("ifPrd")) { 16 | hosts = prefs.getString('urlPath_p'); 17 | scheme = prefs.getString('scheme_p'); 18 | ports = prefs.getInt('ports_p'); 19 | } else { 20 | if (prefs.getBool("ifIOS")) { 21 | hosts = prefs.getString('urlPath_ios_d'); 22 | scheme = prefs.getString('scheme_ios_d'); 23 | ports = prefs.getInt('ports_ios_d'); 24 | } else { 25 | hosts = prefs.getString('urlPath_and_d'); 26 | scheme = prefs.getString('scheme_and_d'); 27 | ports = prefs.getInt('ports_and_d'); 28 | } 29 | } 30 | 31 | if(prefs.getBool("ifReal_d")){ 32 | hosts = prefs.getString('urlPath_real_d'); 33 | scheme = prefs.getString('scheme_real_d'); 34 | ports = prefs.getInt('ports_real_d'); 35 | } 36 | Uri url = Uri( 37 | scheme: scheme, //http https 38 | host: hosts, 39 | port: ports, 40 | path: path, 41 | queryParameters: params); 42 | return url; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | after_layout: 5 | dependency: "direct main" 6 | description: 7 | name: after_layout 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "1.0.7+2" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "0.38.4" 18 | archive: 19 | dependency: transitive 20 | description: 21 | name: archive 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.0.11" 25 | args: 26 | dependency: transitive 27 | description: 28 | name: args 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.5.2" 32 | async: 33 | dependency: transitive 34 | description: 35 | name: async 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "2.4.0" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.0.5" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.2.0" 53 | build_config: 54 | dependency: transitive 55 | description: 56 | name: build_config 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "0.4.1+1" 60 | build_daemon: 61 | dependency: transitive 62 | description: 63 | name: build_daemon 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "2.1.0" 67 | build_resolvers: 68 | dependency: transitive 69 | description: 70 | name: build_resolvers 71 | url: "https://pub.flutter-io.cn" 72 | source: hosted 73 | version: "1.1.1" 74 | build_runner: 75 | dependency: "direct dev" 76 | description: 77 | name: build_runner 78 | url: "https://pub.flutter-io.cn" 79 | source: hosted 80 | version: "1.7.1" 81 | build_runner_core: 82 | dependency: transitive 83 | description: 84 | name: build_runner_core 85 | url: "https://pub.flutter-io.cn" 86 | source: hosted 87 | version: "4.1.0" 88 | built_collection: 89 | dependency: transitive 90 | description: 91 | name: built_collection 92 | url: "https://pub.flutter-io.cn" 93 | source: hosted 94 | version: "4.2.2" 95 | built_value: 96 | dependency: transitive 97 | description: 98 | name: built_value 99 | url: "https://pub.flutter-io.cn" 100 | source: hosted 101 | version: "6.7.1" 102 | camera: 103 | dependency: "direct main" 104 | description: 105 | name: camera 106 | url: "https://pub.flutter-io.cn" 107 | source: hosted 108 | version: "0.5.6" 109 | charcode: 110 | dependency: transitive 111 | description: 112 | name: charcode 113 | url: "https://pub.flutter-io.cn" 114 | source: hosted 115 | version: "1.1.2" 116 | checked_yaml: 117 | dependency: transitive 118 | description: 119 | name: checked_yaml 120 | url: "https://pub.flutter-io.cn" 121 | source: hosted 122 | version: "1.0.2" 123 | code_builder: 124 | dependency: transitive 125 | description: 126 | name: code_builder 127 | url: "https://pub.flutter-io.cn" 128 | source: hosted 129 | version: "3.2.0" 130 | collection: 131 | dependency: transitive 132 | description: 133 | name: collection 134 | url: "https://pub.flutter-io.cn" 135 | source: hosted 136 | version: "1.14.11" 137 | convert: 138 | dependency: transitive 139 | description: 140 | name: convert 141 | url: "https://pub.flutter-io.cn" 142 | source: hosted 143 | version: "2.1.1" 144 | crypto: 145 | dependency: transitive 146 | description: 147 | name: crypto 148 | url: "https://pub.flutter-io.cn" 149 | source: hosted 150 | version: "2.1.3" 151 | csslib: 152 | dependency: transitive 153 | description: 154 | name: csslib 155 | url: "https://pub.flutter-io.cn" 156 | source: hosted 157 | version: "0.16.1" 158 | cupertino_icons: 159 | dependency: "direct main" 160 | description: 161 | name: cupertino_icons 162 | url: "https://pub.flutter-io.cn" 163 | source: hosted 164 | version: "0.1.2" 165 | dart_style: 166 | dependency: transitive 167 | description: 168 | name: dart_style 169 | url: "https://pub.flutter-io.cn" 170 | source: hosted 171 | version: "1.3.1" 172 | firebase_ml_vision: 173 | dependency: "direct main" 174 | description: 175 | name: firebase_ml_vision 176 | url: "https://pub.flutter-io.cn" 177 | source: hosted 178 | version: "0.9.2+2" 179 | fixnum: 180 | dependency: transitive 181 | description: 182 | name: fixnum 183 | url: "https://pub.flutter-io.cn" 184 | source: hosted 185 | version: "0.10.9" 186 | flutter: 187 | dependency: "direct main" 188 | description: flutter 189 | source: sdk 190 | version: "0.0.0" 191 | flutter_page_indicator: 192 | dependency: transitive 193 | description: 194 | name: flutter_page_indicator 195 | url: "https://pub.flutter-io.cn" 196 | source: hosted 197 | version: "0.0.3" 198 | flutter_swiper: 199 | dependency: "direct main" 200 | description: 201 | name: flutter_swiper 202 | url: "https://pub.flutter-io.cn" 203 | source: hosted 204 | version: "1.1.6" 205 | flutter_test: 206 | dependency: "direct dev" 207 | description: flutter 208 | source: sdk 209 | version: "0.0.0" 210 | front_end: 211 | dependency: transitive 212 | description: 213 | name: front_end 214 | url: "https://pub.flutter-io.cn" 215 | source: hosted 216 | version: "0.1.26" 217 | glob: 218 | dependency: transitive 219 | description: 220 | name: glob 221 | url: "https://pub.flutter-io.cn" 222 | source: hosted 223 | version: "1.1.7" 224 | graphs: 225 | dependency: transitive 226 | description: 227 | name: graphs 228 | url: "https://pub.flutter-io.cn" 229 | source: hosted 230 | version: "0.2.0" 231 | html: 232 | dependency: transitive 233 | description: 234 | name: html 235 | url: "https://pub.flutter-io.cn" 236 | source: hosted 237 | version: "0.14.0+3" 238 | http: 239 | dependency: transitive 240 | description: 241 | name: http 242 | url: "https://pub.flutter-io.cn" 243 | source: hosted 244 | version: "0.12.0+2" 245 | http_multi_server: 246 | dependency: transitive 247 | description: 248 | name: http_multi_server 249 | url: "https://pub.flutter-io.cn" 250 | source: hosted 251 | version: "2.1.0" 252 | http_parser: 253 | dependency: transitive 254 | description: 255 | name: http_parser 256 | url: "https://pub.flutter-io.cn" 257 | source: hosted 258 | version: "3.1.3" 259 | image: 260 | dependency: transitive 261 | description: 262 | name: image 263 | url: "https://pub.flutter-io.cn" 264 | source: hosted 265 | version: "2.1.4" 266 | image_gallery_saver: 267 | dependency: "direct main" 268 | description: 269 | name: image_gallery_saver 270 | url: "https://pub.flutter-io.cn" 271 | source: hosted 272 | version: "1.2.2" 273 | image_picker: 274 | dependency: "direct main" 275 | description: 276 | name: image_picker 277 | url: "https://pub.flutter-io.cn" 278 | source: hosted 279 | version: "0.6.1+8" 280 | image_picker_saver: 281 | dependency: "direct main" 282 | description: 283 | name: image_picker_saver 284 | url: "https://pub.flutter-io.cn" 285 | source: hosted 286 | version: "0.3.0" 287 | io: 288 | dependency: transitive 289 | description: 290 | name: io 291 | url: "https://pub.flutter-io.cn" 292 | source: hosted 293 | version: "0.3.3" 294 | js: 295 | dependency: transitive 296 | description: 297 | name: js 298 | url: "https://pub.flutter-io.cn" 299 | source: hosted 300 | version: "0.6.1+1" 301 | json_annotation: 302 | dependency: "direct main" 303 | description: 304 | name: json_annotation 305 | url: "https://pub.flutter-io.cn" 306 | source: hosted 307 | version: "3.0.0" 308 | json_serializable: 309 | dependency: "direct dev" 310 | description: 311 | name: json_serializable 312 | url: "https://pub.flutter-io.cn" 313 | source: hosted 314 | version: "3.2.2" 315 | kernel: 316 | dependency: transitive 317 | description: 318 | name: kernel 319 | url: "https://pub.flutter-io.cn" 320 | source: hosted 321 | version: "0.3.26" 322 | logging: 323 | dependency: transitive 324 | description: 325 | name: logging 326 | url: "https://pub.flutter-io.cn" 327 | source: hosted 328 | version: "0.11.3+2" 329 | lpinyin: 330 | dependency: "direct main" 331 | description: 332 | name: lpinyin 333 | url: "https://pub.flutter-io.cn" 334 | source: hosted 335 | version: "1.0.7" 336 | marquee: 337 | dependency: "direct main" 338 | description: 339 | name: marquee 340 | url: "https://pub.flutter-io.cn" 341 | source: hosted 342 | version: "1.3.0" 343 | marquee_flutter: 344 | dependency: "direct main" 345 | description: 346 | name: marquee_flutter 347 | url: "https://pub.flutter-io.cn" 348 | source: hosted 349 | version: "0.1.4" 350 | matcher: 351 | dependency: transitive 352 | description: 353 | name: matcher 354 | url: "https://pub.flutter-io.cn" 355 | source: hosted 356 | version: "0.12.6" 357 | meta: 358 | dependency: transitive 359 | description: 360 | name: meta 361 | url: "https://pub.flutter-io.cn" 362 | source: hosted 363 | version: "1.1.8" 364 | mime: 365 | dependency: transitive 366 | description: 367 | name: mime 368 | url: "https://pub.flutter-io.cn" 369 | source: hosted 370 | version: "0.9.6+3" 371 | package_config: 372 | dependency: transitive 373 | description: 374 | name: package_config 375 | url: "https://pub.flutter-io.cn" 376 | source: hosted 377 | version: "1.1.0" 378 | package_resolver: 379 | dependency: transitive 380 | description: 381 | name: package_resolver 382 | url: "https://pub.flutter-io.cn" 383 | source: hosted 384 | version: "1.0.10" 385 | path: 386 | dependency: "direct main" 387 | description: 388 | name: path 389 | url: "https://pub.flutter-io.cn" 390 | source: hosted 391 | version: "1.6.4" 392 | path_provider: 393 | dependency: "direct main" 394 | description: 395 | name: path_provider 396 | url: "https://pub.flutter-io.cn" 397 | source: hosted 398 | version: "1.3.1" 399 | pedantic: 400 | dependency: transitive 401 | description: 402 | name: pedantic 403 | url: "https://pub.flutter-io.cn" 404 | source: hosted 405 | version: "1.8.0+1" 406 | petitparser: 407 | dependency: transitive 408 | description: 409 | name: petitparser 410 | url: "https://pub.flutter-io.cn" 411 | source: hosted 412 | version: "2.4.0" 413 | platform: 414 | dependency: transitive 415 | description: 416 | name: platform 417 | url: "https://pub.flutter-io.cn" 418 | source: hosted 419 | version: "2.2.1" 420 | pool: 421 | dependency: transitive 422 | description: 423 | name: pool 424 | url: "https://pub.flutter-io.cn" 425 | source: hosted 426 | version: "1.4.0" 427 | provider: 428 | dependency: "direct main" 429 | description: 430 | name: provider 431 | url: "https://pub.flutter-io.cn" 432 | source: hosted 433 | version: "3.1.0" 434 | pub_semver: 435 | dependency: transitive 436 | description: 437 | name: pub_semver 438 | url: "https://pub.flutter-io.cn" 439 | source: hosted 440 | version: "1.4.2" 441 | pubspec_parse: 442 | dependency: transitive 443 | description: 444 | name: pubspec_parse 445 | url: "https://pub.flutter-io.cn" 446 | source: hosted 447 | version: "0.1.5" 448 | quiver: 449 | dependency: transitive 450 | description: 451 | name: quiver 452 | url: "https://pub.flutter-io.cn" 453 | source: hosted 454 | version: "2.0.5" 455 | shared_preferences: 456 | dependency: "direct main" 457 | description: 458 | name: shared_preferences 459 | url: "https://pub.flutter-io.cn" 460 | source: hosted 461 | version: "0.5.3+4" 462 | shelf: 463 | dependency: transitive 464 | description: 465 | name: shelf 466 | url: "https://pub.flutter-io.cn" 467 | source: hosted 468 | version: "0.7.5" 469 | shelf_web_socket: 470 | dependency: transitive 471 | description: 472 | name: shelf_web_socket 473 | url: "https://pub.flutter-io.cn" 474 | source: hosted 475 | version: "0.2.3" 476 | sky_engine: 477 | dependency: transitive 478 | description: flutter 479 | source: sdk 480 | version: "0.0.99" 481 | source_gen: 482 | dependency: transitive 483 | description: 484 | name: source_gen 485 | url: "https://pub.flutter-io.cn" 486 | source: hosted 487 | version: "0.9.4+5" 488 | source_span: 489 | dependency: transitive 490 | description: 491 | name: source_span 492 | url: "https://pub.flutter-io.cn" 493 | source: hosted 494 | version: "1.5.5" 495 | stack_trace: 496 | dependency: transitive 497 | description: 498 | name: stack_trace 499 | url: "https://pub.flutter-io.cn" 500 | source: hosted 501 | version: "1.9.3" 502 | sticky_headers: 503 | dependency: "direct main" 504 | description: 505 | name: sticky_headers 506 | url: "https://pub.flutter-io.cn" 507 | source: hosted 508 | version: "0.1.8" 509 | stream_channel: 510 | dependency: transitive 511 | description: 512 | name: stream_channel 513 | url: "https://pub.flutter-io.cn" 514 | source: hosted 515 | version: "2.0.0" 516 | stream_transform: 517 | dependency: transitive 518 | description: 519 | name: stream_transform 520 | url: "https://pub.flutter-io.cn" 521 | source: hosted 522 | version: "0.0.19" 523 | stretchy_header: 524 | dependency: "direct main" 525 | description: 526 | name: stretchy_header 527 | url: "https://pub.flutter-io.cn" 528 | source: hosted 529 | version: "1.0.5" 530 | string_scanner: 531 | dependency: transitive 532 | description: 533 | name: string_scanner 534 | url: "https://pub.flutter-io.cn" 535 | source: hosted 536 | version: "1.0.5" 537 | term_glyph: 538 | dependency: transitive 539 | description: 540 | name: term_glyph 541 | url: "https://pub.flutter-io.cn" 542 | source: hosted 543 | version: "1.1.0" 544 | test_api: 545 | dependency: transitive 546 | description: 547 | name: test_api 548 | url: "https://pub.flutter-io.cn" 549 | source: hosted 550 | version: "0.2.11" 551 | timing: 552 | dependency: transitive 553 | description: 554 | name: timing 555 | url: "https://pub.flutter-io.cn" 556 | source: hosted 557 | version: "0.1.1+2" 558 | transformer_page_view: 559 | dependency: transitive 560 | description: 561 | name: transformer_page_view 562 | url: "https://pub.flutter-io.cn" 563 | source: hosted 564 | version: "0.1.6" 565 | typed_data: 566 | dependency: transitive 567 | description: 568 | name: typed_data 569 | url: "https://pub.flutter-io.cn" 570 | source: hosted 571 | version: "1.1.6" 572 | uuid: 573 | dependency: "direct main" 574 | description: 575 | name: uuid 576 | url: "https://pub.flutter-io.cn" 577 | source: hosted 578 | version: "2.0.2" 579 | vector_math: 580 | dependency: transitive 581 | description: 582 | name: vector_math 583 | url: "https://pub.flutter-io.cn" 584 | source: hosted 585 | version: "2.0.8" 586 | video_player: 587 | dependency: "direct main" 588 | description: 589 | name: video_player 590 | url: "https://pub.flutter-io.cn" 591 | source: hosted 592 | version: "0.10.2+1" 593 | watcher: 594 | dependency: transitive 595 | description: 596 | name: watcher 597 | url: "https://pub.flutter-io.cn" 598 | source: hosted 599 | version: "0.9.7+12" 600 | web_socket_channel: 601 | dependency: transitive 602 | description: 603 | name: web_socket_channel 604 | url: "https://pub.flutter-io.cn" 605 | source: hosted 606 | version: "1.0.15" 607 | xml: 608 | dependency: transitive 609 | description: 610 | name: xml 611 | url: "https://pub.flutter-io.cn" 612 | source: hosted 613 | version: "3.5.0" 614 | yaml: 615 | dependency: transitive 616 | description: 617 | name: yaml 618 | url: "https://pub.flutter-io.cn" 619 | source: hosted 620 | version: "2.2.0" 621 | sdks: 622 | dart: ">=2.4.0 <3.0.0" 623 | flutter: ">=1.6.7 <2.0.0" 624 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: douyin_demo 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | marquee: 27 | video_player: 28 | marquee_flutter: 29 | provider: 30 | lpinyin: 31 | sticky_headers: 32 | shared_preferences: ^0.5.3 33 | json_annotation: 34 | flutter_swiper: 35 | stretchy_header: 36 | after_layout: 37 | camera: 38 | path_provider: 39 | path: 40 | uuid: 41 | image_picker_saver: 42 | image_gallery_saver: 43 | firebase_ml_vision: 44 | image_picker: 45 | 46 | dev_dependencies: 47 | flutter_test: 48 | sdk: flutter 49 | build_runner: 50 | json_serializable: 51 | 52 | 53 | # For information on the generic Dart part of this file, see the 54 | # following page: https://dart.dev/tools/pub/pubspec 55 | 56 | # The following section is specific to Flutter. 57 | flutter: 58 | 59 | # The following line ensures that the Material Icons font is 60 | # included with your application, so that you can use the icons in 61 | # the material Icons class. 62 | uses-material-design: true 63 | 64 | # To add assets to your application, add an assets section, like this: 65 | assets: 66 | - lib/images/pingguo.jpeg 67 | - lib/images/sky.jpg 68 | - lib/images/temple.jpg 69 | - lib/images/zhifei.jpg 70 | - lib/images/woman.jpg 71 | - lib/images/woman2.jpg 72 | - lib/images/whitehouse.jpg 73 | - lib/images/waterdrop.jpg 74 | # - lib/images/aaa.png 75 | # - images/a_dot_ham.jpeg 76 | 77 | # An image asset can refer to one or more resolution-specific "variants", see 78 | # https://flutter.dev/assets-and-images/#resolution-aware. 79 | 80 | # For details regarding adding assets from package dependencies, see 81 | # https://flutter.dev/assets-and-images/#from-packages 82 | 83 | # To add custom fonts to your application, add a fonts section here, 84 | # in this "flutter" section. Each entry in this list should have a 85 | # "family" key with the font family name, and a "fonts" key with a 86 | # list giving the asset and other descriptors for the font. For 87 | # example: 88 | # fonts: 89 | # - family: Schyler 90 | # fonts: 91 | # - asset: fonts/Schyler-Regular.ttf 92 | # - asset: fonts/Schyler-Italic.ttf 93 | # style: italic 94 | # - family: Trajan Pro 95 | # fonts: 96 | # - asset: fonts/TrajanPro.ttf 97 | # - asset: fonts/TrajanPro_Bold.ttf 98 | # weight: 700 99 | # 100 | # For details regarding fonts from package dependencies, 101 | # see https://flutter.dev/custom-fonts/#from-packages 102 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:douyin_demo/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------