[Comic Source] New model PageJumpTarget. All page jump operations now use PageJumpTarget.

This commit is contained in:
2025-04-04 22:47:43 +08:00
parent 971fc1da92
commit 463ad5b5bc
12 changed files with 266 additions and 226 deletions

View File

@@ -13,7 +13,7 @@ export "widget_utils.dart";
export "context.dart"; export "context.dart";
class _App { class _App {
final version = "1.3.5"; final version = "1.4.0";
bool get isAndroid => Platform.isAndroid; bool get isAndroid => Platform.isAndroid;

View File

@@ -34,24 +34,28 @@ class CategoryButtonData {
}); });
} }
class CategoryItem {
final String label;
final PageJumpTarget target;
const CategoryItem(this.label, this.target);
}
abstract class BaseCategoryPart { abstract class BaseCategoryPart {
String get title; String get title;
List<String> get categories; List<CategoryItem> get categories;
List<String>? get categoryParams => null;
bool get enableRandom; bool get enableRandom;
String get categoryType;
/// Data class for building a part of category page. /// Data class for building a part of category page.
const BaseCategoryPart(); const BaseCategoryPart();
} }
class FixedCategoryPart extends BaseCategoryPart { class FixedCategoryPart extends BaseCategoryPart {
@override @override
final List<String> categories; final List<CategoryItem> categories;
@override @override
bool get enableRandom => false; bool get enableRandom => false;
@@ -59,19 +63,12 @@ class FixedCategoryPart extends BaseCategoryPart {
@override @override
final String title; final String title;
@override
final String categoryType;
@override
final List<String>? categoryParams;
/// A [BaseCategoryPart] that show fixed tags on category page. /// A [BaseCategoryPart] that show fixed tags on category page.
const FixedCategoryPart(this.title, this.categories, this.categoryType, const FixedCategoryPart(this.title, this.categories);
[this.categoryParams]);
} }
class RandomCategoryPart extends BaseCategoryPart { class RandomCategoryPart extends BaseCategoryPart {
final List<String> tags; final List<CategoryItem> all;
final int randomNumber; final int randomNumber;
@@ -81,67 +78,23 @@ class RandomCategoryPart extends BaseCategoryPart {
@override @override
bool get enableRandom => true; bool get enableRandom => true;
@override List<CategoryItem> _categories() {
final String categoryType; if (randomNumber >= all.length) {
return all;
List<String> _categories() {
if (randomNumber >= tags.length) {
return tags;
} }
var start = math.Random().nextInt(tags.length - randomNumber); var start = math.Random().nextInt(all.length - randomNumber);
return tags.sublist(start, start + randomNumber); return all.sublist(start, start + randomNumber);
} }
@override @override
List<String> get categories => _categories(); List<CategoryItem> get categories => _categories();
/// A [BaseCategoryPart] that show random tags on category page. /// A [BaseCategoryPart] that show a part of random tags on category page.
const RandomCategoryPart( const RandomCategoryPart(
this.title, this.tags, this.randomNumber, this.categoryType); this.title,
} this.all,
this.randomNumber,
class RandomCategoryPartWithRuntimeData extends BaseCategoryPart { );
final Iterable<String> Function() loadTags;
final int randomNumber;
@override
final String title;
@override
bool get enableRandom => true;
@override
final String categoryType;
static final random = math.Random();
List<String> _categories() {
var tags = loadTags();
if (randomNumber >= tags.length) {
return tags.toList();
}
final start = random.nextInt(tags.length - randomNumber);
var res = List.filled(randomNumber, '');
int index = -1;
for (var s in tags) {
index++;
if (start > index) {
continue;
} else if (index == start + randomNumber) {
break;
}
res[index - start] = s;
}
return res;
}
@override
List<String> get categories => _categories();
/// A [BaseCategoryPart] that show random tags on category page.
RandomCategoryPartWithRuntimeData(
this.title, this.loadTags, this.randomNumber, this.categoryType);
} }
CategoryData getCategoryDataWithKey(String key) { CategoryData getCategoryDataWithKey(String key) {

View File

@@ -11,6 +11,8 @@ import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/comic_type.dart'; import 'package:venera/foundation/comic_type.dart';
import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/history.dart';
import 'package:venera/foundation/res.dart'; import 'package:venera/foundation/res.dart';
import 'package:venera/pages/category_comics_page.dart';
import 'package:venera/pages/search_result_page.dart';
import 'package:venera/utils/data_sync.dart'; import 'package:venera/utils/data_sync.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:venera/utils/init.dart'; import 'package:venera/utils/init.dart';
@@ -349,7 +351,7 @@ class ExplorePagePart {
/// - category:categoryName /// - category:categoryName
/// ///
/// End with `@`+`param` if the category has a parameter. /// End with `@`+`param` if the category has a parameter.
final String? viewMore; final PageJumpTarget? viewMore;
const ExplorePagePart(this.title, this.comics, this.viewMore); const ExplorePagePart(this.title, this.comics, this.viewMore);
} }

View File

@@ -430,3 +430,110 @@ class ComicChapters {
} }
} }
} }
class PageJumpTarget {
final String sourceKey;
final String page;
final Map<String, dynamic>? attributes;
const PageJumpTarget(this.sourceKey, this.page, this.attributes);
static PageJumpTarget parse(String sourceKey, dynamic value) {
if (value is Map) {
if (value['page'] != null) {
return PageJumpTarget(
sourceKey,
value["page"] ?? "search",
value["attributes"],
);
} else if (value["action"] != null) {
// old version `onClickTag`
var page = value["action"];
if (page == "search") {
return PageJumpTarget(
sourceKey,
"search",
{
"text": value["keyword"],
},
);
} else if (page == "category") {
return PageJumpTarget(
sourceKey,
"category",
{
"category": value["keyword"],
"param": value["param"],
},
);
} else {
return PageJumpTarget(sourceKey, page, null);
}
}
} else if (value is String) {
// old version string encoding. search: `search:keyword`, category: `category:keyword` or `category:keyword@param`
var segments = value.split(":");
var page = segments[0];
if (page == "search") {
return PageJumpTarget(
sourceKey,
"search",
{
"text": segments[1],
},
);
} else if (page == "category") {
var c = segments[1];
if (c.contains('@')) {
var parts = c.split('@');
return PageJumpTarget(
sourceKey,
"category",
{
"category": parts[0],
"param": parts[1],
},
);
} else {
return PageJumpTarget(
sourceKey,
"category",
{
"category": c,
},
);
}
} else {
return PageJumpTarget(sourceKey, page, null);
}
}
return PageJumpTarget(sourceKey, "Invalid Data", null);
}
void jump(BuildContext context) {
if (page == "search") {
context.to(
() => SearchResultPage(
text: attributes?["text"] ?? attributes?["keyword"] ?? "",
sourceKey: sourceKey,
options: List.from(attributes?["options"] ?? []),
),
);
} else if (page == "category") {
var key = ComicSource.find(sourceKey)!.categoryData!.key;
context.to(
() => CategoryComicsPage(
categoryKey: key,
category: attributes?["category"] ??
(throw ArgumentError("Category name is required")),
options: List.from(attributes?["options"] ?? []),
param: attributes?["param"],
),
);
} else {
Log.error("Page Jump", "Unknown page: $page");
}
}
}

View File

@@ -80,9 +80,8 @@ class ComicSourceParser {
Future<ComicSource> parse(String js, String filePath) async { Future<ComicSource> parse(String js, String filePath) async {
js = js.replaceAll("\r\n", "\n"); js = js.replaceAll("\r\n", "\n");
var line1 = js var line1 =
.split('\n') js.split('\n').firstWhereOrNull((e) => e.trim().startsWith("class "));
.firstWhereOrNull((e) => e.trim().startsWith("class "));
if (line1 == null || if (line1 == null ||
!line1.startsWith("class ") || !line1.startsWith("class ") ||
!line1.contains("extends ComicSource")) { !line1.contains("extends ComicSource")) {
@@ -336,7 +335,7 @@ class ComicSourceParser {
(e['comics'] as List).map((e) { (e['comics'] as List).map((e) {
return Comic.fromJson(e, _key!); return Comic.fromJson(e, _key!);
}).toList(), }).toList(),
e['viewMore'], PageJumpTarget.parse(_key!, e['viewMore']),
); );
}), }),
), ),
@@ -404,6 +403,30 @@ class ComicSourceParser {
var categoryParts = <BaseCategoryPart>[]; var categoryParts = <BaseCategoryPart>[];
for (var c in doc["parts"]) { for (var c in doc["parts"]) {
if (c["categories"] is! List || c["categories"].isEmpty) {
continue;
}
List categories = c["categories"];
if (categories[0] is Map) {
// new format
final String name = c["name"];
final String type = c["type"];
final cs = categories
.map(
(e) => CategoryItem(
e['label'],
PageJumpTarget.parse(_key!, e['target']),
),
)
.toList();
if (type == "fixed") {
categoryParts.add(FixedCategoryPart(name, cs));
} else if (type == "random") {
categoryParts
.add(RandomCategoryPart(name, cs, c["randomNumber"] ?? 1));
}
} else {
// old format
final String name = c["name"]; final String name = c["name"];
final String type = c["type"]; final String type = c["type"];
final List<String> tags = List.from(c["categories"]); final List<String> tags = List.from(c["categories"]);
@@ -413,12 +436,45 @@ class ComicSourceParser {
if (groupParam != null) { if (groupParam != null) {
categoryParams = List.filled(tags.length, groupParam); categoryParams = List.filled(tags.length, groupParam);
} }
var cs = <CategoryItem>[];
for (int i = 0; i < tags.length; i++) {
PageJumpTarget target;
if (itemType == 'category') {
target = PageJumpTarget(
_key!,
'category',
{
"category": tags[i],
"param": categoryParams?.elementAtOrNull(i),
},
);
} else if (itemType == 'search') {
target = PageJumpTarget(
_key!,
'search',
{
"keyword": tags[i],
},
);
} else if (itemType == 'search_with_namespace') {
target = PageJumpTarget(
_key!,
'search',
{
"keyword": "$name:$tags[i]",
},
);
} else {
target = PageJumpTarget(_key!, itemType, null);
}
cs.add(CategoryItem(tags[i], target));
}
if (type == "fixed") { if (type == "fixed") {
categoryParts categoryParts.add(FixedCategoryPart(name, cs));
.add(FixedCategoryPart(name, tags, itemType, categoryParams));
} else if (type == "random") { } else if (type == "random") {
categoryParts.add( categoryParts
RandomCategoryPart(name, tags, c["randomNumber"] ?? 1, itemType)); .add(RandomCategoryPart(name, cs, c["randomNumber"] ?? 1));
}
} }
} }
@@ -620,7 +676,8 @@ class ComicSourceParser {
final bool multiFolder = _getValue("favorites.multiFolder"); final bool multiFolder = _getValue("favorites.multiFolder");
final bool? isOldToNewSort = _getValue("favorites.isOldToNewSort"); final bool? isOldToNewSort = _getValue("favorites.isOldToNewSort");
final bool? singleFolderForSingleComic = _getValue("favorites.singleFolderForSingleComic"); final bool? singleFolderForSingleComic =
_getValue("favorites.singleFolderForSingleComic");
Future<Res<T>> retryZone<T>(Future<Res<T>> Function() func) async { Future<Res<T>> retryZone<T>(Future<Res<T>> Function() func) async {
if (!ComicSource.find(_key!)!.isLogged) { if (!ComicSource.find(_key!)!.isLogged) {

View File

@@ -4,12 +4,10 @@ import 'package:venera/foundation/app.dart';
import 'package:venera/foundation/appdata.dart'; import 'package:venera/foundation/appdata.dart';
import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/pages/ranking_page.dart'; import 'package:venera/pages/ranking_page.dart';
import 'package:venera/pages/search_result_page.dart';
import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/pages/settings/settings_page.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';
import 'category_comics_page.dart';
import 'comic_source_page.dart'; import 'comic_source_page.dart';
class CategoriesPage extends StatefulWidget { class CategoriesPage extends StatefulWidget {
@@ -147,43 +145,6 @@ class _CategoryPage extends StatelessWidget {
return ""; return "";
} }
void handleClick(
String tag,
String? param,
String type,
String namespace,
String categoryKey,
) {
if (type == 'search') {
App.mainNavigatorKey?.currentContext?.to(
() => SearchResultPage(
text: tag,
options: const [],
sourceKey: findComicSourceKey(),
),
);
} else if (type == "search_with_namespace") {
if (tag.contains(" ")) {
tag = '"$tag"';
}
App.mainNavigatorKey?.currentContext?.to(
() => SearchResultPage(
text: "$namespace:$tag",
options: const [],
sourceKey: findComicSourceKey(),
),
);
} else if (type == "category") {
App.mainNavigatorKey!.currentContext!.to(
() => CategoryComicsPage(
category: tag,
categoryKey: categoryKey,
param: param,
),
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var children = <Widget>[]; var children = <Widget>[];
@@ -194,11 +155,11 @@ class _CategoryPage extends StatelessWidget {
child: Wrap( child: Wrap(
children: [ children: [
if (data.enableRankingPage) if (data.enableRankingPage)
buildTag("Ranking".tl, (p0, p1) { buildTag("Ranking".tl, () {
context.to(() => RankingPage(categoryKey: data.key)); context.to(() => RankingPage(categoryKey: data.key));
}), }),
for (var buttonData in data.buttons) for (var buttonData in data.buttons)
buildTag(buttonData.label.tl, (p0, p1) => buttonData.onTap()) buildTag(buttonData.label.tl, buttonData.onTap)
], ],
), ),
)); ));
@@ -212,36 +173,14 @@ class _CategoryPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
buildTitleWithRefresh(part.title, () => updater(() {})), buildTitleWithRefresh(part.title, () => updater(() {})),
buildTagsWithParams( buildTags(part.categories)
part.categories,
part.categoryParams,
part.title,
(key, param) => handleClick(
key,
param,
part.categoryType,
part.title,
category,
),
)
], ],
); );
})); }));
} else { } else {
children.add(buildTitle(part.title)); children.add(buildTitle(part.title));
children.add( children.add(
buildTagsWithParams( buildTags(part.categories),
part.categories,
part.categoryParams,
part.title,
(tag, param) => handleClick(
tag,
param,
part.categoryType,
part.title,
data.key,
),
),
); );
} }
} }
@@ -280,30 +219,28 @@ class _CategoryPage extends StatelessWidget {
); );
} }
Widget buildTagsWithParams( Widget buildTags(
List<String> tags, List<CategoryItem> categories,
List<String>? params,
String? namespace,
ClickTagCallback onClick,
) { ) {
return Padding( return Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 10, 16), padding: const EdgeInsets.fromLTRB(10, 0, 10, 16),
child: Wrap( child: Wrap(
children: List<Widget>.generate( children: List<Widget>.generate(
tags.length, categories.length,
(index) => buildTag( (index) => buildCategory(categories[index]),
tags[index],
onClick,
namespace,
params?.elementAtOrNull(index),
),
), ),
), ),
); );
} }
Widget buildTag(String tag, ClickTagCallback onClick, Widget buildCategory(CategoryItem c) {
[String? namespace, String? param]) { return buildTag(c.label, () {
var context = App.mainNavigatorKey!.currentContext!;
c.target.jump(context);
});
}
Widget buildTag(String label, VoidCallback onClick) {
return Padding( return Padding(
padding: const EdgeInsets.fromLTRB(8, 6, 8, 6), padding: const EdgeInsets.fromLTRB(8, 6, 8, 6),
child: Builder( child: Builder(
@@ -313,10 +250,10 @@ class _CategoryPage extends StatelessWidget {
color: context.colorScheme.primaryContainer.toOpacity(0.72), color: context.colorScheme.primaryContainer.toOpacity(0.72),
child: InkWell( child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
onTap: () => onClick(tag, param), onTap: onClick,
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8), padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: Text(tag), child: Text(label),
), ),
), ),
); );

View File

@@ -9,6 +9,7 @@ class CategoryComicsPage extends StatefulWidget {
required this.category, required this.category,
this.param, this.param,
required this.categoryKey, required this.categoryKey,
this.options,
super.key, super.key,
}); });
@@ -18,6 +19,8 @@ class CategoryComicsPage extends StatefulWidget {
final String categoryKey; final String categoryKey;
final List<String>? options;
@override @override
State<CategoryComicsPage> createState() => _CategoryComicsPageState(); State<CategoryComicsPage> createState() => _CategoryComicsPageState();
} }
@@ -40,7 +43,16 @@ class _CategoryComicsPageState extends State<CategoryComicsPage> {
} }
return true; return true;
}).toList(); }).toList();
optionsValue = options.map((e) => e.options.keys.first).toList(); var defaultOptionsValue =
options.map((e) => e.options.keys.first).toList();
if (optionsValue.length != options.length) {
var newOptionsValue = List<String>.filled(options.length, "");
for (var i = 0; i < options.length; i++) {
newOptionsValue[i] =
optionsValue.elementAtOrNull(i) ?? defaultOptionsValue[i];
}
optionsValue = newOptionsValue;
}
sourceKey = source.key; sourceKey = source.key;
return; return;
} }
@@ -50,6 +62,11 @@ class _CategoryComicsPageState extends State<CategoryComicsPage> {
@override @override
void initState() { void initState() {
if (widget.options != null) {
optionsValue = widget.options!;
} else {
optionsValue = [];
}
findData(); findData();
super.initState(); super.initState();
} }

View File

@@ -300,21 +300,8 @@ abstract mixin class _ComicPageActions {
'keyword': tag, 'keyword': tag,
}; };
var context = App.mainNavigatorKey!.currentContext!; var context = App.mainNavigatorKey!.currentContext!;
if (config['action'] == 'search') { var target = PageJumpTarget.parse(comicSource.key, config);
context.to(() => SearchResultPage( target.jump(context);
text: config['keyword'] ?? '',
sourceKey: comicSource.key,
options: const [],
));
} else if (config['action'] == 'category') {
context.to(
() => CategoryComicsPage(
category: config['keyword'] ?? '',
categoryKey: comicSource.categoryData!.key,
param: config['param'],
),
);
}
} }
void showMoreActions() { void showMoreActions() {

View File

@@ -461,6 +461,7 @@ void _addAllPagesWithComicSource(ComicSource source) {
var explorePages = appdata.settings['explore_pages']; var explorePages = appdata.settings['explore_pages'];
var categoryPages = appdata.settings['categories']; var categoryPages = appdata.settings['categories'];
var networkFavorites = appdata.settings['favorites']; var networkFavorites = appdata.settings['favorites'];
var searchPages = appdata.settings['searchSources'];
if (source.explorePages.isNotEmpty) { if (source.explorePages.isNotEmpty) {
for (var page in source.explorePages) { for (var page in source.explorePages) {
@@ -477,10 +478,15 @@ void _addAllPagesWithComicSource(ComicSource source) {
!networkFavorites.contains(source.favoriteData!.key)) { !networkFavorites.contains(source.favoriteData!.key)) {
networkFavorites.add(source.favoriteData!.key); networkFavorites.add(source.favoriteData!.key);
} }
if (source.searchPageData != null &&
!searchPages.contains(source.key)) {
searchPages.add(source.key);
}
appdata.settings['explore_pages'] = explorePages.toSet().toList(); appdata.settings['explore_pages'] = explorePages.toSet().toList();
appdata.settings['categories'] = categoryPages.toSet().toList(); appdata.settings['categories'] = categoryPages.toSet().toList();
appdata.settings['favorites'] = networkFavorites.toSet().toList(); appdata.settings['favorites'] = networkFavorites.toSet().toList();
appdata.settings['searchSources'] = searchPages.toSet().toList();
appdata.saveData(); appdata.saveData();
} }

View File

@@ -6,13 +6,10 @@ import 'package:venera/foundation/comic_source/comic_source.dart';
import 'package:venera/foundation/global_state.dart'; import 'package:venera/foundation/global_state.dart';
import 'package:venera/foundation/res.dart'; import 'package:venera/foundation/res.dart';
import 'package:venera/pages/comic_source_page.dart'; import 'package:venera/pages/comic_source_page.dart';
import 'package:venera/pages/search_result_page.dart';
import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/pages/settings/settings_page.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';
import 'category_comics_page.dart';
class ExplorePage extends StatefulWidget { class ExplorePage extends StatefulWidget {
const ExplorePage({super.key}); const ExplorePage({super.key});
@@ -445,30 +442,7 @@ Iterable<Widget> _buildExplorePagePart(
TextButton( TextButton(
onPressed: () { onPressed: () {
var context = App.mainNavigatorKey!.currentContext!; var context = App.mainNavigatorKey!.currentContext!;
if (part.viewMore!.startsWith("search:")) { part.viewMore!.jump(context);
context.to(
() => SearchResultPage(
text: part.viewMore!.replaceFirst("search:", ""),
options: const [],
sourceKey: sourceKey,
),
);
} else if (part.viewMore!.startsWith("category:")) {
var cp = part.viewMore!.replaceFirst("category:", "");
var c = cp.split('@').first;
String? p = cp.split('@').last;
if (p == c) {
p = null;
}
context.to(
() => CategoryComicsPage(
category: c,
categoryKey:
ComicSource.find(sourceKey)!.categoryData!.key,
param: p,
),
);
}
}, },
child: Text("View more".tl), child: Text("View more".tl),
) )

View File

@@ -45,10 +45,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: async name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.13.0" version: "2.12.0"
battery_plus: battery_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -182,10 +182,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.3" version: "1.3.2"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@@ -516,10 +516,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.20.2" version: "0.19.0"
io: io:
dependency: transitive dependency: transitive
description: description:
@@ -540,10 +540,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.9" version: "10.0.8"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
@@ -1029,10 +1029,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.0.0" version: "14.3.1"
web: web:
dependency: transitive dependency: transitive
description: description:

View File

@@ -2,7 +2,7 @@ name: venera
description: "A comic app." description: "A comic app."
publish_to: 'none' publish_to: 'none'
version: 1.3.5+135 version: 1.4.0+140
environment: environment:
sdk: '>=3.6.0 <4.0.0' sdk: '>=3.6.0 <4.0.0'