├── .fvm ├── flutter_sdk └── fvm_config.json ├── lib ├── bloc │ ├── bloc.dart │ └── subtitle │ │ ├── subtitle_event.dart │ │ ├── subtitle_state.dart │ │ └── subtitle_bloc.dart ├── data │ ├── constants │ │ ├── constants.dart │ │ └── view_keys.dart │ ├── repository │ │ ├── repositories.dart │ │ └── subtitle_repository.dart │ ├── data.dart │ └── models │ │ ├── models.dart │ │ ├── subtitles.dart │ │ ├── style │ │ ├── subtitle_position.dart │ │ ├── subtitle_border_style.dart │ │ └── subtitle_style.dart │ │ └── subtitle.dart ├── subtitle_wrapper_package.dart ├── deprecated_subtitle_wrapper.dart ├── subtitle_controller.dart ├── subtitle_wrapper.dart └── subtitle_text_view.dart ├── example ├── ios │ ├── Flutter │ │ ├── .last_build_id │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── Flutter.podspec │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── .gitignore │ ├── Podfile.lock │ └── Podfile ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ └── Icon-512.png │ ├── index.html │ └── manifest.json ├── macos │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Release.entitlements │ │ ├── MainFlutterWindow.swift │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ └── MainMenu.xib │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Podfile.lock │ └── Podfile ├── android │ ├── gradle.properties │ ├── .gitignore │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── build.gradle ├── .metadata ├── analysis_options.yaml ├── lib │ ├── data │ │ └── sw_constants.dart │ └── main.dart ├── README.md ├── .gitignore ├── .flutter-plugins-dependencies ├── pubspec.yaml └── pubspec.lock ├── .vscode ├── settings.json └── launch.json ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ └── bug_report.md ├── FUNDING.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── flutter-drive.yml │ └── flutter-release-drive.yml ├── melos.yaml ├── .metadata ├── analysis_options.yaml ├── pubspec.yaml ├── .flutter-plugins-dependencies ├── LICENSE ├── .gitignore ├── test ├── subtitle_controller_test.dart ├── subtitle_bloc_test.dart ├── srt_test.dart └── webvtt_test.dart ├── README.md ├── coverage └── lcov.info ├── CHANGELOG.md └── pubspec.lock /.fvm/flutter_sdk: -------------------------------------------------------------------------------- 1 | /Users/jorandob/fvm/versions/stable -------------------------------------------------------------------------------- /lib/bloc/bloc.dart: -------------------------------------------------------------------------------- 1 | export 'subtitle/subtitle_bloc.dart'; 2 | -------------------------------------------------------------------------------- /lib/data/constants/constants.dart: -------------------------------------------------------------------------------- 1 | export 'view_keys.dart'; 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 4ad617371ad96849536454b194092ffa -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dart.runPubGetOnPubspecChanges": "never" 3 | } -------------------------------------------------------------------------------- /lib/data/repository/repositories.dart: -------------------------------------------------------------------------------- 1 | export 'subtitle_repository.dart'; 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /.fvm/fvm_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "flutterSdkVersion": "stable", 3 | "flavors": {} 4 | } -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Every request must be reviewed and accepted by: 2 | 3 | * @Joran-Dob -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/web/favicon.png -------------------------------------------------------------------------------- /melos.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_subtitle_wrapper 2 | 3 | packages: 4 | - example/** 5 | - "*" 6 | 7 | 8 | # scripts: -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /lib/data/data.dart: -------------------------------------------------------------------------------- 1 | export './constants/constants.dart'; 2 | export './models/models.dart'; 3 | export './repository/repositories.dart'; 4 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/data/constants/view_keys.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | class ViewKeys { 4 | static const Key subtitleTextContent = Key('subtitle_text_content'); 5 | } 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.app.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /lib/data/models/models.dart: -------------------------------------------------------------------------------- 1 | export 'style/subtitle_border_style.dart'; 2 | export 'style/subtitle_position.dart'; 3 | export 'style/subtitle_style.dart'; 4 | export 'subtitle.dart'; 5 | export 'subtitles.dart'; 6 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joran-Dob/flutter_subtitle_wrapper/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /lib/subtitle_wrapper_package.dart: -------------------------------------------------------------------------------- 1 | export './data/data.dart'; 2 | export 'deprecated_subtitle_wrapper.dart'; 3 | export 'subtitle_controller.dart'; 4 | export 'subtitle_text_view.dart'; 5 | export 'subtitle_wrapper.dart'; 6 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 3 | #include "Generated.xcconfig" 4 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/data/models/subtitles.dart: -------------------------------------------------------------------------------- 1 | import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; 2 | 3 | class Subtitles { 4 | Subtitles({ 5 | required this.subtitles, 6 | }); 7 | final List subtitles; 8 | } 9 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | example 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug (example)", 6 | "request": "launch", 7 | "type": "dart", 8 | "program": "example/lib/main.dart", 9 | }, 10 | ], 11 | } -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.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: 0b24a5a2ff9eccfba77bb68a0abcf1b7f0ae5b37 8 | channel: dev 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /example/.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: cc3ca9a916cb1da851a1f36432154a534787da99 8 | channel: dev 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /lib/data/models/style/subtitle_position.dart: -------------------------------------------------------------------------------- 1 | const _defaultSubtitleBottomPosition = 50.0; 2 | 3 | class SubtitlePosition { 4 | const SubtitlePosition({ 5 | this.left = 0.0, 6 | this.right = 0.0, 7 | this.top, 8 | this.bottom = _defaultSubtitleBottomPosition, 9 | }); 10 | final double left; 11 | final double right; 12 | final double? top; 13 | final double bottom; 14 | } 15 | -------------------------------------------------------------------------------- /lib/data/models/subtitle.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class Subtitle extends Equatable { 4 | const Subtitle({ 5 | required this.startTime, 6 | required this.endTime, 7 | required this.text, 8 | }); 9 | final Duration startTime; 10 | final Duration endTime; 11 | final String text; 12 | 13 | @override 14 | List get props => [startTime, endTime, text]; 15 | } 16 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:very_good_analysis/analysis_options.yaml 2 | linter: 3 | rules: 4 | public_member_api_docs: false 5 | prefer_int_literals: false 6 | constant_identifier_names: false 7 | sort_pub_dependencies: false 8 | one_member_abstracts: false 9 | lines_longer_than_80_chars: false 10 | analyzer: 11 | 12 | exclude: [build/**, lib/**.freezed.dart, lib/**.g.dart, lib/**.graphql.dart] 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/data/models/style/subtitle_border_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const _defaultStrokeWidth = 2.0; 4 | 5 | class SubtitleBorderStyle { 6 | const SubtitleBorderStyle({ 7 | this.strokeWidth = _defaultStrokeWidth, 8 | this.style = PaintingStyle.stroke, 9 | this.color = Colors.black, 10 | }); 11 | final double strokeWidth; 12 | final PaintingStyle style; 13 | final Color color; 14 | } 15 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:very_good_analysis/analysis_options.yaml 2 | linter: 3 | rules: 4 | public_member_api_docs: false 5 | prefer_int_literals: false 6 | constant_identifier_names: false 7 | sort_pub_dependencies: false 8 | one_member_abstracts: false 9 | lines_longer_than_80_chars: false 10 | analyzer: 11 | 12 | exclude: [build/**, lib/**.freezed.dart, lib/**.g.dart, lib/**.graphql.dart] 13 | -------------------------------------------------------------------------------- /example/lib/data/sw_constants.dart: -------------------------------------------------------------------------------- 1 | mixin SwConstants { 2 | static const String videoUrl = 3 | 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4'; 4 | 5 | // Subtitles 6 | static const String enSubtitle = 'https://pastebin.com/raw/W5rF45tN'; 7 | static const String esSubtitle = 'https://pastebin.com/raw/tsT3qtHf'; 8 | static const String nlSubtitle = 'https://pastebin.com/raw/L0qEE0cA'; 9 | } 10 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import video_player_avfoundation 9 | import wakelock_macos 10 | 11 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 12 | FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) 13 | WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) 14 | } 15 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.client 10 | 11 | com.apple.security.network.server 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/deprecated_subtitle_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; 2 | 3 | // ignore: prefer-match-file-name, this has a different name because of the deprecation. 4 | class SubTitleWrapper extends SubtitleWrapper { 5 | @Deprecated('Renamed to SubtitleWrapper') 6 | const SubTitleWrapper({ 7 | required super.videoChild, 8 | required super.subtitleController, 9 | required super.videoPlayerController, 10 | super.key, 11 | super.subtitleStyle, 12 | super.backgroundColor, 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /lib/bloc/subtitle/subtitle_event.dart: -------------------------------------------------------------------------------- 1 | part of 'subtitle_bloc.dart'; 2 | 3 | abstract class SubtitleEvent { 4 | const SubtitleEvent(); 5 | } 6 | 7 | class InitSubtitles extends SubtitleEvent { 8 | InitSubtitles({required this.subtitleController}); 9 | final SubtitleController subtitleController; 10 | } 11 | 12 | class LoadSubtitle extends SubtitleEvent {} 13 | 14 | class UpdateLoadedSubtitle extends SubtitleEvent { 15 | UpdateLoadedSubtitle({this.subtitle}); 16 | final Subtitle? subtitle; 17 | } 18 | 19 | class CompletedShowingSubtitles extends SubtitleEvent {} 20 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: "fix: " 5 | labels: bug 6 | --- 7 | 8 | **Description** 9 | A clear and concise description of what the bug is. 10 | 11 | **Steps To Reproduce** 12 | 13 | 1. Go to '...' 14 | 2. Click on '....' 15 | 3. Scroll down to '....' 16 | 4. See error 17 | 18 | **Expected Behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Additional Context** 25 | Add any other context about the problem here. -------------------------------------------------------------------------------- /lib/data/models/style/subtitle_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; 3 | 4 | const _defaultFontSize = 16.0; 5 | 6 | class SubtitleStyle { 7 | const SubtitleStyle({ 8 | this.hasBorder = false, 9 | this.borderStyle = const SubtitleBorderStyle(), 10 | this.fontSize = _defaultFontSize, 11 | this.textColor = Colors.black, 12 | this.position = const SubtitlePosition(), 13 | }); 14 | final bool hasBorder; 15 | final SubtitleBorderStyle borderStyle; 16 | final double fontSize; 17 | final Color textColor; 18 | final SubtitlePosition position; 19 | } 20 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: subtitle_wrapper_package 2 | description: A Subtitle Wrapper package, this subtitle wrapper package displays subtitles for a video player. 3 | version: 2.2.1 4 | homepage: https://github.com/Joran-Dob/flutter_subtitle_wrapper 5 | 6 | environment: 7 | sdk: ">=2.18.0 <3.0.0" 8 | 9 | dependencies: 10 | bloc: ^8.1.4 11 | equatable: ^2.0.5 12 | flutter: 13 | sdk: flutter 14 | flutter_bloc: ^8.1.5 15 | http: ^1.2.1 16 | http_parser: ^4.0.2 17 | video_player: ^2.8.3 18 | 19 | dev_dependencies: 20 | bloc_test: ^9.1.7 21 | flutter_test: 22 | sdk: flutter 23 | very_good_analysis: ^5.1.0 24 | mocktail: ^1.0.3 25 | 26 | flutter: 27 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Generated.xcconfig 20 | Flutter/app.flx 21 | Flutter/app.zip 22 | Flutter/flutter_assets/ 23 | Flutter/flutter_export_environment.sh 24 | ServiceDefinitions.json 25 | Runner/GeneratedPluginRegistrant.* 26 | 27 | # Exceptions to above rules. 28 | !default.mode1v3 29 | !default.mode2v3 30 | !default.pbxuser 31 | !default.perspectivev3 32 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - wakelock_macos (0.0.1): 4 | - FlutterMacOS 5 | 6 | DEPENDENCIES: 7 | - FlutterMacOS (from `Flutter/ephemeral`) 8 | - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) 9 | 10 | EXTERNAL SOURCES: 11 | FlutterMacOS: 12 | :path: Flutter/ephemeral 13 | wakelock_macos: 14 | :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos 15 | 16 | SPEC CHECKSUMS: 17 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 18 | wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 19 | 20 | PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 21 | 22 | COCOAPODS: 1.11.3 23 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Joran-Dob] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Web related 33 | lib/generated_plugin_registrant.dart 34 | 35 | # Exceptions to above rules. 36 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ## Description 10 | 11 | 12 | 13 | ## Type of Change 14 | 15 | 16 | 17 | - [ ] ✨ New feature (non-breaking change which adds functionality) 18 | - [ ] 🛠️ Bug fix (non-breaking change which fixes an issue) 19 | - [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change) 20 | - [ ] 🧹 Code refactor 21 | - [ ] ✅ Build configuration change 22 | - [ ] 📝 Documentation 23 | - [ ] 🗑️ Chore -------------------------------------------------------------------------------- /example/ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # This podspec is NOT to be published. It is only used as a local source! 3 | # This is a generated file; do not edit or check into version control. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'Flutter' 8 | s.version = '1.0.0' 9 | s.summary = 'A UI toolkit for beautiful and fast apps.' 10 | s.homepage = 'https://flutter.dev' 11 | s.license = { :type => 'BSD' } 12 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 13 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 14 | s.ios.deployment_target = '12.0' 15 | # Framework linking is handled by Flutter tooling, not CocoaPods. 16 | # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. 17 | s.vendored_frameworks = 'path/to/nothing' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - video_player_avfoundation (0.0.1): 4 | - Flutter 5 | - FlutterMacOS 6 | - wakelock (0.0.1): 7 | - Flutter 8 | 9 | DEPENDENCIES: 10 | - Flutter (from `Flutter`) 11 | - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) 12 | - wakelock (from `.symlinks/plugins/wakelock/ios`) 13 | 14 | EXTERNAL SOURCES: 15 | Flutter: 16 | :path: Flutter 17 | video_player_avfoundation: 18 | :path: ".symlinks/plugins/video_player_avfoundation/darwin" 19 | wakelock: 20 | :path: ".symlinks/plugins/wakelock/ios" 21 | 22 | SPEC CHECKSUMS: 23 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 24 | video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 25 | wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f 26 | 27 | PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048 28 | 29 | COCOAPODS: 1.15.2 30 | -------------------------------------------------------------------------------- /example/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 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/bloc/subtitle/subtitle_state.dart: -------------------------------------------------------------------------------- 1 | part of 'subtitle_bloc.dart'; 2 | 3 | abstract class SubtitleState extends Equatable { 4 | const SubtitleState(); 5 | } 6 | 7 | class SubtitleInitial extends SubtitleState { 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class SubtitleInitializing extends SubtitleState { 13 | @override 14 | List get props => []; 15 | } 16 | 17 | class SubtitleInitialized extends SubtitleState { 18 | @override 19 | List get props => []; 20 | } 21 | 22 | class LoadingSubtitle extends SubtitleState { 23 | @override 24 | List get props => []; 25 | } 26 | 27 | class LoadedSubtitle extends SubtitleState { 28 | const LoadedSubtitle(this.subtitle); 29 | final Subtitle? subtitle; 30 | 31 | @override 32 | List get props => [subtitle]; 33 | } 34 | 35 | class CompletedSubtitle extends SubtitleState { 36 | @override 37 | List get props => []; 38 | } 39 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player_avfoundation","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"video_player_android","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_android-2.4.12/","native_build":true,"dependencies":[]}],"macos":[{"name":"video_player_avfoundation","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_web-2.3.0/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_android","video_player_avfoundation","video_player_web"]},{"name":"video_player_android","dependencies":[]},{"name":"video_player_avfoundation","dependencies":[]},{"name":"video_player_web","dependencies":[]}],"date_created":"2024-04-02 19:17:56.081921","version":"3.19.3"} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Joran Dob 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/flutter-drive.yml: -------------------------------------------------------------------------------- 1 | name: Test and (dry-run) publish package 🎁 2 | on: [pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: 📚 Git Checkout 8 | uses: actions/checkout@v2 9 | - name: 🐦 Setup Flutter 10 | uses: subosito/flutter-action@v2 11 | with: 12 | flutter-version: 3.7.2 13 | channel: "stable" 14 | cache: true 15 | - name: 📦 Install Dependencies 16 | run: flutter pub get 17 | - name: ✨ Check Formatting 18 | run: flutter format --set-exit-if-changed lib test example 19 | - name: 🕵️ Analyze 20 | run: flutter analyze lib test example 21 | - name: 🧪 Run Tests 22 | run: flutter test --no-pub --coverage --test-randomize-ordering-seed random 23 | - name: 📊 Check Code Coverage 24 | uses: VeryGoodOpenSource/very_good_coverage@v1 25 | with: 26 | # TODO (Joran-Dob), improve code coverage 27 | min_coverage: 50 28 | path: coverage/lcov.info 29 | - name: 🚀 Upload coverage to Codecov 30 | uses: codecov/codecov-action@v1 31 | with: 32 | file: coverage/lcov.info 33 | - name: 📦 Publish (dry-run) 34 | run: flutter pub publish --dry-run 35 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /.github/workflows/flutter-release-drive.yml: -------------------------------------------------------------------------------- 1 | name: Test and publish package 🎁 2 | on: 3 | release: 4 | types: 5 | - created 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: 📚 Git Checkout 11 | uses: actions/checkout@v2 12 | - name: 🐦 Setup Flutter 13 | uses: subosito/flutter-action@v2 14 | with: 15 | flutter-version: 3.7.2 16 | channel: "stable" 17 | cache: true 18 | - name: 📦 Install Dependencies 19 | run: flutter pub get 20 | - name: ✨ Check Formatting 21 | run: flutter format --set-exit-if-changed lib test example 22 | - name: 🕵️ Analyze 23 | run: flutter analyze lib test example 24 | - name: 🧪 Run Tests 25 | run: flutter test --no-pub --coverage --test-randomize-ordering-seed random 26 | 27 | - name: 📊 Check Code Coverage 28 | uses: VeryGoodOpenSource/very_good_coverage@v1 29 | with: 30 | # TODO (Joran-Dob), improve code coverage 31 | min_coverage: 50 32 | path: coverage/lcov.info 33 | - name: 🚀 Upload coverage to Codecov 34 | uses: codecov/codecov-action@v1 35 | with: 36 | file: coverage/lcov.info 37 | - name: Publish 38 | uses: sakebook/actions-flutter-pub-publisher@v1.4.0 39 | with: 40 | credential: ${{ secrets.PUBSPEC_CREDENTIALS }} 41 | flutter_package: true 42 | skip_test: true 43 | dry_run: false -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '12.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 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/subtitle_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:subtitle_wrapper_package/bloc/subtitle/subtitle_bloc.dart'; 2 | 3 | class SubtitleController { 4 | SubtitleController({ 5 | this.subtitleUrl, 6 | this.subtitlesContent, 7 | this.showSubtitles = true, 8 | this.subtitleDecoder, 9 | this.subtitleType = SubtitleType.webvtt, 10 | }); 11 | String? subtitlesContent; 12 | String? subtitleUrl; 13 | final bool showSubtitles; 14 | SubtitleDecoder? subtitleDecoder; 15 | SubtitleType subtitleType; 16 | bool _attached = false; 17 | SubtitleBloc? _subtitleBloc; 18 | 19 | void attach(SubtitleBloc subtitleBloc) { 20 | _subtitleBloc = subtitleBloc; 21 | _attached = true; 22 | } 23 | 24 | void detach() { 25 | _attached = false; 26 | _subtitleBloc = null; 27 | } 28 | 29 | void updateSubtitleUrl({ 30 | required String url, 31 | }) { 32 | if (_attached) { 33 | subtitleUrl = url; 34 | _subtitleBloc!.add( 35 | InitSubtitles( 36 | subtitleController: this, 37 | ), 38 | ); 39 | } else { 40 | throw Exception('Seems that the controller is not correctly attached.'); 41 | } 42 | } 43 | 44 | void updateSubtitleContent({ 45 | required String content, 46 | }) { 47 | if (_attached) { 48 | subtitlesContent = content; 49 | _subtitleBloc!.add( 50 | InitSubtitles( 51 | subtitleController: this, 52 | ), 53 | ); 54 | } else { 55 | throw Exception('Seems that the controller is not correctly attached.'); 56 | } 57 | } 58 | } 59 | 60 | enum SubtitleDecoder { 61 | utf8, 62 | latin1, 63 | } 64 | 65 | enum SubtitleType { 66 | webvtt, 67 | srt, 68 | } 69 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"video_player_avfoundation","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"wakelock","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/wakelock-0.6.1+2/","native_build":true,"dependencies":[]}],"android":[{"name":"video_player_android","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_android-2.4.12/","native_build":true,"dependencies":[]},{"name":"wakelock","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/wakelock-0.6.1+2/","native_build":true,"dependencies":[]}],"macos":[{"name":"video_player_avfoundation","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_avfoundation-2.5.6/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"wakelock_macos","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/wakelock_macos-0.4.0/","native_build":true,"dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"video_player_web","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/video_player_web-2.3.0/","dependencies":[]},{"name":"wakelock_web","path":"/Users/jorandob/.pub-cache/hosted/pub.dev/wakelock_web-0.4.0/","dependencies":[]}]},"dependencyGraph":[{"name":"video_player","dependencies":["video_player_android","video_player_avfoundation","video_player_web"]},{"name":"video_player_android","dependencies":[]},{"name":"video_player_avfoundation","dependencies":[]},{"name":"video_player_web","dependencies":[]},{"name":"wakelock","dependencies":["wakelock_macos","wakelock_web"]},{"name":"wakelock_macos","dependencies":[]},{"name":"wakelock_web","dependencies":[]}],"date_created":"2024-04-02 19:19:59.773904","version":"3.19.3"} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/Flutter/flutter_export_environment.sh 65 | **/ios/ServiceDefinitions.json 66 | **/ios/Runner/GeneratedPluginRegistrant.* 67 | 68 | # Exceptions to above rules. 69 | !**/ios/**/default.mode1v3 70 | !**/ios/**/default.mode2v3 71 | !**/ios/**/default.pbxuser 72 | !**/ios/**/default.perspectivev3 73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 74 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/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 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /lib/subtitle_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:subtitle_wrapper_package/bloc/subtitle/subtitle_bloc.dart'; 4 | import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; 5 | import 'package:video_player/video_player.dart'; 6 | 7 | class SubtitleWrapper extends StatelessWidget { 8 | const SubtitleWrapper({ 9 | required this.videoChild, 10 | required this.subtitleController, 11 | required this.videoPlayerController, 12 | super.key, 13 | this.subtitleStyle = const SubtitleStyle(), 14 | this.backgroundColor, 15 | }); 16 | final Widget videoChild; 17 | final SubtitleController subtitleController; 18 | final VideoPlayerController videoPlayerController; 19 | final SubtitleStyle subtitleStyle; 20 | final Color? backgroundColor; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Stack( 25 | children: [ 26 | videoChild, 27 | if (subtitleController.showSubtitles) 28 | Positioned( 29 | top: subtitleStyle.position.top, 30 | bottom: subtitleStyle.position.bottom, 31 | left: subtitleStyle.position.left, 32 | right: subtitleStyle.position.right, 33 | child: BlocProvider( 34 | create: (context) => SubtitleBloc( 35 | videoPlayerController: videoPlayerController, 36 | subtitleRepository: SubtitleDataRepository( 37 | subtitleController: subtitleController, 38 | ), 39 | subtitleController: subtitleController, 40 | )..add( 41 | InitSubtitles( 42 | subtitleController: subtitleController, 43 | ), 44 | ), 45 | child: SubtitleTextView( 46 | subtitleStyle: subtitleStyle, 47 | backgroundColor: backgroundColor, 48 | ), 49 | ), 50 | ) 51 | else 52 | Container(), 53 | ], 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /example/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.example" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.1' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 67 | } 68 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | publish_to: none 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.18.0 <3.0.0" 18 | 19 | dependencies: 20 | chewie: ^1.3.3 21 | cupertino_icons: ^1.0.4 22 | flutter: 23 | sdk: flutter 24 | subtitle_wrapper_package: 25 | path: ../ 26 | video_player: ^2.4.2 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | very_good_analysis: ^4.0.0+1 32 | 33 | # For information on the generic Dart part of this file, see the 34 | # following page: https://dart.dev/tools/pub/pubspec 35 | 36 | # The following section is specific to Flutter. 37 | flutter: 38 | # The following line ensures that the Material Icons font is 39 | # included with your application, so that you can use the icons in 40 | # the material Icons class. 41 | uses-material-design: true 42 | 43 | # To add assets to your application, add an assets section, like this: 44 | # assets: 45 | # - images/a_dot_burr.jpeg 46 | # - images/a_dot_ham.jpeg 47 | 48 | # An image asset can refer to one or more resolution-specific "variants", see 49 | # https://flutter.dev/assets-and-images/#resolution-aware. 50 | 51 | # For details regarding adding assets from package dependencies, see 52 | # https://flutter.dev/assets-and-images/#from-packages 53 | 54 | # To add custom fonts to your application, add a fonts section here, 55 | # in this "flutter" section. Each entry in this list should have a 56 | # "family" key with the font family name, and a "fonts" key with a 57 | # list giving the asset and other descriptors for the font. For 58 | # example: 59 | # fonts: 60 | # - family: Schyler 61 | # fonts: 62 | # - asset: fonts/Schyler-Regular.ttf 63 | # - asset: fonts/Schyler-Italic.ttf 64 | # style: italic 65 | # - family: Trajan Pro 66 | # fonts: 67 | # - asset: fonts/TrajanPro.ttf 68 | # - asset: fonts/TrajanPro_Bold.ttf 69 | # weight: 700 70 | # 71 | # For details regarding fonts from package dependencies, 72 | # see https://flutter.dev/custom-fonts/#from-packages 73 | -------------------------------------------------------------------------------- /test/subtitle_controller_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:mocktail/mocktail.dart'; 3 | import 'package:subtitle_wrapper_package/bloc/subtitle/subtitle_bloc.dart'; 4 | import 'package:subtitle_wrapper_package/data/repository/subtitle_repository.dart'; 5 | import 'package:subtitle_wrapper_package/subtitle_controller.dart'; 6 | import 'package:video_player/video_player.dart'; 7 | 8 | class MockVideoPlayerController extends Mock implements VideoPlayerController {} 9 | 10 | void main() { 11 | final subtitleController = SubtitleController( 12 | subtitleUrl: 'https://pastebin.com/raw/ZWWAL7fK', 13 | subtitleDecoder: SubtitleDecoder.utf8, 14 | ); 15 | 16 | group( 17 | 'Subtitle controller', 18 | () { 19 | test('attach', () async { 20 | subtitleController.attach( 21 | SubtitleBloc( 22 | subtitleController: subtitleController, 23 | subtitleRepository: SubtitleDataRepository( 24 | subtitleController: subtitleController, 25 | ), 26 | videoPlayerController: MockVideoPlayerController(), 27 | ), 28 | ); 29 | }); 30 | test('detach', () async { 31 | subtitleController.detach(); 32 | }); 33 | 34 | test('update subtitle url', () async { 35 | subtitleController 36 | ..attach( 37 | SubtitleBloc( 38 | subtitleController: subtitleController, 39 | subtitleRepository: SubtitleDataRepository( 40 | subtitleController: subtitleController, 41 | ), 42 | videoPlayerController: MockVideoPlayerController(), 43 | ), 44 | ) 45 | ..updateSubtitleUrl( 46 | url: 'https://pastebin.com/raw/ZWWAL7fK', 47 | ); 48 | }); 49 | 50 | test('update subtitle content', () async { 51 | subtitleController 52 | ..attach( 53 | SubtitleBloc( 54 | subtitleController: subtitleController, 55 | subtitleRepository: SubtitleDataRepository( 56 | subtitleController: subtitleController, 57 | ), 58 | videoPlayerController: MockVideoPlayerController(), 59 | ), 60 | ) 61 | ..updateSubtitleContent( 62 | content: '', 63 | ); 64 | }); 65 | 66 | test( 67 | 'update subtitle content without attach', 68 | () { 69 | expect( 70 | () { 71 | subtitleController 72 | ..detach() 73 | ..updateSubtitleContent( 74 | content: '', 75 | ); 76 | }, 77 | throwsException, 78 | ); 79 | }, 80 | ); 81 | 82 | test('update subtitle url without attach', () { 83 | expect( 84 | () { 85 | subtitleController 86 | ..detach() 87 | ..updateSubtitleUrl( 88 | url: 'https://pastebin.com/raw/ZWWAL7fK', 89 | ); 90 | }, 91 | throwsException, 92 | ); 93 | }); 94 | }, 95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # subtitle_wrapper_package 2 | 3 | [![](https://img.shields.io/badge/pub-v2.1.0-brightgreen.svg)](https://pub.dev/packages/subtitle_wrapper_package) 4 | 5 | [![Test and (dry-run) publish package](https://github.com/Joran-Dob/flutter_subtitle_wrapper/actions/workflows/flutter-drive.yml/badge.svg)](https://github.com/Joran-Dob/flutter_subtitle_wrapper/actions/workflows/flutter-drive.yml) 6 | 7 | [![codecov](https://codecov.io/gh/Joran-Dob/flutter_subtitle_wrapper/branch/master/graph/badge.svg)](https://codecov.io/gh/Joran-Dob/flutter_subtitle_wrapper) 8 | [![licence](https://img.shields.io/badge/licence-MIT-blue.svg)](https://github.com/IamTobi/spotify_sdk/blob/master/LICENSE) 9 | 10 | ## Features 11 | 12 | Subtitle playback for the 2 most widely used subtitle formats are supported currently which can be dynamically updated during playback from a url of content string. As well as basic styling of the subtitle text item. 13 | 14 | The package is nearly completely unit tested and widget tests are in progress. 15 | 16 | | Function | Description| Implemented | 17 | |---|---|---| 18 | | Parse WebVTT | Parsing of WebVtt subtitles. | ✔ | 19 | | Parse SubRip (.srt) | Parsing of SubRip subtitles. | ✔ | 20 | | Remote loading utf8 encoded subtitles | The parsing of subtitle files with the utf8 encoding from an url.| ✔ | 21 | | Remote loading latin1 encoded subtitles |The parsing of subtitle files with the latin1 encoding from an url. | ✔ | 22 | | Dynamic updating of subtitle | Update subtitle content during playback. | ✔ | 23 | | Standard subtitle styling | Standard styling of subtitle items. | ✔ | 24 | | Advance subtitle styling | Advance styling of subtitle items. Like custom fonts.| ✔ | 25 | 26 | ## Installation 27 | 28 | The basic setup of the package is really straight forward, create a instance of `SubtitleController` with a `subtitleUrl` or `subtitlesContent` depending if your resource is remote or local. 29 | 30 | Unfortunately currently its required to specify the subtitle type so `webvtt` or `srt` . 31 | 32 | After this you need to wrap your video player with the `SubTitleWrapper` and add the `SubtitleController` and `videoPlayerController` to the `SubTitleWrapper` . That's it :tada: 33 | 34 | ``` dart 35 | final SubtitleController subtitleController = SubtitleController( 36 | subtitleUrl: "https://pastebin.com/raw/ZWWAL7fK", 37 | subtitleType: SubtitleType.webvtt, 38 | ); 39 | 40 | SubtitleWrapper( 41 | videoPlayerController: videoPlayerController, 42 | subtitleController: subtitleController, 43 | subtitleStyle: SubtitleStyle( 44 | textColor: Colors.white, 45 | hasBorder: true, 46 | ), 47 | videoChild: Chewie( 48 | controller: chewieController, 49 | ), 50 | ), 51 | ``` 52 | 53 | ## Example 54 | 55 | Demonstrates how to use the subtitle_wrapper_package plugin. 56 | 57 | See the [example documentation](example/README.md) for more information. 58 | 59 | ## Changelog 60 | 61 | See [CHANGELOG.md](CHANGELOG.md). 62 | 63 | ## Contributing 64 | 65 | Feel free to contribute by opening issues and/or pull requests. Your feedback is very welcome! 66 | 67 | ## License 68 | 69 | MIT License 70 | 71 | Copyright (c) [2019] [Joran Dob] 72 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test/subtitle_bloc_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc_test/bloc_test.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:mocktail/mocktail.dart'; 4 | import 'package:subtitle_wrapper_package/bloc/subtitle/subtitle_bloc.dart'; 5 | import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; 6 | import 'package:subtitle_wrapper_package/data/repository/subtitle_repository.dart'; 7 | import 'package:subtitle_wrapper_package/subtitle_controller.dart'; 8 | import 'package:video_player/video_player.dart'; 9 | 10 | class MockVideoPlayerController extends Mock implements VideoPlayerController {} 11 | 12 | void main() { 13 | final subtitleController = SubtitleController( 14 | subtitleUrl: 'https://pastebin.com/raw/ZWWAL7fK', 15 | subtitleDecoder: SubtitleDecoder.utf8, 16 | ); 17 | 18 | group( 19 | 'Subtitle BLoC', 20 | () { 21 | blocTest( 22 | 'subtitle init', 23 | build: () => SubtitleBloc( 24 | subtitleController: subtitleController, 25 | subtitleRepository: SubtitleDataRepository( 26 | subtitleController: subtitleController, 27 | ), 28 | videoPlayerController: MockVideoPlayerController(), 29 | ), 30 | act: (SubtitleBloc bloc) => bloc.add( 31 | InitSubtitles( 32 | subtitleController: subtitleController, 33 | ), 34 | ), 35 | expect: () => [ 36 | SubtitleInitializing(), 37 | ], 38 | ); 39 | blocTest( 40 | 'subtitle update', 41 | build: () => SubtitleBloc( 42 | subtitleController: subtitleController, 43 | subtitleRepository: SubtitleDataRepository( 44 | subtitleController: subtitleController, 45 | ), 46 | videoPlayerController: MockVideoPlayerController(), 47 | ), 48 | act: (SubtitleBloc bloc) => bloc.add( 49 | UpdateLoadedSubtitle( 50 | subtitle: const Subtitle( 51 | startTime: Duration.zero, 52 | endTime: Duration( 53 | seconds: 10, 54 | ), 55 | text: 'test', 56 | ), 57 | ), 58 | ), 59 | expect: () => [ 60 | const LoadedSubtitle( 61 | Subtitle( 62 | startTime: Duration.zero, 63 | endTime: Duration( 64 | seconds: 10, 65 | ), 66 | text: 'test', 67 | ), 68 | ), 69 | ], 70 | ); 71 | 72 | blocTest( 73 | 'subtitle load', 74 | build: () => SubtitleBloc( 75 | subtitleController: subtitleController, 76 | subtitleRepository: SubtitleDataRepository( 77 | subtitleController: subtitleController, 78 | ), 79 | videoPlayerController: MockVideoPlayerController(), 80 | ), 81 | act: (SubtitleBloc bloc) { 82 | bloc.videoPlayerController.notifyListeners(); 83 | return bloc.add( 84 | LoadSubtitle(), 85 | ); 86 | }, 87 | expect: () => [ 88 | LoadingSubtitle(), 89 | ], 90 | ); 91 | }, 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /lib/subtitle_text_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:subtitle_wrapper_package/bloc/subtitle/subtitle_bloc.dart'; 4 | import 'package:subtitle_wrapper_package/data/constants/view_keys.dart'; 5 | import 'package:subtitle_wrapper_package/data/models/style/subtitle_style.dart'; 6 | 7 | class SubtitleTextView extends StatelessWidget { 8 | const SubtitleTextView({ 9 | required this.subtitleStyle, 10 | super.key, 11 | this.backgroundColor, 12 | }); 13 | final SubtitleStyle subtitleStyle; 14 | final Color? backgroundColor; 15 | 16 | TextStyle get _textStyle { 17 | return subtitleStyle.hasBorder 18 | ? TextStyle( 19 | fontSize: subtitleStyle.fontSize, 20 | foreground: Paint() 21 | ..style = subtitleStyle.borderStyle.style 22 | ..strokeWidth = subtitleStyle.borderStyle.strokeWidth 23 | ..color = subtitleStyle.borderStyle.color, 24 | ) 25 | : TextStyle( 26 | fontSize: subtitleStyle.fontSize, 27 | color: subtitleStyle.textColor, 28 | ); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | final subtitleBloc = BlocProvider.of(context); 34 | 35 | // TODO(Joran-Dob): improve this workaround. 36 | void subtitleBlocListener(BuildContext _, SubtitleState state) { 37 | if (state is SubtitleInitialized) { 38 | subtitleBloc.add(LoadSubtitle()); 39 | } 40 | } 41 | 42 | return BlocConsumer( 43 | listener: subtitleBlocListener, 44 | builder: (context, state) { 45 | if (state is LoadedSubtitle && state.subtitle != null) { 46 | return Stack( 47 | children: [ 48 | Center( 49 | child: Container( 50 | color: backgroundColor, 51 | child: _TextContent( 52 | text: state.subtitle!.text, 53 | textStyle: _textStyle, 54 | ), 55 | ), 56 | ), 57 | if (subtitleStyle.hasBorder) 58 | Center( 59 | child: Container( 60 | color: backgroundColor, 61 | child: _TextContent( 62 | text: state.subtitle!.text, 63 | textStyle: TextStyle( 64 | color: subtitleStyle.textColor, 65 | fontSize: subtitleStyle.fontSize, 66 | ), 67 | ), 68 | ), 69 | ), 70 | ], 71 | ); 72 | } 73 | 74 | return const SizedBox(); 75 | }, 76 | ); 77 | } 78 | } 79 | 80 | class _TextContent extends StatelessWidget { 81 | const _TextContent({ 82 | required this.textStyle, 83 | required this.text, 84 | }); 85 | 86 | final TextStyle textStyle; 87 | final String text; 88 | 89 | @override 90 | Widget build(BuildContext context) { 91 | return Text( 92 | text, 93 | key: ViewKeys.subtitleTextContent, 94 | textAlign: TextAlign.center, 95 | style: textStyle, 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/bloc/subtitle/subtitle_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; 6 | import 'package:video_player/video_player.dart'; 7 | 8 | part 'subtitle_event.dart'; 9 | part 'subtitle_state.dart'; 10 | 11 | class SubtitleBloc extends Bloc { 12 | SubtitleBloc({ 13 | required this.videoPlayerController, 14 | required this.subtitleRepository, 15 | required this.subtitleController, 16 | }) : super(SubtitleInitial()) { 17 | subtitleController.attach(this); 18 | on((event, emit) => loadSubtitle(emit: emit)); 19 | on((event, emit) => initSubtitles(emit: emit)); 20 | on( 21 | (event, emit) => emit(LoadedSubtitle(event.subtitle)), 22 | ); 23 | on( 24 | (event, emit) => emit(CompletedSubtitle()), 25 | ); 26 | } 27 | 28 | final VideoPlayerController videoPlayerController; 29 | final SubtitleRepository subtitleRepository; 30 | final SubtitleController subtitleController; 31 | 32 | late Subtitles subtitles; 33 | Subtitle? _currentSubtitle; 34 | 35 | Future initSubtitles({ 36 | required Emitter emit, 37 | }) async { 38 | emit(SubtitleInitializing()); 39 | subtitles = await subtitleRepository.getSubtitles(); 40 | emit(SubtitleInitialized()); 41 | } 42 | 43 | Future loadSubtitle({ 44 | required Emitter emit, 45 | }) async { 46 | emit(LoadingSubtitle()); 47 | videoPlayerController.addListener( 48 | () { 49 | final videoPlayerPosition = videoPlayerController.value.position; 50 | if (subtitles.subtitles.isNotEmpty && 51 | videoPlayerPosition.inMilliseconds > 52 | subtitles.subtitles.last.endTime.inMilliseconds) { 53 | add(CompletedShowingSubtitles()); 54 | } 55 | for (final subtitleItem in subtitles.subtitles) { 56 | final validStartTime = videoPlayerPosition.inMilliseconds > 57 | subtitleItem.startTime.inMilliseconds; 58 | final validEndTime = videoPlayerPosition.inMilliseconds < 59 | subtitleItem.endTime.inMilliseconds; 60 | final subtitle = validStartTime && validEndTime ? subtitleItem : null; 61 | if (validStartTime && validEndTime && subtitle != _currentSubtitle) { 62 | _currentSubtitle = subtitle; 63 | } else if (!_currentSubtitleIsValid( 64 | videoPlayerPosition: videoPlayerPosition.inMilliseconds, 65 | )) { 66 | _currentSubtitle = null; 67 | } 68 | add( 69 | UpdateLoadedSubtitle( 70 | subtitle: _currentSubtitle, 71 | ), 72 | ); 73 | } 74 | }, 75 | ); 76 | } 77 | 78 | @override 79 | Future close() { 80 | subtitleController.detach(); 81 | 82 | return super.close(); 83 | } 84 | 85 | bool _currentSubtitleIsValid({required int videoPlayerPosition}) { 86 | if (_currentSubtitle == null) return false; 87 | final validStartTime = 88 | videoPlayerPosition > _currentSubtitle!.startTime.inMilliseconds; 89 | final validEndTime = 90 | videoPlayerPosition < _currentSubtitle!.endTime.inMilliseconds; 91 | 92 | return validStartTime && validEndTime; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /test/srt_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; 3 | import 'package:subtitle_wrapper_package/data/repository/subtitle_repository.dart'; 4 | import 'package:subtitle_wrapper_package/subtitle_controller.dart'; 5 | 6 | void main() { 7 | final subtitleController = SubtitleController( 8 | subtitleType: SubtitleType.srt, 9 | subtitleUrl: 'https://pastebin.com/raw/1gt7fAHW', 10 | ); 11 | 12 | const subtitleContentString = '1\r\n' 13 | '00:00:03,400 --> 00:00:06,177\r\n' 14 | "In this lesson, we're
going to be talking about finance. And\r\n" 15 | '\r\n' 16 | '2\r\n' 17 | '00:00:06,177 --> 00:00:10,009\r\n' 18 | 'one of the most important aspects of finance is interest.'; 19 | 20 | test('Loading remote of Srt subtitle file', () async { 21 | final subtitleDataRepository = SubtitleDataRepository( 22 | subtitleController: subtitleController, 23 | ); 24 | final subtitleContent = 25 | await subtitleDataRepository.loadRemoteSubtitleContent( 26 | subtitleUrl: subtitleController.subtitleUrl!, 27 | ); 28 | expect( 29 | subtitleContent, 30 | subtitleContentString, 31 | ); 32 | }); 33 | 34 | test('Loading remote of Srt subtitle file with latin1 codec', () async { 35 | subtitleController.subtitleDecoder = SubtitleDecoder.latin1; 36 | final subtitleDataRepository = SubtitleDataRepository( 37 | subtitleController: subtitleController, 38 | ); 39 | final subtitleContent = 40 | await subtitleDataRepository.loadRemoteSubtitleContent( 41 | subtitleUrl: subtitleController.subtitleUrl!, 42 | ); 43 | expect( 44 | subtitleContent, 45 | subtitleContentString, 46 | ); 47 | }); 48 | 49 | test('Loading remote of Srt subtitle file with utf8 codec', () async { 50 | subtitleController.subtitleDecoder = SubtitleDecoder.utf8; 51 | final subtitleDataRepository = SubtitleDataRepository( 52 | subtitleController: subtitleController, 53 | ); 54 | final subtitleContent = 55 | await subtitleDataRepository.loadRemoteSubtitleContent( 56 | subtitleUrl: subtitleController.subtitleUrl!, 57 | ); 58 | expect( 59 | subtitleContent, 60 | subtitleContentString, 61 | ); 62 | }); 63 | test( 64 | 'Parsing remote of Srt subtitle file', 65 | () async { 66 | final subtitleDataRepository = SubtitleDataRepository( 67 | subtitleController: subtitleController, 68 | ); 69 | final subtitles = await subtitleDataRepository.getSubtitles(); 70 | expect( 71 | subtitles.subtitles, 72 | [ 73 | Subtitle( 74 | startTime: const Duration( 75 | seconds: 3, 76 | milliseconds: 400, 77 | ), 78 | endTime: const Duration( 79 | seconds: 6, 80 | milliseconds: 177, 81 | ), 82 | text: subtitles.subtitles[0].text, 83 | ), 84 | const Subtitle( 85 | startTime: Duration( 86 | seconds: 6, 87 | milliseconds: 177, 88 | ), 89 | endTime: Duration( 90 | seconds: 10, 91 | milliseconds: 009, 92 | ), 93 | text: 'one of the most important aspects of finance is interest.', 94 | ), 95 | ], 96 | ); 97 | }, 98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /example/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 | -------------------------------------------------------------------------------- /coverage/lcov.info: -------------------------------------------------------------------------------- 1 | SF:lib/bloc/subtitle/subtitle_event.dart 2 | DA:4,2 3 | DA:8,2 4 | DA:15,1 5 | LF:3 6 | LH:3 7 | end_of_record 8 | SF:lib/bloc/subtitle/subtitle_state.dart 9 | DA:4,3 10 | DA:8,0 11 | DA:9,0 12 | DA:13,1 13 | DA:14,1 14 | DA:18,0 15 | DA:19,0 16 | DA:23,1 17 | DA:24,1 18 | DA:28,2 19 | DA:31,1 20 | DA:32,2 21 | DA:36,0 22 | DA:37,0 23 | LF:14 24 | LH:8 25 | end_of_record 26 | SF:lib/bloc/subtitle/subtitle_bloc.dart 27 | DA:12,2 28 | DA:16,4 29 | DA:17,4 30 | DA:18,4 31 | DA:19,6 32 | DA:20,2 33 | DA:21,4 34 | DA:23,2 35 | DA:24,0 36 | DA:35,2 37 | DA:38,4 38 | DA:39,5 39 | DA:40,2 40 | DA:43,1 41 | DA:46,2 42 | DA:47,2 43 | DA:48,0 44 | DA:49,0 45 | DA:50,0 46 | DA:51,0 47 | DA:52,0 48 | DA:54,0 49 | DA:55,0 50 | DA:56,0 51 | DA:57,0 52 | DA:58,0 53 | DA:60,0 54 | DA:61,0 55 | DA:62,0 56 | DA:63,0 57 | DA:65,0 58 | DA:67,0 59 | DA:68,0 60 | DA:69,0 61 | DA:77,1 62 | DA:79,2 63 | DA:81,1 64 | DA:84,0 65 | DA:85,0 66 | DA:87,0 67 | DA:89,0 68 | LF:41 69 | LH:18 70 | end_of_record 71 | SF:lib/data/repository/subtitle_repository.dart 72 | DA:13,4 73 | DA:17,2 74 | DA:19,2 75 | DA:21,2 76 | DA:25,2 77 | DA:30,2 78 | DA:34,4 79 | DA:36,8 80 | DA:37,8 81 | DA:42,3 82 | DA:49,3 83 | DA:51,6 84 | DA:56,3 85 | DA:59,6 86 | DA:62,3 87 | DA:63,3 88 | DA:67,4 89 | DA:68,2 90 | DA:69,2 91 | DA:70,2 92 | DA:75,2 93 | DA:76,2 94 | DA:77,2 95 | DA:83,2 96 | DA:84,2 97 | DA:87,2 98 | DA:88,2 99 | DA:89,2 100 | DA:94,1 101 | DA:95,1 102 | DA:96,1 103 | DA:107,3 104 | DA:112,3 105 | DA:113,2 106 | DA:118,1 107 | DA:119,1 108 | DA:125,0 109 | DA:128,6 110 | DA:129,3 111 | DA:131,5 112 | DA:132,4 113 | DA:133,4 114 | DA:134,4 115 | DA:135,4 116 | DA:137,4 117 | DA:138,4 118 | DA:139,4 119 | DA:140,4 120 | DA:141,4 121 | DA:143,2 122 | DA:149,2 123 | DA:156,2 124 | DA:157,4 125 | DA:161,3 126 | DA:164,2 127 | DA:165,2 128 | DA:170,6 129 | DA:171,2 130 | DA:172,4 131 | DA:173,2 132 | DA:174,2 133 | DA:182,2 134 | DA:183,2 135 | DA:184,6 136 | DA:188,2 137 | DA:189,2 138 | DA:190,2 139 | DA:191,0 140 | DA:194,2 141 | DA:198,2 142 | DA:199,10 143 | DA:203,0 144 | DA:204,0 145 | LF:73 146 | LH:69 147 | end_of_record 148 | SF:lib/subtitle_controller.dart 149 | DA:4,4 150 | DA:19,2 151 | DA:20,2 152 | DA:21,2 153 | DA:24,2 154 | DA:25,2 155 | DA:26,2 156 | DA:29,1 157 | DA:32,1 158 | DA:33,1 159 | DA:34,2 160 | DA:35,1 161 | DA:40,1 162 | DA:44,1 163 | DA:47,1 164 | DA:48,1 165 | DA:49,2 166 | DA:50,1 167 | DA:55,1 168 | LF:19 169 | LH:19 170 | end_of_record 171 | SF:lib/data/models/style/subtitle_border_style.dart 172 | DA:6,4 173 | LF:1 174 | LH:1 175 | end_of_record 176 | SF:lib/data/models/style/subtitle_position.dart 177 | DA:4,4 178 | LF:1 179 | LH:1 180 | end_of_record 181 | SF:lib/data/models/style/subtitle_style.dart 182 | DA:7,8 183 | LF:1 184 | LH:1 185 | end_of_record 186 | SF:lib/data/models/subtitle.dart 187 | DA:4,5 188 | DA:13,3 189 | DA:14,12 190 | LF:3 191 | LH:3 192 | end_of_record 193 | SF:lib/data/models/subtitles.dart 194 | DA:4,3 195 | LF:1 196 | LH:1 197 | end_of_record 198 | SF:lib/deprecated_subtitle_wrapper.dart 199 | DA:5,0 200 | LF:1 201 | LH:0 202 | end_of_record 203 | SF:lib/subtitle_wrapper.dart 204 | DA:8,0 205 | DA:22,0 206 | DA:24,0 207 | DA:25,0 208 | DA:26,0 209 | DA:27,0 210 | DA:28,0 211 | DA:29,0 212 | DA:30,0 213 | DA:31,0 214 | DA:32,0 215 | DA:33,0 216 | DA:34,0 217 | DA:35,0 218 | DA:36,0 219 | DA:37,0 220 | DA:39,0 221 | DA:40,0 222 | DA:41,0 223 | DA:42,0 224 | DA:45,0 225 | DA:46,0 226 | DA:47,0 227 | DA:52,0 228 | LF:24 229 | LH:0 230 | end_of_record 231 | SF:lib/subtitle_text_view.dart 232 | DA:8,0 233 | DA:16,0 234 | DA:17,0 235 | DA:18,0 236 | DA:19,0 237 | DA:20,0 238 | DA:21,0 239 | DA:22,0 240 | DA:23,0 241 | DA:25,0 242 | DA:26,0 243 | DA:27,0 244 | DA:31,0 245 | DA:33,0 246 | DA:36,0 247 | DA:37,0 248 | DA:38,0 249 | DA:42,0 250 | DA:44,0 251 | DA:45,0 252 | DA:46,0 253 | DA:47,0 254 | DA:48,0 255 | DA:49,0 256 | DA:50,0 257 | DA:51,0 258 | DA:52,0 259 | DA:53,0 260 | DA:57,0 261 | DA:58,0 262 | DA:59,0 263 | DA:60,0 264 | DA:61,0 265 | DA:62,0 266 | DA:63,0 267 | DA:64,0 268 | DA:65,0 269 | DA:81,0 270 | DA:89,0 271 | DA:91,0 272 | DA:92,0 273 | DA:95,0 274 | LF:42 275 | LH:0 276 | end_of_record 277 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [2.2.1] - 02/04/2024. 9 | 10 | ### Fixed 11 | - Updated dependencies. 12 | - Resolved minor lint issues. 13 | 14 | ## [2.2.0] - 30/09/2023. 15 | 16 | ### Fixed 17 | - Hide subtitle item when time is not matching 18 | - Hide subtitles when there is no subtitle content or subtitle url 19 | 20 | 21 | ## [2.1.0] - 22/05/2022. 22 | 23 | - **BREAKING**: Name changed from `SubTitleWrapper` to `SubtitleWrapper`. 24 | - Code quality improvements and refactoring. 25 | 26 | ## [2.0.3] - 21/05/2022. 27 | 28 | - Added possibility to add background color to the text. 29 | - After the last subtitle item the text will be removed. 30 | 31 | ## [2.0.2] - 21/05/2022. 32 | 33 | - Updated dependencies. 34 | - Resolved minor lint issues. 35 | 36 | ## [2.0.1] - 15/04/2021. 37 | 38 | - Changed all pre-release package versions to release ones. 39 | 40 | ## [2.0.0] - 7/03/2021. 41 | 42 | - Migrated to sound null safety. 43 | - Cleaned up null safety code and typing. 44 | - Fixed some deprecation issues. 45 | - Changed linting rules to be stricter. 46 | 47 | ## [2.0.0-nullsafety.0] - 3/02/2021. 48 | 49 | - Migrated to sound null safety. 50 | - Fixed some deprecation issues. 51 | 52 | ## [1.0.4] - 27 november 2020. 53 | 54 | - Added a check to see if content-types end with a semicolon 👀. 55 | - Added a fix for content-types that end with a semicolon 👨‍🔧. 56 | 57 | ## [1.0.3] - 6 october 2020. 58 | 59 | - Added more tests and completer coverage. 60 | - Added option to dynamically update the subtitles during playback. 61 | - Updated the readme to provide some better information. 62 | 63 | ## [1.0.2] - 2 august 2020. 64 | 65 | - Added unit tests and code coverage to the package. 66 | - Added a MIT licence to the package. 67 | - Implemented BLoC pattern for handling subtitles and state changes. 68 | - Added support for loading local subtitles by using subtitle content on the controller. 69 | - Added support for srt. 70 | 71 | ## [1.0.1] - 2 august 2020. 72 | 73 | - Added unit tests and code coverage to the package. 74 | - Implemented BLoC pattern for handling subtitles and state changes. 75 | - Added support for loading local subtitles by using subtitle content on the controller. 76 | - Added support for srt. 77 | 78 | ## [1.0.0] - 1 august 2020. 79 | 80 | - Implemented BLoC pattern for handling subtitles and state changes. 81 | - Added support for loading local subtitles by using subtitle content on the controller. 82 | - Added support for srt. 83 | 84 | ## [0.1.6] - 26 june 2020. 85 | 86 | - Added support to select an specific decoder. Defaults to utf8 87 | - Added support for dynamic setting of decoder depending on server site charset, Defaults to utf8 88 | - Fixed issue related to fallback to utf8 89 | 90 | ## [0.1.5] - 26 june 2020. 91 | 92 | - Added support to select an specific decoder. Defaults to utf8 93 | - Added support for dynamic setting of decoder depending on server site charset, Defaults to utf8 94 | 95 | ## [0.1.4] - 25 june 2020. 96 | 97 | - Added support to select an specific decoder. Defaults to utf8 98 | 99 | ## [0.1.3] - 24 june 2020. 100 | 101 | - Fixed issue with special text items 102 | 103 | ## [0.1.2] - 23 june 2020. 104 | 105 | - Fixed issue with special text items 106 | 107 | ## [0.1.1] - 24 april 2020. 108 | 109 | - Fixed issue with special text items 110 | 111 | ## [0.1.0] - 24 april 2020. 112 | 113 | - Fixed issue with multiple lines 114 | 115 | ## [0.0.10] - 14 feb 2020. 116 | 117 | - Added stripping for most tags 118 | - Fixed numbers not showing in subtitles. 119 | 120 | ## [0.0.9] - Feb 7, 2020. 121 | 122 | - Fixed issue with regex 123 | 124 | ## [0.0.8] - Feb 7, 2020. 125 | 126 | - Fixed issue with regex 127 | 128 | ## [0.0.7] - 10 dec 2019. 129 | 130 | - Fixed issue with regex 131 | 132 | ## [0.0.6] - 10 dec 2019. 133 | 134 | - Fixed issue with regex 135 | 136 | ## [0.0.5] - 02 oct 2019. 137 | 138 | - Updated readme 139 | 140 | ## [0.0.4] - 02 oct 2019. 141 | 142 | - Fix for empty lines 143 | - No default bool for show subs 144 | 145 | ## [0.0.3] - 02 oct 2019. 146 | 147 | - Added styling options for subtitles 148 | - Added positioning for subtitles 149 | 150 | ## [0.0.2] - 02 oct 2019. 151 | 152 | - Fixed regex problem for vtt 153 | 154 | ## [0.0.1] - 01 oct 2019. 155 | 156 | - initial release. 157 | -------------------------------------------------------------------------------- /lib/data/repository/subtitle_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:http/http.dart' as http; 5 | import 'package:http_parser/http_parser.dart'; 6 | import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; 7 | 8 | abstract class SubtitleRepository { 9 | Future getSubtitles(); 10 | } 11 | 12 | class SubtitleDataRepository extends SubtitleRepository { 13 | SubtitleDataRepository({required this.subtitleController}); 14 | final SubtitleController subtitleController; 15 | 16 | // Gets the subtitle content type. 17 | SubtitleDecoder requestContentType(Map headers) { 18 | // Extracts the subtitle content type from the headers. 19 | final encoding = _encodingForHeaders(headers as Map); 20 | 21 | return encoding == latin1 ? SubtitleDecoder.latin1 : SubtitleDecoder.utf8; 22 | } 23 | 24 | // Gets the encoding type for the charset string with a fall back to utf8. 25 | Encoding encodingForCharset(String? charset, [Encoding fallback = utf8]) { 26 | // If the charset is empty we use the encoding fallback. 27 | if (charset == null) return fallback; 28 | // If the charset is not empty we will return the encoding type for this charset. 29 | 30 | return Encoding.getByName(charset) ?? fallback; 31 | } 32 | 33 | // Handles the subtitle loading, parsing. 34 | @override 35 | Future getSubtitles() async { 36 | var subtitlesContent = subtitleController.subtitlesContent; 37 | final subtitleUrl = subtitleController.subtitleUrl; 38 | 39 | // If the subtitle content parameter is empty we will load the subtitle from the specified url. 40 | if (subtitlesContent == null && subtitleUrl != null) { 41 | // Lets load the subtitle content from the url. 42 | subtitlesContent = await loadRemoteSubtitleContent( 43 | subtitleUrl: subtitleUrl, 44 | ); 45 | } 46 | // Tries parsing the subtitle data 47 | // Lets try to parse the subtitle content with the specified subtitle type 48 | if (subtitlesContent != null) { 49 | return getSubtitlesData( 50 | subtitlesContent, 51 | subtitleController.subtitleType, 52 | ); 53 | } 54 | 55 | return Subtitles(subtitles: []); 56 | } 57 | 58 | // Loads the remote subtitle content. 59 | Future loadRemoteSubtitleContent({ 60 | required String subtitleUrl, 61 | }) async { 62 | final subtitleDecoder = subtitleController.subtitleDecoder; 63 | String? subtitlesContent; 64 | // Try loading the subtitle content with http.get. 65 | try { 66 | final response = await http.get( 67 | Uri.parse(subtitleUrl), 68 | ); 69 | 70 | // Lets check if the request was successful. 71 | // If the subtitle decoder type is utf8 lets decode it with utf8. 72 | if (response.statusCode == HttpStatus.ok) { 73 | if (subtitleDecoder == SubtitleDecoder.utf8) { 74 | subtitlesContent = utf8.decode( 75 | response.bodyBytes, 76 | allowMalformed: true, 77 | ); 78 | } 79 | // If the subtitle decoder type is latin1 lets decode it with latin1. 80 | else if (subtitleDecoder == SubtitleDecoder.latin1) { 81 | subtitlesContent = latin1.decode( 82 | response.bodyBytes, 83 | allowInvalid: true, 84 | ); 85 | } 86 | // The subtitle decoder was not defined so we will extract it from the response headers send from the server. 87 | else { 88 | final subtitleServerDecoder = requestContentType( 89 | response.headers, 90 | ); 91 | // If the subtitle decoder type is utf8 lets decode it with utf8. 92 | if (subtitleServerDecoder == SubtitleDecoder.utf8) { 93 | subtitlesContent = utf8.decode( 94 | response.bodyBytes, 95 | allowMalformed: true, 96 | ); 97 | } 98 | // If the subtitle decoder type is latin1 lets decode it with latin1. 99 | else if (subtitleServerDecoder == SubtitleDecoder.latin1) { 100 | subtitlesContent = latin1.decode( 101 | response.bodyBytes, 102 | allowInvalid: true, 103 | ); 104 | } 105 | } 106 | } 107 | } catch (e) { 108 | subtitlesContent = ''; 109 | } 110 | 111 | // Return the subtitle content. 112 | return subtitlesContent; 113 | } 114 | 115 | Subtitles getSubtitlesData( 116 | String subtitlesContent, 117 | SubtitleType subtitleType, 118 | ) { 119 | RegExp regExp; 120 | if (subtitleType == SubtitleType.webvtt) { 121 | regExp = RegExp( 122 | r'((\d{2}):(\d{2}):(\d{2})\.(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\.(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))', 123 | caseSensitive: false, 124 | multiLine: true, 125 | ); 126 | } else if (subtitleType == SubtitleType.srt) { 127 | regExp = RegExp( 128 | r'((\d{2}):(\d{2}):(\d{2})\,(\d+)) +--> +((\d{2}):(\d{2}):(\d{2})\,(\d{3})).*[\r\n]+\s*((?:(?!\r?\n\r?).)*(\r\n|\r|\n)(?:.*))', 129 | caseSensitive: false, 130 | multiLine: true, 131 | ); 132 | } else { 133 | throw Exception('Incorrect subtitle type'); 134 | } 135 | 136 | final matches = regExp.allMatches(subtitlesContent).toList(); 137 | final subtitleList = []; 138 | 139 | for (final regExpMatch in matches) { 140 | final startTimeHours = int.parse(regExpMatch.group(2)!); 141 | final startTimeMinutes = int.parse(regExpMatch.group(3)!); 142 | final startTimeSeconds = int.parse(regExpMatch.group(4)!); 143 | final startTimeMilliseconds = int.parse(regExpMatch.group(5)!); 144 | 145 | final endTimeHours = int.parse(regExpMatch.group(7)!); 146 | final endTimeMinutes = int.parse(regExpMatch.group(8)!); 147 | final endTimeSeconds = int.parse(regExpMatch.group(9)!); 148 | final endTimeMilliseconds = int.parse(regExpMatch.group(10)!); 149 | final text = removeAllHtmlTags(regExpMatch.group(11)!); 150 | 151 | final startTime = Duration( 152 | hours: startTimeHours, 153 | minutes: startTimeMinutes, 154 | seconds: startTimeSeconds, 155 | milliseconds: startTimeMilliseconds, 156 | ); 157 | final endTime = Duration( 158 | hours: endTimeHours, 159 | minutes: endTimeMinutes, 160 | seconds: endTimeSeconds, 161 | milliseconds: endTimeMilliseconds, 162 | ); 163 | 164 | subtitleList.add( 165 | Subtitle(startTime: startTime, endTime: endTime, text: text.trim()), 166 | ); 167 | } 168 | 169 | return Subtitles(subtitles: subtitleList); 170 | } 171 | 172 | String removeAllHtmlTags(String htmlText) { 173 | final exp = RegExp( 174 | '(<[^>]*>)', 175 | multiLine: true, 176 | ); 177 | var newHtmlText = htmlText; 178 | exp.allMatches(htmlText).toList().forEach( 179 | (RegExpMatch regExpMatch) { 180 | newHtmlText = regExpMatch.group(0) == '
' 181 | ? newHtmlText.replaceAll(regExpMatch.group(0)!, '\n') 182 | : newHtmlText.replaceAll(regExpMatch.group(0)!, ''); 183 | }, 184 | ); 185 | 186 | return newHtmlText; 187 | } 188 | 189 | // Extract the encoding type from the headers. 190 | Encoding _encodingForHeaders(Map headers) => 191 | encodingForCharset( 192 | _contentTypeForHeaders(headers).parameters['charset'], 193 | ); 194 | 195 | // Gets the content type from the headers and returns it as a media type. 196 | MediaType _contentTypeForHeaders(Map headers) { 197 | var contentType = headers['content-type']!; 198 | if (_hasSemiColonEnding(contentType)) { 199 | contentType = _fixSemiColonEnding(contentType); 200 | } 201 | 202 | return MediaType.parse(contentType); 203 | } 204 | 205 | // Check if the string is ending with a semicolon. 206 | bool _hasSemiColonEnding(String string) { 207 | return string.substring(string.length - 1, string.length) == ';'; 208 | } 209 | 210 | // Remove ending semicolon from string. 211 | String _fixSemiColonEnding(String string) { 212 | return string.substring(0, string.length - 1); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:chewie/chewie.dart'; 2 | import 'package:example/data/sw_constants.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:subtitle_wrapper_package/subtitle_wrapper_package.dart'; 5 | import 'package:video_player/video_player.dart'; 6 | 7 | void main() => runApp(const MyApp()); 8 | 9 | class MyApp extends StatelessWidget { 10 | const MyApp({super.key}); 11 | 12 | // This widget is the root of your application. 13 | @override 14 | Widget build(BuildContext context) { 15 | return const MaterialApp( 16 | home: MyHomePage(), 17 | ); 18 | } 19 | } 20 | 21 | class MyHomePage extends StatefulWidget { 22 | const MyHomePage({ 23 | super.key, 24 | }); 25 | 26 | @override 27 | MyHomePageState createState() => MyHomePageState(); 28 | } 29 | 30 | class MyHomePageState extends State { 31 | final String link = SwConstants.videoUrl; 32 | late ChewieController _chewieController; 33 | final SubtitleController subtitleController = SubtitleController( 34 | subtitleUrl: SwConstants.enSubtitle, 35 | subtitleDecoder: SubtitleDecoder.utf8, 36 | ); 37 | 38 | @override 39 | void initState() { 40 | _chewieController = chewieController; 41 | super.initState(); 42 | } 43 | 44 | VideoPlayerController get videoPlayerController { 45 | return VideoPlayerController.networkUrl(Uri.parse(link)); 46 | } 47 | 48 | ChewieController get chewieController { 49 | return ChewieController( 50 | videoPlayerController: videoPlayerController, 51 | aspectRatio: 3 / 2, 52 | autoPlay: true, 53 | autoInitialize: true, 54 | allowFullScreen: false, 55 | ); 56 | } 57 | 58 | void updateSubtitleUrl({ 59 | required ExampleSubtitleLanguage subtitleLanguage, 60 | }) { 61 | String? subtitleUrl; 62 | switch (subtitleLanguage) { 63 | case ExampleSubtitleLanguage.english: 64 | subtitleUrl = SwConstants.enSubtitle; 65 | break; 66 | case ExampleSubtitleLanguage.spanish: 67 | subtitleUrl = SwConstants.esSubtitle; 68 | break; 69 | case ExampleSubtitleLanguage.dutch: 70 | subtitleUrl = SwConstants.nlSubtitle; 71 | break; 72 | } 73 | subtitleController.updateSubtitleUrl( 74 | url: subtitleUrl, 75 | ); 76 | } 77 | 78 | @override 79 | Widget build(BuildContext context) { 80 | return Scaffold( 81 | backgroundColor: const Color(0xff0b090a), 82 | body: Column( 83 | children: [ 84 | Padding( 85 | padding: EdgeInsets.only( 86 | top: MediaQuery.of(context).padding.top, 87 | ), 88 | child: SizedBox( 89 | height: 270, 90 | child: SubtitleWrapper( 91 | videoPlayerController: _chewieController.videoPlayerController, 92 | subtitleController: subtitleController, 93 | subtitleStyle: const SubtitleStyle( 94 | textColor: Colors.white, 95 | hasBorder: true, 96 | ), 97 | videoChild: Chewie( 98 | controller: _chewieController, 99 | ), 100 | ), 101 | ), 102 | ), 103 | Expanded( 104 | child: ColoredBox( 105 | color: const Color( 106 | 0xff161a1d, 107 | ), 108 | child: Row( 109 | children: [ 110 | Expanded( 111 | child: Padding( 112 | padding: const EdgeInsets.all( 113 | 16.0, 114 | ), 115 | child: Column( 116 | crossAxisAlignment: CrossAxisAlignment.start, 117 | children: [ 118 | Text( 119 | 'Flutter subtitle wrapper package', 120 | style: TextStyle( 121 | fontSize: 28.0, 122 | color: Colors.white.withOpacity( 123 | 0.8, 124 | ), 125 | ), 126 | ), 127 | Padding( 128 | padding: const EdgeInsets.symmetric( 129 | vertical: 18.0, 130 | ), 131 | child: Text( 132 | 'This package can display SRT and WebVtt subtitles. With a lot of customizable options and dynamic updating support.', 133 | style: TextStyle( 134 | fontSize: 14.0, 135 | color: Colors.white.withOpacity( 136 | 0.8, 137 | ), 138 | ), 139 | ), 140 | ), 141 | Text( 142 | 'Options.', 143 | style: TextStyle( 144 | fontSize: 14.0, 145 | color: Colors.white.withOpacity( 146 | 0.8, 147 | ), 148 | ), 149 | ), 150 | const Divider( 151 | color: Colors.grey, 152 | ), 153 | Wrap( 154 | children: [ 155 | ElevatedButton( 156 | style: ButtonStyle( 157 | elevation: MaterialStateProperty.all(8.0), 158 | shape: MaterialStateProperty.all( 159 | RoundedRectangleBorder( 160 | borderRadius: BorderRadius.circular( 161 | 8.0, 162 | ), 163 | ), 164 | ), 165 | ), 166 | onPressed: () => updateSubtitleUrl( 167 | subtitleLanguage: ExampleSubtitleLanguage.english, 168 | ), 169 | child: const Text('Switch to 🇬🇧'), 170 | ), 171 | const SizedBox( 172 | width: 8.0, 173 | ), 174 | ElevatedButton( 175 | style: ButtonStyle( 176 | elevation: MaterialStateProperty.all(8.0), 177 | shape: MaterialStateProperty.all( 178 | RoundedRectangleBorder( 179 | borderRadius: BorderRadius.circular( 180 | 8.0, 181 | ), 182 | ), 183 | ), 184 | ), 185 | onPressed: () => updateSubtitleUrl( 186 | subtitleLanguage: ExampleSubtitleLanguage.spanish, 187 | ), 188 | child: const Text('Switch to 🇪🇸'), 189 | ), 190 | const SizedBox( 191 | width: 8.0, 192 | ), 193 | ElevatedButton( 194 | style: ButtonStyle( 195 | elevation: MaterialStateProperty.all(8.0), 196 | shape: MaterialStateProperty.all( 197 | RoundedRectangleBorder( 198 | borderRadius: BorderRadius.circular( 199 | 8.0, 200 | ), 201 | ), 202 | ), 203 | ), 204 | onPressed: () => updateSubtitleUrl( 205 | subtitleLanguage: ExampleSubtitleLanguage.dutch, 206 | ), 207 | child: const Text('Switch to 🇳🇱'), 208 | ), 209 | ], 210 | ), 211 | ], 212 | ), 213 | ), 214 | ), 215 | ], 216 | ), 217 | ), 218 | ), 219 | ], 220 | ), 221 | ); 222 | } 223 | 224 | @override 225 | void dispose() { 226 | super.dispose(); 227 | _chewieController.dispose(); 228 | } 229 | } 230 | 231 | enum ExampleSubtitleLanguage { 232 | english, 233 | spanish, 234 | dutch, 235 | } 236 | -------------------------------------------------------------------------------- /test/webvtt_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | import 'package:subtitle_wrapper_package/data/models/subtitle.dart'; 3 | import 'package:subtitle_wrapper_package/data/repository/subtitle_repository.dart'; 4 | import 'package:subtitle_wrapper_package/subtitle_controller.dart'; 5 | 6 | void main() { 7 | const subtitleUtf8Url = 'https://pastebin.com/raw/ZWWAL7fK'; 8 | const subtitleLatin1Url = 'https://pastebin.com/raw/CUUcK90U'; 9 | 10 | const subtitleContentString = 'WEBVTT\r\n' 11 | '\r\n' 12 | '1\r\n' 13 | '00:00:00.420 --> 00:00:03.510\r\n' 14 | 'Löksås ipsum själv vi ännu därmed trevnadens kom, häst kanske dimma\r\n' 15 | '\r\n' 16 | '2\r\n' 17 | '00:00:03.510 --> 00:00:07.531\r\n' 18 | 'annat bäckasiner därmed redan gamla, dimmhöljd miljoner groda hela\r\n' 19 | '\r\n' 20 | '3\r\n' 21 | '00:00:07.531 --> 00:00:11.440\r\n' 22 | 'mjuka nu. Smultron icke tre ännu varit denna enligt kan häst, del bäckasiner\r\n' 23 | '\r\n' 24 | '4\r\n' 25 | '00:00:11.440 --> 00:00:14.930\r\n' 26 | 'som tre rot så rot därmed ingalunda, hela ser genom smultron lax flera\r\n' 27 | '\r\n' 28 | '5\r\n' 29 | '00:00:14.930 --> 00:00:16.570\r\n' 30 | 'ordningens. Vi olika del vi samma nya samtidigt vidsträckt dag omfångsrik'; 31 | 32 | const latin1SubtitleContentString = 'WEBVTT\r\n' 33 | '\r\n' 34 | '1\r\n' 35 | '00:00:00.420 --> 00:00:03.510\r\n' 36 | 'LöksÃ¥s ipsum själv vi ännu därmed trevnadens kom, häst kanske dimma\r\n' 37 | '\r\n' 38 | '2\r\n' 39 | '00:00:03.510 --> 00:00:07.531\r\n' 40 | 'annat bäckasiner därmed redan gamla, dimmhöljd miljoner groda hela\r\n' 41 | '\r\n' 42 | '3\r\n' 43 | '00:00:07.531 --> 00:00:11.440\r\n' 44 | 'mjuka nu. Smultron icke tre ännu varit denna enligt kan häst, del bäckasiner\r\n' 45 | '\r\n' 46 | '4\r\n' 47 | '00:00:11.440 --> 00:00:14.930\r\n' 48 | 'som tre rot sÃ¥ rot därmed ingalunda, hela ser genom smultron lax flera\r\n' 49 | '\r\n' 50 | '5\r\n' 51 | '00:00:14.930 --> 00:00:16.570\r\n' 52 | 'ordningens. Vi olika del vi samma nya samtidigt vidsträckt dag omfÃ¥ngsrik'; 53 | 54 | final utf8SubtitleItems = [ 55 | const Subtitle( 56 | startTime: Duration( 57 | milliseconds: 420, 58 | ), 59 | endTime: Duration( 60 | seconds: 3, 61 | milliseconds: 510, 62 | ), 63 | text: 64 | 'Löksås ipsum själv vi ännu därmed trevnadens kom, häst kanske dimma', 65 | ), 66 | const Subtitle( 67 | startTime: Duration( 68 | seconds: 3, 69 | milliseconds: 510, 70 | ), 71 | endTime: Duration( 72 | seconds: 7, 73 | milliseconds: 531, 74 | ), 75 | text: 76 | 'annat bäckasiner därmed redan gamla, dimmhöljd miljoner groda hela', 77 | ), 78 | const Subtitle( 79 | startTime: Duration( 80 | seconds: 7, 81 | milliseconds: 531, 82 | ), 83 | endTime: Duration( 84 | seconds: 11, 85 | milliseconds: 440, 86 | ), 87 | text: 88 | 'mjuka nu. Smultron icke tre ännu varit denna enligt kan häst, del bäckasiner', 89 | ), 90 | const Subtitle( 91 | startTime: Duration( 92 | seconds: 11, 93 | milliseconds: 440, 94 | ), 95 | endTime: Duration( 96 | seconds: 14, 97 | milliseconds: 930, 98 | ), 99 | text: 100 | 'som tre rot så rot därmed ingalunda, hela ser genom smultron lax flera', 101 | ), 102 | const Subtitle( 103 | startTime: Duration( 104 | seconds: 14, 105 | milliseconds: 930, 106 | ), 107 | endTime: Duration( 108 | seconds: 16, 109 | milliseconds: 570, 110 | ), 111 | text: 112 | 'ordningens. Vi olika del vi samma nya samtidigt vidsträckt dag omfångsrik', 113 | ), 114 | ]; 115 | 116 | final latin1SubtitleItems = [ 117 | const Subtitle( 118 | startTime: Duration( 119 | milliseconds: 420, 120 | ), 121 | endTime: Duration( 122 | seconds: 3, 123 | milliseconds: 510, 124 | ), 125 | text: 126 | 'LöksÃ¥s ipsum själv vi ännu därmed trevnadens kom, häst kanske dimma', 127 | ), 128 | const Subtitle( 129 | startTime: Duration( 130 | seconds: 3, 131 | milliseconds: 510, 132 | ), 133 | endTime: Duration( 134 | seconds: 7, 135 | milliseconds: 531, 136 | ), 137 | text: 138 | 'annat bäckasiner därmed redan gamla, dimmhöljd miljoner groda hela', 139 | ), 140 | const Subtitle( 141 | startTime: Duration( 142 | seconds: 7, 143 | milliseconds: 531, 144 | ), 145 | endTime: Duration( 146 | seconds: 11, 147 | milliseconds: 440, 148 | ), 149 | text: 150 | 'mjuka nu. Smultron icke tre ännu varit denna enligt kan häst, del bäckasiner', 151 | ), 152 | const Subtitle( 153 | startTime: Duration( 154 | seconds: 11, 155 | milliseconds: 440, 156 | ), 157 | endTime: Duration( 158 | seconds: 14, 159 | milliseconds: 930, 160 | ), 161 | text: 162 | 'som tre rot sÃ¥ rot därmed ingalunda, hela ser genom smultron lax flera', 163 | ), 164 | const Subtitle( 165 | startTime: Duration( 166 | seconds: 14, 167 | milliseconds: 930, 168 | ), 169 | endTime: Duration( 170 | seconds: 16, 171 | milliseconds: 570, 172 | ), 173 | text: 174 | 'ordningens. Vi olika del vi samma nya samtidigt vidsträckt dag omfÃ¥ngsrik', 175 | ), 176 | ]; 177 | 178 | test('Loading remote of WebVtt subtitle file', () async { 179 | final subtitleController = SubtitleController( 180 | subtitleUrl: subtitleUtf8Url, 181 | ); 182 | final subtitleDataRepository = SubtitleDataRepository( 183 | subtitleController: subtitleController, 184 | ); 185 | final subtitleContent = 186 | await subtitleDataRepository.loadRemoteSubtitleContent( 187 | subtitleUrl: subtitleController.subtitleUrl!, 188 | ); 189 | expect( 190 | subtitleContent, 191 | subtitleContentString, 192 | ); 193 | }); 194 | 195 | test('Loading remote of WebVtt subtitle file with latin1 codec', () async { 196 | final subtitleController = SubtitleController( 197 | subtitleUrl: subtitleUtf8Url, 198 | subtitleDecoder: SubtitleDecoder.latin1, 199 | ); 200 | final subtitleDataRepository = SubtitleDataRepository( 201 | subtitleController: subtitleController, 202 | ); 203 | final subtitleContent = 204 | await subtitleDataRepository.loadRemoteSubtitleContent( 205 | subtitleUrl: subtitleController.subtitleUrl!, 206 | ); 207 | expect( 208 | subtitleContent, 209 | latin1SubtitleContentString, 210 | ); 211 | }); 212 | 213 | test('Loading remote of WebVtt subtitle file with utf8 codec', () async { 214 | final subtitleController = SubtitleController( 215 | subtitleUrl: subtitleUtf8Url, 216 | subtitleDecoder: SubtitleDecoder.utf8, 217 | ); 218 | final subtitleDataRepository = SubtitleDataRepository( 219 | subtitleController: subtitleController, 220 | ); 221 | final subtitleContent = 222 | await subtitleDataRepository.loadRemoteSubtitleContent( 223 | subtitleUrl: subtitleController.subtitleUrl!, 224 | ); 225 | expect( 226 | subtitleContent, 227 | subtitleContentString, 228 | ); 229 | }); 230 | test( 231 | 'Parsing remote of WebVtt with latin1 encoding', 232 | () async { 233 | final subtitleController = SubtitleController( 234 | subtitleUrl: subtitleUtf8Url, 235 | subtitleDecoder: SubtitleDecoder.latin1, 236 | ); 237 | final subtitleDataRepository = SubtitleDataRepository( 238 | subtitleController: subtitleController, 239 | ); 240 | final subtitles = await subtitleDataRepository.getSubtitles(); 241 | expect( 242 | subtitles.subtitles, 243 | latin1SubtitleItems, 244 | ); 245 | }, 246 | ); 247 | 248 | test( 249 | 'Parsing remote of WebVtt with automatic latin1 encoding', 250 | () async { 251 | final subtitleController = SubtitleController( 252 | subtitleUrl: subtitleLatin1Url, 253 | ); 254 | final subtitleDataRepository = SubtitleDataRepository( 255 | subtitleController: subtitleController, 256 | ); 257 | final subtitles = await subtitleDataRepository.getSubtitles(); 258 | expect( 259 | subtitles.subtitles.sublist( 260 | 0, 261 | 3, 262 | ), 263 | latin1SubtitleItems.sublist( 264 | 0, 265 | 3, 266 | ), 267 | ); 268 | }, 269 | ); 270 | 271 | test( 272 | 'Parsing remote of WebVtt subtitle file with utf8 encoding', 273 | () async { 274 | final subtitleController = SubtitleController( 275 | subtitleUrl: subtitleUtf8Url, 276 | subtitleDecoder: SubtitleDecoder.utf8, 277 | ); 278 | final subtitleDataRepository = SubtitleDataRepository( 279 | subtitleController: subtitleController, 280 | ); 281 | final subtitles = await subtitleDataRepository.getSubtitles(); 282 | expect( 283 | subtitles.subtitles, 284 | utf8SubtitleItems, 285 | ); 286 | }, 287 | ); 288 | } 289 | -------------------------------------------------------------------------------- /example/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 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | bloc: 13 | dependency: transitive 14 | description: 15 | name: bloc 16 | sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "8.1.4" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.1" 28 | characters: 29 | dependency: transitive 30 | description: 31 | name: characters 32 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.3.0" 36 | chewie: 37 | dependency: "direct main" 38 | description: 39 | name: chewie 40 | sha256: "90a75721f8cb881be0a38878b246183f69a0490e409f1d8f294b686b89a25f24" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.3.3" 44 | clock: 45 | dependency: transitive 46 | description: 47 | name: clock 48 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.1.1" 52 | collection: 53 | dependency: transitive 54 | description: 55 | name: collection 56 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.18.0" 60 | csslib: 61 | dependency: transitive 62 | description: 63 | name: csslib 64 | sha256: d1cd6d6e4b39a4ad295204722b8608f19981677b223f3e942c0b5a33dcf57ec0 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "0.17.1" 68 | cupertino_icons: 69 | dependency: "direct main" 70 | description: 71 | name: cupertino_icons 72 | sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.0.4" 76 | equatable: 77 | dependency: transitive 78 | description: 79 | name: equatable 80 | sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "2.0.5" 84 | fake_async: 85 | dependency: transitive 86 | description: 87 | name: fake_async 88 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "1.3.1" 92 | ffi: 93 | dependency: transitive 94 | description: 95 | name: ffi 96 | sha256: "13a6ccf6a459a125b3fcdb6ec73bd5ff90822e071207c663bfd1f70062d51d18" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "1.2.1" 100 | flutter: 101 | dependency: "direct main" 102 | description: flutter 103 | source: sdk 104 | version: "0.0.0" 105 | flutter_bloc: 106 | dependency: transitive 107 | description: 108 | name: flutter_bloc 109 | sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 110 | url: "https://pub.dev" 111 | source: hosted 112 | version: "8.1.5" 113 | flutter_test: 114 | dependency: "direct dev" 115 | description: flutter 116 | source: sdk 117 | version: "0.0.0" 118 | flutter_web_plugins: 119 | dependency: transitive 120 | description: flutter 121 | source: sdk 122 | version: "0.0.0" 123 | html: 124 | dependency: transitive 125 | description: 126 | name: html 127 | sha256: bfef906cbd4e78ef49ae511d9074aebd1d2251482ef601a280973e8b58b51bbf 128 | url: "https://pub.dev" 129 | source: hosted 130 | version: "0.15.0" 131 | http: 132 | dependency: transitive 133 | description: 134 | name: http 135 | sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" 136 | url: "https://pub.dev" 137 | source: hosted 138 | version: "1.2.1" 139 | http_parser: 140 | dependency: transitive 141 | description: 142 | name: http_parser 143 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 144 | url: "https://pub.dev" 145 | source: hosted 146 | version: "4.0.2" 147 | js: 148 | dependency: transitive 149 | description: 150 | name: js 151 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "0.6.5" 155 | leak_tracker: 156 | dependency: transitive 157 | description: 158 | name: leak_tracker 159 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "10.0.0" 163 | leak_tracker_flutter_testing: 164 | dependency: transitive 165 | description: 166 | name: leak_tracker_flutter_testing 167 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "2.0.1" 171 | leak_tracker_testing: 172 | dependency: transitive 173 | description: 174 | name: leak_tracker_testing 175 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "2.0.1" 179 | matcher: 180 | dependency: transitive 181 | description: 182 | name: matcher 183 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "0.12.16+1" 187 | material_color_utilities: 188 | dependency: transitive 189 | description: 190 | name: material_color_utilities 191 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "0.8.0" 195 | meta: 196 | dependency: transitive 197 | description: 198 | name: meta 199 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "1.11.0" 203 | nested: 204 | dependency: transitive 205 | description: 206 | name: nested 207 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "1.0.0" 211 | path: 212 | dependency: transitive 213 | description: 214 | name: path 215 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "1.9.0" 219 | plugin_platform_interface: 220 | dependency: transitive 221 | description: 222 | name: plugin_platform_interface 223 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 224 | url: "https://pub.dev" 225 | source: hosted 226 | version: "2.1.8" 227 | provider: 228 | dependency: transitive 229 | description: 230 | name: provider 231 | sha256: "7896193cf752c40ba7f7732a95264319a787871e5d628225357f5c909182bc06" 232 | url: "https://pub.dev" 233 | source: hosted 234 | version: "6.0.2" 235 | sky_engine: 236 | dependency: transitive 237 | description: flutter 238 | source: sdk 239 | version: "0.0.99" 240 | source_span: 241 | dependency: transitive 242 | description: 243 | name: source_span 244 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 245 | url: "https://pub.dev" 246 | source: hosted 247 | version: "1.10.0" 248 | stack_trace: 249 | dependency: transitive 250 | description: 251 | name: stack_trace 252 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 253 | url: "https://pub.dev" 254 | source: hosted 255 | version: "1.11.1" 256 | stream_channel: 257 | dependency: transitive 258 | description: 259 | name: stream_channel 260 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 261 | url: "https://pub.dev" 262 | source: hosted 263 | version: "2.1.2" 264 | string_scanner: 265 | dependency: transitive 266 | description: 267 | name: string_scanner 268 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 269 | url: "https://pub.dev" 270 | source: hosted 271 | version: "1.2.0" 272 | subtitle_wrapper_package: 273 | dependency: "direct main" 274 | description: 275 | path: ".." 276 | relative: true 277 | source: path 278 | version: "2.2.0" 279 | term_glyph: 280 | dependency: transitive 281 | description: 282 | name: term_glyph 283 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 284 | url: "https://pub.dev" 285 | source: hosted 286 | version: "1.2.1" 287 | test_api: 288 | dependency: transitive 289 | description: 290 | name: test_api 291 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 292 | url: "https://pub.dev" 293 | source: hosted 294 | version: "0.6.1" 295 | typed_data: 296 | dependency: transitive 297 | description: 298 | name: typed_data 299 | sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" 300 | url: "https://pub.dev" 301 | source: hosted 302 | version: "1.3.0" 303 | vector_math: 304 | dependency: transitive 305 | description: 306 | name: vector_math 307 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 308 | url: "https://pub.dev" 309 | source: hosted 310 | version: "2.1.4" 311 | very_good_analysis: 312 | dependency: "direct dev" 313 | description: 314 | name: very_good_analysis 315 | sha256: ebc48c51db35beeeec8c414e32f7bd78e612bd7f5992ccb0d46e19edaeb40b08 316 | url: "https://pub.dev" 317 | source: hosted 318 | version: "4.0.0+1" 319 | video_player: 320 | dependency: "direct main" 321 | description: 322 | name: video_player 323 | sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23 324 | url: "https://pub.dev" 325 | source: hosted 326 | version: "2.8.3" 327 | video_player_android: 328 | dependency: transitive 329 | description: 330 | name: video_player_android 331 | sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2" 332 | url: "https://pub.dev" 333 | source: hosted 334 | version: "2.4.12" 335 | video_player_avfoundation: 336 | dependency: transitive 337 | description: 338 | name: video_player_avfoundation 339 | sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" 340 | url: "https://pub.dev" 341 | source: hosted 342 | version: "2.5.6" 343 | video_player_platform_interface: 344 | dependency: transitive 345 | description: 346 | name: video_player_platform_interface 347 | sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" 348 | url: "https://pub.dev" 349 | source: hosted 350 | version: "6.2.2" 351 | video_player_web: 352 | dependency: transitive 353 | description: 354 | name: video_player_web 355 | sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4" 356 | url: "https://pub.dev" 357 | source: hosted 358 | version: "2.3.0" 359 | vm_service: 360 | dependency: transitive 361 | description: 362 | name: vm_service 363 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 364 | url: "https://pub.dev" 365 | source: hosted 366 | version: "13.0.0" 367 | wakelock: 368 | dependency: transitive 369 | description: 370 | name: wakelock 371 | sha256: b18a1a7b1ec71b1a9105766e06a73a2411e3eb5564bc569d99df92b57530bb4b 372 | url: "https://pub.dev" 373 | source: hosted 374 | version: "0.6.1+2" 375 | wakelock_macos: 376 | dependency: transitive 377 | description: 378 | name: wakelock_macos 379 | sha256: "047c6be2f88cb6b76d02553bca5a3a3b95323b15d30867eca53a19a0a319d4cd" 380 | url: "https://pub.dev" 381 | source: hosted 382 | version: "0.4.0" 383 | wakelock_platform_interface: 384 | dependency: transitive 385 | description: 386 | name: wakelock_platform_interface 387 | sha256: "1f4aeb81fb592b863da83d2d0f7b8196067451e4df91046c26b54a403f9de621" 388 | url: "https://pub.dev" 389 | source: hosted 390 | version: "0.3.0" 391 | wakelock_web: 392 | dependency: transitive 393 | description: 394 | name: wakelock_web 395 | sha256: "1b256b811ee3f0834888efddfe03da8d18d0819317f20f6193e2922b41a501b5" 396 | url: "https://pub.dev" 397 | source: hosted 398 | version: "0.4.0" 399 | wakelock_windows: 400 | dependency: transitive 401 | description: 402 | name: wakelock_windows 403 | sha256: "108b1b73711f1664ee462e73af34a9286ff496e27d4d8371e2fb4da8fde4cdac" 404 | url: "https://pub.dev" 405 | source: hosted 406 | version: "0.2.0" 407 | web: 408 | dependency: transitive 409 | description: 410 | name: web 411 | sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" 412 | url: "https://pub.dev" 413 | source: hosted 414 | version: "0.5.1" 415 | win32: 416 | dependency: transitive 417 | description: 418 | name: win32 419 | sha256: c0e3a4f7be7dae51d8f152230b86627e3397c1ba8c3fa58e63d44a9f3edc9cef 420 | url: "https://pub.dev" 421 | source: hosted 422 | version: "2.6.1" 423 | sdks: 424 | dart: ">=3.3.0 <4.0.0" 425 | flutter: ">=3.19.0" 426 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "67.0.0" 12 | analyzer: 13 | dependency: transitive 14 | description: 15 | name: analyzer 16 | sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "6.4.1" 20 | args: 21 | dependency: transitive 22 | description: 23 | name: args 24 | sha256: "37a4264b0b7fb930e94c0c47558f3b6c4f4e9cb7e655a3ea373131d79b2dc0cc" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.0.0" 28 | async: 29 | dependency: transitive 30 | description: 31 | name: async 32 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "2.11.0" 36 | bloc: 37 | dependency: "direct main" 38 | description: 39 | name: bloc 40 | sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "8.1.4" 44 | bloc_test: 45 | dependency: "direct dev" 46 | description: 47 | name: bloc_test 48 | sha256: "165a6ec950d9252ebe36dc5335f2e6eb13055f33d56db0eeb7642768849b43d2" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "9.1.7" 52 | boolean_selector: 53 | dependency: transitive 54 | description: 55 | name: boolean_selector 56 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "2.1.1" 60 | characters: 61 | dependency: transitive 62 | description: 63 | name: characters 64 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.0" 68 | clock: 69 | dependency: transitive 70 | description: 71 | name: clock 72 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.1.1" 76 | collection: 77 | dependency: transitive 78 | description: 79 | name: collection 80 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "1.18.0" 84 | convert: 85 | dependency: transitive 86 | description: 87 | name: convert 88 | sha256: df567b950053d83b4dba3e8c5799c411895d146f82b2147114b666a4fd9a80dd 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "3.0.0" 92 | coverage: 93 | dependency: transitive 94 | description: 95 | name: coverage 96 | sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "1.7.2" 100 | crypto: 101 | dependency: transitive 102 | description: 103 | name: crypto 104 | sha256: "8be10341257b613566fdc9fd073c46f7c032ed329b1c732bda17aca29f2366c8" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "3.0.0" 108 | csslib: 109 | dependency: transitive 110 | description: 111 | name: csslib 112 | sha256: d1cd6d6e4b39a4ad295204722b8608f19981677b223f3e942c0b5a33dcf57ec0 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "0.17.1" 116 | diff_match_patch: 117 | dependency: transitive 118 | description: 119 | name: diff_match_patch 120 | sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "0.4.1" 124 | equatable: 125 | dependency: "direct main" 126 | description: 127 | name: equatable 128 | sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 129 | url: "https://pub.dev" 130 | source: hosted 131 | version: "2.0.5" 132 | fake_async: 133 | dependency: transitive 134 | description: 135 | name: fake_async 136 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 137 | url: "https://pub.dev" 138 | source: hosted 139 | version: "1.3.1" 140 | file: 141 | dependency: transitive 142 | description: 143 | name: file 144 | sha256: "716a02ed2fabe2d0cc0fa0bcaefae83c412e8aef6c559aa394ed7c12ec5b981b" 145 | url: "https://pub.dev" 146 | source: hosted 147 | version: "6.0.0" 148 | flutter: 149 | dependency: "direct main" 150 | description: flutter 151 | source: sdk 152 | version: "0.0.0" 153 | flutter_bloc: 154 | dependency: "direct main" 155 | description: 156 | name: flutter_bloc 157 | sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 158 | url: "https://pub.dev" 159 | source: hosted 160 | version: "8.1.5" 161 | flutter_test: 162 | dependency: "direct dev" 163 | description: flutter 164 | source: sdk 165 | version: "0.0.0" 166 | flutter_web_plugins: 167 | dependency: transitive 168 | description: flutter 169 | source: sdk 170 | version: "0.0.0" 171 | frontend_server_client: 172 | dependency: transitive 173 | description: 174 | name: frontend_server_client 175 | sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "3.2.0" 179 | glob: 180 | dependency: transitive 181 | description: 182 | name: glob 183 | sha256: "8321dd2c0ab0683a91a51307fa844c6db4aa8e3981219b78961672aaab434658" 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "2.0.2" 187 | html: 188 | dependency: transitive 189 | description: 190 | name: html 191 | sha256: bfef906cbd4e78ef49ae511d9074aebd1d2251482ef601a280973e8b58b51bbf 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "0.15.0" 195 | http: 196 | dependency: "direct main" 197 | description: 198 | name: http 199 | sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "1.2.1" 203 | http_multi_server: 204 | dependency: transitive 205 | description: 206 | name: http_multi_server 207 | sha256: ab298ef2b2acd283bd36837df7801dcf6e6b925f8da6e09efb81111230aa9037 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "3.2.0" 211 | http_parser: 212 | dependency: "direct main" 213 | description: 214 | name: http_parser 215 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "4.0.2" 219 | io: 220 | dependency: transitive 221 | description: 222 | name: io 223 | sha256: "0d4c73c3653ab85bf696d51a9657604c900a370549196a91f33e4c39af760852" 224 | url: "https://pub.dev" 225 | source: hosted 226 | version: "1.0.3" 227 | js: 228 | dependency: transitive 229 | description: 230 | name: js 231 | sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" 232 | url: "https://pub.dev" 233 | source: hosted 234 | version: "0.6.5" 235 | leak_tracker: 236 | dependency: transitive 237 | description: 238 | name: leak_tracker 239 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" 240 | url: "https://pub.dev" 241 | source: hosted 242 | version: "10.0.0" 243 | leak_tracker_flutter_testing: 244 | dependency: transitive 245 | description: 246 | name: leak_tracker_flutter_testing 247 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 248 | url: "https://pub.dev" 249 | source: hosted 250 | version: "2.0.1" 251 | leak_tracker_testing: 252 | dependency: transitive 253 | description: 254 | name: leak_tracker_testing 255 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 256 | url: "https://pub.dev" 257 | source: hosted 258 | version: "2.0.1" 259 | logging: 260 | dependency: transitive 261 | description: 262 | name: logging 263 | sha256: "293ae2d49fd79d4c04944c3a26dfd313382d5f52e821ec57119230ae16031ad4" 264 | url: "https://pub.dev" 265 | source: hosted 266 | version: "1.0.2" 267 | matcher: 268 | dependency: transitive 269 | description: 270 | name: matcher 271 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 272 | url: "https://pub.dev" 273 | source: hosted 274 | version: "0.12.16+1" 275 | material_color_utilities: 276 | dependency: transitive 277 | description: 278 | name: material_color_utilities 279 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" 280 | url: "https://pub.dev" 281 | source: hosted 282 | version: "0.8.0" 283 | meta: 284 | dependency: transitive 285 | description: 286 | name: meta 287 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 288 | url: "https://pub.dev" 289 | source: hosted 290 | version: "1.11.0" 291 | mime: 292 | dependency: transitive 293 | description: 294 | name: mime 295 | sha256: dab22e92b41aa1255ea90ddc4bc2feaf35544fd0728e209638cad041a6e3928a 296 | url: "https://pub.dev" 297 | source: hosted 298 | version: "1.0.2" 299 | mocktail: 300 | dependency: "direct dev" 301 | description: 302 | name: mocktail 303 | sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 304 | url: "https://pub.dev" 305 | source: hosted 306 | version: "1.0.3" 307 | nested: 308 | dependency: transitive 309 | description: 310 | name: nested 311 | sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" 312 | url: "https://pub.dev" 313 | source: hosted 314 | version: "1.0.0" 315 | node_preamble: 316 | dependency: transitive 317 | description: 318 | name: node_preamble 319 | sha256: "8ebdbaa3b96d5285d068f80772390d27c21e1fa10fb2df6627b1b9415043608d" 320 | url: "https://pub.dev" 321 | source: hosted 322 | version: "2.0.1" 323 | package_config: 324 | dependency: transitive 325 | description: 326 | name: package_config 327 | sha256: "20e7154d701fedaeb219dad732815ecb66677667871127998a9a6581c2aba4ba" 328 | url: "https://pub.dev" 329 | source: hosted 330 | version: "2.0.0" 331 | path: 332 | dependency: transitive 333 | description: 334 | name: path 335 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 336 | url: "https://pub.dev" 337 | source: hosted 338 | version: "1.9.0" 339 | plugin_platform_interface: 340 | dependency: transitive 341 | description: 342 | name: plugin_platform_interface 343 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 344 | url: "https://pub.dev" 345 | source: hosted 346 | version: "2.1.8" 347 | pool: 348 | dependency: transitive 349 | description: 350 | name: pool 351 | sha256: "05955e3de2683e1746222efd14b775df7131139e07695dc8e24650f6b4204504" 352 | url: "https://pub.dev" 353 | source: hosted 354 | version: "1.5.0" 355 | provider: 356 | dependency: transitive 357 | description: 358 | name: provider 359 | sha256: "7896193cf752c40ba7f7732a95264319a787871e5d628225357f5c909182bc06" 360 | url: "https://pub.dev" 361 | source: hosted 362 | version: "6.0.2" 363 | pub_semver: 364 | dependency: transitive 365 | description: 366 | name: pub_semver 367 | sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" 368 | url: "https://pub.dev" 369 | source: hosted 370 | version: "2.1.4" 371 | shelf: 372 | dependency: transitive 373 | description: 374 | name: shelf 375 | sha256: "4592f6cb6c417632ebdfb63e4db42a7e3ad49d1bd52d9f93b6eb883035ddc0c3" 376 | url: "https://pub.dev" 377 | source: hosted 378 | version: "1.3.0" 379 | shelf_packages_handler: 380 | dependency: transitive 381 | description: 382 | name: shelf_packages_handler 383 | sha256: e0b44ebddec91e70a713e13adf93c1b2100821303b86a18e1ef1d082bd8bd9b8 384 | url: "https://pub.dev" 385 | source: hosted 386 | version: "3.0.0" 387 | shelf_static: 388 | dependency: transitive 389 | description: 390 | name: shelf_static 391 | sha256: "4a0d12cd512aa4fc55fed5f6280f02ef183f47ba29b4b0dfd621b1c99b7e6361" 392 | url: "https://pub.dev" 393 | source: hosted 394 | version: "1.1.0" 395 | shelf_web_socket: 396 | dependency: transitive 397 | description: 398 | name: shelf_web_socket 399 | sha256: fd84910bf7d58db109082edf7326b75322b8f186162028482f53dc892f00332d 400 | url: "https://pub.dev" 401 | source: hosted 402 | version: "1.0.1" 403 | sky_engine: 404 | dependency: transitive 405 | description: flutter 406 | source: sdk 407 | version: "0.0.99" 408 | source_map_stack_trace: 409 | dependency: transitive 410 | description: 411 | name: source_map_stack_trace 412 | sha256: "8c463326277f68a628abab20580047b419c2ff66756fd0affd451f73f9508c11" 413 | url: "https://pub.dev" 414 | source: hosted 415 | version: "2.1.0" 416 | source_maps: 417 | dependency: transitive 418 | description: 419 | name: source_maps 420 | sha256: "52de2200bb098de739794c82d09c41ac27b2e42fd7e23cce7b9c74bf653c7296" 421 | url: "https://pub.dev" 422 | source: hosted 423 | version: "0.10.10" 424 | source_span: 425 | dependency: transitive 426 | description: 427 | name: source_span 428 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 429 | url: "https://pub.dev" 430 | source: hosted 431 | version: "1.10.0" 432 | stack_trace: 433 | dependency: transitive 434 | description: 435 | name: stack_trace 436 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 437 | url: "https://pub.dev" 438 | source: hosted 439 | version: "1.11.1" 440 | stream_channel: 441 | dependency: transitive 442 | description: 443 | name: stream_channel 444 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 445 | url: "https://pub.dev" 446 | source: hosted 447 | version: "2.1.2" 448 | string_scanner: 449 | dependency: transitive 450 | description: 451 | name: string_scanner 452 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 453 | url: "https://pub.dev" 454 | source: hosted 455 | version: "1.2.0" 456 | term_glyph: 457 | dependency: transitive 458 | description: 459 | name: term_glyph 460 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 461 | url: "https://pub.dev" 462 | source: hosted 463 | version: "1.2.1" 464 | test: 465 | dependency: transitive 466 | description: 467 | name: test 468 | sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f 469 | url: "https://pub.dev" 470 | source: hosted 471 | version: "1.24.9" 472 | test_api: 473 | dependency: transitive 474 | description: 475 | name: test_api 476 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 477 | url: "https://pub.dev" 478 | source: hosted 479 | version: "0.6.1" 480 | test_core: 481 | dependency: transitive 482 | description: 483 | name: test_core 484 | sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a 485 | url: "https://pub.dev" 486 | source: hosted 487 | version: "0.5.9" 488 | typed_data: 489 | dependency: transitive 490 | description: 491 | name: typed_data 492 | sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" 493 | url: "https://pub.dev" 494 | source: hosted 495 | version: "1.3.0" 496 | vector_math: 497 | dependency: transitive 498 | description: 499 | name: vector_math 500 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 501 | url: "https://pub.dev" 502 | source: hosted 503 | version: "2.1.4" 504 | very_good_analysis: 505 | dependency: "direct dev" 506 | description: 507 | name: very_good_analysis 508 | sha256: "9ae7f3a3bd5764fb021b335ca28a34f040cd0ab6eec00a1b213b445dae58a4b8" 509 | url: "https://pub.dev" 510 | source: hosted 511 | version: "5.1.0" 512 | video_player: 513 | dependency: "direct main" 514 | description: 515 | name: video_player 516 | sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23 517 | url: "https://pub.dev" 518 | source: hosted 519 | version: "2.8.3" 520 | video_player_android: 521 | dependency: transitive 522 | description: 523 | name: video_player_android 524 | sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2" 525 | url: "https://pub.dev" 526 | source: hosted 527 | version: "2.4.12" 528 | video_player_avfoundation: 529 | dependency: transitive 530 | description: 531 | name: video_player_avfoundation 532 | sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" 533 | url: "https://pub.dev" 534 | source: hosted 535 | version: "2.5.6" 536 | video_player_platform_interface: 537 | dependency: transitive 538 | description: 539 | name: video_player_platform_interface 540 | sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" 541 | url: "https://pub.dev" 542 | source: hosted 543 | version: "6.2.2" 544 | video_player_web: 545 | dependency: transitive 546 | description: 547 | name: video_player_web 548 | sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4" 549 | url: "https://pub.dev" 550 | source: hosted 551 | version: "2.3.0" 552 | vm_service: 553 | dependency: transitive 554 | description: 555 | name: vm_service 556 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 557 | url: "https://pub.dev" 558 | source: hosted 559 | version: "13.0.0" 560 | watcher: 561 | dependency: transitive 562 | description: 563 | name: watcher 564 | sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" 565 | url: "https://pub.dev" 566 | source: hosted 567 | version: "1.1.0" 568 | web: 569 | dependency: transitive 570 | description: 571 | name: web 572 | sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" 573 | url: "https://pub.dev" 574 | source: hosted 575 | version: "0.5.1" 576 | web_socket_channel: 577 | dependency: transitive 578 | description: 579 | name: web_socket_channel 580 | sha256: "3a969ddcc204a3e34e863d204b29c0752716f78b6f9cc8235083208d268a4ccd" 581 | url: "https://pub.dev" 582 | source: hosted 583 | version: "2.2.0" 584 | webkit_inspection_protocol: 585 | dependency: transitive 586 | description: 587 | name: webkit_inspection_protocol 588 | sha256: f66577ed748712574b076e48a300aa3d8bc7aba93e83490c679707187e135ee4 589 | url: "https://pub.dev" 590 | source: hosted 591 | version: "1.1.0" 592 | yaml: 593 | dependency: transitive 594 | description: 595 | name: yaml 596 | sha256: "3cee79b1715110341012d27756d9bae38e650588acd38d3f3c610822e1337ace" 597 | url: "https://pub.dev" 598 | source: hosted 599 | version: "3.1.0" 600 | sdks: 601 | dart: ">=3.3.0 <4.0.0" 602 | flutter: ">=3.19.0" 603 | -------------------------------------------------------------------------------- /example/macos/Runner/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | --------------------------------------------------------------------------------