├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── README.md ├── android ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── testapp │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── zoe.png │ │ │ ├── mipmap-mdpi │ │ │ └── zoe.png │ │ │ ├── mipmap-xhdpi │ │ │ └── zoe.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── zoe.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── zoe.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── Flutter │ ├── .last_build_id │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Flutter.podspec │ └── 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 │ │ ├── appZoe20x20.png │ │ ├── appZoe20x20@2x.png │ │ ├── appZoe20x20@3x.png │ │ ├── appZoe29x29.png │ │ ├── appZoe29x29@2x.png │ │ ├── appZoe29x29@3x.png │ │ ├── appZoe40x40.png │ │ ├── appZoe40x40@2x.png │ │ ├── appZoe40x40@3x.png │ │ ├── appZoe60x60@2x.png │ │ ├── appZoe60x60@3x.png │ │ ├── appZoe76x76.png │ │ ├── appZoe76x76@2x.png │ │ └── appZoe83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── barrage.dart ├── core.dart ├── event.dart ├── main.dart ├── mock.dart └── video.dart ├── pubspec.lock ├── pubspec.yaml ├── py ├── 1.frames.py ├── 2.discern.py ├── 3.translate.py ├── config.py ├── product_app_icon.py ├── requirements.txt ├── res.json └── source.mp4 ├── test └── widget_test.dart └── zoe.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Generated.xcconfig 62 | **/ios/Flutter/app.flx 63 | **/ios/Flutter/app.zip 64 | **/ios/Flutter/flutter_assets/ 65 | **/ios/Flutter/flutter_export_environment.sh 66 | **/ios/ServiceDefinitions.json 67 | **/ios/Runner/GeneratedPluginRegistrant.* 68 | 69 | # Exceptions to above rules. 70 | !**/ios/**/default.mode1v3 71 | !**/ios/**/default.mode2v3 72 | !**/ios/**/default.pbxuser 73 | !**/ios/**/default.perspectivev3 74 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 75 | 76 | /py/clip 77 | /py/images 78 | /py/mask -------------------------------------------------------------------------------- /.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: c382b8e990b6976f610764179f94e0416d82c057 8 | channel: unknown 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart", 11 | "flutterMode": "release" 12 | }, 13 | { 14 | "name":"Python", 15 | "type":"python", 16 | "request":"launch", 17 | "program":"${file}" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Zoe barrage

2 | 3 |

4 | flutter 1.22 5 | python 3.8 6 | android 7 | ios 8 |

9 | 10 |

Flutter~Python AI弹幕播放器来袭!

11 | 12 | `flutter run --release`构建正式包体验
13 | 14 | `./py`路径下包含视频帧处理脚本源码,处理后的结果已导出`res.json`文件,flutter构建时会打包进去
15 | 16 | 如果想预处理自己导入的视频(因为是离线演示,体积最好不要超过5M): 17 | 1. 确保本地有`python3.6+`环境 18 | 2. 根目录下安装依赖`pip install -r ./py/requirements.txt -i https://mirrors.aliyun.com/pypi/simple`(国内请使用阿里云镜像,mac下请加`sudo`管理员前缀) 19 | 3. 将视频放入`./py`目录,修改视频文件名为`source.mp4` 20 | 4. 根目录下依次运行脚本: 21 | - `python ./py/1.frames.py` 22 | - `python ./py/2.discern.py` 23 | - `python ./py/3.translate.py` 24 | 25 | 完整实现教程可见文章:《跨平台AI弹幕播放器》 26 | 27 | ### APP效果预览 28 | IPhone运行时录屏:戳这里观看
29 | android & ios ScreenShot 30 | 31 | 32 | 33 | 36 | 39 | 42 | 45 | 46 |
34 | 35 | 37 | 38 | 40 | 41 | 43 | 44 |
47 | -------------------------------------------------------------------------------- /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.dy" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "android.support.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 'com.android.support.test:runner:1.0.2' 66 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 67 | } 68 | -------------------------------------------------------------------------------- /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 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/testapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.dy 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/zoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/android/app/src/main/res/mipmap-hdpi/zoe.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/zoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/android/app/src/main/res/mipmap-mdpi/zoe.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/zoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/android/app/src/main/res/mipmap-xhdpi/zoe.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/zoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/android/app/src/main/res/mipmap-xxhdpi/zoe.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/zoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/android/app/src/main/res/mipmap-xxxhdpi/zoe.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 | maven { url 'https://maven.aliyun.com/repository/google' } 7 | maven { url 'https://maven.aliyun.com/repository/jcenter' } 8 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:3.2.1' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | //google() 20 | //jcenter() 21 | maven { url 'https://maven.aliyun.com/repository/google' } 22 | maven { url 'https://maven.aliyun.com/repository/jcenter' } 23 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public' } 24 | } 25 | } 26 | 27 | rootProject.buildDir = '../build' 28 | subprojects { 29 | project.buildDir = "${rootProject.buildDir}/${project.name}" 30 | } 31 | subprojects { 32 | project.evaluationDependsOn(':app') 33 | } 34 | 35 | task clean(type: Delete) { 36 | delete rootProject.buildDir 37 | } 38 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableJetifier=true 3 | android.useAndroidX=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 0f10945f5c59cf89e9288af602abe96a -------------------------------------------------------------------------------- /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/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'Flutter' 7 | s.version = '1.0.0' 8 | s.summary = 'High-performance, high-fidelity mobile apps.' 9 | s.description = <<-DESC 10 | Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. 11 | DESC 12 | s.homepage = 'https://flutter.io' 13 | s.license = { :type => 'MIT' } 14 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 15 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 16 | s.ios.deployment_target = '8.0' 17 | s.vendored_frameworks = 'Flutter.framework' 18 | end 19 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - video_player (0.0.1): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - video_player (from `.symlinks/plugins/video_player/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | video_player: 14 | :path: ".symlinks/plugins/video_player/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 18 | video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e 19 | 20 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 21 | 22 | COCOAPODS: 1.9.3 23 | -------------------------------------------------------------------------------- /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 | 12D29FAFB63F65F637B2B990 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C5F36D3D6764BE8C02284D3 /* Pods_Runner.framework */; }; 11 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 0DBA930640E452E1552CA022 /* 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 = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 1C5F36D3D6764BE8C02284D3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 38B44CF005927F85400C9942 /* 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 = ""; }; 39 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | AA9A6A565237394E4CAD1FFF /* 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 = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | 12D29FAFB63F65F637B2B990 /* Pods_Runner.framework in Frameworks */, 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | /* End PBXFrameworksBuildPhase section */ 63 | 64 | /* Begin PBXGroup section */ 65 | 9740EEB11CF90186004384FC /* Flutter */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 69 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 70 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 71 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 72 | ); 73 | name = Flutter; 74 | sourceTree = ""; 75 | }; 76 | 97C146E51CF9000F007C117D = { 77 | isa = PBXGroup; 78 | children = ( 79 | 9740EEB11CF90186004384FC /* Flutter */, 80 | 97C146F01CF9000F007C117D /* Runner */, 81 | 97C146EF1CF9000F007C117D /* Products */, 82 | A41B837516390224B8316022 /* Pods */, 83 | A1E746203A0B2345F4F26592 /* Frameworks */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 97C146EF1CF9000F007C117D /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 97C146EE1CF9000F007C117D /* Runner.app */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 97C146F01CF9000F007C117D /* Runner */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 99 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 100 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 101 | 97C147021CF9000F007C117D /* Info.plist */, 102 | 97C146F11CF9000F007C117D /* Supporting Files */, 103 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 104 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 105 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 106 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 107 | ); 108 | path = Runner; 109 | sourceTree = ""; 110 | }; 111 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | ); 115 | name = "Supporting Files"; 116 | sourceTree = ""; 117 | }; 118 | A1E746203A0B2345F4F26592 /* Frameworks */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 1C5F36D3D6764BE8C02284D3 /* Pods_Runner.framework */, 122 | ); 123 | name = Frameworks; 124 | sourceTree = ""; 125 | }; 126 | A41B837516390224B8316022 /* Pods */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | AA9A6A565237394E4CAD1FFF /* Pods-Runner.debug.xcconfig */, 130 | 38B44CF005927F85400C9942 /* Pods-Runner.release.xcconfig */, 131 | 0DBA930640E452E1552CA022 /* Pods-Runner.profile.xcconfig */, 132 | ); 133 | path = Pods; 134 | sourceTree = ""; 135 | }; 136 | /* End PBXGroup section */ 137 | 138 | /* Begin PBXNativeTarget section */ 139 | 97C146ED1CF9000F007C117D /* Runner */ = { 140 | isa = PBXNativeTarget; 141 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 142 | buildPhases = ( 143 | A95602EB8F00ABF39CCCD17B /* [CP] Check Pods Manifest.lock */, 144 | 9740EEB61CF901F6004384FC /* Run Script */, 145 | 97C146EA1CF9000F007C117D /* Sources */, 146 | 97C146EB1CF9000F007C117D /* Frameworks */, 147 | 97C146EC1CF9000F007C117D /* Resources */, 148 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 149 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 150 | 737D23AC163148681DA9BF3B /* [CP] Embed Pods Frameworks */, 151 | ); 152 | buildRules = ( 153 | ); 154 | dependencies = ( 155 | ); 156 | name = Runner; 157 | productName = Runner; 158 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 159 | productType = "com.apple.product-type.application"; 160 | }; 161 | /* End PBXNativeTarget section */ 162 | 163 | /* Begin PBXProject section */ 164 | 97C146E61CF9000F007C117D /* Project object */ = { 165 | isa = PBXProject; 166 | attributes = { 167 | LastUpgradeCheck = 1020; 168 | ORGANIZATIONNAME = "The Chromium Authors"; 169 | TargetAttributes = { 170 | 97C146ED1CF9000F007C117D = { 171 | CreatedOnToolsVersion = 7.3.1; 172 | DevelopmentTeam = 2HMR65C2CA; 173 | LastSwiftMigration = 0910; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 178 | compatibilityVersion = "Xcode 3.2"; 179 | developmentRegion = en; 180 | hasScannedForEncodings = 0; 181 | knownRegions = ( 182 | en, 183 | Base, 184 | ); 185 | mainGroup = 97C146E51CF9000F007C117D; 186 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 187 | projectDirPath = ""; 188 | projectRoot = ""; 189 | targets = ( 190 | 97C146ED1CF9000F007C117D /* Runner */, 191 | ); 192 | }; 193 | /* End PBXProject section */ 194 | 195 | /* Begin PBXResourcesBuildPhase section */ 196 | 97C146EC1CF9000F007C117D /* Resources */ = { 197 | isa = PBXResourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 201 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 202 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXResourcesBuildPhase section */ 209 | 210 | /* Begin PBXShellScriptBuildPhase section */ 211 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 212 | isa = PBXShellScriptBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | inputPaths = ( 217 | ); 218 | name = "Thin Binary"; 219 | outputPaths = ( 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | shellPath = /bin/sh; 223 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 224 | }; 225 | 737D23AC163148681DA9BF3B /* [CP] Embed Pods Frameworks */ = { 226 | isa = PBXShellScriptBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | inputPaths = ( 231 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 232 | "${PODS_ROOT}/../Flutter/Flutter.framework", 233 | "${BUILT_PRODUCTS_DIR}/video_player/video_player.framework", 234 | ); 235 | name = "[CP] Embed Pods Frameworks"; 236 | outputPaths = ( 237 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", 238 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework", 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | shellPath = /bin/sh; 242 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 243 | showEnvVarsInLog = 0; 244 | }; 245 | 9740EEB61CF901F6004384FC /* Run Script */ = { 246 | isa = PBXShellScriptBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | inputPaths = ( 251 | ); 252 | name = "Run Script"; 253 | outputPaths = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 258 | }; 259 | A95602EB8F00ABF39CCCD17B /* [CP] Check Pods Manifest.lock */ = { 260 | isa = PBXShellScriptBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | inputFileListPaths = ( 265 | ); 266 | inputPaths = ( 267 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 268 | "${PODS_ROOT}/Manifest.lock", 269 | ); 270 | name = "[CP] Check Pods Manifest.lock"; 271 | outputFileListPaths = ( 272 | ); 273 | outputPaths = ( 274 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | shellPath = /bin/sh; 278 | 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"; 279 | showEnvVarsInLog = 0; 280 | }; 281 | /* End PBXShellScriptBuildPhase section */ 282 | 283 | /* Begin PBXSourcesBuildPhase section */ 284 | 97C146EA1CF9000F007C117D /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 289 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | /* End PBXSourcesBuildPhase section */ 294 | 295 | /* Begin PBXVariantGroup section */ 296 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 297 | isa = PBXVariantGroup; 298 | children = ( 299 | 97C146FB1CF9000F007C117D /* Base */, 300 | ); 301 | name = Main.storyboard; 302 | sourceTree = ""; 303 | }; 304 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 305 | isa = PBXVariantGroup; 306 | children = ( 307 | 97C147001CF9000F007C117D /* Base */, 308 | ); 309 | name = LaunchScreen.storyboard; 310 | sourceTree = ""; 311 | }; 312 | /* End PBXVariantGroup section */ 313 | 314 | /* Begin XCBuildConfiguration section */ 315 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 325 | CLANG_WARN_BOOL_CONVERSION = YES; 326 | CLANG_WARN_COMMA = YES; 327 | CLANG_WARN_CONSTANT_CONVERSION = YES; 328 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 329 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 330 | CLANG_WARN_EMPTY_BODY = YES; 331 | CLANG_WARN_ENUM_CONVERSION = YES; 332 | CLANG_WARN_INFINITE_RECURSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 335 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 336 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 338 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 339 | CLANG_WARN_STRICT_PROTOTYPES = YES; 340 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 341 | CLANG_WARN_UNREACHABLE_CODE = YES; 342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 343 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 344 | COPY_PHASE_STRIP = NO; 345 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 346 | ENABLE_NS_ASSERTIONS = NO; 347 | ENABLE_STRICT_OBJC_MSGSEND = YES; 348 | GCC_C_LANGUAGE_STANDARD = gnu99; 349 | GCC_NO_COMMON_BLOCKS = YES; 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 357 | MTL_ENABLE_DEBUG_INFO = NO; 358 | SDKROOT = iphoneos; 359 | TARGETED_DEVICE_FAMILY = "1,2"; 360 | VALIDATE_PRODUCT = YES; 361 | }; 362 | name = Profile; 363 | }; 364 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 365 | isa = XCBuildConfiguration; 366 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 367 | buildSettings = { 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | CLANG_ENABLE_MODULES = YES; 370 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 371 | DEVELOPMENT_TEAM = 2HMR65C2CA; 372 | ENABLE_BITCODE = NO; 373 | FRAMEWORK_SEARCH_PATHS = ( 374 | "$(inherited)", 375 | "$(PROJECT_DIR)/Flutter", 376 | ); 377 | INFOPLIST_FILE = Runner/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 379 | LIBRARY_SEARCH_PATHS = ( 380 | "$(inherited)", 381 | "$(PROJECT_DIR)/Flutter", 382 | ); 383 | PRODUCT_BUNDLE_IDENTIFIER = "-34652479-qq.com.barrage"; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 386 | SWIFT_VERSION = 4.0; 387 | VERSIONING_SYSTEM = "apple-generic"; 388 | }; 389 | name = Profile; 390 | }; 391 | 97C147031CF9000F007C117D /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | ALWAYS_SEARCH_USER_PATHS = NO; 395 | CLANG_ANALYZER_NONNULL = YES; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 401 | CLANG_WARN_BOOL_CONVERSION = YES; 402 | CLANG_WARN_COMMA = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 405 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INFINITE_RECURSION = YES; 409 | CLANG_WARN_INT_CONVERSION = YES; 410 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 412 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 414 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 415 | CLANG_WARN_STRICT_PROTOTYPES = YES; 416 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 417 | CLANG_WARN_UNREACHABLE_CODE = YES; 418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 419 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 420 | COPY_PHASE_STRIP = NO; 421 | DEBUG_INFORMATION_FORMAT = dwarf; 422 | ENABLE_STRICT_OBJC_MSGSEND = YES; 423 | ENABLE_TESTABILITY = YES; 424 | GCC_C_LANGUAGE_STANDARD = gnu99; 425 | GCC_DYNAMIC_NO_PIC = NO; 426 | GCC_NO_COMMON_BLOCKS = YES; 427 | GCC_OPTIMIZATION_LEVEL = 0; 428 | GCC_PREPROCESSOR_DEFINITIONS = ( 429 | "DEBUG=1", 430 | "$(inherited)", 431 | ); 432 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 433 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 434 | GCC_WARN_UNDECLARED_SELECTOR = YES; 435 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 436 | GCC_WARN_UNUSED_FUNCTION = YES; 437 | GCC_WARN_UNUSED_VARIABLE = YES; 438 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 439 | MTL_ENABLE_DEBUG_INFO = YES; 440 | ONLY_ACTIVE_ARCH = YES; 441 | SDKROOT = iphoneos; 442 | TARGETED_DEVICE_FAMILY = "1,2"; 443 | }; 444 | name = Debug; 445 | }; 446 | 97C147041CF9000F007C117D /* Release */ = { 447 | isa = XCBuildConfiguration; 448 | buildSettings = { 449 | ALWAYS_SEARCH_USER_PATHS = NO; 450 | CLANG_ANALYZER_NONNULL = YES; 451 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 452 | CLANG_CXX_LIBRARY = "libc++"; 453 | CLANG_ENABLE_MODULES = YES; 454 | CLANG_ENABLE_OBJC_ARC = YES; 455 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 456 | CLANG_WARN_BOOL_CONVERSION = YES; 457 | CLANG_WARN_COMMA = YES; 458 | CLANG_WARN_CONSTANT_CONVERSION = YES; 459 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 460 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 461 | CLANG_WARN_EMPTY_BODY = YES; 462 | CLANG_WARN_ENUM_CONVERSION = YES; 463 | CLANG_WARN_INFINITE_RECURSION = YES; 464 | CLANG_WARN_INT_CONVERSION = YES; 465 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 466 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 467 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 468 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 469 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 470 | CLANG_WARN_STRICT_PROTOTYPES = YES; 471 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 472 | CLANG_WARN_UNREACHABLE_CODE = YES; 473 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 474 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 475 | COPY_PHASE_STRIP = NO; 476 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 477 | ENABLE_NS_ASSERTIONS = NO; 478 | ENABLE_STRICT_OBJC_MSGSEND = YES; 479 | GCC_C_LANGUAGE_STANDARD = gnu99; 480 | GCC_NO_COMMON_BLOCKS = YES; 481 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 482 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 483 | GCC_WARN_UNDECLARED_SELECTOR = YES; 484 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 485 | GCC_WARN_UNUSED_FUNCTION = YES; 486 | GCC_WARN_UNUSED_VARIABLE = YES; 487 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 488 | MTL_ENABLE_DEBUG_INFO = NO; 489 | SDKROOT = iphoneos; 490 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 491 | TARGETED_DEVICE_FAMILY = "1,2"; 492 | VALIDATE_PRODUCT = YES; 493 | }; 494 | name = Release; 495 | }; 496 | 97C147061CF9000F007C117D /* Debug */ = { 497 | isa = XCBuildConfiguration; 498 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 499 | buildSettings = { 500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 501 | CLANG_ENABLE_MODULES = YES; 502 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 503 | DEVELOPMENT_TEAM = 2HMR65C2CA; 504 | ENABLE_BITCODE = NO; 505 | FRAMEWORK_SEARCH_PATHS = ( 506 | "$(inherited)", 507 | "$(PROJECT_DIR)/Flutter", 508 | ); 509 | INFOPLIST_FILE = Runner/Info.plist; 510 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 511 | LIBRARY_SEARCH_PATHS = ( 512 | "$(inherited)", 513 | "$(PROJECT_DIR)/Flutter", 514 | ); 515 | PRODUCT_BUNDLE_IDENTIFIER = "-34652479-qq.com.barrage"; 516 | PRODUCT_NAME = "$(TARGET_NAME)"; 517 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 518 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 519 | SWIFT_VERSION = 4.0; 520 | VERSIONING_SYSTEM = "apple-generic"; 521 | }; 522 | name = Debug; 523 | }; 524 | 97C147071CF9000F007C117D /* Release */ = { 525 | isa = XCBuildConfiguration; 526 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 527 | buildSettings = { 528 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 529 | CLANG_ENABLE_MODULES = YES; 530 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 531 | DEVELOPMENT_TEAM = 2HMR65C2CA; 532 | ENABLE_BITCODE = NO; 533 | FRAMEWORK_SEARCH_PATHS = ( 534 | "$(inherited)", 535 | "$(PROJECT_DIR)/Flutter", 536 | ); 537 | INFOPLIST_FILE = Runner/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 539 | LIBRARY_SEARCH_PATHS = ( 540 | "$(inherited)", 541 | "$(PROJECT_DIR)/Flutter", 542 | ); 543 | PRODUCT_BUNDLE_IDENTIFIER = "-34652479-qq.com.barrage"; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 546 | SWIFT_VERSION = 4.0; 547 | VERSIONING_SYSTEM = "apple-generic"; 548 | }; 549 | name = Release; 550 | }; 551 | /* End XCBuildConfiguration section */ 552 | 553 | /* Begin XCConfigurationList section */ 554 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 97C147031CF9000F007C117D /* Debug */, 558 | 97C147041CF9000F007C117D /* Release */, 559 | 249021D3217E4FDB00AE95B9 /* Profile */, 560 | ); 561 | defaultConfigurationIsVisible = 0; 562 | defaultConfigurationName = Release; 563 | }; 564 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 97C147061CF9000F007C117D /* Debug */, 568 | 97C147071CF9000F007C117D /* Release */, 569 | 249021D4217E4FDB00AE95B9 /* Profile */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | /* End XCConfigurationList section */ 575 | }; 576 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 577 | } 578 | -------------------------------------------------------------------------------- /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 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "images" : [ 4 | { 5 | "idiom" : "iphone", 6 | "size" : "20x20", 7 | "filename" : "appZoe20x20@2x.png", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "idiom" : "iphone", 12 | "size" : "20x20", 13 | "filename" : "appZoe20x20@3x.png", 14 | "scale" : "3x" 15 | }, 16 | { 17 | "size" : "29x29", 18 | "idiom" : "iphone", 19 | "filename" : "appZoe29x29.png", 20 | "scale" : "1x" 21 | }, 22 | { 23 | "size" : "29x29", 24 | "idiom" : "iphone", 25 | "filename" : "appZoe29x29@2x.png", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "size" : "29x29", 30 | "idiom" : "iphone", 31 | "filename" : "appZoe29x29@3x.png", 32 | "scale" : "3x" 33 | }, 34 | { 35 | "size" : "40x40", 36 | "idiom" : "iphone", 37 | "filename" : "appZoe40x40@2x.png", 38 | "scale" : "2x" 39 | }, 40 | { 41 | "size" : "40x40", 42 | "idiom" : "iphone", 43 | "filename" : "appZoe40x40@3x.png", 44 | "scale" : "3x" 45 | }, 46 | { 47 | "size" : "60x60", 48 | "idiom" : "iphone", 49 | "filename" : "appZoe60x60@2x.png", 50 | "scale" : "2x" 51 | }, 52 | { 53 | "size" : "60x60", 54 | "idiom" : "iphone", 55 | "filename" : "appZoe60x60@3x.png", 56 | "scale" : "3x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "20x20", 61 | "filename" : "appZoe20x20.png", 62 | "scale" : "1x" 63 | }, 64 | { 65 | "idiom" : "ipad", 66 | "size" : "20x20", 67 | "filename" : "appZoe20x20@2x.png", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "size" : "29x29", 72 | "idiom" : "ipad", 73 | "filename" : "appZoe29x29.png", 74 | "scale" : "1x" 75 | }, 76 | { 77 | "size" : "29x29", 78 | "idiom" : "ipad", 79 | "filename" : "appZoe29x29@2x.png", 80 | "scale" : "2x" 81 | }, 82 | { 83 | "size" : "40x40", 84 | "idiom" : "ipad", 85 | "filename" : "appZoe40x40.png", 86 | "scale" : "1x" 87 | }, 88 | { 89 | "size" : "40x40", 90 | "idiom" : "ipad", 91 | "filename" : "appZoe40x40@2x.png", 92 | "scale" : "2x" 93 | }, 94 | { 95 | "size" : "76x76", 96 | "idiom" : "ipad", 97 | "filename" : "appZoe76x76.png", 98 | "scale" : "1x" 99 | }, 100 | { 101 | "size" : "76x76", 102 | "idiom" : "ipad", 103 | "filename" : "appZoe76x76@2x.png", 104 | "scale" : "2x" 105 | }, 106 | { 107 | "size" : "83.5x83.5", 108 | "idiom" : "ipad", 109 | "filename" : "appZoe83.5x83.5@2x.png", 110 | "scale" : "2x" 111 | } 112 | ], 113 | "info" : { 114 | "version" : 1, 115 | "author" : "xcode" 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe20x20.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe29x29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe29x29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe40x40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe76x76.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/AppIcon.appiconset/appZoe83.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/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Zoe barrage 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | NSAllowsArbitraryLoads 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 | 47 | 48 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/barrage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'mock.dart'; 6 | import 'core.dart'; 7 | import 'event.dart'; 8 | 9 | class BarrageInit extends StatefulWidget { 10 | final Map cfg; 11 | const BarrageInit({Key key, this.cfg}) : super(key: key); 12 | 13 | @override 14 | BarrageInitState createState() => BarrageInitState(); 15 | } 16 | 17 | class BarrageInitState extends State { 18 | BarrageWallController _controller; 19 | BarrageData barrageDatas; 20 | Timer _timer; 21 | bool isPlaying = false; 22 | List curMaskData; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | _controller = BarrageWallController.all( 28 | options: ChannelOptions(height: 24.0), 29 | channelCount: 25, 30 | ); 31 | barrageDatas = BarrageData(); 32 | 33 | eventBus.on().listen((event) { 34 | setState(() { 35 | curMaskData = widget.cfg[event.time] ?? curMaskData; 36 | }); 37 | }); 38 | change(); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | _timer?.cancel(); 44 | super.dispose(); 45 | } 46 | 47 | void change() { 48 | setState(() { 49 | isPlaying = !isPlaying; 50 | }); 51 | 52 | if (isPlaying) { 53 | _controller.play(); 54 | // 投放弹幕CD时间 55 | _timer = Timer.periodic(const Duration(milliseconds: 100), (_) => _addBarrage()); 56 | } else { 57 | _controller.pause(); 58 | _timer.cancel(); 59 | } 60 | } 61 | 62 | int _addBarrage() { 63 | Widget content = Row( 64 | mainAxisSize: MainAxisSize.min, 65 | children: [ 66 | barrageDatas.randomIcon, 67 | const SizedBox(width: 8.0), 68 | Text( 69 | barrageDatas.randomWord, 70 | style: TextStyle( 71 | fontSize: 17.0, 72 | color: barrageDatas.randomTextColor(), 73 | ), 74 | ), 75 | ], 76 | ); 77 | 78 | var item = BarrageItem( 79 | content: content, 80 | // 随机一个 0.5 到 1.5 之间的滚动速度 81 | speed: Random().nextDouble() + 0.5, 82 | start: isPlaying, 83 | ); 84 | 85 | return _controller.add(item); 86 | } 87 | 88 | @override 89 | Widget build(BuildContext context) { 90 | num scale = MediaQuery.of(context).size.width / widget.cfg['frame_width']; 91 | return ClipPath( 92 | clipper: curMaskData != null ? TrianglePath(curMaskData, scale) : null, 93 | child: Container( 94 | color: Colors.transparent, 95 | child: _controller.buildView(), 96 | ), 97 | ); 98 | } 99 | } 100 | 101 | class TrianglePath extends CustomClipper { 102 | List curMaskData; 103 | num scale; 104 | 105 | TrianglePath(this.curMaskData, this.scale); 106 | 107 | @override 108 | Path getClip(Size size) { 109 | var path = Path(); 110 | curMaskData.forEach((maskEach) { 111 | for (var i = 0; i < maskEach.length; i++) { 112 | if (i == 0) { 113 | path.moveTo(maskEach[i][0] * scale, maskEach[i][1] * scale); 114 | } else { 115 | path.lineTo(maskEach[i][0] * scale, maskEach[i][1] * scale); 116 | } 117 | } 118 | }); 119 | 120 | return path; 121 | } 122 | 123 | @override 124 | bool shouldReclip(CustomClipper oldClipper) { 125 | return true; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/core.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class BarrageValue { 4 | const BarrageValue({ 5 | this.isPlaying = false, 6 | this.scrollRate = 0.0, 7 | }); 8 | 9 | /// 弹幕是否滚动 10 | /// default to false 11 | final bool isPlaying; 12 | 13 | /// 弹幕滚动比例 from 0.0 to 1.0 14 | /// 0.0 还未出现在屏幕 15 | /// 0.0 - 0.5 正在进入屏幕 16 | /// 0.5 完全进入屏幕 17 | /// 0.5 - 1.0 正在滚出屏幕 18 | /// 1.0 已完全滚出屏幕 19 | final double scrollRate; 20 | 21 | bool get completed => scrollRate == 1.0; 22 | 23 | /// 弹幕正在进入屏幕 24 | bool get scrollIn => scrollRate < 0.5; 25 | 26 | BarrageValue copy({ 27 | bool isPlaying, 28 | double scrollRate, 29 | }) { 30 | return BarrageValue( 31 | isPlaying: isPlaying ?? this.isPlaying, 32 | scrollRate: scrollRate ?? this.scrollRate, 33 | ); 34 | } 35 | 36 | @override 37 | String toString() { 38 | return '$runtimeType(' 39 | 'isPlaying: $isPlaying, ' 40 | 'scrollRate: $scrollRate)'; 41 | } 42 | } 43 | 44 | /// 控制单条弹幕 45 | class BarrageController extends ValueNotifier { 46 | static const defaultDuration = Duration(seconds: 8); 47 | 48 | BarrageController({ 49 | @required this.content, 50 | this.duration = defaultDuration, 51 | }) : assert(content != null), 52 | super(const BarrageValue()); 53 | 54 | /// 弹幕内容 55 | final Widget content; 56 | 57 | /// 弹幕从开始进入屏幕到完全滚出屏幕花费的时间 58 | /// default to [defaultDuration] 59 | final Duration duration; 60 | 61 | /// 开始滚动 62 | void play() { 63 | value = value.copy(isPlaying: true); 64 | } 65 | 66 | /// 暂停滚动 67 | void pause() { 68 | value = value.copy(isPlaying: false); 69 | } 70 | 71 | void setScrollRate(double rate) { 72 | value = value.copy(scrollRate: rate.clamp(0.0, 1.0)); 73 | } 74 | } 75 | 76 | /// 单条弹幕视图 77 | class Barrage extends StatefulWidget { 78 | Barrage(this.barrageController, {Key key}) : super(key: key); 79 | 80 | final BarrageController barrageController; 81 | 82 | @override 83 | _BarrageState createState() => _BarrageState(); 84 | } 85 | 86 | class _BarrageState extends State with TickerProviderStateMixin { 87 | AnimationController _animationController; 88 | 89 | Animation _offsetAnimation; 90 | 91 | _PlayPauseState _playPauseState; 92 | 93 | void _initAnimation() { 94 | final barrageController = widget.barrageController; 95 | 96 | _animationController = AnimationController( 97 | value: barrageController.value.scrollRate, 98 | duration: barrageController.duration, 99 | vsync: this, 100 | ); 101 | 102 | _animationController.addListener(() { 103 | barrageController.setScrollRate(_animationController.value); 104 | }); 105 | 106 | _offsetAnimation = Tween( 107 | begin: const Offset(1.0, 0.0), 108 | end: const Offset(-1.0, 0.0), 109 | ).animate(_animationController); 110 | 111 | _playPauseState = _PlayPauseState(barrageController) 112 | ..init() 113 | ..addListener(() { 114 | _playPauseState.isPlaying ? _animationController.forward() : _animationController.stop(canceled: false); 115 | }); 116 | 117 | if (_playPauseState.isPlaying) { 118 | _animationController.forward(); 119 | } 120 | } 121 | 122 | void _disposeAnimation() { 123 | _animationController.dispose(); 124 | _playPauseState.dispose(); 125 | } 126 | 127 | @override 128 | void initState() { 129 | super.initState(); 130 | _initAnimation(); 131 | } 132 | 133 | @override 134 | void didUpdateWidget(Barrage oldWidget) { 135 | super.didUpdateWidget(oldWidget); 136 | _disposeAnimation(); 137 | _initAnimation(); 138 | } 139 | 140 | @override 141 | void deactivate() { 142 | _disposeAnimation(); 143 | super.deactivate(); 144 | } 145 | 146 | @override 147 | Widget build(BuildContext context) { 148 | return SlideTransition( 149 | position: _offsetAnimation, 150 | child: SizedBox( 151 | width: double.infinity, 152 | child: widget.barrageController.content, 153 | ), 154 | ); 155 | } 156 | } 157 | 158 | /// 用于监听弹幕滚动状态 159 | class _PlayPauseState extends ValueNotifier { 160 | _PlayPauseState(this.controller) : super(controller.value.isPlaying) { 161 | _listener = () { 162 | value = controller.value.isPlaying; 163 | }; 164 | } 165 | 166 | final BarrageController controller; 167 | 168 | VoidCallback _listener; 169 | 170 | bool get isPlaying => value; 171 | 172 | void init() { 173 | controller.addListener(_listener); 174 | } 175 | 176 | @override 177 | void dispose() { 178 | controller.removeListener(_listener); 179 | super.dispose(); 180 | } 181 | } 182 | 183 | /// 单条弹幕信息 184 | class BarrageItem { 185 | BarrageItem({ 186 | @required this.content, 187 | this.speed = 1.0, 188 | this.start = true, 189 | }) : assert(content != null), 190 | assert(speed != null), 191 | assert(speed > 0.0); 192 | 193 | /// 弹幕内容 194 | final Widget content; 195 | 196 | /// 弹幕移动速度 197 | /// default to 1.0 198 | final double speed; 199 | 200 | /// See [ChannelController.add] 201 | /// if true 添加到弹道后自动开始滚动 202 | /// if false [ChannelController.playOne] 之后开始滚动 203 | /// default to true 204 | final bool start; 205 | 206 | @override 207 | String toString() { 208 | return '$runtimeType(' 209 | 'content: $content, ' 210 | 'speed: $speed, ' 211 | 'start: $start)'; 212 | } 213 | } 214 | 215 | /// 弹道设置 216 | class ChannelOptions { 217 | ChannelOptions({ 218 | @required this.height, 219 | this.direction = ChannelDirection.rtl, 220 | }) : assert(height != null), 221 | assert(height >= 0.0); 222 | 223 | /// 弹道高度 224 | final double height; 225 | 226 | /// default to [ChannelDirection.rtl] 227 | final ChannelDirection direction; 228 | } 229 | 230 | enum ChannelDirection { 231 | /// Barrage scroll from right to left. 232 | rtl, 233 | 234 | /// Barrage scroll from left to right. 235 | ltr, 236 | } 237 | 238 | /// 控制单条弹道 239 | class ChannelController extends ValueNotifier> { 240 | ChannelController(this.options) 241 | : assert(options != null), 242 | super(const {}); 243 | 244 | final ChannelOptions options; 245 | 246 | /// 是否空闲状态 247 | bool get isIdle { 248 | for (final entry in value.entries) { 249 | final barrageController = entry.value; 250 | if (barrageController.value.scrollIn) { 251 | // 此弹道还有弹幕未完全进入屏幕 252 | return false; 253 | } 254 | } 255 | // 若此弹道所有弹幕都已完全进入屏幕,则视为空闲 256 | // 此时可以添加新的弹幕进来 257 | return true; 258 | } 259 | 260 | /// 弹道中最后一条弹幕滚动比例 from 0.0 to 1.0 261 | /// See [BarrageValue.scrollRate] 262 | double get lastItemScrollRate { 263 | double minScrollRate = 1.0; 264 | for (final entry in value.entries) { 265 | final barrageController = entry.value; 266 | final scrollRate = barrageController.value.scrollRate; 267 | if (scrollRate < minScrollRate) { 268 | minScrollRate = scrollRate; 269 | } 270 | } 271 | return minScrollRate; 272 | } 273 | 274 | /// 向弹道添加一条弹幕 275 | void add(BarrageItem item) { 276 | final micros = BarrageController.defaultDuration.inMicroseconds; 277 | final controller = BarrageController( 278 | content: item.content, 279 | duration: Duration(microseconds: micros ~/ item.speed), 280 | ); 281 | 282 | if (item.start) { 283 | controller.play(); 284 | } 285 | 286 | controller.addListener(() { 287 | if (controller.value.completed) { 288 | // 当滚动完成,从弹道移除 289 | remove(item); 290 | } 291 | }); 292 | 293 | value = Map.of(value)..[item] = controller; 294 | } 295 | 296 | void remove(BarrageItem item) { 297 | // value = Map.of(value)..remove(item)?.dispose(); 298 | value = Map.of(value)..remove(item); 299 | } 300 | 301 | void clear() { 302 | /*value.forEach((_, v) { 303 | v.dispose(); 304 | });*/ 305 | value = const {}; 306 | } 307 | 308 | /// 暂停此弹道所有弹幕 309 | void pause() { 310 | value.forEach((_, barrageController) { 311 | barrageController.pause(); 312 | }); 313 | } 314 | 315 | void play() { 316 | value.forEach((_, barrageController) { 317 | barrageController.play(); 318 | }); 319 | } 320 | 321 | void playOne(BarrageItem item) { 322 | value[item]?.play(); 323 | } 324 | 325 | /// 暂停单条弹幕 326 | void pauseOne(BarrageItem item) { 327 | value[item]?.pause(); 328 | } 329 | 330 | /*@override 331 | void dispose() { 332 | value.forEach((_, v) { 333 | v.dispose(); 334 | }); 335 | super.dispose(); 336 | }*/ 337 | } 338 | 339 | /// 单条弹道 340 | class BarrageChannel extends StatefulWidget { 341 | const BarrageChannel(this.controller, {Key key}) : super(key: key); 342 | 343 | final ChannelController controller; 344 | 345 | @override 346 | _BarrageChannelState createState() => _BarrageChannelState(); 347 | } 348 | 349 | class _BarrageChannelState extends State { 350 | _BarrageChannelState() { 351 | _listener = () { 352 | setState(() {}); 353 | }; 354 | } 355 | 356 | VoidCallback _listener; 357 | 358 | ChannelController get _controller => widget.controller; 359 | 360 | @override 361 | void initState() { 362 | super.initState(); 363 | _controller.addListener(_listener); 364 | } 365 | 366 | @override 367 | void didUpdateWidget(BarrageChannel oldWidget) { 368 | super.didUpdateWidget(oldWidget); 369 | oldWidget.controller.removeListener(_listener); 370 | _controller.addListener(_listener); 371 | } 372 | 373 | @override 374 | void deactivate() { 375 | _controller.removeListener(_listener); 376 | super.deactivate(); 377 | } 378 | 379 | List get _barrages => _controller.value.values.map((e) => Barrage(e)).toList(); 380 | 381 | @override 382 | Widget build(BuildContext context) { 383 | return SizedBox( 384 | height: _controller.options.height, 385 | child: Stack( 386 | alignment: Alignment.centerLeft, 387 | children: _barrages, 388 | ), 389 | ); 390 | } 391 | } 392 | 393 | /// 弹幕墙,可包含多条 [BarrageChannel] 394 | class BarrageWall extends StatelessWidget { 395 | const BarrageWall({ 396 | Key key, 397 | @required this.controller, 398 | }) : super(key: key); 399 | 400 | final BarrageWallController controller; 401 | 402 | @override 403 | Widget build(BuildContext context) => controller.buildView(); 404 | } 405 | 406 | class BarrageWallController { 407 | BarrageWallController.options(List options) 408 | : assert(options != null), 409 | _channels = options.map((e) => ChannelController(e)).toList(growable: false); 410 | 411 | BarrageWallController.all({ 412 | @required ChannelOptions options, 413 | @required int channelCount, 414 | }) : assert(options != null), 415 | assert(channelCount != null), 416 | assert(channelCount >= 0), 417 | _channels = List.generate( 418 | channelCount, 419 | (index) => ChannelController(options), 420 | growable: false, 421 | ); 422 | 423 | final List _channels; 424 | 425 | /// 自动调度,添加到最空闲弹道 426 | /// Returns the index of channel in [_channels] 427 | int add(BarrageItem item) { 428 | final channel = _resolveOptimalChannel(); 429 | channel?.add(item); 430 | return channel == null ? -1 : _channels.indexOf(channel); 431 | } 432 | 433 | ChannelController _resolveOptimalChannel() { 434 | for (final channel in _channels) { 435 | if (channel.isIdle) { 436 | return channel; 437 | } 438 | } 439 | 440 | ChannelController result; 441 | for (final channel in _channels) { 442 | if (result == null || channel.lastItemScrollRate > result.lastItemScrollRate) { 443 | result = channel; 444 | } 445 | } 446 | return result; 447 | } 448 | 449 | /// 添加到指定弹道 450 | void addTo(BarrageItem item, int index) { 451 | assert(index >= 0); 452 | assert(() { 453 | if (index >= _channels.length) { 454 | throw RangeError.index(index, _channels, "index", null, _channels.length); 455 | } 456 | return true; 457 | }()); 458 | _channels[index].add(item); 459 | } 460 | 461 | /// 移除某条弹幕 462 | void removeFrom(BarrageItem item, int index) { 463 | if (index < 0 || index >= _channels.length) { 464 | return; 465 | } 466 | _channels[index].remove(item); 467 | } 468 | 469 | void clear() { 470 | _channels.forEach((channel) { 471 | channel.clear(); 472 | }); 473 | } 474 | 475 | /// 清空某条弹道 476 | void clearOne(int index) { 477 | if (index < 0 || index >= _channels.length) { 478 | return; 479 | } 480 | _channels[index].clear(); 481 | } 482 | 483 | /// 暂停所有弹道 484 | void pause() { 485 | _channels.forEach((channel) { 486 | channel.pause(); 487 | }); 488 | } 489 | 490 | void play() { 491 | _channels.forEach((channel) { 492 | channel.play(); 493 | }); 494 | } 495 | 496 | /// 暂停某条弹道 497 | void pauseOne(int index) { 498 | if (index < 0 || index >= _channels.length) { 499 | return; 500 | } 501 | _channels[index].pause(); 502 | } 503 | 504 | void playOne(int index) { 505 | if (index < 0 || index >= _channels.length) { 506 | return; 507 | } 508 | _channels[index].play(); 509 | } 510 | 511 | /// 暂停某条弹幕 512 | void pauseItem(BarrageItem item, int index) { 513 | if (index < 0 || index >= _channels.length) { 514 | return; 515 | } 516 | _channels[index].pauseOne(item); 517 | } 518 | 519 | void playItem(BarrageItem item, int index) { 520 | if (index < 0 || index >= _channels.length) { 521 | return; 522 | } 523 | _channels[index].playOne(item); 524 | } 525 | 526 | /*void dispose() { 527 | _channels.forEach((channel) { 528 | channel.dispose(); 529 | }); 530 | }*/ 531 | 532 | List get _barrageChannels => _channels.map((e) => BarrageChannel(e)).toList(); 533 | 534 | Widget buildView() { 535 | return Column( 536 | mainAxisSize: MainAxisSize.min, 537 | children: _barrageChannels, 538 | ); 539 | } 540 | } 541 | -------------------------------------------------------------------------------- /lib/event.dart: -------------------------------------------------------------------------------- 1 | import 'package:event_bus/event_bus.dart'; 2 | 3 | EventBus eventBus = EventBus(); 4 | 5 | class ChangeMaskEvent { 6 | String time; 7 | ChangeMaskEvent(this.time); 8 | } 9 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'dart:convert'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'barrage.dart'; 7 | import 'video.dart'; 8 | 9 | void main() async { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); 12 | await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); 13 | runApp(MyApp()); 14 | } 15 | 16 | class MyApp extends StatelessWidget { 17 | static final String _title = 'mask barrage'; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return MaterialApp( 22 | title: _title, 23 | home: Scaffold( 24 | body: Column( 25 | children: [ 26 | Expanded(flex: 1, child: Index()), 27 | ], 28 | ), 29 | ), 30 | ); 31 | } 32 | } 33 | 34 | class Index extends StatefulWidget { 35 | const Index({Key key}) : super(key: key); 36 | 37 | @override 38 | IndexState createState() => IndexState(); 39 | } 40 | 41 | class IndexState extends State with WidgetsBindingObserver { 42 | static final GlobalKey barrageKey = GlobalKey(); 43 | static final GlobalKey videoKey = GlobalKey(); 44 | 45 | Map cfg; 46 | 47 | @override 48 | void initState() { 49 | super.initState(); 50 | WidgetsBinding.instance.addObserver(this); 51 | Future loadString = DefaultAssetBundle.of(context).loadString("py/res.json"); 52 | 53 | loadString.then((String value) { 54 | setState(() { 55 | cfg = json.decode(value); 56 | }); 57 | }); 58 | } 59 | 60 | @override 61 | void dispose() { 62 | WidgetsBinding.instance.removeObserver(this); 63 | super.dispose(); 64 | } 65 | 66 | @override 67 | void didChangeAppLifecycleState(AppLifecycleState state) { 68 | switch (state) { 69 | case AppLifecycleState.paused: 70 | SystemChannels.platform.invokeMethod('SystemNavigator.pop'); 71 | break; 72 | default: 73 | break; 74 | } 75 | } 76 | 77 | @override 78 | Widget build(BuildContext context) { 79 | Widget render; 80 | if (cfg == null) { 81 | render = Center( 82 | child: CircularProgressIndicator( 83 | valueColor: AlwaysStoppedAnimation(Color.fromARGB(255, 255, 230, 15)), 84 | ), 85 | ); 86 | } else { 87 | num statusHeight = MediaQueryData.fromWindow(window).padding.top; 88 | num videoHeight = MediaQuery.of(context).size.width * cfg['frame_height'] / cfg['frame_width']; 89 | num marginTop = (MediaQuery.of(context).size.height - statusHeight - videoHeight) / 2 + statusHeight; 90 | 91 | render = Stack( 92 | alignment: AlignmentDirectional.topCenter, 93 | children: [ 94 | Positioned( 95 | top: marginTop, 96 | child: Container( 97 | width: MediaQuery.of(context).size.width, 98 | height: videoHeight, 99 | child: VedioBg( 100 | key: videoKey, 101 | cfg: cfg, 102 | ), 103 | ), 104 | ), 105 | Positioned( 106 | top: marginTop, 107 | child: Container( 108 | width: MediaQuery.of(context).size.width, 109 | height: videoHeight, 110 | child: BarrageInit( 111 | key: barrageKey, 112 | cfg: cfg, 113 | ), 114 | ), 115 | ), 116 | // GestureDetector( 117 | // onTap: () { 118 | // barrageKey.currentState.change(); 119 | // videoKey.currentState.change(); 120 | // }, 121 | // child: Container(color: Colors.transparent), 122 | // ), 123 | ], 124 | ); 125 | } 126 | 127 | return Container(color: Colors.black, child: render); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/mock.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class BarrageData { 6 | static final List _icons = [ 7 | Icon(Icons.favorite, color: Color(0xFFF44336)), 8 | Icon(Icons.sentiment_dissatisfied, color: Colors.green), 9 | Icon(Icons.sentiment_satisfied_alt, color: Colors.yellow), 10 | Icon(Icons.flash_on, color: Colors.purple), 11 | Icon(Icons.border_color, color: Colors.orange), 12 | Icon(Icons.brightness_3, color: Colors.blue), 13 | Icon(Icons.attach_money, color: Color(0xFFD15FEE)), 14 | Icon(Icons.favorite_border, color: Color(0xFFF44336)), 15 | Icon(Icons.flare, color: Color(0xFF66FF33)), 16 | ]; 17 | 18 | static const List barrageMock = [ 19 | "表白我允儿", 20 | "允允允允允允允允允允儿", 21 | "보고 싶어~~", 22 | "(づ ̄3 ̄)づ╭❤~", 23 | "把漂亮打在公屏上", 24 | "纯路人,请问这是仙女吗??", 25 | "Yooooooooooona~", 26 | "喜欢看你跳舞", 27 | "什么时候开演唱会呀", 28 | "그게 사랑 일지 도 몰라", 29 | "missing you", 30 | "林大俊~~", 31 | "哇。。😯!!", 32 | "林允儿=永远滴神😊" 33 | "炒鸡好看的小姐姐", 34 | "跳的真好噢..很🉑️", 35 | "新剧马上就要开播了,支持啊", 36 | "又要上热搜了吗" 37 | ]; 38 | 39 | String get randomWord => barrageMock[Random().nextInt(barrageMock.length)]; 40 | 41 | Icon get randomIcon => _icons[Random().nextInt(_icons.length)]; 42 | 43 | Color randomTextColor() { 44 | return Random().nextInt(10) < 9 ? Colors.white : Colors.yellow; 45 | } 46 | 47 | BarrageData() { 48 | int count = 0; 49 | while (count < 9) { 50 | _icons.add(Icon(Icons.favorite, color: Colors.transparent)); 51 | count++; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/video.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:video_player/video_player.dart'; 5 | import 'event.dart'; 6 | 7 | class VedioBg extends StatefulWidget { 8 | final Map cfg; 9 | const VedioBg({Key key, this.cfg}) : super(key: key); 10 | 11 | @override 12 | VedioBgState createState() => VedioBgState(); 13 | } 14 | 15 | class VedioBgState extends State { 16 | VideoPlayerController _controller; 17 | Future _initializeVideoPlayerFuture; 18 | bool _playing; 19 | num inMilliseconds = 0; 20 | Timer timer; 21 | 22 | void change() { 23 | if (_playing) { 24 | _controller.pause(); 25 | } else { 26 | _controller.play(); 27 | } 28 | } 29 | 30 | @override 31 | void initState() { 32 | super.initState(); 33 | int cd = widget.cfg['mask_cd']; 34 | _controller = VideoPlayerController.asset('py/source.mp4') 35 | ..setLooping(true) 36 | ..addListener(() { 37 | final bool isPlaying = _controller.value.isPlaying; 38 | final int nowMilliseconds = _controller.value.position.inMilliseconds; 39 | if ((inMilliseconds == 0 && nowMilliseconds > 0) || nowMilliseconds < inMilliseconds) { 40 | timer?.cancel(); 41 | int stepsTime = (nowMilliseconds / cd).round() * cd; 42 | timer = Timer.periodic(Duration(milliseconds: cd), (timer) { 43 | stepsTime += cd; 44 | eventBus.fire(ChangeMaskEvent(stepsTime.toString())); 45 | }); 46 | } 47 | inMilliseconds = nowMilliseconds; 48 | _playing = isPlaying; 49 | }); 50 | 51 | _initializeVideoPlayerFuture = _controller.initialize().then((_) {}); 52 | _controller.play(); 53 | } 54 | 55 | @override 56 | void dispose() { 57 | _controller.dispose(); 58 | timer?.cancel(); 59 | super.dispose(); 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | return FutureBuilder( 65 | future: _initializeVideoPlayerFuture, 66 | builder: (context, snapshot) { 67 | if (snapshot.hasError) { 68 | print(snapshot.error); 69 | } 70 | if (snapshot.connectionState == ConnectionState.done) { 71 | return AspectRatio( 72 | aspectRatio: _controller.value.aspectRatio, 73 | child: VideoPlayer(_controller), 74 | ); 75 | } else { 76 | return Center(); 77 | } 78 | }, 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "2.5.0-nullsafety.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "2.1.0-nullsafety.1" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "1.1.0-nullsafety.3" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.2.0-nullsafety.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.1.0-nullsafety.1" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.15.0-nullsafety.3" 46 | event_bus: 47 | dependency: "direct main" 48 | description: 49 | name: event_bus 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.1.1" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "1.2.0-nullsafety.1" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_test: 66 | dependency: "direct dev" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | flutter_web_plugins: 71 | dependency: transitive 72 | description: flutter 73 | source: sdk 74 | version: "0.0.0" 75 | matcher: 76 | dependency: transitive 77 | description: 78 | name: matcher 79 | url: "https://pub.flutter-io.cn" 80 | source: hosted 81 | version: "0.12.10-nullsafety.1" 82 | meta: 83 | dependency: transitive 84 | description: 85 | name: meta 86 | url: "https://pub.flutter-io.cn" 87 | source: hosted 88 | version: "1.3.0-nullsafety.3" 89 | path: 90 | dependency: transitive 91 | description: 92 | name: path 93 | url: "https://pub.flutter-io.cn" 94 | source: hosted 95 | version: "1.8.0-nullsafety.1" 96 | sky_engine: 97 | dependency: transitive 98 | description: flutter 99 | source: sdk 100 | version: "0.0.99" 101 | source_span: 102 | dependency: transitive 103 | description: 104 | name: source_span 105 | url: "https://pub.flutter-io.cn" 106 | source: hosted 107 | version: "1.8.0-nullsafety.2" 108 | stack_trace: 109 | dependency: transitive 110 | description: 111 | name: stack_trace 112 | url: "https://pub.flutter-io.cn" 113 | source: hosted 114 | version: "1.10.0-nullsafety.1" 115 | stream_channel: 116 | dependency: transitive 117 | description: 118 | name: stream_channel 119 | url: "https://pub.flutter-io.cn" 120 | source: hosted 121 | version: "2.1.0-nullsafety.1" 122 | string_scanner: 123 | dependency: transitive 124 | description: 125 | name: string_scanner 126 | url: "https://pub.flutter-io.cn" 127 | source: hosted 128 | version: "1.1.0-nullsafety.1" 129 | term_glyph: 130 | dependency: transitive 131 | description: 132 | name: term_glyph 133 | url: "https://pub.flutter-io.cn" 134 | source: hosted 135 | version: "1.2.0-nullsafety.1" 136 | test_api: 137 | dependency: transitive 138 | description: 139 | name: test_api 140 | url: "https://pub.flutter-io.cn" 141 | source: hosted 142 | version: "0.2.19-nullsafety.2" 143 | typed_data: 144 | dependency: transitive 145 | description: 146 | name: typed_data 147 | url: "https://pub.flutter-io.cn" 148 | source: hosted 149 | version: "1.3.0-nullsafety.3" 150 | vector_math: 151 | dependency: transitive 152 | description: 153 | name: vector_math 154 | url: "https://pub.flutter-io.cn" 155 | source: hosted 156 | version: "2.1.0-nullsafety.3" 157 | video_player: 158 | dependency: "direct main" 159 | description: 160 | name: video_player 161 | url: "https://pub.flutter-io.cn" 162 | source: hosted 163 | version: "1.0.1" 164 | video_player_platform_interface: 165 | dependency: transitive 166 | description: 167 | name: video_player_platform_interface 168 | url: "https://pub.flutter-io.cn" 169 | source: hosted 170 | version: "2.2.0" 171 | video_player_web: 172 | dependency: transitive 173 | description: 174 | name: video_player_web 175 | url: "https://pub.flutter-io.cn" 176 | source: hosted 177 | version: "0.1.4+1" 178 | sdks: 179 | dart: ">=2.10.0-110 <2.11.0" 180 | flutter: ">=1.12.13+hotfix.5 <2.0.0" 181 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: zoe_barrage 2 | description: AI mask barrage video show. 3 | version: 1.0.0 4 | 5 | environment: 6 | sdk: ">=2.8.0 <3.0.0" 7 | flutter: ">=1.10.0 <2.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | event_bus: ^1.1.0 14 | video_player: ^1.0.1 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | 20 | flutter: 21 | uses-material-design: true 22 | assets: 23 | - py/source.mp4 24 | - py/res.json -------------------------------------------------------------------------------- /py/1.frames.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Desc: 视频帧提取 3 | ''' 4 | import os 5 | import shutil 6 | import cv2 7 | import config 8 | 9 | dirPath = os.path.dirname(os.path.abspath(__file__)) 10 | images_path = dirPath + '/images' 11 | cap = cv2.VideoCapture(os.path.join(dirPath, config.VIDEO_NAME)) 12 | count = 1 13 | 14 | if os.path.exists(images_path): 15 | shutil.rmtree(images_path) 16 | os.makedirs(images_path) 17 | 18 | # 循环读取视频的每一帧 19 | while True: 20 | ret, frame = cap.read() 21 | if ret: 22 | if(count % config.FRAME_CD == 0): 23 | print('the number of frames:' + str(count)) 24 | # 保存截取帧到本地 25 | cv2.imwrite(images_path + '/frame' + str(count) + '.jpg', frame) 26 | count += 1 27 | cv2.waitKey(0) 28 | else: 29 | print('frames were created successfully') 30 | break 31 | 32 | cap.release() 33 | -------------------------------------------------------------------------------- /py/2.discern.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Desc: 调用算法接口返回人体模型灰度图 3 | ''' 4 | import os 5 | import shutil 6 | import base64 7 | import re 8 | import json 9 | import threading 10 | import requests 11 | import config 12 | 13 | dirPath = os.path.dirname(os.path.abspath(__file__)) 14 | clip_path = dirPath + '/clip' 15 | 16 | if not os.path.exists(clip_path): 17 | os.makedirs(clip_path) 18 | 19 | # 图像识别类 20 | class multiple_req: 21 | reqTimes = 0 22 | filename = None 23 | data = { 24 | 'api_key': config.FACE_KEY, 25 | 'api_secret': config.FACE_SECRET, 26 | 'return_grayscale': 1 27 | } 28 | 29 | def __init__(self, filename): 30 | self.filename = filename 31 | 32 | def once_again(self): 33 | # 成功率大约10%,记录一下被限流失败的次数 :) 34 | self.reqTimes += 1 35 | print(self.filename +' fail times:' + str(self.reqTimes)) 36 | return self.reqfaceplus() 37 | 38 | def reqfaceplus(self): 39 | abs_path_name = os.path.join(dirPath, 'images', self.filename) 40 | # 图片以二进制提交 41 | files = {'image_file': open(abs_path_name, 'rb')} 42 | try: 43 | response = requests.post( 44 | 'https://api-cn.faceplusplus.com/humanbodypp/v2/segment', data=self.data, files=files) 45 | res_data = json.loads(response.text) 46 | 47 | # 免费的API 很大概率被限流返回失败,这里递归调用,一直到这个图片成功识别后返回 48 | if 'error_message' in res_data: 49 | return self.once_again() 50 | else: 51 | # 识别成功返回结果 52 | return res_data 53 | except requests.exceptions.RequestException as e: 54 | return self.once_again() 55 | 56 | # 多线程并行函数 57 | def thread_req(n): 58 | # 创建图像识别类 59 | multiple_req_ins = multiple_req(filename=n) 60 | res = multiple_req_ins.reqfaceplus() 61 | # 返回结果为base64编码彩色图、灰度图 62 | img_data_color = base64.b64decode(res['body_image']) 63 | img_data = base64.b64decode(res['result']) 64 | 65 | with open(dirPath + '/clip/clip-color-' + n, 'wb') as f: 66 | # 保存彩色图片 67 | f.write(img_data_color) 68 | with open(dirPath + '/clip/clip-' + n, 'wb') as f: 69 | # 保存灰度图片 70 | f.write(img_data) 71 | print(n + ' clip saved.') 72 | 73 | # 读取之前准备好的所有视频帧图片进行识别 74 | image_list = os.listdir(os.path.join(dirPath, 'images')) 75 | image_list_sort = sorted(image_list, key=lambda name: int(re.sub(r'\D', '', name))) 76 | has_cliped_list = os.listdir(clip_path) 77 | for n in image_list_sort: 78 | if 'clip-' + n in has_cliped_list and 'clip-color-' + n in has_cliped_list: 79 | continue 80 | ''' 81 | 为每帧图片起一个单独的线程来递归调用,达到并行效果。所有图片被识别保存完毕后退出主进程,此过程需要几分钟。 82 | (这里每个线程中都是不断地递归网络请求、挂起等待、IO写入,不占用CPU) 83 | ''' 84 | t = threading.Thread(target=thread_req, name=n, args=[n]) 85 | t.start() 86 | -------------------------------------------------------------------------------- /py/3.translate.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Desc: openCV转换灰度图 & 轮廓判定转换坐标JSON 3 | ''' 4 | import os 5 | import json 6 | import re 7 | import shutil 8 | import cv2 9 | import config 10 | 11 | dirPath = os.path.dirname(os.path.abspath(__file__)) 12 | clip_path = os.path.join(dirPath, 'mask') 13 | cap = cv2.VideoCapture(os.path.join(dirPath, config.VIDEO_NAME)) 14 | frame_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) # 分辨率(宽) 15 | frame_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) # 分辨率(高) 16 | FPS = round(cap.get(cv2.CAP_PROP_FPS), 0) # 视频FPS 17 | mask_cd = int(1000 / FPS * config.FRAME_CD) # 初始帧时间 18 | milli_seconds_plus = mask_cd # 每次递增一帧的增加时间 19 | jsonTemp = { # 最后要存入的json配置 20 | 'mask_cd': mask_cd, 21 | 'frame_width': frame_width, 22 | 'frame_height': frame_height 23 | } 24 | 25 | if os.path.exists(clip_path): 26 | shutil.rmtree(clip_path) 27 | os.makedirs(clip_path) 28 | 29 | # 输出灰度图与轮廓坐标集合 30 | def output_clip(filename): 31 | global mask_cd 32 | # 读取原图(这里我们原图就已经是灰度图了) 33 | img = cv2.imread(os.path.join(dirPath, 'clip', filename)) 34 | # 转换成灰度图(openCV必须要转换一次才能喂给下一层) 35 | gray_in = cv2.cvtColor(img , cv2.COLOR_BGR2GRAY) 36 | # 反色变换,gray_in为一个三维矩阵,代表着灰度图的色值0~255,我们将黑白对调 37 | gray = 255 - gray_in 38 | # 将灰度图转换为纯黑白图,要么是0要么是255,没有中间值 39 | _, binary = cv2.threshold(gray , 220 , 255 , cv2.THRESH_BINARY) 40 | # 保存黑白图做参考 41 | cv2.imwrite(clip_path + '/invert-' + filename, binary) 42 | # 从黑白图中识趣包围图形,形成轮廓数据 43 | contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 44 | # 解析轮廓数据存入缓存 45 | clip_list = [] 46 | for item in contours: 47 | if item.size > 0: 48 | # 每个轮廓是一个三维矩阵,shape为(n, 1, 2) ,n为构成这个面的坐标数量,1没什么意义,2代表两个坐标x和y 49 | rows, _, __ = item.shape 50 | clip = [] 51 | clip_list.append(clip) 52 | for i in range(rows): 53 | # 将np.ndarray转为list,不然后面JSON序列化解析不了 54 | clip.append(item[i, 0].tolist()) 55 | 56 | millisecondsStr = str(mask_cd) 57 | # 将每一个轮廓信息保存到key为帧所对应时间的list 58 | jsonTemp[millisecondsStr] = clip_list 59 | 60 | print(filename + ' time(' + millisecondsStr +') data.') 61 | mask_cd += milli_seconds_plus 62 | 63 | # 列举刚才算法返回的灰度图 64 | clipFrame = [] 65 | for name in os.listdir(os.path.join(dirPath, 'clip')): 66 | if not re.match(r'^clip-frame', name): 67 | continue 68 | clipFrame.append(name) 69 | 70 | # 对文件名进行排序,按照帧顺序输出 71 | clipFrameSort = sorted(clipFrame, key=lambda name: int(re.sub(r'\D', '', name))) 72 | for name in clipFrameSort: 73 | output_clip(name) 74 | 75 | # 全部坐标提取完成后写成json提供给flutter 76 | jsObj = json.dumps(jsonTemp) 77 | 78 | fileObject = open(os.path.join(dirPath, 'res.json'), 'w') 79 | fileObject.write(jsObj) 80 | fileObject.close() 81 | 82 | print('calc done') 83 | -------------------------------------------------------------------------------- /py/config.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Desc: 配置文件 3 | ''' 4 | import os 5 | import cv2 6 | 7 | VIDEO_NAME = 'source.mp4' # 处理的视频文件名 8 | FACE_KEY = 'SgogTB9ZpBLwcEYAG4AXu4MkbNookgnJ' # 免费的识别key 9 | FACE_SECRET = '2_lrgs8L1qjXf4by_Yi20CNcG3LtIYDc' # 免费的密钥 10 | 11 | dirPath = os.path.dirname(os.path.abspath(__file__)) 12 | cap = cv2.VideoCapture(os.path.join(dirPath, VIDEO_NAME)) 13 | FPS = round(cap.get(cv2.CAP_PROP_FPS), 0) 14 | 15 | # 进行识别的关键帧,FPS每上升30,关键帧间隔+1(保证flutter在重绘蒙版时的性能的一致性) 16 | FRAME_CD = max(1, round(FPS / 30)) 17 | 18 | if cv2.CAP_PROP_FRAME_COUNT / FRAME_CD >= 900: 19 | raise Warning('经计算你的视频关键帧已经超过了900,建议减少视频时长或FPS帧率!') 20 | -------------------------------------------------------------------------------- /py/product_app_icon.py: -------------------------------------------------------------------------------- 1 | ''' 2 | @Desc: 自动生成APP打包图标 3 | ''' 4 | import os 5 | import shutil 6 | from PIL import Image 7 | 8 | dirPath = os.path.dirname(os.path.abspath(__file__)) 9 | outPutPath_ios = os.path.abspath(os.path.join(dirPath, '../ios/Runner/Assets.xcassets/AppIcon.appiconset/')) 10 | outPutPath_android = os.path.abspath(os.path.join(dirPath, '../android/app/src/main/res/')) 11 | 12 | ImageName = os.path.abspath(os.path.join(dirPath, '../zoe.png')) 13 | originImg = '' 14 | 15 | try: 16 | originImg = Image.open(ImageName) 17 | except: 18 | print ('\033[31m' + '\'' + ImageName + '\'' + ',文件不存在或不是图片,请检查文件路径.' + '\033[0m') 19 | quit() 20 | 21 | def create_new_path(path): 22 | if os.path.exists(path): 23 | shutil.rmtree(path) 24 | os.makedirs(path) 25 | return path 26 | 27 | # IOS 28 | create_new_path(outPutPath_ios) 29 | 30 | # 20x20 31 | img0 = originImg.resize((20,20), Image.ANTIALIAS) 32 | img1 = originImg.resize((40,40), Image.ANTIALIAS) 33 | img2 = originImg.resize((60,60), Image.ANTIALIAS) 34 | img0.save(os.path.join(outPutPath_ios, 'appZoe20x20.png'),"png") 35 | img1.save(os.path.join(outPutPath_ios, 'appZoe20x20@2x.png'),"png") 36 | img2.save(os.path.join(outPutPath_ios, 'appZoe20x20@3x.png'),"png") 37 | 38 | # 29x29 39 | img3 = originImg.resize((29,29), Image.ANTIALIAS) 40 | img4 = originImg.resize((58,58), Image.ANTIALIAS) 41 | img5 = originImg.resize((87,87), Image.ANTIALIAS) 42 | img3.save(os.path.join(outPutPath_ios, 'appZoe29x29.png'),"png") 43 | img4.save(os.path.join(outPutPath_ios, 'appZoe29x29@2x.png'),"png") 44 | img5.save(os.path.join(outPutPath_ios, 'appZoe29x29@3x.png'),"png") 45 | 46 | # 40x40 47 | img6 = originImg.resize((40,40), Image.ANTIALIAS) 48 | img7 = originImg.resize((80,80), Image.ANTIALIAS) 49 | img8 = originImg.resize((120,120), Image.ANTIALIAS) 50 | img6.save(os.path.join(outPutPath_ios, 'appZoe40x40.png'),"png") 51 | img7.save(os.path.join(outPutPath_ios, 'appZoe40x40@2x.png'),"png") 52 | img8.save(os.path.join(outPutPath_ios, 'appZoe40x40@3x.png'),"png") 53 | 54 | # 60x60 55 | img9 = originImg.resize((120,120), Image.ANTIALIAS) 56 | img10 = originImg.resize((180,180), Image.ANTIALIAS) 57 | img9.save(os.path.join(outPutPath_ios, 'appZoe60x60@2x.png'),"png") 58 | img10.save(os.path.join(outPutPath_ios, 'appZoe60x60@3x.png'),"png") 59 | 60 | # ipad 61 | img11 = originImg.resize((76,76), Image.ANTIALIAS) 62 | img12 = originImg.resize((152,152), Image.ANTIALIAS) 63 | img13 = originImg.resize((167,167), Image.ANTIALIAS) 64 | img11.save(os.path.join(outPutPath_ios, 'appZoe76x76.png'),"png") 65 | img12.save(os.path.join(outPutPath_ios, 'appZoe76x76@2x.png'),"png") 66 | img13.save(os.path.join(outPutPath_ios, 'appZoe83.5x83.5@2x.png'),"png") 67 | 68 | # 创建Contents.json文件 69 | 70 | content = ''' 71 | { 72 | "images" : [ 73 | { 74 | "idiom" : "iphone", 75 | "size" : "20x20", 76 | "filename" : "appZoe20x20@2x.png", 77 | "scale" : "2x" 78 | }, 79 | { 80 | "idiom" : "iphone", 81 | "size" : "20x20", 82 | "filename" : "appZoe20x20@3x.png", 83 | "scale" : "3x" 84 | }, 85 | { 86 | "size" : "29x29", 87 | "idiom" : "iphone", 88 | "filename" : "appZoe29x29.png", 89 | "scale" : "1x" 90 | }, 91 | { 92 | "size" : "29x29", 93 | "idiom" : "iphone", 94 | "filename" : "appZoe29x29@2x.png", 95 | "scale" : "2x" 96 | }, 97 | { 98 | "size" : "29x29", 99 | "idiom" : "iphone", 100 | "filename" : "appZoe29x29@3x.png", 101 | "scale" : "3x" 102 | }, 103 | { 104 | "size" : "40x40", 105 | "idiom" : "iphone", 106 | "filename" : "appZoe40x40@2x.png", 107 | "scale" : "2x" 108 | }, 109 | { 110 | "size" : "40x40", 111 | "idiom" : "iphone", 112 | "filename" : "appZoe40x40@3x.png", 113 | "scale" : "3x" 114 | }, 115 | { 116 | "size" : "60x60", 117 | "idiom" : "iphone", 118 | "filename" : "appZoe60x60@2x.png", 119 | "scale" : "2x" 120 | }, 121 | { 122 | "size" : "60x60", 123 | "idiom" : "iphone", 124 | "filename" : "appZoe60x60@3x.png", 125 | "scale" : "3x" 126 | }, 127 | { 128 | "idiom" : "ipad", 129 | "size" : "20x20", 130 | "filename" : "appZoe20x20.png", 131 | "scale" : "1x" 132 | }, 133 | { 134 | "idiom" : "ipad", 135 | "size" : "20x20", 136 | "filename" : "appZoe20x20@2x.png", 137 | "scale" : "2x" 138 | }, 139 | { 140 | "size" : "29x29", 141 | "idiom" : "ipad", 142 | "filename" : "appZoe29x29.png", 143 | "scale" : "1x" 144 | }, 145 | { 146 | "size" : "29x29", 147 | "idiom" : "ipad", 148 | "filename" : "appZoe29x29@2x.png", 149 | "scale" : "2x" 150 | }, 151 | { 152 | "size" : "40x40", 153 | "idiom" : "ipad", 154 | "filename" : "appZoe40x40.png", 155 | "scale" : "1x" 156 | }, 157 | { 158 | "size" : "40x40", 159 | "idiom" : "ipad", 160 | "filename" : "appZoe40x40@2x.png", 161 | "scale" : "2x" 162 | }, 163 | { 164 | "size" : "76x76", 165 | "idiom" : "ipad", 166 | "filename" : "appZoe76x76.png", 167 | "scale" : "1x" 168 | }, 169 | { 170 | "size" : "76x76", 171 | "idiom" : "ipad", 172 | "filename" : "appZoe76x76@2x.png", 173 | "scale" : "2x" 174 | }, 175 | { 176 | "size" : "83.5x83.5", 177 | "idiom" : "ipad", 178 | "filename" : "appZoe83.5x83.5@2x.png", 179 | "scale" : "2x" 180 | } 181 | ], 182 | "info" : { 183 | "version" : 1, 184 | "author" : "xcode" 185 | } 186 | } 187 | ''' 188 | 189 | f = open(os.path.join(outPutPath_ios, 'Contents.json'), 'w') 190 | f.write(content) 191 | f.close() 192 | 193 | print('\033[7;32m' + 'IOS输出文件夹:' + outPutPath_ios + '\033[0m') 194 | 195 | # 安卓 196 | icon_name = 'zoe.png' 197 | 198 | path1 = create_new_path(os.path.join(outPutPath_android, 'mipmap-mdpi')) 199 | path2 = create_new_path(os.path.join(outPutPath_android, 'mipmap-hdpi')) 200 | path3 = create_new_path(os.path.join(outPutPath_android, 'mipmap-xhdpi')) 201 | path4 = create_new_path(os.path.join(outPutPath_android, 'mipmap-xxhdpi')) 202 | path5 = create_new_path(os.path.join(outPutPath_android, 'mipmap-xxxhdpi')) 203 | 204 | img14 = originImg.resize((48,48), Image.ANTIALIAS) 205 | img15 = originImg.resize((72,72), Image.ANTIALIAS) 206 | img16 = originImg.resize((96,96), Image.ANTIALIAS) 207 | img17 = originImg.resize((144,144), Image.ANTIALIAS) 208 | img18 = originImg.resize((192,192), Image.ANTIALIAS) 209 | img14.save(os.path.join(path1, icon_name),"png") 210 | img15.save(os.path.join(path2, icon_name),"png") 211 | img16.save(os.path.join(path3, icon_name),"png") 212 | img17.save(os.path.join(path4, icon_name),"png") 213 | img18.save(os.path.join(path5, icon_name),"png") 214 | 215 | print('\033[7;32m' + '安卓输出文件夹:' + outPutPath_android + '\033[0m') 216 | -------------------------------------------------------------------------------- /py/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.22.0 2 | opencv-python==4.4.0.46 3 | Image==1.5.33 4 | flask==1.1.2 -------------------------------------------------------------------------------- /py/source.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/py/source.mp4 -------------------------------------------------------------------------------- /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:zoe_barrage/main.dart'; 12 | 13 | void main() {} 14 | -------------------------------------------------------------------------------- /zoe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yukilzw/zoe_barrage/bbf908a9c820e2f7485f09fe94ce316d0fee1ec1/zoe.png --------------------------------------------------------------------------------