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
This commit is contained in:
Tommy
2023-05-30 21:10:04 -04:00
committed by GitHub
parent b98dd967cb
commit 9bfcd49037
36 changed files with 492 additions and 513 deletions

3
.gitignore vendored
View File

@ -32,9 +32,6 @@ migrate_working_dir/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols

View File

@ -1,10 +1,33 @@
# This file tracks properties of this Flutter project.
# 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:
revision: f4abaa0735eba4dfd8f33f73363911d63931fe03
revision: 84a1e904f44f9b0e9c4510138010edcc653163f8
channel: stable
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'

View File

@ -9,9 +9,6 @@
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
analyzer:
exclude: [lib/generated_plugin_registrant.dart]
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`

View File

@ -37,7 +37,9 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 33
namespace "com.tommychow.frosty"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tommychow.frosty">
<!-- Flutter needs it to communicate with the running application
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tommychow.frosty">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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>

View File

@ -3,7 +3,7 @@
<!-- 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">
<!-- 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:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>

View File

@ -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>

View File

@ -3,10 +3,11 @@
<!-- 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">
<!-- 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:forceDarkAllowed">false</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tommychow.frosty">
<!-- Flutter needs it to communicate with the running application
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -6,11 +6,11 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.3.0'
// START: FlutterFire Configuration
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:perf-plugin:1.4.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:perf-plugin:1.4.2'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
// END: FlutterFire Configuration
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
@ -31,6 +31,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

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

View File

@ -3,55 +3,55 @@ PODS:
- Flutter
- device_info_plus (0.0.1):
- Flutter
- Firebase/Analytics (10.7.0):
- Firebase/Analytics (10.9.0):
- Firebase/Core
- Firebase/Core (10.7.0):
- Firebase/Core (10.9.0):
- Firebase/CoreOnly
- FirebaseAnalytics (~> 10.7.0)
- Firebase/CoreOnly (10.7.0):
- FirebaseCore (= 10.7.0)
- Firebase/Crashlytics (10.7.0):
- FirebaseAnalytics (~> 10.9.0)
- Firebase/CoreOnly (10.9.0):
- FirebaseCore (= 10.9.0)
- Firebase/Crashlytics (10.9.0):
- Firebase/CoreOnly
- FirebaseCrashlytics (~> 10.7.0)
- Firebase/Performance (10.7.0):
- FirebaseCrashlytics (~> 10.9.0)
- Firebase/Performance (10.9.0):
- Firebase/CoreOnly
- FirebasePerformance (~> 10.7.0)
- firebase_analytics (10.2.1):
- Firebase/Analytics (= 10.7.0)
- FirebasePerformance (~> 10.9.0)
- firebase_analytics (10.4.1):
- Firebase/Analytics (= 10.9.0)
- firebase_core
- Flutter
- firebase_core (2.10.0):
- Firebase/CoreOnly (= 10.7.0)
- firebase_core (2.13.0):
- Firebase/CoreOnly (= 10.9.0)
- Flutter
- firebase_crashlytics (3.1.2):
- Firebase/Crashlytics (= 10.7.0)
- firebase_crashlytics (3.3.1):
- Firebase/Crashlytics (= 10.9.0)
- firebase_core
- Flutter
- firebase_performance (0.9.1-1):
- Firebase/Performance (= 10.7.0)
- firebase_performance (0.9.2-1):
- Firebase/Performance (= 10.9.0)
- firebase_core
- Flutter
- FirebaseABTesting (10.9.0):
- FirebaseCore (~> 10.0)
- FirebaseAnalytics (10.7.0):
- FirebaseAnalytics/AdIdSupport (= 10.7.0)
- FirebaseAnalytics (10.9.0):
- FirebaseAnalytics/AdIdSupport (= 10.9.0)
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/MethodSwizzler (~> 7.8)
- GoogleUtilities/Network (~> 7.8)
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- FirebaseAnalytics/AdIdSupport (10.7.0):
- FirebaseAnalytics/AdIdSupport (10.9.0):
- FirebaseCore (~> 10.0)
- FirebaseInstallations (~> 10.0)
- GoogleAppMeasurement (= 10.7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/MethodSwizzler (~> 7.8)
- GoogleUtilities/Network (~> 7.8)
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- GoogleAppMeasurement (= 10.9.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- FirebaseCore (10.7.0):
- FirebaseCore (10.9.0):
- FirebaseCoreInternal (~> 10.0)
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/Logger (~> 7.8)
@ -59,7 +59,7 @@ PODS:
- FirebaseCore (~> 10.0)
- FirebaseCoreInternal (10.9.0):
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- FirebaseCrashlytics (10.7.0):
- FirebaseCrashlytics (10.9.0):
- FirebaseCore (~> 10.5)
- FirebaseInstallations (~> 10.0)
- FirebaseSessions (~> 10.5)
@ -72,7 +72,7 @@ PODS:
- GoogleUtilities/Environment (~> 7.8)
- GoogleUtilities/UserDefaults (~> 7.8)
- PromisesObjC (~> 2.1)
- FirebasePerformance (10.7.0):
- FirebasePerformance (10.9.0):
- FirebaseCore (~> 10.5)
- FirebaseInstallations (~> 10.0)
- FirebaseRemoteConfig (~> 10.0)
@ -104,27 +104,27 @@ PODS:
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- GoogleAppMeasurement (10.7.0):
- GoogleAppMeasurement/AdIdSupport (= 10.7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/MethodSwizzler (~> 7.8)
- GoogleUtilities/Network (~> 7.8)
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- GoogleAppMeasurement (10.9.0):
- GoogleAppMeasurement/AdIdSupport (= 10.9.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- GoogleAppMeasurement/AdIdSupport (10.7.0):
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.7.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/MethodSwizzler (~> 7.8)
- GoogleUtilities/Network (~> 7.8)
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- GoogleAppMeasurement/AdIdSupport (10.9.0):
- GoogleAppMeasurement/WithoutAdIdSupport (= 10.9.0)
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- GoogleAppMeasurement/WithoutAdIdSupport (10.7.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
- GoogleUtilities/MethodSwizzler (~> 7.8)
- GoogleUtilities/Network (~> 7.8)
- "GoogleUtilities/NSData+zlib (~> 7.8)"
- GoogleAppMeasurement/WithoutAdIdSupport (10.9.0):
- GoogleUtilities/AppDelegateSwizzler (~> 7.11)
- GoogleUtilities/MethodSwizzler (~> 7.11)
- GoogleUtilities/Network (~> 7.11)
- "GoogleUtilities/NSData+zlib (~> 7.11)"
- nanopb (< 2.30910.0, >= 2.30908.0)
- GoogleDataTransport (9.2.2):
- GoogleDataTransport (9.2.3):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30910.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
@ -166,7 +166,7 @@ PODS:
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite (0.0.2):
- sqflite (0.0.3):
- Flutter
- FMDB (>= 2.7.5)
- url_launcher_ios (0.0.1):
@ -188,8 +188,8 @@ DEPENDENCIES:
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- in_app_review (from `.symlinks/plugins/in_app_review/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock (from `.symlinks/plugins/wakelock/ios`)
@ -240,9 +240,9 @@ EXTERNAL SOURCES:
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/ios"
:path: ".symlinks/plugins/path_provider_foundation/darwin"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/ios"
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
url_launcher_ios:
@ -255,39 +255,39 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
advanced_in_app_review: fe8fd50b8670cb3dbe67e4c26ca643f7aea8dbb8
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
Firebase: 0219acf760880eeec8ce479895bd7767466d9f81
firebase_analytics: e8e294333de66e5429d4aac365966281b4dbfb7d
firebase_core: 18d44f087248303a4a8f05d0099a000c46e3c77a
firebase_crashlytics: 621d91c0c5752e8219f8fe05a4d1e76394f4896a
firebase_performance: 7dd98298ea2f19f480efd59ccbfcad3972ced984
Firebase: bd152f0f3d278c4060c5c71359db08ebcfd5a3e2
firebase_analytics: 24c9089f960ea2255654488d219cb15a913adf8e
firebase_core: fc68c0f9eec4e800b9418deff14a7e0a504016f3
firebase_crashlytics: 6c82b5f80f225b2429ebc5fdadde5b7e7735ed4e
firebase_performance: 516fbfa3ace3c84add028be727ff08f15d8a3502
FirebaseABTesting: 005b70969e2817e2a1e631e8dba29134a04c0622
FirebaseAnalytics: f8133442ee6f8512e28ff19e62ce15398bfaeace
FirebaseCore: e317665b9d744727a97e623edbbed009320afdd7
FirebaseAnalytics: 5ea0db4893825e7b0149d575352cd838236313dc
FirebaseCore: b68d3616526ec02e4d155166bbafb8eca64af557
FirebaseCoreExtension: d3e9bba2930a8033042112397cd9f006a1bb203d
FirebaseCoreInternal: d2b4acb827908e72eca47a9fd896767c3053921e
FirebaseCrashlytics: 35fdd1a433b31e28adcf5c8933f4c526691a1e0b
FirebaseCrashlytics: b60329455285aff853e54139d8ddbfe1e5f2b9f9
FirebaseInstallations: c58489c9caacdbf27d1da60891a87318e20218e0
FirebasePerformance: 8281bbaf08aad194001018b932115b7d58a6f00b
FirebasePerformance: eee2f5da94fd7e5d15487649f8fe10a90c87c174
FirebaseRemoteConfig: 5ea5834e8c518f377bf1af2d97ebd611914ebf2d
FirebaseSessions: 44a6782502eb279a214d4adca20891353278760c
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
GoogleAppMeasurement: fe17c92a32207dd5cdd4e8d742767f2da74857f6
GoogleDataTransport: 8378d1fa8ac49753ea6ce70d65a7cb70ce5f66e6
GoogleAppMeasurement: 373bcbead1bb6a85be7a64d5d8f96284b762ea9c
GoogleDataTransport: f0308f5905a745f94fb91fea9c6cbaf3831cb1bd
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3

View File

@ -268,6 +268,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -55,6 +55,6 @@
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
<true/>
</dict>
</plist>

View File

@ -3,7 +3,6 @@ import 'dart:convert';
import 'package:frosty/models/badges.dart';
import 'package:frosty/models/emotes.dart';
import 'package:http/http.dart';
import 'package:tuple/tuple.dart';
/// The FFZ service for making API calls.
class FFZApi {
@ -36,7 +35,7 @@ class FFZApi {
}
/// 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 response = await _client.get(url);
@ -48,11 +47,12 @@ class FFZApi {
final emotes = emoticons.map((emote) => EmoteFFZ.fromJson(emote));
return Tuple2(
roomInfo,
emotes
.map((emote) => Emote.fromFFZ(emote, EmoteType.ffzChannel))
.toList());
return (
roomInfo,
emotes
.map((emote) => Emote.fromFFZ(emote, EmoteType.ffzChannel))
.toList()
);
} else {
return Future.error('Failed to get FFZ room info');
}

View File

@ -2,7 +2,6 @@ import 'dart:convert';
import 'dart:io';
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_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';
@ -75,21 +74,16 @@ void main() async {
await authStore.init();
runApp(
DevicePreview(
enabled: false,
builder: (context) {
return MultiProvider(
providers: [
Provider<AuthStore>(create: (_) => authStore),
Provider<SettingsStore>(create: (_) => settingsStore),
Provider<TwitchApi>(create: (_) => twitchApiService),
Provider<BTTVApi>(create: (_) => bttvApiService),
Provider<FFZApi>(create: (_) => ffzApiService),
Provider<SevenTVApi>(create: (_) => sevenTVApiService),
],
child: MyApp(firstRun: firstRun),
);
},
MultiProvider(
providers: [
Provider<AuthStore>(create: (_) => authStore),
Provider<SettingsStore>(create: (_) => settingsStore),
Provider<TwitchApi>(create: (_) => twitchApiService),
Provider<BTTVApi>(create: (_) => bttvApiService),
Provider<FFZApi>(create: (_) => ffzApiService),
Provider<SevenTVApi>(create: (_) => sevenTVApiService),
],
child: MyApp(firstRun: firstRun),
),
);
}
@ -247,8 +241,6 @@ class _MyAppState extends State<MyApp> {
final settingsStore = context.read<SettingsStore>();
return MaterialApp(
useInheritedMediaQuery: true,
locale: DevicePreview.locale(context),
title: 'Frosty',
theme: lightTheme,
darkTheme: settingsStore.themeType == ThemeType.dark ||

View File

@ -157,8 +157,10 @@ abstract class ChatAssetsStoreBase with Store {
sevenTVApi.getEmotesGlobal().catchError(onError),
sevenTVApi.getEmotesChannel(id: channelId).catchError(onError),
ffzApi.getRoomInfo(id: channelId).then((ffzRoom) {
ffzRoomInfo = ffzRoom.item1;
return ffzRoom.item2;
final (roomInfo, emotes) = ffzRoom;
ffzRoomInfo = roomInfo;
return emotes;
}).catchError(onError),
]).then((assets) => assets.expand((list) => list)).then((emotes) =>
_emoteToObject = {for (final emote in emotes) emote.name: emote});

View File

@ -44,7 +44,7 @@ class ChatMessage extends StatelessWidget {
Future<void> onLongPressMessage() async {
HapticFeedback.lightImpact();
await Clipboard.setData(ClipboardData(text: ircMessage.message));
await Clipboard.setData(ClipboardData(text: ircMessage.message ?? ''));
chatStore.updateNotification('Message copied');
}

View File

@ -37,17 +37,8 @@ class _VideoState extends State<Video> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
return WebView(
backgroundColor: Colors.black,
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,
return WebViewWidget(
controller: widget.videoStore.videoWebViewController,
);
}

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'dart:io';
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:frosty/apis/twitch_api.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:simple_pip_mode/simple_pip.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';
@ -29,8 +31,42 @@ abstract class VideoStoreBase with Store {
/// The [SimplePip] instance used for initiating PiP on Android.
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.
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
late Timer _overlayTimer;
@ -38,36 +74,6 @@ abstract class VideoStoreBase with Store {
/// Disposes the overlay reactions.
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.
///
/// 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.
_disposeOverlayReaction = reaction(
(_) => 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();
}
@ -123,9 +145,9 @@ abstract class VideoStoreBase with Store {
Future<void> initVideo() async {
// Add event listeners to notify the JavaScript channels when the video plays and pauses.
try {
controller?.runJavascript(
videoWebViewController.runJavaScript(
'document.getElementsByTagName("video")[0].addEventListener("pause", () => VideoPause.postMessage("video paused"));');
controller?.runJavascript(
videoWebViewController.runJavaScript(
'document.getElementsByTagName("video")[0].addEventListener("playing", () => VideoPlaying.postMessage("video playing"));');
} catch (e) {
debugPrint(e.toString());
@ -202,7 +224,7 @@ abstract class VideoStoreBase with Store {
@action
void handleRefresh() {
HapticFeedback.lightImpact();
controller?.reload();
videoWebViewController.reload();
updateStreamInfo();
}
@ -210,10 +232,10 @@ abstract class VideoStoreBase with Store {
void handlePausePlay() {
try {
if (_paused) {
controller?.runJavascript(
'document.getElementsByTagName("video")[0].play();');
videoWebViewController
.runJavaScript('document.getElementsByTagName("video")[0].play();');
} else {
controller?.runJavascript(
videoWebViewController.runJavaScript(
'document.getElementsByTagName("video")[0].pause();');
}
} catch (e) {
@ -230,7 +252,7 @@ abstract class VideoStoreBase with Store {
if (Platform.isAndroid) {
pip.enterPipMode(autoEnter: true);
} else if (Platform.isIOS) {
controller?.runJavascript(
videoWebViewController.runJavaScript(
'document.getElementsByTagName("video")[0].requestPictureInPicture();');
}
} 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.
// 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();
}

View File

@ -27,11 +27,10 @@ class OnboardingLogin extends StatelessWidget {
appBar: const FrostyAppBar(
title: Text('Connect with Twitch'),
),
body: WebView(
initialUrl: authStore.loginUri.toString(),
navigationDelegate: (navigation) => authStore.handleNavigation(
navigation: navigation, routeAfter: const OnboardingSetup()),
javascriptMode: JavascriptMode.unrestricted,
body: WebViewWidget(
controller: authStore.createAuthWebViewController(
routeAfter: const OnboardingSetup(),
),
),
),
);

View File

@ -34,11 +34,8 @@ class ProfileCard extends StatelessWidget {
appBar: const FrostyAppBar(
title: Text('Connect with Twitch'),
),
body: WebView(
initialUrl: authStore.loginUri.toString(),
navigationDelegate: (navigation) =>
authStore.handleNavigation(navigation: navigation),
javascriptMode: JavascriptMode.unrestricted,
body: WebViewWidget(
controller: authStore.createAuthWebViewController(),
),
);
},

View File

@ -51,28 +51,13 @@ abstract class AuthBase with Store {
@readonly
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).
FutureOr<NavigationDecision> handleNavigation(
{required NavigationRequest navigation, Widget? routeAfter}) {
{required NavigationRequest request, Widget? routeAfter}) {
// 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.
final uri = Uri.parse(navigation.url.replaceFirst('#', '?'));
final uri = Uri.parse(request.url.replaceFirst('#', '?'));
final token = uri.queryParameters['access_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".
// 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.
if (navigation.url == 'https://www.twitch.tv/?no-reload=true') {
if (request.url == 'https://www.twitch.tv/?no-reload=true') {
if (routeAfter != null) {
navigatorKey.currentState?.pop();
navigatorKey.currentState
@ -98,6 +83,32 @@ abstract class AuthBase with Store {
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.
Future<void> showBlockDialog(
BuildContext context, {

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
class FrostyAppBar extends StatelessWidget with PreferredSizeWidget {
class FrostyAppBar extends StatelessWidget implements PreferredSizeWidget {
final Widget title;
final bool? centerTitle;
final List<Widget>? actions;

View File

@ -44,9 +44,12 @@ class BlockReportModal extends StatelessWidget {
appBar: FrostyAppBar(
title: Text('Report $name'),
),
body: WebView(
initialUrl: 'https://www.twitch.tv/$userLogin/report',
javascriptMode: JavascriptMode.unrestricted,
body: WebViewWidget(
controller: WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..loadRequest(
Uri.parse('https://www.twitch.tv/$userLogin/report'),
),
),
);
},

File diff suppressed because it is too large Load Diff

View File

@ -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.
# 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
# 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
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.1.1+37
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.
# To automatically upgrade your package dependencies to the latest versions
@ -37,23 +37,21 @@ dependencies:
cupertino_icons: ^1.0.2
http: ^0.13.3
web_socket_channel: ^2.1.0
flutter_secure_storage: ^7.0.1
intl: ^0.17.0
flutter_secure_storage: ^8.0.0
intl: ^0.18.1
cached_network_image: ^3.1.0
webview_flutter: ^3.0.0
webview_flutter: ^4.2.0
provider: ^6.0.1
mobx: ^2.0.7+4
flutter_mobx: ^2.0.0
shared_preferences: ^2.0.8
tuple: ^2.0.0
flutter_svg: ^1.0.0
flutter_svg: ^2.0.5
url_launcher: ^6.0.17
package_info_plus: ^3.0.2
collection: ^1.16.0
flutter_cache_manager: ^3.3.0
device_info_plus: ^8.0.0
wakelock: ^0.6.2
device_preview: ^1.1.0
simple_icons: ^7.10.0
simple_pip_mode:
git:
@ -63,11 +61,13 @@ dependencies:
firebase_performance: ^0.9.1+1
firebase_analytics: ^10.2.1
advanced_in_app_review: ^1.1.3
webview_flutter_android: ^3.7.0
webview_flutter_wkwebview: ^3.4.3
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# 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
@ -78,12 +78,12 @@ dev_dependencies:
json_serializable: ^6.0.0
mobx_codegen: ^2.0.6
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
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
@ -113,7 +113,7 @@ flutter:
- assets/icons/logo.svg
# 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
# https://flutter.dev/assets-and-images/#from-packages