add popular manga service , widget , manga home

This commit is contained in:
kodjodevf 2023-04-03 17:13:05 +01:00
parent 92a03e8a5d
commit c90b78831f
24 changed files with 1116 additions and 8 deletions

View file

@ -47,7 +47,7 @@ android {
applicationId "com.kodjodevf.mangayomi"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

View file

@ -0,0 +1,7 @@
class MangaType {
String? lang;
bool? isFullData;
String? source;
MangaType(
{required this.isFullData, required this.lang, required this.source});
}

View file

@ -0,0 +1,60 @@
import 'package:hive/hive.dart';
part 'model_manga.g.dart';
@HiveType(typeId: 0)
class ModelManga extends HiveObject {
@HiveField(0)
String? name;
@HiveField(1)
String? link;
@HiveField(2)
String? imageUrl;
@HiveField(3)
String? description = '';
@HiveField(4)
String? author = '';
@HiveField(5)
String? status = '';
@HiveField(6)
List<String>? genre;
@HiveField(7)
bool favorite = false;
@HiveField(8)
List<String>? chapterTitle;
@HiveField(9)
List<String>? chapterUrl;
@HiveField(10)
List<String>? chapterDate;
@HiveField(11)
String? source;
@HiveField(12)
String? lang;
ModelManga(
{required this.chapterDate,
required this.source,
required this.chapterTitle,
required this.chapterUrl,
required this.author,
required this.favorite,
required this.genre,
required this.imageUrl,
required this.lang,
required this.link,
required this.name,
required this.status,
required this.description});
}

View file

@ -0,0 +1,77 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'model_manga.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ModelMangaAdapter extends TypeAdapter<ModelManga> {
@override
final int typeId = 0;
@override
ModelManga read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ModelManga(
chapterDate: (fields[10] as List?)?.cast<String>(),
source: fields[11] as String?,
chapterTitle: (fields[8] as List?)?.cast<String>(),
chapterUrl: (fields[9] as List?)?.cast<String>(),
author: fields[4] as String?,
favorite: fields[7] as bool,
genre: (fields[6] as List?)?.cast<String>(),
imageUrl: fields[2] as String?,
lang: fields[12] as String?,
link: fields[1] as String?,
name: fields[0] as String?,
status: fields[5] as String?,
description: fields[3] as String?,
);
}
@override
void write(BinaryWriter writer, ModelManga obj) {
writer
..writeByte(13)
..writeByte(0)
..write(obj.name)
..writeByte(1)
..write(obj.link)
..writeByte(2)
..write(obj.imageUrl)
..writeByte(3)
..write(obj.description)
..writeByte(4)
..write(obj.author)
..writeByte(5)
..write(obj.status)
..writeByte(6)
..write(obj.genre)
..writeByte(7)
..write(obj.favorite)
..writeByte(8)
..write(obj.chapterTitle)
..writeByte(9)
..write(obj.chapterUrl)
..writeByte(10)
..write(obj.chapterDate)
..writeByte(11)
..write(obj.source)
..writeByte(12)
..write(obj.lang);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ModelMangaAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View file

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/views/browse/browse_screen.dart';
import 'package:mangayomi/views/general/general_screen.dart';
import 'package:mangayomi/views/history/history_screen.dart';
import 'package:mangayomi/views/library/library_screen.dart';
import 'package:mangayomi/views/manga/home/home.dart';
import 'package:mangayomi/views/more/more_screen.dart';
import 'package:mangayomi/views/updates/updates_screen.dart';
@ -70,6 +72,24 @@ class AsyncRouterNotifier extends ChangeNotifier {
),
),
]),
GoRoute(
path: "/mangaHome",
name: "mangaHome",
builder: (context, state) {
final mangaType = state.extra as MangaType?;
return MangaHomeScreen(
mangaType: mangaType!,
);
},
pageBuilder: (context, state) {
final mangaType = state.extra as MangaType?;
return CustomTransition(
key: state.pageKey,
child: MangaHomeScreen(
mangaType: mangaType!,
),
);
}),
];
}

View file

@ -0,0 +1,150 @@
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_manga_detail.g.dart';
class GetMangaDetailModel {
List<String> genre = [];
List<String> detail = [];
List<String> chapterTitle = [];
List<String> chapterUrl = [];
List<String> chapterDate = [];
String? source;
String? url;
String? name;
String? image;
String? synopsys;
GetMangaDetailModel({
required this.genre,
required this.detail,
required this.chapterDate,
required this.chapterTitle,
required this.chapterUrl,
required this.image,
required this.synopsys,
required this.url,
required this.name,
required this.source,
});
}
@riverpod
Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
{required String image,
required String url,
required String name,
String lang = '',
required String source}) async {
List<String> genre = [];
List<String> detail = [];
List<String> chapterTitle = [];
List<String> chapterUrl = [];
List<String> chapterDate = [];
String? synopsys;
if (source == "mangahere") {
final response = await http.get(Uri.parse("http://www.mangahere.cc$url"),
headers: {
"Referer": "https://www.mangahere.cc/",
"Cookie": "isAdult=1"
});
dom.Document htmll = dom.Document.html(response.body);
if (htmll
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.isNotEmpty) {
final tt = htmll
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
} else {
detail.add("");
}
if (htmll
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.isNotEmpty) {
final tt = htmll
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
} else {
detail.add("");
}
if (htmll
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.isNotEmpty) {
final tt = htmll
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.map((e) => e.text.trim())
.toList();
synopsys = tt.first;
}
if (htmll.querySelectorAll('ul > li > a').isNotEmpty) {
final udl = htmll
.querySelectorAll('ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
for (var ok in udl) {
chapterUrl.add(ok!);
}
}
if (htmll.querySelectorAll('ul > li > a > div > p.title3').isNotEmpty) {
final tt = htmll
.querySelectorAll('ul > li > a > div > p.title3')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
chapterTitle.add(ok);
}
}
if (htmll.querySelectorAll('ul > li > a > div > p.title2').isNotEmpty) {
final tt = htmll
.querySelectorAll('ul > li > a > div > p.title2')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
chapterDate.add(ok);
}
}
if (htmll
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.isNotEmpty) {
final tt = htmll
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.map((e) => e.text.trim())
.toList();
for (var ok in tt) {
genre.add(ok);
}
}
}
return GetMangaDetailModel(
chapterDate: chapterDate,
chapterTitle: chapterTitle,
chapterUrl: chapterUrl,
detail: detail,
genre: genre,
image: image,
synopsys: synopsys,
name: name,
url: url,
source: source);
}

View file

@ -0,0 +1,146 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_manga_detail.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$getMangaDetailHash() => r'f8a4047fc7d1661b6abdfcfa0786e0d78a20c8ed';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
typedef GetMangaDetailRef = AutoDisposeFutureProviderRef<GetMangaDetailModel>;
/// See also [getMangaDetail].
@ProviderFor(getMangaDetail)
const getMangaDetailProvider = GetMangaDetailFamily();
/// See also [getMangaDetail].
class GetMangaDetailFamily extends Family<AsyncValue<GetMangaDetailModel>> {
/// See also [getMangaDetail].
const GetMangaDetailFamily();
/// See also [getMangaDetail].
GetMangaDetailProvider call({
required String image,
required String url,
required String name,
String lang = '',
required String source,
}) {
return GetMangaDetailProvider(
image: image,
url: url,
name: name,
lang: lang,
source: source,
);
}
@override
GetMangaDetailProvider getProviderOverride(
covariant GetMangaDetailProvider provider,
) {
return call(
image: provider.image,
url: provider.url,
name: provider.name,
lang: provider.lang,
source: provider.source,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'getMangaDetailProvider';
}
/// See also [getMangaDetail].
class GetMangaDetailProvider
extends AutoDisposeFutureProvider<GetMangaDetailModel> {
/// See also [getMangaDetail].
GetMangaDetailProvider({
required this.image,
required this.url,
required this.name,
this.lang = '',
required this.source,
}) : super.internal(
(ref) => getMangaDetail(
ref,
image: image,
url: url,
name: name,
lang: lang,
source: source,
),
from: getMangaDetailProvider,
name: r'getMangaDetailProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$getMangaDetailHash,
dependencies: GetMangaDetailFamily._dependencies,
allTransitiveDependencies:
GetMangaDetailFamily._allTransitiveDependencies,
);
final String image;
final String url;
final String name;
final String lang;
final String source;
@override
bool operator ==(Object other) {
return other is GetMangaDetailProvider &&
other.image == image &&
other.url == url &&
other.name == name &&
other.lang == lang &&
other.source == source;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, image.hashCode);
hash = _SystemHash.combine(hash, url.hashCode);
hash = _SystemHash.combine(hash, name.hashCode);
hash = _SystemHash.combine(hash, lang.hashCode);
hash = _SystemHash.combine(hash, source.hashCode);
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

View file

@ -0,0 +1,59 @@
import 'dart:async';
import 'package:mangayomi/services/http_res_to_dom_html.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'get_popular_manga.g.dart';
class GetMangaModel {
late List<String?> url;
late List<String?> name;
late List<String?> image;
GetMangaModel({
required this.name,
required this.url,
required this.image,
});
}
@riverpod
Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
{required String source, required int page}) async {
List<String?> url = [];
List<String?> name = [];
List<String?> image = [];
//mangahere
if (source == "mangahere") {
final dom = await httpResToDom(url: 'https://www.mangahere.cc/ranking/');
if (dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a')
.isNotEmpty) {
url = dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
.toList();
image = dom
.querySelectorAll(
' body > div.container.weekrank.ranking > div > div > ul > li > a > img')
.where((e) => e.attributes.containsKey('src'))
.where((e) => e.attributes['src']!.contains("cover"))
.map((e) => e.attributes['src'])
.toList();
name = dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a ')
.where((e) => e.attributes.containsKey('title'))
.map((e) => e.attributes['title'])
.toList();
}
}
return GetMangaModel(
name: name,
url: url,
image: image,
);
}

View file

@ -0,0 +1,121 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_popular_manga.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$getPopularMangaHash() => r'4ab45f760fe457710bb9d09c72faef2fc09261e2';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
typedef GetPopularMangaRef = AutoDisposeFutureProviderRef<GetMangaModel>;
/// See also [getPopularManga].
@ProviderFor(getPopularManga)
const getPopularMangaProvider = GetPopularMangaFamily();
/// See also [getPopularManga].
class GetPopularMangaFamily extends Family<AsyncValue<GetMangaModel>> {
/// See also [getPopularManga].
const GetPopularMangaFamily();
/// See also [getPopularManga].
GetPopularMangaProvider call({
required String source,
required int page,
}) {
return GetPopularMangaProvider(
source: source,
page: page,
);
}
@override
GetPopularMangaProvider getProviderOverride(
covariant GetPopularMangaProvider provider,
) {
return call(
source: provider.source,
page: provider.page,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'getPopularMangaProvider';
}
/// See also [getPopularManga].
class GetPopularMangaProvider extends AutoDisposeFutureProvider<GetMangaModel> {
/// See also [getPopularManga].
GetPopularMangaProvider({
required this.source,
required this.page,
}) : super.internal(
(ref) => getPopularManga(
ref,
source: source,
page: page,
),
from: getPopularMangaProvider,
name: r'getPopularMangaProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$getPopularMangaHash,
dependencies: GetPopularMangaFamily._dependencies,
allTransitiveDependencies:
GetPopularMangaFamily._allTransitiveDependencies,
);
final String source;
final int page;
@override
bool operator ==(Object other) {
return other is GetPopularMangaProvider &&
other.source == source &&
other.page == page;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, source.hashCode);
hash = _SystemHash.combine(hash, page.hashCode);
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

View file

@ -0,0 +1,7 @@
import 'package:html/dom.dart';
import 'package:http/http.dart' as http;
Future<Document> httpResToDom({required String url}) async {
final response = await http.get(Uri.parse(url));
return Document.html(response.body);
}

View file

@ -0,0 +1,33 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
Widget cachedNetworkImage(
{Map<String, String>? headers,
required String imageUrl,
required double? width,
required double? height,
required BoxFit? fit,
AlignmentGeometry? alignment}) {
if (kIsWeb) {
return Image.network(
imageUrl,
width: width,
height: height,
fit: fit,
alignment: alignment ?? Alignment.center,
);
} else {
return CachedNetworkImage(
httpHeaders: headers ?? {},
imageUrl: imageUrl,
fit: fit,
width: width,
height: height,
errorWidget: (context, url, error) => const Icon(
Icons.error,
size: 50,
),
);
}
}

View file

@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/models/manga_type.dart';
class SourcesScreen extends StatefulWidget {
const SourcesScreen({super.key});
@ -13,7 +15,11 @@ class _SourcesScreenState extends State<SourcesScreen> {
return Column(
children: [
ListTile(
onTap: () {},
onTap: () {
context.push('/mangaHome',
extra: MangaType(
isFullData: true, lang: 'en', source: 'mangahere'));
},
leading: Container(
height: 37,
width: 37,
@ -24,7 +30,8 @@ class _SourcesScreenState extends State<SourcesScreen> {
title: const Text('MangaHere'),
trailing: SizedBox(
width: 110,
child: Row(mainAxisAlignment: MainAxisAlignment.spaceAround,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Text(
"Latest",

View file

@ -0,0 +1,183 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/services/get_popular_manga.dart';
import 'package:mangayomi/views/widgets/bottom_text_widget.dart';
import 'package:mangayomi/views/widgets/cover_view_widget.dart';
import 'package:mangayomi/views/widgets/gridview_widget.dart';
import 'package:mangayomi/views/widgets/manga_image_card_widget.dart';
class MangaHomeScreen extends ConsumerStatefulWidget {
final MangaType mangaType;
const MangaHomeScreen({required this.mangaType, super.key});
@override
ConsumerState<MangaHomeScreen> createState() => _MangaHomeScreenState();
}
class _MangaHomeScreenState extends ConsumerState<MangaHomeScreen> {
bool _isLoading = false;
final ScrollController _scrollController = ScrollController();
int _length = 10;
int _page = 1;
@override
Widget build(BuildContext context) {
final getManga = ref.watch(
getPopularMangaProvider(source: widget.mangaType.source!, page: _page));
return Scaffold(
appBar: AppBar(
title: Text('${widget.mangaType.source}'),
actions: [],
),
body: getManga.when(
data: (data) {
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (!_isLoading) {
if (mounted) {
setState(() {
_isLoading = true;
});
}
if (widget.mangaType.isFullData!) {
Future.delayed(const Duration(seconds: 1)).then((value) {
_length = _length + 10;
if (mounted) {
setState(() {
_isLoading = false;
});
}
});
} else {
if (mounted) {
setState(() {
_page = _page + 1;
});
}
ref
.watch(getPopularMangaProvider(
source: widget.mangaType.source!, page: _page)
.future)
.then(
(value) {
if (mounted) {
setState(() {
data.url.addAll(value.url);
data.name.addAll(value.name);
data.image.addAll(value.image);
_isLoading = false;
});
}
},
);
}
}
}
});
return Column(
children: [
Flexible(
child: GridViewWidget(
controller: _scrollController,
itemCount:
widget.mangaType.isFullData! ? _length : data.url.length,
itemBuilder: (context, index) {
if (index == data.url.length - 1) {
return _buildProgressIndicator();
}
return MangaHomeImageCard(
url: data.url[index]!,
name: data.name[index]!,
image: data.image[index]!,
source: widget.mangaType.source!,
lang: widget.mangaType.lang!,
);
},
)),
],
);
},
error: (error, stackTrace) => const Center(child: Text("Error")),
loading: () => const Center(
child: CircularProgressIndicator(),
),
));
}
Widget _buildProgressIndicator() {
return _isLoading
? const Center(
child: SizedBox(
height: 100,
width: 200,
child: Center(
child: CircularProgressIndicator(),
),
),
)
: Container();
}
}
class MangaHomeImageCard extends ConsumerStatefulWidget {
final String image;
final String url;
final String name;
final String source;
final String lang;
const MangaHomeImageCard({
super.key,
required this.url,
required this.name,
required this.image,
required this.source,
required this.lang,
});
@override
ConsumerState<MangaHomeImageCard> createState() => _MangaHomeImageCardState();
}
class _MangaHomeImageCardState extends ConsumerState<MangaHomeImageCard>
with AutomaticKeepAliveClientMixin<MangaHomeImageCard> {
@override
Widget build(BuildContext context) {
super.build(context);
final getMangaDetail = ref.watch(getMangaDetailProvider(
source: widget.source,
image: widget.image,
name: widget.name,
url: widget.url,
));
return getMangaDetail.when(
data: (data) {
return MangaImageCardWidget(
getMangaDetailModel: data,
lang: widget.lang,
);
},
loading: () => CoverViewWidget(children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
color: Theme.of(context).cardColor,
width: 200,
height: 270,
),
),
BottomTextWidget(text: widget.name)
]),
error: (error, stackTrace) => const Center(child: Text("Error")),
);
}
@override
bool get wantKeepAlive => true;
}

View file

@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
class BottomTextWidget extends StatelessWidget {
final String text;
const BottomTextWidget({super.key, required this.text});
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3), //New
offset: const Offset(0.5, 0.9),
blurRadius: 3.0)
],
),
child: Text(
text,
style: const TextStyle(
fontSize: 13.0,
color: Colors.white,
shadows: <Shadow>[
Shadow(offset: Offset(0.5, 0.9), blurRadius: 3.0)
],
),
textAlign: TextAlign.center,
),
),
);
}
}

View file

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class CoverViewWidget extends StatelessWidget {
final List<Widget> children;
const CoverViewWidget({super.key, required this.children});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Stack(
children: children,
),
),
);
}
}

View file

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
class GridViewWidget extends StatelessWidget {
final ScrollController? controller;
final int? itemCount;
final Widget? Function(BuildContext, int) itemBuilder;
const GridViewWidget(
{super.key,
this.controller,
required this.itemCount,
required this.itemBuilder});
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: GridView.builder(
controller: controller,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 220,
mainAxisExtent: 280,
),
itemCount: itemCount,
itemBuilder: itemBuilder),
);
}
}

View file

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/views/widgets/bottom_text_widget.dart';
import 'package:mangayomi/views/widgets/cover_view_widget.dart';
class MangaImageCardWidget extends ConsumerStatefulWidget {
final String lang;
final bool isLoading;
final GetMangaDetailModel? getMangaDetailModel;
const MangaImageCardWidget({
required this.lang,
super.key,
this.isLoading = false,
required this.getMangaDetailModel,
});
@override
ConsumerState<MangaImageCardWidget> createState() =>
_MangaImageCardWidgetState();
}
class _MangaImageCardWidgetState extends ConsumerState<MangaImageCardWidget> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () async {},
child: CoverViewWidget(children: [
cachedNetworkImage(
headers: {
"Referer": "https://www.mangahere.cc/",
"Cookie": "isAdult=1"
},
imageUrl: widget.getMangaDetailModel!.image!,
width: 200,
height: 270,
fit: BoxFit.cover),
BottomTextWidget(text: widget.getMangaDetailModel!.name!)
]),
);
}
}

View file

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
#include <flutter_js/flutter_js_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_js_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterJsPlugin");
flutter_js_plugin_register_with_registrar(flutter_js_registrar);
}

View file

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_js
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View file

@ -5,10 +5,12 @@
import FlutterMacOS
import Foundation
import flutter_js
import path_provider_foundation
import sqflite
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterJsPlugin.register(with: registry.registrar(forPlugin: "FlutterJsPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
}

View file

@ -5,18 +5,26 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: a36ec4843dc30ea6bf652bf25e3448db6c5e8bcf4aa55f063a5d1dad216d8214
sha256: "98d1d33ed129b372846e862de23a0fc365745f4d7b5e786ce667fcbbb7ac5c07"
url: "https://pub.dev"
source: hosted
version: "58.0.0"
version: "55.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: cc4242565347e98424ce9945c819c192ec0838cb9d1f6aa4a97cc96becbc5b27
sha256: "881348aed9b0b425882c97732629a6a31093c8ff20fc4b3b03fb9d3d50a3a126"
url: "https://pub.dev"
source: hosted
version: "5.10.0"
version: "5.7.1"
analyzer_plugin:
dependency: transitive
description:
name: analyzer_plugin
sha256: c1d5f167683de03d5ab6c3b53fc9aeefc5d59476e7810ba7bbddff50c6f4392d
url: "https://pub.dev"
source: hosted
version: "0.11.2"
args:
dependency: transitive
description:
@ -145,6 +153,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.2"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: "66f86e916d285c1a93d3b79587d94bd71984a66aac4ff74e524cfa7877f1395c"
url: "https://pub.dev"
source: hosted
version: "0.3.5"
clock:
dependency: transitive
description:
@ -185,6 +201,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
csslib:
dependency: transitive
description:
name: csslib
sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
url: "https://pub.dev"
source: hosted
version: "0.17.2"
cupertino_icons:
dependency: "direct main"
description:
@ -193,6 +217,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
custom_lint:
dependency: transitive
description:
name: custom_lint
sha256: "32648ef4f1dda618d98a22bc958fa79d479895891061e63338bec510b67e821a"
url: "https://pub.dev"
source: hosted
version: "0.3.2"
custom_lint_core:
dependency: transitive
description:
name: custom_lint_core
sha256: cc20ce83432675abcc109b766aad2e420946eaeecc32ffd34bada389cdaa9a74
url: "https://pub.dev"
source: hosted
version: "0.3.2"
dart_style:
dependency: transitive
description:
@ -254,6 +294,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.3.0"
flutter_js:
dependency: "direct main"
description:
name: flutter_js
sha256: "86922b593f80512dfe72586aab826123708992ecd07476349828e9893d7cbaff"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_lints:
dependency: "direct dev"
description:
@ -280,6 +328,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
freezed_annotation:
dependency: transitive
description:
name: freezed_annotation
sha256: aeac15850ef1b38ee368d4c53ba9a847e900bb2c53a4db3f6881cbb3cb684338
url: "https://pub.dev"
source: hosted
version: "2.2.0"
frontend_server_client:
dependency: transitive
description:
@ -344,8 +400,16 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
html:
dependency: "direct main"
description:
name: html
sha256: "79d498e6d6761925a34ee5ea8fa6dfef38607781d2fa91e37523474282af55cb"
url: "https://pub.dev"
source: hosted
version: "0.15.2"
http:
dependency: transitive
dependency: "direct main"
description:
name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
@ -576,6 +640,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
riverpod_analyzer_utils:
dependency: transitive
description:
name: riverpod_analyzer_utils
sha256: "7c2d4de69ba06107c3d7f1b3f43dc3fbdcb2f666b480560af654b4eb89af0d6d"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
riverpod_annotation:
dependency: "direct main"
description:
name: riverpod_annotation
sha256: c0f51b3fc5a0cefcbcddb35a10ad542d6c38919c081a25279045158ac7955cfb
url: "https://pub.dev"
source: hosted
version: "2.0.2"
riverpod_generator:
dependency: "direct dev"
description:
name: riverpod_generator
sha256: "2c08a6fbbe80d489f1c5208e5358bfdd4d612f1777c47fdb9ff91bb7a2670529"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
rxdart:
dependency: transitive
description:
@ -685,6 +773,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
sync_http:
dependency: transitive
description:
name: sync_http
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
synchronized:
dependency: transitive
description:

View file

@ -37,6 +37,10 @@ dependencies:
hive_flutter: ^1.1.0
hive_generator: ^2.0.0
hive: ^2.2.3
http: ^0.13.5
riverpod_annotation: ^2.0.2
html: ^0.15.2
flutter_js: ^0.6.0
# The following adds the Cupertino Icons font to your application.
@ -47,6 +51,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.3.3
riverpod_generator: ^2.1.4
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is

View file

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h"
#include <flutter_js/flutter_js_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterJsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterJsPlugin"));
}

View file

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
flutter_js
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST