Upgrade Flutter/Dart and dependencies (#265)
* Fix new lint issues * Upgrade packages and remove `device_preview` * Regenerate splash assets * Migrate to `webview_flutter` 4.0 * Upgrade to Flutter 3.10 and Dart 3 * Update Podfile * Fix video not disposing when leaving channel * Remove `tuple` package * Upgrade packages * Upgrade packages * Update podfile * Upgrade packages * Upgrade packages
3
.gitignore
vendored
|
|
@ -32,9 +32,6 @@ migrate_working_dir/
|
||||||
.pub/
|
.pub/
|
||||||
/build/
|
/build/
|
||||||
|
|
||||||
# Web related
|
|
||||||
lib/generated_plugin_registrant.dart
|
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
|
||||||
|
|
|
||||||
27
.metadata
|
|
@ -1,10 +1,33 @@
|
||||||
# This file tracks properties of this Flutter project.
|
# This file tracks properties of this Flutter project.
|
||||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
#
|
#
|
||||||
# This file should be version controlled and should not be manually edited.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
|
revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
channel: stable
|
channel: stable
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
|
base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
|
- platform: android
|
||||||
|
create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
|
base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
|
- platform: ios
|
||||||
|
create_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
|
base_revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,6 @@
|
||||||
# packages, and plugins designed to encourage good coding practices.
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
analyzer:
|
|
||||||
exclude: [lib/generated_plugin_registrant.dart]
|
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
# The lint rules applied to this project can be customized in the
|
||||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@ if (keystorePropertiesFile.exists()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
namespace "com.tommychow.frosty"
|
||||||
|
compileSdkVersion flutter.compileSdkVersion
|
||||||
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.tommychow.frosty">
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
<!-- Flutter needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.tommychow.frosty">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<item name="android:windowSplashScreenBackground">@drawable/launch_background</item>
|
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">false</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
Flutter draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
<item name="android:forceDarkAllowed">false</item>
|
||||||
<item name="android:windowFullscreen">false</item>
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">false</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
</style>
|
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
||||||
This theme determines the color of the Android Window while your
|
|
||||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
|
||||||
running.
|
|
||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">?android:colorBackground</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
||||||
|
|
@ -3,10 +3,11 @@
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
Flutter draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
<item name="android:forceDarkAllowed">false</item>
|
||||||
<item name="android:windowFullscreen">false</item>
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
||||||
</style>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
package="com.tommychow.frosty">
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
<!-- Flutter needs it to communicate with the running application
|
the Flutter tool needs it to communicate with the running application
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||||
// START: FlutterFire Configuration
|
// START: FlutterFire Configuration
|
||||||
classpath 'com.google.gms:google-services:4.3.10'
|
classpath 'com.google.gms:google-services:4.3.15'
|
||||||
classpath 'com.google.firebase:perf-plugin:1.4.1'
|
classpath 'com.google.firebase:perf-plugin:1.4.2'
|
||||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
|
||||||
// END: FlutterFire Configuration
|
// END: FlutterFire Configuration
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +31,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#Fri Jun 23 08:50:38 CEST 2017
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
||||||
|
|
|
||||||
142
ios/Podfile.lock
|
|
@ -3,55 +3,55 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Firebase/Analytics (10.7.0):
|
- Firebase/Analytics (10.9.0):
|
||||||
- Firebase/Core
|
- Firebase/Core
|
||||||
- Firebase/Core (10.7.0):
|
- Firebase/Core (10.9.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseAnalytics (~> 10.7.0)
|
- FirebaseAnalytics (~> 10.9.0)
|
||||||
- Firebase/CoreOnly (10.7.0):
|
- Firebase/CoreOnly (10.9.0):
|
||||||
- FirebaseCore (= 10.7.0)
|
- FirebaseCore (= 10.9.0)
|
||||||
- Firebase/Crashlytics (10.7.0):
|
- Firebase/Crashlytics (10.9.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebaseCrashlytics (~> 10.7.0)
|
- FirebaseCrashlytics (~> 10.9.0)
|
||||||
- Firebase/Performance (10.7.0):
|
- Firebase/Performance (10.9.0):
|
||||||
- Firebase/CoreOnly
|
- Firebase/CoreOnly
|
||||||
- FirebasePerformance (~> 10.7.0)
|
- FirebasePerformance (~> 10.9.0)
|
||||||
- firebase_analytics (10.2.1):
|
- firebase_analytics (10.4.1):
|
||||||
- Firebase/Analytics (= 10.7.0)
|
- Firebase/Analytics (= 10.9.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_core (2.10.0):
|
- firebase_core (2.13.0):
|
||||||
- Firebase/CoreOnly (= 10.7.0)
|
- Firebase/CoreOnly (= 10.9.0)
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_crashlytics (3.1.2):
|
- firebase_crashlytics (3.3.1):
|
||||||
- Firebase/Crashlytics (= 10.7.0)
|
- Firebase/Crashlytics (= 10.9.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- firebase_performance (0.9.1-1):
|
- firebase_performance (0.9.2-1):
|
||||||
- Firebase/Performance (= 10.7.0)
|
- Firebase/Performance (= 10.9.0)
|
||||||
- firebase_core
|
- firebase_core
|
||||||
- Flutter
|
- Flutter
|
||||||
- FirebaseABTesting (10.9.0):
|
- FirebaseABTesting (10.9.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseAnalytics (10.7.0):
|
- FirebaseAnalytics (10.9.0):
|
||||||
- FirebaseAnalytics/AdIdSupport (= 10.7.0)
|
- FirebaseAnalytics/AdIdSupport (= 10.9.0)
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 10.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.8)
|
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/Network (~> 7.8)
|
- GoogleUtilities/Network (~> 7.11)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- FirebaseAnalytics/AdIdSupport (10.7.0):
|
- FirebaseAnalytics/AdIdSupport (10.9.0):
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 10.0)
|
||||||
- GoogleAppMeasurement (= 10.7.0)
|
- GoogleAppMeasurement (= 10.9.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.8)
|
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/Network (~> 7.8)
|
- GoogleUtilities/Network (~> 7.11)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- FirebaseCore (10.7.0):
|
- FirebaseCore (10.9.0):
|
||||||
- FirebaseCoreInternal (~> 10.0)
|
- FirebaseCoreInternal (~> 10.0)
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 7.8)
|
||||||
- GoogleUtilities/Logger (~> 7.8)
|
- GoogleUtilities/Logger (~> 7.8)
|
||||||
|
|
@ -59,7 +59,7 @@ PODS:
|
||||||
- FirebaseCore (~> 10.0)
|
- FirebaseCore (~> 10.0)
|
||||||
- FirebaseCoreInternal (10.9.0):
|
- FirebaseCoreInternal (10.9.0):
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
||||||
- FirebaseCrashlytics (10.7.0):
|
- FirebaseCrashlytics (10.9.0):
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCore (~> 10.5)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 10.0)
|
||||||
- FirebaseSessions (~> 10.5)
|
- FirebaseSessions (~> 10.5)
|
||||||
|
|
@ -72,7 +72,7 @@ PODS:
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
- GoogleUtilities/Environment (~> 7.8)
|
||||||
- GoogleUtilities/UserDefaults (~> 7.8)
|
- GoogleUtilities/UserDefaults (~> 7.8)
|
||||||
- PromisesObjC (~> 2.1)
|
- PromisesObjC (~> 2.1)
|
||||||
- FirebasePerformance (10.7.0):
|
- FirebasePerformance (10.9.0):
|
||||||
- FirebaseCore (~> 10.5)
|
- FirebaseCore (~> 10.5)
|
||||||
- FirebaseInstallations (~> 10.0)
|
- FirebaseInstallations (~> 10.0)
|
||||||
- FirebaseRemoteConfig (~> 10.0)
|
- FirebaseRemoteConfig (~> 10.0)
|
||||||
|
|
@ -104,27 +104,27 @@ PODS:
|
||||||
- FMDB (2.7.5):
|
- FMDB (2.7.5):
|
||||||
- FMDB/standard (= 2.7.5)
|
- FMDB/standard (= 2.7.5)
|
||||||
- FMDB/standard (2.7.5)
|
- FMDB/standard (2.7.5)
|
||||||
- GoogleAppMeasurement (10.7.0):
|
- GoogleAppMeasurement (10.9.0):
|
||||||
- GoogleAppMeasurement/AdIdSupport (= 10.7.0)
|
- GoogleAppMeasurement/AdIdSupport (= 10.9.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.8)
|
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/Network (~> 7.8)
|
- GoogleUtilities/Network (~> 7.11)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- GoogleAppMeasurement/AdIdSupport (10.7.0):
|
- GoogleAppMeasurement/AdIdSupport (10.9.0):
|
||||||
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.7.0)
|
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.9.0)
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.8)
|
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/Network (~> 7.8)
|
- GoogleUtilities/Network (~> 7.11)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- GoogleAppMeasurement/WithoutAdIdSupport (10.7.0):
|
- GoogleAppMeasurement/WithoutAdIdSupport (10.9.0):
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/MethodSwizzler (~> 7.8)
|
- GoogleUtilities/MethodSwizzler (~> 7.11)
|
||||||
- GoogleUtilities/Network (~> 7.8)
|
- GoogleUtilities/Network (~> 7.11)
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
- "GoogleUtilities/NSData+zlib (~> 7.11)"
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- GoogleDataTransport (9.2.2):
|
- GoogleDataTransport (9.2.3):
|
||||||
- GoogleUtilities/Environment (~> 7.7)
|
- GoogleUtilities/Environment (~> 7.7)
|
||||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||||
- PromisesObjC (< 3.0, >= 1.2)
|
- PromisesObjC (< 3.0, >= 1.2)
|
||||||
|
|
@ -166,7 +166,7 @@ PODS:
|
||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- sqflite (0.0.2):
|
- sqflite (0.0.3):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FMDB (>= 2.7.5)
|
- FMDB (>= 2.7.5)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
|
|
@ -188,8 +188,8 @@ DEPENDENCIES:
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||||
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
- wakelock (from `.symlinks/plugins/wakelock/ios`)
|
||||||
|
|
@ -240,9 +240,9 @@ EXTERNAL SOURCES:
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
sqflite:
|
sqflite:
|
||||||
:path: ".symlinks/plugins/sqflite/ios"
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
|
|
@ -255,39 +255,39 @@ EXTERNAL SOURCES:
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
advanced_in_app_review: fe8fd50b8670cb3dbe67e4c26ca643f7aea8dbb8
|
advanced_in_app_review: fe8fd50b8670cb3dbe67e4c26ca643f7aea8dbb8
|
||||||
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
||||||
Firebase: 0219acf760880eeec8ce479895bd7767466d9f81
|
Firebase: bd152f0f3d278c4060c5c71359db08ebcfd5a3e2
|
||||||
firebase_analytics: e8e294333de66e5429d4aac365966281b4dbfb7d
|
firebase_analytics: 24c9089f960ea2255654488d219cb15a913adf8e
|
||||||
firebase_core: 18d44f087248303a4a8f05d0099a000c46e3c77a
|
firebase_core: fc68c0f9eec4e800b9418deff14a7e0a504016f3
|
||||||
firebase_crashlytics: 621d91c0c5752e8219f8fe05a4d1e76394f4896a
|
firebase_crashlytics: 6c82b5f80f225b2429ebc5fdadde5b7e7735ed4e
|
||||||
firebase_performance: 7dd98298ea2f19f480efd59ccbfcad3972ced984
|
firebase_performance: 516fbfa3ace3c84add028be727ff08f15d8a3502
|
||||||
FirebaseABTesting: 005b70969e2817e2a1e631e8dba29134a04c0622
|
FirebaseABTesting: 005b70969e2817e2a1e631e8dba29134a04c0622
|
||||||
FirebaseAnalytics: f8133442ee6f8512e28ff19e62ce15398bfaeace
|
FirebaseAnalytics: 5ea0db4893825e7b0149d575352cd838236313dc
|
||||||
FirebaseCore: e317665b9d744727a97e623edbbed009320afdd7
|
FirebaseCore: b68d3616526ec02e4d155166bbafb8eca64af557
|
||||||
FirebaseCoreExtension: d3e9bba2930a8033042112397cd9f006a1bb203d
|
FirebaseCoreExtension: d3e9bba2930a8033042112397cd9f006a1bb203d
|
||||||
FirebaseCoreInternal: d2b4acb827908e72eca47a9fd896767c3053921e
|
FirebaseCoreInternal: d2b4acb827908e72eca47a9fd896767c3053921e
|
||||||
FirebaseCrashlytics: 35fdd1a433b31e28adcf5c8933f4c526691a1e0b
|
FirebaseCrashlytics: b60329455285aff853e54139d8ddbfe1e5f2b9f9
|
||||||
FirebaseInstallations: c58489c9caacdbf27d1da60891a87318e20218e0
|
FirebaseInstallations: c58489c9caacdbf27d1da60891a87318e20218e0
|
||||||
FirebasePerformance: 8281bbaf08aad194001018b932115b7d58a6f00b
|
FirebasePerformance: eee2f5da94fd7e5d15487649f8fe10a90c87c174
|
||||||
FirebaseRemoteConfig: 5ea5834e8c518f377bf1af2d97ebd611914ebf2d
|
FirebaseRemoteConfig: 5ea5834e8c518f377bf1af2d97ebd611914ebf2d
|
||||||
FirebaseSessions: 44a6782502eb279a214d4adca20891353278760c
|
FirebaseSessions: 44a6782502eb279a214d4adca20891353278760c
|
||||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||||
GoogleAppMeasurement: fe17c92a32207dd5cdd4e8d742767f2da74857f6
|
GoogleAppMeasurement: 373bcbead1bb6a85be7a64d5d8f96284b762ea9c
|
||||||
GoogleDataTransport: 8378d1fa8ac49753ea6ce70d65a7cb70ce5f66e6
|
GoogleDataTransport: f0308f5905a745f94fb91fea9c6cbaf3831cb1bd
|
||||||
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
|
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
|
||||||
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
|
||||||
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
|
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
|
||||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
|
||||||
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
|
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
|
||||||
PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959
|
PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959
|
||||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
||||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
|
||||||
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||||
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f
|
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
|
||||||
|
|
||||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -268,6 +268,7 @@
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||||
);
|
);
|
||||||
name = "Thin Binary";
|
name = "Thin Binary";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -55,6 +55,6 @@
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'dart:convert';
|
||||||
import 'package:frosty/models/badges.dart';
|
import 'package:frosty/models/badges.dart';
|
||||||
import 'package:frosty/models/emotes.dart';
|
import 'package:frosty/models/emotes.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
|
||||||
|
|
||||||
/// The FFZ service for making API calls.
|
/// The FFZ service for making API calls.
|
||||||
class FFZApi {
|
class FFZApi {
|
||||||
|
|
@ -36,7 +35,7 @@ class FFZApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a channel's FFZ room info including custom badges and emote used.
|
/// Returns a channel's FFZ room info including custom badges and emote used.
|
||||||
Future<Tuple2<RoomFFZ, List<Emote>>> getRoomInfo({required String id}) async {
|
Future<(RoomFFZ, List<Emote>)> getRoomInfo({required String id}) async {
|
||||||
final url = Uri.parse('https://api.frankerfacez.com/v1/room/id/$id');
|
final url = Uri.parse('https://api.frankerfacez.com/v1/room/id/$id');
|
||||||
|
|
||||||
final response = await _client.get(url);
|
final response = await _client.get(url);
|
||||||
|
|
@ -48,11 +47,12 @@ class FFZApi {
|
||||||
|
|
||||||
final emotes = emoticons.map((emote) => EmoteFFZ.fromJson(emote));
|
final emotes = emoticons.map((emote) => EmoteFFZ.fromJson(emote));
|
||||||
|
|
||||||
return Tuple2(
|
return (
|
||||||
roomInfo,
|
roomInfo,
|
||||||
emotes
|
emotes
|
||||||
.map((emote) => Emote.fromFFZ(emote, EmoteType.ffzChannel))
|
.map((emote) => Emote.fromFFZ(emote, EmoteType.ffzChannel))
|
||||||
.toList());
|
.toList()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return Future.error('Failed to get FFZ room info');
|
return Future.error('Failed to get FFZ room info');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:advanced_in_app_review/advanced_in_app_review.dart';
|
import 'package:advanced_in_app_review/advanced_in_app_review.dart';
|
||||||
import 'package:device_preview/device_preview.dart';
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
@ -75,21 +74,16 @@ void main() async {
|
||||||
await authStore.init();
|
await authStore.init();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
DevicePreview(
|
MultiProvider(
|
||||||
enabled: false,
|
providers: [
|
||||||
builder: (context) {
|
Provider<AuthStore>(create: (_) => authStore),
|
||||||
return MultiProvider(
|
Provider<SettingsStore>(create: (_) => settingsStore),
|
||||||
providers: [
|
Provider<TwitchApi>(create: (_) => twitchApiService),
|
||||||
Provider<AuthStore>(create: (_) => authStore),
|
Provider<BTTVApi>(create: (_) => bttvApiService),
|
||||||
Provider<SettingsStore>(create: (_) => settingsStore),
|
Provider<FFZApi>(create: (_) => ffzApiService),
|
||||||
Provider<TwitchApi>(create: (_) => twitchApiService),
|
Provider<SevenTVApi>(create: (_) => sevenTVApiService),
|
||||||
Provider<BTTVApi>(create: (_) => bttvApiService),
|
],
|
||||||
Provider<FFZApi>(create: (_) => ffzApiService),
|
child: MyApp(firstRun: firstRun),
|
||||||
Provider<SevenTVApi>(create: (_) => sevenTVApiService),
|
|
||||||
],
|
|
||||||
child: MyApp(firstRun: firstRun),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -247,8 +241,6 @@ class _MyAppState extends State<MyApp> {
|
||||||
final settingsStore = context.read<SettingsStore>();
|
final settingsStore = context.read<SettingsStore>();
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
useInheritedMediaQuery: true,
|
|
||||||
locale: DevicePreview.locale(context),
|
|
||||||
title: 'Frosty',
|
title: 'Frosty',
|
||||||
theme: lightTheme,
|
theme: lightTheme,
|
||||||
darkTheme: settingsStore.themeType == ThemeType.dark ||
|
darkTheme: settingsStore.themeType == ThemeType.dark ||
|
||||||
|
|
|
||||||
|
|
@ -157,8 +157,10 @@ abstract class ChatAssetsStoreBase with Store {
|
||||||
sevenTVApi.getEmotesGlobal().catchError(onError),
|
sevenTVApi.getEmotesGlobal().catchError(onError),
|
||||||
sevenTVApi.getEmotesChannel(id: channelId).catchError(onError),
|
sevenTVApi.getEmotesChannel(id: channelId).catchError(onError),
|
||||||
ffzApi.getRoomInfo(id: channelId).then((ffzRoom) {
|
ffzApi.getRoomInfo(id: channelId).then((ffzRoom) {
|
||||||
ffzRoomInfo = ffzRoom.item1;
|
final (roomInfo, emotes) = ffzRoom;
|
||||||
return ffzRoom.item2;
|
|
||||||
|
ffzRoomInfo = roomInfo;
|
||||||
|
return emotes;
|
||||||
}).catchError(onError),
|
}).catchError(onError),
|
||||||
]).then((assets) => assets.expand((list) => list)).then((emotes) =>
|
]).then((assets) => assets.expand((list) => list)).then((emotes) =>
|
||||||
_emoteToObject = {for (final emote in emotes) emote.name: emote});
|
_emoteToObject = {for (final emote in emotes) emote.name: emote});
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class ChatMessage extends StatelessWidget {
|
||||||
Future<void> onLongPressMessage() async {
|
Future<void> onLongPressMessage() async {
|
||||||
HapticFeedback.lightImpact();
|
HapticFeedback.lightImpact();
|
||||||
|
|
||||||
await Clipboard.setData(ClipboardData(text: ircMessage.message));
|
await Clipboard.setData(ClipboardData(text: ircMessage.message ?? ''));
|
||||||
|
|
||||||
chatStore.updateNotification('Message copied');
|
chatStore.updateNotification('Message copied');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,17 +37,8 @@ class _VideoState extends State<Video> with WidgetsBindingObserver {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return WebView(
|
return WebViewWidget(
|
||||||
backgroundColor: Colors.black,
|
controller: widget.videoStore.videoWebViewController,
|
||||||
initialUrl: widget.videoStore.videoUrl,
|
|
||||||
javascriptMode: JavascriptMode.unrestricted,
|
|
||||||
allowsInlineMediaPlayback: true,
|
|
||||||
initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
|
|
||||||
onWebViewCreated: (controller) =>
|
|
||||||
widget.videoStore.controller = controller,
|
|
||||||
onPageFinished: (string) => widget.videoStore.initVideo(),
|
|
||||||
navigationDelegate: widget.videoStore.handleNavigation,
|
|
||||||
javascriptChannels: widget.videoStore.javascriptChannels,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:frosty/apis/twitch_api.dart';
|
import 'package:frosty/apis/twitch_api.dart';
|
||||||
import 'package:frosty/models/stream.dart';
|
import 'package:frosty/models/stream.dart';
|
||||||
|
|
@ -11,6 +11,8 @@ import 'package:frosty/screens/settings/stores/settings_store.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:simple_pip_mode/simple_pip.dart';
|
import 'package:simple_pip_mode/simple_pip.dart';
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
import 'package:webview_flutter_android/webview_flutter_android.dart';
|
||||||
|
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
|
||||||
|
|
||||||
part 'video_store.g.dart';
|
part 'video_store.g.dart';
|
||||||
|
|
||||||
|
|
@ -29,8 +31,42 @@ abstract class VideoStoreBase with Store {
|
||||||
/// The [SimplePip] instance used for initiating PiP on Android.
|
/// The [SimplePip] instance used for initiating PiP on Android.
|
||||||
final pip = SimplePip();
|
final pip = SimplePip();
|
||||||
|
|
||||||
|
/// The video web view params used for enabling auto play.
|
||||||
|
late final PlatformWebViewControllerCreationParams _videoWebViewParams;
|
||||||
|
|
||||||
/// The webview controller used for injecting JavaScript to control the webview and video player.
|
/// The webview controller used for injecting JavaScript to control the webview and video player.
|
||||||
WebViewController? controller;
|
late final WebViewController videoWebViewController =
|
||||||
|
WebViewController.fromPlatformCreationParams(_videoWebViewParams)
|
||||||
|
..setBackgroundColor(Colors.black)
|
||||||
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
|
..addJavaScriptChannel('VideoPause', onMessageReceived: (message) {
|
||||||
|
_paused = true;
|
||||||
|
if (Platform.isAndroid) pip.setIsPlaying(false);
|
||||||
|
})
|
||||||
|
..addJavaScriptChannel('VideoPlaying', onMessageReceived: (message) {
|
||||||
|
_paused = false;
|
||||||
|
if (Platform.isAndroid) pip.setIsPlaying(true);
|
||||||
|
videoWebViewController.runJavaScript(
|
||||||
|
'document.getElementsByTagName("video")[0].muted = false;');
|
||||||
|
videoWebViewController.runJavaScript(
|
||||||
|
'document.getElementsByTagName("video")[0].volume = 1.0;');
|
||||||
|
})
|
||||||
|
..setNavigationDelegate(
|
||||||
|
NavigationDelegate(
|
||||||
|
onPageFinished: (_) => initVideo(),
|
||||||
|
// Used for preventing accidental navigation in the webview.
|
||||||
|
onNavigationRequest: (request) {
|
||||||
|
if (request.url.startsWith('https://player.twitch.tv')) {
|
||||||
|
return NavigationDecision.navigate;
|
||||||
|
}
|
||||||
|
return NavigationDecision.prevent;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..loadRequest(Uri.parse(videoUrl));
|
||||||
|
|
||||||
|
// allowsInlineMediaPlayback: true,
|
||||||
|
// initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
|
||||||
|
|
||||||
/// The timer that handles hiding the overlay automatically
|
/// The timer that handles hiding the overlay automatically
|
||||||
late Timer _overlayTimer;
|
late Timer _overlayTimer;
|
||||||
|
|
@ -38,36 +74,6 @@ abstract class VideoStoreBase with Store {
|
||||||
/// Disposes the overlay reactions.
|
/// Disposes the overlay reactions.
|
||||||
late final ReactionDisposer _disposeOverlayReaction;
|
late final ReactionDisposer _disposeOverlayReaction;
|
||||||
|
|
||||||
/// The JavaScript channels used to communicate play/pause from the webview to Flutter.
|
|
||||||
late final javascriptChannels = {
|
|
||||||
JavascriptChannel(
|
|
||||||
name: 'VideoPause',
|
|
||||||
onMessageReceived: (message) {
|
|
||||||
_paused = true;
|
|
||||||
if (Platform.isAndroid) pip.setIsPlaying(false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
JavascriptChannel(
|
|
||||||
name: 'VideoPlaying',
|
|
||||||
onMessageReceived: (message) {
|
|
||||||
_paused = false;
|
|
||||||
if (Platform.isAndroid) pip.setIsPlaying(true);
|
|
||||||
controller?.runJavascript(
|
|
||||||
'document.getElementsByTagName("video")[0].muted = false;');
|
|
||||||
controller?.runJavascript(
|
|
||||||
'document.getElementsByTagName("video")[0].volume = 1.0;');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Used for preventing accidental navigation in the webview.
|
|
||||||
FutureOr<NavigationDecision> handleNavigation(NavigationRequest navigation) {
|
|
||||||
if (navigation.url.startsWith('https://player.twitch.tv')) {
|
|
||||||
return NavigationDecision.navigate;
|
|
||||||
}
|
|
||||||
return NavigationDecision.prevent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If the video is currently paused.
|
/// If the video is currently paused.
|
||||||
///
|
///
|
||||||
/// Does not pause or play the video, only used for rendering state of the overlay.
|
/// Does not pause or play the video, only used for rendering state of the overlay.
|
||||||
|
|
@ -112,9 +118,25 @@ abstract class VideoStoreBase with Store {
|
||||||
// Initialize a reaction that will reload the webview whenever the overlay is toggled.
|
// Initialize a reaction that will reload the webview whenever the overlay is toggled.
|
||||||
_disposeOverlayReaction = reaction(
|
_disposeOverlayReaction = reaction(
|
||||||
(_) => settingsStore.showOverlay,
|
(_) => settingsStore.showOverlay,
|
||||||
(_) => controller?.loadUrl(videoUrl),
|
(_) => videoWebViewController.loadRequest(Uri.parse(videoUrl)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Initialize the video webview params for iOS to enable video autoplay.
|
||||||
|
if (WebViewPlatform.instance is WebKitWebViewPlatform) {
|
||||||
|
_videoWebViewParams = WebKitWebViewControllerCreationParams(
|
||||||
|
allowsInlineMediaPlayback: true,
|
||||||
|
mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_videoWebViewParams = const PlatformWebViewControllerCreationParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the video webview params for Android to enable video autoplay.
|
||||||
|
if (videoWebViewController.platform is AndroidWebViewController) {
|
||||||
|
(videoWebViewController.platform as AndroidWebViewController)
|
||||||
|
.setMediaPlaybackRequiresUserGesture(false);
|
||||||
|
}
|
||||||
|
|
||||||
updateStreamInfo();
|
updateStreamInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,9 +145,9 @@ abstract class VideoStoreBase with Store {
|
||||||
Future<void> initVideo() async {
|
Future<void> initVideo() async {
|
||||||
// Add event listeners to notify the JavaScript channels when the video plays and pauses.
|
// Add event listeners to notify the JavaScript channels when the video plays and pauses.
|
||||||
try {
|
try {
|
||||||
controller?.runJavascript(
|
videoWebViewController.runJavaScript(
|
||||||
'document.getElementsByTagName("video")[0].addEventListener("pause", () => VideoPause.postMessage("video paused"));');
|
'document.getElementsByTagName("video")[0].addEventListener("pause", () => VideoPause.postMessage("video paused"));');
|
||||||
controller?.runJavascript(
|
videoWebViewController.runJavaScript(
|
||||||
'document.getElementsByTagName("video")[0].addEventListener("playing", () => VideoPlaying.postMessage("video playing"));');
|
'document.getElementsByTagName("video")[0].addEventListener("playing", () => VideoPlaying.postMessage("video playing"));');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint(e.toString());
|
debugPrint(e.toString());
|
||||||
|
|
@ -202,7 +224,7 @@ abstract class VideoStoreBase with Store {
|
||||||
@action
|
@action
|
||||||
void handleRefresh() {
|
void handleRefresh() {
|
||||||
HapticFeedback.lightImpact();
|
HapticFeedback.lightImpact();
|
||||||
controller?.reload();
|
videoWebViewController.reload();
|
||||||
updateStreamInfo();
|
updateStreamInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,10 +232,10 @@ abstract class VideoStoreBase with Store {
|
||||||
void handlePausePlay() {
|
void handlePausePlay() {
|
||||||
try {
|
try {
|
||||||
if (_paused) {
|
if (_paused) {
|
||||||
controller?.runJavascript(
|
videoWebViewController
|
||||||
'document.getElementsByTagName("video")[0].play();');
|
.runJavaScript('document.getElementsByTagName("video")[0].play();');
|
||||||
} else {
|
} else {
|
||||||
controller?.runJavascript(
|
videoWebViewController.runJavaScript(
|
||||||
'document.getElementsByTagName("video")[0].pause();');
|
'document.getElementsByTagName("video")[0].pause();');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -230,7 +252,7 @@ abstract class VideoStoreBase with Store {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
pip.enterPipMode(autoEnter: true);
|
pip.enterPipMode(autoEnter: true);
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
controller?.runJavascript(
|
videoWebViewController.runJavaScript(
|
||||||
'document.getElementsByTagName("video")[0].requestPictureInPicture();');
|
'document.getElementsByTagName("video")[0].requestPictureInPicture();');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -249,7 +271,7 @@ abstract class VideoStoreBase with Store {
|
||||||
|
|
||||||
// Not ideal, but seems like the only way of disposing of the video properly.
|
// Not ideal, but seems like the only way of disposing of the video properly.
|
||||||
// Will both prevent the video from continuing to play when dismissed and closes PiP on iOS.
|
// Will both prevent the video from continuing to play when dismissed and closes PiP on iOS.
|
||||||
if (Platform.isIOS) controller?.reload();
|
if (Platform.isIOS) videoWebViewController.reload();
|
||||||
|
|
||||||
_disposeOverlayReaction();
|
_disposeOverlayReaction();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,10 @@ class OnboardingLogin extends StatelessWidget {
|
||||||
appBar: const FrostyAppBar(
|
appBar: const FrostyAppBar(
|
||||||
title: Text('Connect with Twitch'),
|
title: Text('Connect with Twitch'),
|
||||||
),
|
),
|
||||||
body: WebView(
|
body: WebViewWidget(
|
||||||
initialUrl: authStore.loginUri.toString(),
|
controller: authStore.createAuthWebViewController(
|
||||||
navigationDelegate: (navigation) => authStore.handleNavigation(
|
routeAfter: const OnboardingSetup(),
|
||||||
navigation: navigation, routeAfter: const OnboardingSetup()),
|
),
|
||||||
javascriptMode: JavascriptMode.unrestricted,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,8 @@ class ProfileCard extends StatelessWidget {
|
||||||
appBar: const FrostyAppBar(
|
appBar: const FrostyAppBar(
|
||||||
title: Text('Connect with Twitch'),
|
title: Text('Connect with Twitch'),
|
||||||
),
|
),
|
||||||
body: WebView(
|
body: WebViewWidget(
|
||||||
initialUrl: authStore.loginUri.toString(),
|
controller: authStore.createAuthWebViewController(),
|
||||||
navigationDelegate: (navigation) =>
|
|
||||||
authStore.handleNavigation(navigation: navigation),
|
|
||||||
javascriptMode: JavascriptMode.unrestricted,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -51,28 +51,13 @@ abstract class AuthBase with Store {
|
||||||
@readonly
|
@readonly
|
||||||
String? _error;
|
String? _error;
|
||||||
|
|
||||||
/// OAuth URI for the user to login.
|
|
||||||
final loginUri = Uri(
|
|
||||||
scheme: 'https',
|
|
||||||
host: 'id.twitch.tv',
|
|
||||||
path: '/oauth2/authorize',
|
|
||||||
queryParameters: {
|
|
||||||
'client_id': clientId,
|
|
||||||
'redirect_uri': 'https://twitch.tv/login',
|
|
||||||
'response_type': 'token',
|
|
||||||
'scope':
|
|
||||||
'chat:read chat:edit user:read:follows user:read:blocked_users user:manage:blocked_users',
|
|
||||||
'force_verify': 'true',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Navigation handler for the login webview. Fires on every navigation request (whenever the URL changes).
|
/// Navigation handler for the login webview. Fires on every navigation request (whenever the URL changes).
|
||||||
FutureOr<NavigationDecision> handleNavigation(
|
FutureOr<NavigationDecision> handleNavigation(
|
||||||
{required NavigationRequest navigation, Widget? routeAfter}) {
|
{required NavigationRequest request, Widget? routeAfter}) {
|
||||||
// Check if the URL is the redirect URI.
|
// Check if the URL is the redirect URI.
|
||||||
if (navigation.url.startsWith('https://twitch.tv/login')) {
|
if (request.url.startsWith('https://twitch.tv/login')) {
|
||||||
// Extract the token from the query parameters.
|
// Extract the token from the query parameters.
|
||||||
final uri = Uri.parse(navigation.url.replaceFirst('#', '?'));
|
final uri = Uri.parse(request.url.replaceFirst('#', '?'));
|
||||||
final token = uri.queryParameters['access_token'];
|
final token = uri.queryParameters['access_token'];
|
||||||
|
|
||||||
// Login with the provided token.
|
// Login with the provided token.
|
||||||
|
|
@ -82,7 +67,7 @@ abstract class AuthBase with Store {
|
||||||
// Check if the the URL has been redirected to "https://www.twitch.tv/?no-reload=true".
|
// Check if the the URL has been redirected to "https://www.twitch.tv/?no-reload=true".
|
||||||
// When redirected to the redirect_uri, there will be another redirect to "https://www.twitch.tv/?no-reload=true".
|
// When redirected to the redirect_uri, there will be another redirect to "https://www.twitch.tv/?no-reload=true".
|
||||||
// Checking for this will ensure that the user has automatically logged in to Twitch on the WebView itself.
|
// Checking for this will ensure that the user has automatically logged in to Twitch on the WebView itself.
|
||||||
if (navigation.url == 'https://www.twitch.tv/?no-reload=true') {
|
if (request.url == 'https://www.twitch.tv/?no-reload=true') {
|
||||||
if (routeAfter != null) {
|
if (routeAfter != null) {
|
||||||
navigatorKey.currentState?.pop();
|
navigatorKey.currentState?.pop();
|
||||||
navigatorKey.currentState
|
navigatorKey.currentState
|
||||||
|
|
@ -98,6 +83,32 @@ abstract class AuthBase with Store {
|
||||||
return NavigationDecision.navigate;
|
return NavigationDecision.navigate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebViewController createAuthWebViewController({Widget? routeAfter}) {
|
||||||
|
return WebViewController()
|
||||||
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
|
..setNavigationDelegate(
|
||||||
|
NavigationDelegate(
|
||||||
|
onNavigationRequest: (request) =>
|
||||||
|
handleNavigation(request: request, routeAfter: routeAfter),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..loadRequest(
|
||||||
|
Uri(
|
||||||
|
scheme: 'https',
|
||||||
|
host: 'id.twitch.tv',
|
||||||
|
path: '/oauth2/authorize',
|
||||||
|
queryParameters: {
|
||||||
|
'client_id': clientId,
|
||||||
|
'redirect_uri': 'https://twitch.tv/login',
|
||||||
|
'response_type': 'token',
|
||||||
|
'scope':
|
||||||
|
'chat:read chat:edit user:read:follows user:read:blocked_users user:manage:blocked_users',
|
||||||
|
'force_verify': 'true',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Shows a dialog verifying that the user is sure they want to block/unblock the target user.
|
/// Shows a dialog verifying that the user is sure they want to block/unblock the target user.
|
||||||
Future<void> showBlockDialog(
|
Future<void> showBlockDialog(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class FrostyAppBar extends StatelessWidget with PreferredSizeWidget {
|
class FrostyAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final Widget title;
|
final Widget title;
|
||||||
final bool? centerTitle;
|
final bool? centerTitle;
|
||||||
final List<Widget>? actions;
|
final List<Widget>? actions;
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,12 @@ class BlockReportModal extends StatelessWidget {
|
||||||
appBar: FrostyAppBar(
|
appBar: FrostyAppBar(
|
||||||
title: Text('Report $name'),
|
title: Text('Report $name'),
|
||||||
),
|
),
|
||||||
body: WebView(
|
body: WebViewWidget(
|
||||||
initialUrl: 'https://www.twitch.tv/$userLogin/report',
|
controller: WebViewController()
|
||||||
javascriptMode: JavascriptMode.unrestricted,
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
|
..loadRequest(
|
||||||
|
Uri.parse('https://www.twitch.tv/$userLogin/report'),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
485
pubspec.lock
24
pubspec.yaml
|
|
@ -12,13 +12,13 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# build by specifying --build-name and --build-number, respectively.
|
# build by specifying --build-name and --build-number, respectively.
|
||||||
# In Android, build-name is used as versionName while build-number used as versionCode.
|
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||||
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 3.1.1+37
|
version: 3.1.1+37
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.15.0 <3.0.0"
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
# Dependencies specify other packages that your package needs in order to work.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
|
@ -37,23 +37,21 @@ dependencies:
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
http: ^0.13.3
|
http: ^0.13.3
|
||||||
web_socket_channel: ^2.1.0
|
web_socket_channel: ^2.1.0
|
||||||
flutter_secure_storage: ^7.0.1
|
flutter_secure_storage: ^8.0.0
|
||||||
intl: ^0.17.0
|
intl: ^0.18.1
|
||||||
cached_network_image: ^3.1.0
|
cached_network_image: ^3.1.0
|
||||||
webview_flutter: ^3.0.0
|
webview_flutter: ^4.2.0
|
||||||
provider: ^6.0.1
|
provider: ^6.0.1
|
||||||
mobx: ^2.0.7+4
|
mobx: ^2.0.7+4
|
||||||
flutter_mobx: ^2.0.0
|
flutter_mobx: ^2.0.0
|
||||||
shared_preferences: ^2.0.8
|
shared_preferences: ^2.0.8
|
||||||
tuple: ^2.0.0
|
flutter_svg: ^2.0.5
|
||||||
flutter_svg: ^1.0.0
|
|
||||||
url_launcher: ^6.0.17
|
url_launcher: ^6.0.17
|
||||||
package_info_plus: ^3.0.2
|
package_info_plus: ^3.0.2
|
||||||
collection: ^1.16.0
|
collection: ^1.16.0
|
||||||
flutter_cache_manager: ^3.3.0
|
flutter_cache_manager: ^3.3.0
|
||||||
device_info_plus: ^8.0.0
|
device_info_plus: ^8.0.0
|
||||||
wakelock: ^0.6.2
|
wakelock: ^0.6.2
|
||||||
device_preview: ^1.1.0
|
|
||||||
simple_icons: ^7.10.0
|
simple_icons: ^7.10.0
|
||||||
simple_pip_mode:
|
simple_pip_mode:
|
||||||
git:
|
git:
|
||||||
|
|
@ -63,11 +61,13 @@ dependencies:
|
||||||
firebase_performance: ^0.9.1+1
|
firebase_performance: ^0.9.1+1
|
||||||
firebase_analytics: ^10.2.1
|
firebase_analytics: ^10.2.1
|
||||||
advanced_in_app_review: ^1.1.3
|
advanced_in_app_review: ^1.1.3
|
||||||
|
webview_flutter_android: ^3.7.0
|
||||||
|
webview_flutter_wkwebview: ^3.4.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
# The "flutter_lints" package below contains a set of recommended lints to
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
# encourage good coding practices. The lint set provided by the package is
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
# activated in the `analysis_options.yaml` file located at the root of your
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
|
|
@ -78,12 +78,12 @@ dev_dependencies:
|
||||||
json_serializable: ^6.0.0
|
json_serializable: ^6.0.0
|
||||||
mobx_codegen: ^2.0.6
|
mobx_codegen: ^2.0.6
|
||||||
flutter_native_splash: ^2.0.5
|
flutter_native_splash: ^2.0.5
|
||||||
flutter_launcher_icons: ^0.11.0
|
flutter_launcher_icons: ^0.13.1
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
# The following section is specific to Flutter.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
|
|
@ -113,7 +113,7 @@ flutter:
|
||||||
- assets/icons/logo.svg
|
- assets/icons/logo.svg
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
||||||
# For details regarding adding assets from package dependencies, see
|
# For details regarding adding assets from package dependencies, see
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
# https://flutter.dev/assets-and-images/#from-packages
|
||||||
|
|
|
||||||