Compare commits

..

22 Commits

Author SHA1 Message Date
Ushie
8af62b917c build: bump version to 1.3.0 (#892) 2023-05-24 23:33:04 +03:00
Steven
311f114132 chore: update v1.3.0 2023-05-24 21:46:00 +02:00
Ushie
d015bd03f7 chore: merge dev to main (#891) 2023-05-24 22:17:16 +03:00
oSumAtrIX
a61b9de0fa chore(deps): bump revanced-patcher to 9.0.0 2023-05-24 02:02:59 +02:00
Ushie
ef1b283917 chore(deps): bump revanced-patcher to 8.0.0 (#880) 2023-05-24 01:07:22 +05:30
Ushie
c677f00105 build: bump version to v1.2.0 2023-05-20 02:30:18 +03:00
Ushie
8d2f778dfe chore: merge dev to main (#881) 2023-05-20 02:28:58 +03:00
Aunali321
c549d102f6 build: migrate to dart 3 (#871) 2023-05-20 04:07:53 +05:30
Aunali321
39a9ee4e9d chore: remove coreLibraryDesugaring (#873) 2023-05-20 04:05:46 +05:30
Sangam Shrestha
a27dc6ad1c feat: migrate to dart3 2023-05-19 14:23:48 +05:45
kitadai31
8ccb75fc8d chore: remove coreLibraryDesugaring 2023-05-17 15:38:03 +09:00
Ushie
8fc86dbe02 feat: allow selecting installed if app is full apk 2023-05-13 04:54:35 +03:00
Sebok Andras
359f052608 fix: export keystore not working in some conditions (#862) 2023-05-12 19:36:47 +03:00
Hokora Yinphine
4150e2265c fix: use correct version in update download dialog (#859) 2023-05-10 13:23:02 +03:00
Dhruvan Bhalara
b803ce7435 fix: system navigation overlapping UI (#853) 2023-05-10 13:18:51 +03:00
Aunali321
289c6cd7a9 chore: merge dev to main (#857) 2023-05-08 21:46:33 +05:30
Aunali321
31fc7b74c2 build: bump version to v1.1.0 2023-05-08 21:38:19 +05:30
Aunali321
3e565f25be fix(appselector): closing dialog closes app selector 2023-05-08 21:27:07 +05:30
Aunali321
e509be4e21 chore(deps): bump patcher to 7.1.1 2023-05-08 21:19:45 +05:30
Aunali321
170fc537ac fix: fix armv7 dialog shown for x86, x86_64 2023-05-07 04:14:57 +05:30
Aunali321
3fe5882145 feat: remove cronet 2023-05-06 05:39:46 +05:30
Aunali321
a290791410 chore: merge dev to main (#844) 2023-05-06 03:26:14 +05:30
18 changed files with 80 additions and 128 deletions

5
.gitignore vendored
View File

@@ -138,4 +138,7 @@ app.*.map.json
.firebase .firebase
# Dependency directories # Dependency directories
node_modules/ node_modules/
# FVM
.fvm

View File

@@ -98,7 +98,6 @@ linter:
- prefer_const_declarations - prefer_const_declarations
- prefer_const_literals_to_create_immutables - prefer_const_literals_to_create_immutables
- prefer_contains - prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields - prefer_final_fields
- prefer_final_in_for_each - prefer_final_in_for_each
- prefer_final_locals - prefer_final_locals

View File

@@ -30,7 +30,6 @@ android {
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11
} }
@@ -49,7 +48,6 @@ android {
targetSdkVersion 33 targetSdkVersion 33
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
multiDexEnabled true
} }
buildTypes { buildTypes {
@@ -73,21 +71,10 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// ReVanced // ReVanced
implementation "app.revanced:revanced-patcher:7.0.0" implementation "app.revanced:revanced-patcher:9.0.0"
// Signing & aligning // Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70") implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
implementation("com.android.tools.build:apksig:7.2.2") implementation("com.android.tools.build:apksig:7.2.2")
// MicroG cronet
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

@@ -1,5 +1,4 @@
buildscript { buildscript {
ext.cronetVersion = '102.5005.125'
ext.kotlin_version = '1.7.10' ext.kotlin_version = '1.7.10'
repositories { repositories {
google() google()
@@ -32,6 +31,6 @@ subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }
task clean(type: Delete) { tasks.register("clean", Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }

View File

@@ -78,7 +78,7 @@
"errorMessage": "Unable to use selected application", "errorMessage": "Unable to use selected application",
"downloadToast": "Download function is not available yet", "downloadToast": "Download function is not available yet",
"featureNotAvailable": "Feature not implemented", "featureNotAvailable": "Feature not implemented",
"featureNotAvailableText": "This feature has not been added yet for non-root. You'll need to select APK files from storage for now." "featureNotAvailableText": "This application is a split APK and cannot be selected. Unfortunately, this feature is only available for rooted users at the moment. However, you can still install the application by selecting its APK files from your device's storage instead"
}, },
"patchesSelectorView": { "patchesSelectorView": {
"viewTitle": "Select patches", "viewTitle": "Select patches",

View File

@@ -7,13 +7,12 @@ import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:native_dio_adapter/native_dio_adapter.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
@lazySingleton @lazySingleton
class GithubAPI { class GithubAPI {
late Dio _dio = Dio(); late Dio _dio = Dio();
final _cacheOptions = CacheOptions( final _cacheOptions = CacheOptions(
store: MemCacheStore(), store: MemCacheStore(),
maxStale: const Duration(days: 1), maxStale: const Duration(days: 1),
@@ -33,22 +32,6 @@ class GithubAPI {
Future<void> initialize(String repoUrl) async { Future<void> initialize(String repoUrl) async {
try { try {
if (Platform.isIOS || Platform.isMacOS || Platform.isAndroid) {
final CronetEngine androidCronetEngine = await CronetEngine.build(
userAgent: 'ReVanced Manager',
enableBrotli: true,
enableQuic: true,
);
_dio.httpClientAdapter =
NativeAdapter(androidCronetEngine: androidCronetEngine);
_dio = Dio(
BaseOptions(
baseUrl: repoUrl,
),
);
}
_dio = Dio( _dio = Dio(
BaseOptions( BaseOptions(
baseUrl: repoUrl, baseUrl: repoUrl,
@@ -107,7 +90,7 @@ class GithubAPI {
final List<dynamic> commits = response.data; final List<dynamic> commits = response.data;
return commits return commits
.map( .map(
(commit) => (commit['commit']['message']).split('\n')[0] + (commit) => commit['commit']['message'].split('\n')[0] +
' - ' + ' - ' +
commit['commit']['author']['name'] + commit['commit']['author']['name'] +
'\n' as String, '\n' as String,
@@ -126,8 +109,7 @@ class GithubAPI {
String repoName, String repoName,
) async { ) async {
try { try {
final Map<String, dynamic>? release = final Map<String, dynamic>? release = await getLatestRelease(repoName);
await getLatestRelease(repoName);
if (release != null) { if (release != null) {
final Map<String, dynamic>? asset = final Map<String, dynamic>? asset =
(release['assets'] as List<dynamic>).firstWhereOrNull( (release['assets'] as List<dynamic>).firstWhereOrNull(
@@ -166,8 +148,7 @@ class GithubAPI {
Future<String> getLastestReleaseVersion(String repoName) async { Future<String> getLastestReleaseVersion(String repoName) async {
try { try {
final Map<String, dynamic>? release = final Map<String, dynamic>? release = await getLatestRelease(repoName);
await getLatestRelease(repoName);
if (release != null) { if (release != null) {
return release['tag_name']; return release['tag_name'];
} else { } else {

View File

@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
@@ -8,9 +7,7 @@ import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:native_dio_adapter/native_dio_adapter.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/utils/check_for_gms.dart';
import 'package:timeago/timeago.dart'; import 'package:timeago/timeago.dart';
@lazySingleton @lazySingleton
@@ -25,34 +22,12 @@ class RevancedAPI {
Future<void> initialize(String apiUrl) async { Future<void> initialize(String apiUrl) async {
try { try {
final bool isGMSInstalled = await checkForGMS(); _dio = Dio(
BaseOptions(
baseUrl: apiUrl,
),
);
if (!isGMSInstalled) {
_dio = Dio(
BaseOptions(
baseUrl: apiUrl,
),
);
log('ReVanced API: Using default engine + $isGMSInstalled');
} else {
if (Platform.isIOS || Platform.isMacOS || Platform.isAndroid) {
final CronetEngine androidCronetEngine = await CronetEngine.build(
userAgent: 'ReVanced Manager',
enableBrotli: true,
enableQuic: true,
);
_dio.httpClientAdapter =
NativeAdapter(androidCronetEngine: androidCronetEngine);
_dio = Dio(
BaseOptions(
baseUrl: apiUrl,
),
);
}
log('ReVanced API: Using CronetEngine + $isGMSInstalled');
}
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions)); _dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
} on Exception catch (e) { } on Exception catch (e) {
if (kDebugMode) { if (kDebugMode) {

View File

@@ -1,4 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart' hide SearchBar;
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart'; import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
@@ -78,7 +78,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
child: model.noApps child: model.noApps
? Center( ? Center(
child: I18nText( child: I18nText(
'appSelectorView.noApps', 'appSelectorView.noAppsLabel',
child: Text( child: Text(
'', '',
style: TextStyle( style: TextStyle(
@@ -92,7 +92,10 @@ class _AppSelectorViewState extends State<AppSelectorView> {
? const AppSkeletonLoader() ? const AppSkeletonLoader()
: Padding( : Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0) padding: const EdgeInsets.symmetric(horizontal: 12.0)
.copyWith(bottom: 80), .copyWith(
bottom:
MediaQuery.of(context).viewPadding.bottom + 8.0,
),
child: Column( child: Column(
children: [ children: [
...model ...model
@@ -108,16 +111,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
model.getSuggestedVersion( model.getSuggestedVersion(
app.packageName, app.packageName,
), ),
onTap: () { onTap: () => model.canSelectInstalled(context, app.packageName),
model.isRooted
? model.selectApp(app).then(
(_) => Navigator.of(context)
.pop(),
)
: model.showSelectFromStorageDialog(
context,
);
},
), ),
) )
.toList(), .toList(),

View File

@@ -65,6 +65,14 @@ class AppSelectorViewModel extends BaseViewModel {
return _patcherAPI.getSuggestedVersion(packageName); return _patcherAPI.getSuggestedVersion(packageName);
} }
Future<bool> checkSplitApk(String packageName) async {
final app = await DeviceApps.getApp(packageName);
if (app != null) {
return app.isSplit;
}
return true;
}
Future<void> selectApp(ApplicationWithIcon application) async { Future<void> selectApp(ApplicationWithIcon application) async {
locator<PatcherViewModel>().selectedApp = PatchedApplication( locator<PatcherViewModel>().selectedApp = PatchedApplication(
name: application.appName, name: application.appName,
@@ -78,6 +86,22 @@ class AppSelectorViewModel extends BaseViewModel {
locator<PatcherViewModel>().loadLastSelectedPatches(); locator<PatcherViewModel>().loadLastSelectedPatches();
} }
Future<void> canSelectInstalled(
BuildContext context,
String packageName,
) async {
final app =
await DeviceApps.getApp(packageName, true) as ApplicationWithIcon?;
if (app != null) {
if (await checkSplitApk(packageName) && !isRooted) {
return showSelectFromStorageDialog(context);
} else if (!await checkSplitApk(packageName) || isRooted) {
selectApp(app);
Navigator.pop(context);
}
}
}
Future showSelectFromStorageDialog(BuildContext context) async { Future showSelectFromStorageDialog(BuildContext context) async {
return showDialog( return showDialog(
context: context, context: context,
@@ -137,7 +161,6 @@ class AppSelectorViewModel extends BaseViewModel {
isFilled: false, isFilled: false,
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
Navigator.pop(context);
}, },
label: Row( label: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,

View File

@@ -57,6 +57,7 @@ class ContributorsView extends StatelessWidget {
title: 'contributorsView.managerContributors', title: 'contributorsView.managerContributors',
contributors: model.managerContributors, contributors: model.managerContributors,
), ),
SizedBox(height: MediaQuery.of(context).viewPadding.bottom)
], ],
), ),
), ),

View File

@@ -39,13 +39,10 @@ class HomeViewModel extends BaseViewModel {
bool showUpdatableApps = false; bool showUpdatableApps = false;
List<PatchedApplication> patchedInstalledApps = []; List<PatchedApplication> patchedInstalledApps = [];
List<PatchedApplication> patchedUpdatableApps = []; List<PatchedApplication> patchedUpdatableApps = [];
String _managerVersion = ''; String? _latestManagerVersion = '';
Future<void> initialize(BuildContext context) async { Future<void> initialize(BuildContext context) async {
_managerVersion = await AboutInfo.getInfo().then( _latestManagerVersion = await _managerAPI.getLatestManagerVersion();
(value) => value.keys.contains('version') ? value['version']! : '',
);
_managerVersion = await _managerAPI.getCurrentManagerVersion();
await flutterLocalNotificationsPlugin.initialize( await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings( const InitializationSettings(
android: AndroidInitializationSettings('ic_notification'), android: AndroidInitializationSettings('ic_notification'),
@@ -115,7 +112,6 @@ class HomeViewModel extends BaseViewModel {
} }
Future<bool> hasManagerUpdates() async { Future<bool> hasManagerUpdates() async {
final String? latestVersion = await _managerAPI.getLatestManagerVersion();
String currentVersion = await _managerAPI.getCurrentManagerVersion(); String currentVersion = await _managerAPI.getCurrentManagerVersion();
// add v to current version // add v to current version
@@ -123,7 +119,7 @@ class HomeViewModel extends BaseViewModel {
currentVersion = 'v$currentVersion'; currentVersion = 'v$currentVersion';
} }
if (latestVersion != currentVersion) { if (_latestManagerVersion != currentVersion) {
return true; return true;
} }
return false; return false;
@@ -194,7 +190,7 @@ class HomeViewModel extends BaseViewModel {
), ),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
Text( Text(
'v$_managerVersion', 'v$_latestManagerVersion',
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

View File

@@ -20,6 +20,7 @@ class InstallerView extends StatelessWidget {
builder: (context, model, child) => WillPopScope( builder: (context, model, child) => WillPopScope(
child: SafeArea( child: SafeArea(
top: false, top: false,
bottom: false,
child: Scaffold( child: Scaffold(
body: CustomScrollView( body: CustomScrollView(
controller: model.scrollController, controller: model.scrollController,
@@ -153,6 +154,11 @@ class InstallerView extends StatelessWidget {
), ),
), ),
), ),
SliverFillRemaining(
hasScrollBody: false,
child: SizedBox(
height: MediaQuery.of(context).viewPadding.bottom),
),
], ],
), ),
), ),

View File

@@ -86,13 +86,11 @@ class PatcherViewModel extends BaseViewModel {
} }
Future<void> showArmv7WarningDialog(BuildContext context) async { Future<void> showArmv7WarningDialog(BuildContext context) async {
final bool armv7 = await AboutInfo.getInfo().then( final bool armv7 = await AboutInfo.getInfo().then((info) {
(info) => final List<String> archs = info['arch'];
info['arch'] != null && final supportedAbis = ['arm64-v8a', 'x86', 'x86_64'];
info['arch']!.contains('armeabi-v7a') && return !archs.any((arch) => supportedAbis.contains(arch));
!info['arch']!.contains('arm64-v8a'), });
);
if (context.mounted && armv7) { if (context.mounted && armv7) {
return showDialog( return showDialog(
context: context, context: context,

View File

@@ -1,4 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart' hide SearchBar;
import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/patchesSelectorView/patch_item.dart'; import 'package:revanced_manager/ui/widgets/patchesSelectorView/patch_item.dart';
@@ -129,8 +129,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
), ),
) )
: Padding( : Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0) padding:
.copyWith(bottom: 80), const EdgeInsets.symmetric(horizontal: 12.0).copyWith(
bottom: MediaQuery.of(context).viewPadding.bottom + 8.0,
),
child: Column( child: Column(
children: [ children: [
Row( Row(

View File

@@ -33,8 +33,8 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( const Row(
children: const <Widget>[ children: <Widget>[
Text('ReVanced Manager'), Text('ReVanced Manager'),
], ],
), ),
@@ -82,8 +82,8 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( const Row(
children: const <Widget>[ children: <Widget>[
Text('ReVanced Patches'), Text('ReVanced Patches'),
], ],
), ),

View File

@@ -39,9 +39,9 @@ class SocialMediaWidget extends StatelessWidget {
), ),
expanded: Padding( expanded: Padding(
padding: padding ?? EdgeInsets.zero, padding: padding ?? EdgeInsets.zero,
child: CustomCard( child: const CustomCard(
child: Column( child: Column(
children: const <Widget>[ children: <Widget>[
SocialMediaItem( SocialMediaItem(
icon: FaIcon(FontAwesomeIcons.github), icon: FaIcon(FontAwesomeIcons.github),
title: Text('GitHub'), title: Text('GitHub'),

View File

@@ -1,10 +0,0 @@
// Check for google mobile services on device
import 'package:device_apps/device_apps.dart';
Future<bool> checkForGMS() async {
bool isGMSInstalled = true;
isGMSInstalled = await DeviceApps.isAppInstalled('com.google.android.gms') ||
await DeviceApps.isAppInstalled('com.android.vending');
return isGMSInstalled;
}

View File

@@ -4,10 +4,10 @@ homepage: https://github.com/revanced/revanced-manager
publish_to: 'none' publish_to: 'none'
version: 1.0.0+100000000 version: 1.3.0+100300000
environment: environment:
sdk: ">=2.17.5 <3.0.0" sdk: '>=3.0.0 <4.0.0'
dependencies: dependencies:
animations: ^2.0.7 animations: ^2.0.7
@@ -16,8 +16,8 @@ dependencies:
cross_connectivity: ^3.0.5 cross_connectivity: ^3.0.5
cr_file_saver: cr_file_saver:
git: git:
url: https://github.com/dhruvanbhalara/cr_file_saver.git url: https://github.com/dhruvanbhalara/cr_file_saver
ref: a08326ecb48f581b4b09e2e2665d31ed1704c7af ref: "fix/incorrect_file_name"
device_apps: device_apps:
git: git:
url: https://github.com/ponces/flutter_plugin_device_apps url: https://github.com/ponces/flutter_plugin_device_apps
@@ -36,7 +36,7 @@ dependencies:
sdk: flutter sdk: flutter
flutter_background: ^1.2.0 flutter_background: ^1.2.0
flutter_cache_manager: ^3.3.0 flutter_cache_manager: ^3.3.0
flutter_i18n: ^0.32.4 flutter_i18n: ^0.33.0
flutter_local_notifications: ^13.0.0 flutter_local_notifications: ^13.0.0
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
@@ -47,7 +47,7 @@ dependencies:
google_fonts: ^4.0.3 google_fonts: ^4.0.3
http: ^0.13.5 http: ^0.13.5
injectable: ^2.1.1 injectable: ^2.1.1
intl: ^0.17.0 intl: ^0.18.0
json_annotation: ^4.8.0 json_annotation: ^4.8.0
logcat: logcat:
git: git:
@@ -56,7 +56,6 @@ dependencies:
package_info_plus: ^3.0.3 package_info_plus: ^3.0.3
path_provider: ^2.0.14 path_provider: ^2.0.14
permission_handler: ^10.2.0 permission_handler: ^10.2.0
native_dio_adapter: ^0.1.0
pull_to_refresh: ^2.0.0 pull_to_refresh: ^2.0.0
root: root:
git: git:
@@ -75,7 +74,6 @@ dependencies:
wakelock: ^0.6.2 wakelock: ^0.6.2
flutter_dotenv: ^5.0.2 flutter_dotenv: ^5.0.2
flutter_markdown: ^0.6.14 flutter_markdown: ^0.6.14
pub_release: ^8.0.3
dio_cache_interceptor: ^3.4.0 dio_cache_interceptor: ^3.4.0
dev_dependencies: dev_dependencies: