chore: update dependencies (#772)

* chore: updated some dependencies

* refactor: reimplemented cache interceptor

* Revert "Updated dependencies & migrated breaking changes"

This reverts commit e6743b0d6b2552fdbf1c99d23e158e682362dd5d.

* chore: migrated flutter_local_notifications

* revert: reimplemented cache interceptor
This commit is contained in:
Aman Sikarwar 2023-04-18 19:45:29 +05:30 committed by GitHub
parent 37b583f560
commit 197770b68b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 199 additions and 92 deletions

View File

@ -30,6 +30,7 @@ android {
ndkVersion flutter.ndkVersion
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
@ -48,6 +49,7 @@ android {
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
}
buildTypes {
@ -81,4 +83,11 @@ dependencies {
implementation("org.microg:cronet-common:$cronetVersion")
implementation("org.microg:cronet-native:$cronetVersion")
// Core libraries
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// Window
implementation 'androidx.window:window:1.0.0'
implementation 'androidx.window:window-java:1.0.0'
}

View File

@ -13,7 +13,7 @@ import 'package:revanced_manager/models/patch.dart';
@lazySingleton
class GithubAPI {
late Dio _dio = Dio();
final _cacheOptions = CacheOptions(
store: MemCacheStore(),
maxStale: const Duration(days: 1),
@ -73,7 +73,9 @@ class GithubAPI {
}
}
Future<Map<String, dynamic>?> getLatestRelease(String repoName) async {
Future<Map<String, dynamic>?> getLatestRelease(
String repoName,
) async {
try {
final response = await _dio.get(
'/repos/$repoName/releases',
@ -119,9 +121,13 @@ class GithubAPI {
return [];
}
Future<File?> getLatestReleaseFile(String extension, String repoName) async {
Future<File?> getLatestReleaseFile(
String extension,
String repoName,
) async {
try {
final Map<String, dynamic>? release = await getLatestRelease(repoName);
final Map<String, dynamic>? release =
await getLatestRelease(repoName);
if (release != null) {
final Map<String, dynamic>? asset =
(release['assets'] as List<dynamic>).firstWhereOrNull(
@ -160,7 +166,8 @@ class GithubAPI {
Future<String> getLastestReleaseVersion(String repoName) async {
try {
final Map<String, dynamic>? release = await getLatestRelease(repoName);
final Map<String, dynamic>? release =
await getLatestRelease(repoName);
if (release != null) {
return release['tag_name'];
} else {

View File

@ -39,7 +39,8 @@ class ManagerAPI {
Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance();
storedPatchesFile =
(await getApplicationDocumentsDirectory()).path + storedPatchesFile;
(await getApplicationDocumentsDirectory()).path +
storedPatchesFile;
}
String getApiUrl() {
@ -78,7 +79,8 @@ class ManagerAPI {
}
String getIntegrationsRepo() {
return _prefs.getString('integrationsRepo') ?? defaultIntegrationsRepo;
return _prefs.getString('integrationsRepo') ??
defaultIntegrationsRepo;
}
Future<void> setIntegrationsRepo(String value) async {
@ -146,10 +148,14 @@ class ManagerAPI {
List<PatchedApplication> getPatchedApps() {
final List<String> apps = _prefs.getStringList('patchedApps') ?? [];
return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList();
return apps
.map((a) => PatchedApplication.fromJson(jsonDecode(a)))
.toList();
}
Future<void> setPatchedApps(List<PatchedApplication> patchedApps) async {
Future<void> setPatchedApps(
List<PatchedApplication> patchedApps,
) async {
if (patchedApps.length > 1) {
patchedApps.sort((a, b) => a.name.compareTo(b.name));
}
@ -251,15 +257,24 @@ class ManagerAPI {
}
Future<File?> downloadManager() async {
return await _revancedAPI.getLatestReleaseFile('.apk', defaultManagerRepo);
return await _revancedAPI.getLatestReleaseFile(
'.apk',
defaultManagerRepo,
);
}
Future<String?> getLatestPatcherReleaseTime() async {
return await _revancedAPI.getLatestReleaseTime('.gz', defaultPatcherRepo);
return await _revancedAPI.getLatestReleaseTime(
'.gz',
defaultPatcherRepo,
);
}
Future<String?> getLatestManagerReleaseTime() async {
return await _revancedAPI.getLatestReleaseTime('.apk', defaultManagerRepo);
return await _revancedAPI.getLatestReleaseTime(
'.apk',
defaultManagerRepo,
);
}
Future<String?> getLatestManagerVersion() async {
@ -313,10 +328,12 @@ class ManagerAPI {
final List<PatchedApplication> unsavedApps = [];
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
final List<String> installedApps = await _rootAPI.getInstalledApps();
final List<String> installedApps =
await _rootAPI.getInstalledApps();
for (final String packageName in installedApps) {
if (!patchedApps.any((app) => app.packageName == packageName)) {
final ApplicationWithIcon? application = await DeviceApps.getApp(
final ApplicationWithIcon? application =
await DeviceApps.getApp(
packageName,
true,
) as ApplicationWithIcon?;
@ -342,8 +359,10 @@ class ManagerAPI {
for (final Application app in userApps) {
if (app.packageName.startsWith('app.revanced') &&
!app.packageName.startsWith('app.revanced.manager.') &&
!patchedApps.any((uapp) => uapp.packageName == app.packageName)) {
final ApplicationWithIcon? application = await DeviceApps.getApp(
!patchedApps
.any((uapp) => uapp.packageName == app.packageName)) {
final ApplicationWithIcon? application =
await DeviceApps.getApp(
app.packageName,
true,
) as ApplicationWithIcon?;
@ -386,8 +405,9 @@ class ManagerAPI {
final int currentInstalledVersionInt = int.parse(
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''),
);
final int currentSavedVersionInt =
int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''));
final int currentSavedVersionInt = int.parse(
currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''),
);
if (currentInstalledVersionInt > currentSavedVersionInt) {
app.hasUpdates = true;
}
@ -399,9 +419,11 @@ class ManagerAPI {
Future<bool> isAppUninstalled(PatchedApplication app) async {
bool existsRoot = false;
final bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
final bool existsNonRoot =
await DeviceApps.isAppInstalled(app.packageName);
if (app.isRooted) {
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
final bool hasRootPermissions =
await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
}
@ -410,7 +432,10 @@ class ManagerAPI {
return !existsNonRoot;
}
Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {
Future<bool> hasAppUpdates(
String packageName,
DateTime patchDate,
) async {
final List<String> commits = await _githubAPI.getCommits(
packageName,
getPatchesRepo(),
@ -448,9 +473,13 @@ class ManagerAPI {
return app != null && app.isSplit;
}
Future<void> setSelectedPatches(String app, List<String> patches) async {
Future<void> setSelectedPatches(
String app,
List<String> patches,
) async {
final File selectedPatchesFile = File(storedPatchesFile);
final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
final Map<String, dynamic> patchesMap =
await readSelectedPatchesFile();
if (patches.isEmpty) {
patchesMap.remove(app);
} else {
@ -460,7 +489,8 @@ class ManagerAPI {
}
Future<List<String>> getSelectedPatches(String app) async {
final Map<String, dynamic> patchesMap = await readSelectedPatchesFile();
final Map<String, dynamic> patchesMap =
await readSelectedPatchesFile();
return List.from(patchesMap.putIfAbsent(app, () => List.empty()));
}

View File

@ -69,7 +69,8 @@ class PatcherAPI {
onlyAppsWithLaunchIntent: true,
);
for (final pkg in allPackages) {
if (!filteredApps.any((app) => app.packageName == pkg.packageName)) {
if (!filteredApps
.any((app) => app.packageName == pkg.packageName)) {
final appInfo = await DeviceApps.getApp(
pkg.packageName,
true,
@ -83,7 +84,8 @@ class PatcherAPI {
for (final Patch patch in _patches) {
for (final Package package in patch.compatiblePackages) {
try {
if (!filteredApps.any((app) => app.packageName == package.name)) {
if (!filteredApps
.any((app) => app.packageName == package.name)) {
final ApplicationWithIcon? app = await DeviceApps.getApp(
package.name,
true,
@ -118,13 +120,17 @@ class PatcherAPI {
return filteredPatches[packageName];
}
Future<List<Patch>> getAppliedPatches(List<String> appliedPatches) async {
Future<List<Patch>> getAppliedPatches(
List<String> appliedPatches,
) async {
return _patches
.where((patch) => appliedPatches.contains(patch.name))
.toList();
}
Future<bool> needsResourcePatching(List<Patch> selectedPatches) async {
Future<bool> needsResourcePatching(
List<Patch> selectedPatches,
) async {
return selectedPatches.any(
(patch) => patch.dependencies.any(
(dep) => dep.contains('resource-'),
@ -145,7 +151,8 @@ class PatcherAPI {
String originalFilePath,
) async {
try {
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
final bool hasRootPermissions =
await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
originalFilePath = await _rootAPI.getOriginalFilePath(
packageName,
@ -166,13 +173,15 @@ class PatcherAPI {
String originalFilePath,
List<Patch> selectedPatches,
) async {
final bool includeSettings = await needsSettingsPatch(selectedPatches);
final bool includeSettings =
await needsSettingsPatch(selectedPatches);
if (includeSettings) {
try {
final Patch? settingsPatch = _patches.firstWhereOrNull(
(patch) =>
patch.name.contains('settings') &&
patch.compatiblePackages.any((pack) => pack.name == packageName),
patch.compatiblePackages
.any((pack) => pack.name == packageName),
);
if (settingsPatch != null) {
selectedPatches.add(settingsPatch);
@ -184,7 +193,8 @@ class PatcherAPI {
}
}
final File? patchBundleFile = await _managerAPI.downloadPatches();
final File? integrationsFile = await _managerAPI.downloadIntegrations();
final File? integrationsFile =
await _managerAPI.downloadIntegrations();
if (patchBundleFile != null) {
_dataDir.createSync();
_tmpDir.createSync();
@ -207,7 +217,8 @@ class PatcherAPI {
'patchedFilePath': patchedFile.path,
'outFilePath': _outFile!.path,
'integrationsPath': integrationsFile!.path,
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
'selectedPatches':
selectedPatches.map((p) => p.name).toList(),
'cacheDirPath': cacheDir.path,
'keyStoreFilePath': _keyStoreFile.path,
'keystorePassword': _managerAPI.getKeystorePassword(),
@ -225,7 +236,8 @@ class PatcherAPI {
if (_outFile != null) {
try {
if (patchedApp.isRooted) {
final bool hasRootPermissions = await _rootAPI.hasRootPermissions();
final bool hasRootPermissions =
await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
return _rootAPI.installApp(
patchedApp.packageName,
@ -235,7 +247,9 @@ class PatcherAPI {
}
} else {
await AppInstaller.installApk(_outFile!.path);
return await DeviceApps.isAppInstalled(patchedApp.packageName);
return await DeviceApps.isAppInstalled(
patchedApp.packageName,
);
}
} on Exception catch (e) {
if (kDebugMode) {
@ -307,7 +321,8 @@ class PatcherAPI {
String getRecommendedVersion(String packageName) {
final Map<String, int> versions = {};
for (final Patch patch in _patches) {
final Package? package = patch.compatiblePackages.firstWhereOrNull(
final Package? package =
patch.compatiblePackages.firstWhereOrNull(
(pack) => pack.name == packageName,
);
if (package != null) {
@ -326,7 +341,8 @@ class PatcherAPI {
versions
..clear()
..addEntries(entries);
versions.removeWhere((key, value) => value != versions.values.last);
versions
.removeWhere((key, value) => value != versions.values.last);
return (versions.keys.toList()..sort()).last;
}
return '';

View File

@ -15,7 +15,7 @@ import 'package:timeago/timeago.dart';
@lazySingleton
class RevancedAPI {
late Dio _dio = Dio();
final _cacheOptions = CacheOptions(
store: MemCacheStore(),
maxStale: const Duration(days: 1),
@ -142,7 +142,10 @@ class RevancedAPI {
return null;
}
Future<File?> getLatestReleaseFile(String extension, String repoName) async {
Future<File?> getLatestReleaseFile(
String extension,
String repoName,
) async {
try {
final Map<String, dynamic>? release = await _getLatestRelease(
extension,

View File

@ -3,7 +3,6 @@ import 'dart:io';
import 'package:app_installer/app_installer.dart';
import 'package:cross_connectivity/cross_connectivity.dart';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
@ -25,12 +24,14 @@ import 'package:timezone/timezone.dart' as tz;
@lazySingleton
class HomeViewModel extends BaseViewModel {
final NavigationService _navigationService = locator<NavigationService>();
final NavigationService _navigationService =
locator<NavigationService>();
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final GithubAPI _githubAPI = locator<GithubAPI>();
final Toast _toast = locator<Toast>();
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
final flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
DateTime? _lastUpdate;
bool showUpdatableApps = false;
List<PatchedApplication> patchedInstalledApps = [];
@ -41,8 +42,17 @@ class HomeViewModel extends BaseViewModel {
const InitializationSettings(
android: AndroidInitializationSettings('ic_notification'),
),
onSelectNotification: (p) =>
DeviceApps.openApp('app.revanced.manager.flutter'),
onDidReceiveNotificationResponse: (response) async {
if (response.id == 0) {
_toast.showBottom('homeView.installingMessage');
final File? managerApk = await _managerAPI.downloadManager();
if (managerApk != null) {
await AppInstaller.installApk(managerApk.path);
} else {
_toast.showBottom('homeView.errorDownloadMessage');
}
}
},
);
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
@ -52,6 +62,19 @@ class HomeViewModel extends BaseViewModel {
if (!isConnected) {
_toast.showBottom('homeView.noConnection');
}
final NotificationAppLaunchDetails? notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin
.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ??
false) {
_toast.showBottom('homeView.installingMessage');
final File? managerApk = await _managerAPI.downloadManager();
if (managerApk != null) {
await AppInstaller.installApk(managerApk.path);
} else {
_toast.showBottom('homeView.errorDownloadMessage');
}
}
_getPatchedApps();
_managerAPI.reAssessSavedApps().then((_) => _getPatchedApps());
}
@ -86,8 +109,10 @@ class HomeViewModel extends BaseViewModel {
}
Future<bool> hasManagerUpdates() async {
final String? latestVersion = await _managerAPI.getLatestManagerVersion();
final String currentVersion = await _managerAPI.getCurrentManagerVersion();
final String? latestVersion =
await _managerAPI.getLatestManagerVersion();
final String currentVersion =
await _managerAPI.getCurrentManagerVersion();
if (latestVersion != null) {
try {
final int latestVersionInt =
@ -171,7 +196,9 @@ class HomeViewModel extends BaseViewModel {
_toast.showBottom('homeView.updatesDisabled');
}
Future<void> showUpdateConfirmationDialog(BuildContext parentContext) {
Future<void> showUpdateConfirmationDialog(
BuildContext parentContext,
) {
return showModalBottomSheet(
context: parentContext,
isScrollControlled: true,

View File

@ -17,12 +17,14 @@ import 'package:stacked/stacked.dart';
class NavigationViewModel extends IndexTrackingViewModel {
Future<void> initialize(BuildContext context) async {
locator<Toast>().initialize(context);
final SharedPreferences prefs = await SharedPreferences.getInstance();
final SharedPreferences prefs =
await SharedPreferences.getInstance();
if (prefs.getBool('permissionsRequested') == null) {
await prefs.setBool('permissionsRequested', true);
RootAPI().hasRootPermissions().then(
(value) => Permission.requestInstallPackages.request().then(
(value) => Permission.ignoreBatteryOptimizations.request(),
(value) =>
Permission.ignoreBatteryOptimizations.request(),
),
);
}
@ -37,7 +39,8 @@ class NavigationViewModel extends IndexTrackingViewModel {
SystemUiOverlayStyle(
systemNavigationBarColor: Colors.transparent,
systemNavigationBarIconBrightness:
DynamicTheme.of(context)!.theme.brightness == Brightness.light
DynamicTheme.of(context)!.theme.brightness ==
Brightness.light
? Brightness.dark
: Brightness.light,
),

View File

@ -17,7 +17,8 @@ import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
class SettingsViewModel extends BaseViewModel {
final NavigationService _navigationService = locator<NavigationService>();
final NavigationService _navigationService =
locator<NavigationService>();
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final Toast _toast = locator<Toast>();
@ -62,8 +63,11 @@ class SettingsViewModel extends BaseViewModel {
try {
final File outFile = File(_managerAPI.storedPatchesFile);
if (outFile.existsSync()) {
final String dateTime =
DateTime.now().toString().replaceAll(' ', '_').split('.').first;
final String dateTime = DateTime.now()
.toString()
.replaceAll(' ', '_')
.split('.')
.first;
await CRFileSaver.saveFileWithDialog(
SaveFileDialogParams(
sourceFilePath: outFile.path,
@ -83,7 +87,8 @@ class SettingsViewModel extends BaseViewModel {
Future<void> importPatches() async {
try {
final FilePickerResult? result = await FilePicker.platform.pickFiles(
final FilePickerResult? result =
await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['json'],
);
@ -151,7 +156,7 @@ class SettingsViewModel extends BaseViewModel {
Future<int> getSdkVersion() async {
final AndroidDeviceInfo info = await DeviceInfoPlugin().androidInfo;
return info.version.sdkInt ?? -1;
return info.version.sdkInt;
}
Future<void> deleteLogs() async {

View File

@ -16,12 +16,12 @@ class UpdateConfirmationDialog extends StatelessWidget {
expand: false,
snap: true,
snapSizes: const [0.5],
builder: (context, scrollController) => SingleChildScrollView(
builder: (_, scrollController) => SingleChildScrollView(
controller: scrollController,
child: SafeArea(
child: FutureBuilder<Map<String, dynamic>?>(
future: model.getLatestManagerRelease(),
builder: (context, snapshot) {
builder: (_, snapshot) {
if (!snapshot.hasData) {
return const SizedBox(
height: 300,
@ -45,7 +45,8 @@ class UpdateConfirmationDialog extends StatelessWidget {
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
I18nText(
'homeView.updateDialogTitle',
@ -62,12 +63,14 @@ class UpdateConfirmationDialog extends StatelessWidget {
children: [
Icon(
Icons.new_releases_outlined,
color:
Theme.of(context).colorScheme.secondary,
color: Theme.of(context)
.colorScheme
.secondary,
),
const SizedBox(width: 8.0),
Text(
snapshot.data!['tag_name'] ?? 'Unknown',
snapshot.data!['tag_name'] ??
'Unknown',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
@ -93,7 +96,8 @@ class UpdateConfirmationDialog extends StatelessWidget {
),
),
Padding(
padding: const EdgeInsets.only(left: 24.0, bottom: 12.0),
padding:
const EdgeInsets.only(left: 24.0, bottom: 12.0),
child: I18nText(
'homeView.updateChangelogTitle',
child: Text(
@ -109,9 +113,12 @@ class UpdateConfirmationDialog extends StatelessWidget {
),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 24.0),
margin:
const EdgeInsets.symmetric(horizontal: 24.0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondaryContainer,
color: Theme.of(context)
.colorScheme
.secondaryContainer,
borderRadius: BorderRadius.circular(12.0),
),
child: Markdown(

View File

@ -10,79 +10,79 @@ environment:
sdk: ">=2.17.5 <3.0.0"
dependencies:
animations: ^2.0.4
animations: ^2.0.7
app_installer: ^1.1.0
collection: ^1.16.0
collection: ^1.17.0
cross_connectivity: ^3.0.5
cr_file_saver: ^0.0.2
cr_file_saver: ^0.0.2+1
device_apps:
git:
url: https://github.com/ponces/flutter_plugin_device_apps
ref: revanced-manager
device_info_plus: ^4.1.2
device_info_plus: ^8.1.0
dynamic_color: ^1.6.3
dio: ^5.0.0
dynamic_color: ^1.5.4
dynamic_themes: ^1.1.0
expandable: ^5.0.1
file_picker:
git:
url: https://github.com/alexmercerind/flutter_file_picker
ref: master
flex_color_scheme: ^6.0.0
flex_color_scheme: ^7.0.1
flutter:
sdk: flutter
flutter_background: ^1.1.0
flutter_background: ^1.2.0
flutter_cache_manager: ^3.3.0
flutter_i18n: ^0.32.4
flutter_local_notifications: ^9.8.0+1
flutter_local_notifications: ^13.0.0
flutter_localizations:
sdk: flutter
flutter_svg: ^1.1.1+1
fluttertoast: ^8.0.9
font_awesome_flutter: ^10.1.0
flutter_svg: ^2.0.4
fluttertoast: ^8.2.1
font_awesome_flutter: ^10.4.0
get_it: ^7.2.0
google_fonts: ^3.0.1
http: ^0.13.4
injectable: ^1.5.3
google_fonts: ^4.0.3
http: ^0.13.5
injectable: ^2.1.1
intl: ^0.17.0
json_annotation: ^4.6.0
json_annotation: ^4.8.0
logcat:
git:
url: https://github.com/SuaMusica/logcat
ref: feature/nullSafe
package_info_plus: ^3.0.3
path_provider: ^2.0.14
permission_handler: ^10.2.0
native_dio_adapter: ^0.1.0
package_info_plus: ^1.4.3+1
path_provider: ^2.0.11
permission_handler: ^10.0.0
pull_to_refresh: ^2.0.0
root:
git:
url: https://github.com/gokul1630/root
ref: main
share_extend: ^2.0.0
shared_preferences: ^2.0.15
shared_preferences: ^2.1.0
skeletons: ^0.0.3
stacked: ^3.2.0
stacked_generator: ^1.0.0
stacked_generator: ^1.1.0
stacked_services: ^1.0.0
stacked_themes: ^0.3.9
timeago: ^3.2.2
timezone: ^0.8.0
url_launcher: ^6.1.5
stacked_themes: ^0.3.10
timeago: ^3.3.0
timezone: ^0.9.0
url_launcher: ^6.1.10
wakelock: ^0.6.2
flutter_dotenv: ^5.0.2
flutter_markdown: ^0.6.14
pub_release: ^8.0.3
flutter_markdown: ^0.6.13
dio_cache_interceptor: ^3.4.0
dev_dependencies:
json_serializable: ^6.3.1
json_serializable: ^6.6.1
build_runner: any
flutter_launcher_icons: ^0.10.0
flutter_launcher_icons: ^0.13.0
flutter_lints: ^2.0.1
flutter_test:
sdk: flutter
injectable_generator: ^1.5.4
injectable_generator: ^2.1.5