add manga reader detail view

This commit is contained in:
kodjodevf 2023-04-03 20:11:17 +01:00
parent 2b05a3ab52
commit f9976f0148
17 changed files with 731 additions and 68 deletions

4
lib/constant.dart Normal file
View file

@ -0,0 +1,4 @@
class HiveConstant {
static String get hiveBoxManga => "manga_box";
static String get hiveBoxMangaInfo => "manga_box_info";
}

View file

@ -1,8 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/constant.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/router/router.dart';
void main() {
void main() async {
await Hive.initFlutter();
Hive.registerAdapter(ModelMangaAdapter());
await Hive.openBox<ModelManga>(
HiveConstant.hiveBoxManga,
); await Hive.openBox(
HiveConstant.hiveBoxMangaInfo,
);
runApp(const ProviderScope(child: MyApp()));
}

View file

@ -0,0 +1,12 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/constant.dart';
import 'package:mangayomi/models/model_manga.dart';
final hiveBoxManga = Provider<Box<ModelManga>>((ref) {
return Hive.box<ModelManga>(HiveConstant.hiveBoxManga);
});
final hiveBoxMangaInfo = Provider<Box>((ref) {
return Hive.box(HiveConstant.hiveBoxMangaInfo);
});

View file

@ -2,10 +2,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/models/model_manga.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/detail/manga_reader_detail.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';
@ -90,6 +92,28 @@ class AsyncRouterNotifier extends ChangeNotifier {
),
);
}),
GoRoute(
path: '/manga-reader/detail',
builder: (context, state) {
ModelManga? model;
model = state.extra as ModelManga;
return MangaReaderDetail(
modelManga: model,
);
},
pageBuilder: (context, state) {
ModelManga? model;
model = state.extra as ModelManga;
return CustomTransition(
key: state.pageKey,
child: MangaReaderDetail(
modelManga: model,
));
}),
];
}

View file

@ -1,28 +1,30 @@
import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'dart:developer';
import 'package:mangayomi/services/http_res_to_dom_html.dart';
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? author;
String? status;
String? source;
String? url;
String? name;
String? image;
String? synopsys;
String? imageUrl;
String? description;
GetMangaDetailModel({
required this.genre,
required this.detail,
required this.author,
required this.status,
required this.chapterDate,
required this.chapterTitle,
required this.chapterUrl,
required this.image,
required this.synopsys,
required this.imageUrl,
required this.description,
required this.url,
required this.name,
required this.source,
@ -31,69 +33,71 @@ class GetMangaDetailModel {
@riverpod
Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
{required String image,
{required String imageUrl,
required String url,
required String name,
String lang = '',
required String lang,
required String source}) async {
List<String> genre = [];
List<String> detail = [];
String? author;
String? status;
List<String> chapterTitle = [];
List<String> chapterUrl = [];
List<String> chapterDate = [];
String? synopsys;
source = source.toLowerCase();
String? description;
if (source == "mangahere") {
final response = await http.get(Uri.parse("http://www.mangahere.cc$url"),
final dom = await httpResToDom(
url: "http://www.mangahere.cc$url",
headers: {
"Referer": "https://www.mangahere.cc/",
"Cookie": "isAdult=1"
});
dom.Document htmll = dom.Document.html(response.body);
if (htmll
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-title > span.detail-info-right-title-tip')
.isNotEmpty) {
final tt = htmll
final tt = dom
.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]);
status = tt[0];
} else {
detail.add("");
status = "";
}
if (htmll
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.isNotEmpty) {
final tt = htmll
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-say > a')
.map((e) => e.text.trim())
.toList();
detail.add(tt[0]);
author = tt[0];
} else {
detail.add("");
author = "";
}
if (htmll
if (dom
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.isNotEmpty) {
final tt = htmll
final tt = dom
.querySelectorAll(
'body > div > div > div.detail-info-right > p.detail-info-right-content')
.map((e) => e.text.trim())
.toList();
synopsys = tt.first;
description = tt.first;
}
if (htmll.querySelectorAll('ul > li > a').isNotEmpty) {
final udl = htmll
if (dom.querySelectorAll('ul > li > a').isNotEmpty) {
final udl = dom
.querySelectorAll('ul > li > a ')
.where((e) => e.attributes.containsKey('href'))
.map((e) => e.attributes['href'])
@ -103,8 +107,8 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
chapterUrl.add(ok!);
}
}
if (htmll.querySelectorAll('ul > li > a > div > p.title3').isNotEmpty) {
final tt = htmll
if (dom.querySelectorAll('ul > li > a > div > p.title3').isNotEmpty) {
final tt = dom
.querySelectorAll('ul > li > a > div > p.title3')
.map((e) => e.text.trim())
.toList();
@ -112,8 +116,8 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
chapterTitle.add(ok);
}
}
if (htmll.querySelectorAll('ul > li > a > div > p.title2').isNotEmpty) {
final tt = htmll
if (dom.querySelectorAll('ul > li > a > div > p.title2').isNotEmpty) {
final tt = dom
.querySelectorAll('ul > li > a > div > p.title2')
.map((e) => e.text.trim())
.toList();
@ -121,11 +125,11 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
chapterDate.add(ok);
}
}
if (htmll
if (dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.isNotEmpty) {
final tt = htmll
final tt = dom
.querySelectorAll(
' body > div > div > div.detail-info-right > p.detail-info-right-tag-list > a')
.map((e) => e.text.trim())
@ -136,15 +140,18 @@ Future<GetMangaDetailModel> getMangaDetail(GetMangaDetailRef ref,
}
}
}
return GetMangaDetailModel(
chapterDate: chapterDate,
chapterTitle: chapterTitle,
chapterUrl: chapterUrl,
detail: detail,
genre: genre,
image: image,
synopsys: synopsys,
name: name,
url: url,
source: source);
chapterDate: chapterDate,
chapterTitle: chapterTitle,
chapterUrl: chapterUrl,
status: status,
genre: genre,
author: author,
description: description,
name: name,
url: url,
source: source,
imageUrl: imageUrl,
);
}

View file

@ -6,7 +6,7 @@ part of 'get_manga_detail.dart';
// RiverpodGenerator
// **************************************************************************
String _$getMangaDetailHash() => r'f8a4047fc7d1661b6abdfcfa0786e0d78a20c8ed';
String _$getMangaDetailHash() => r'17d72a984e6428e71778ade3650b56062ee779d7';
/// Copied from Dart SDK
class _SystemHash {
@ -42,14 +42,14 @@ class GetMangaDetailFamily extends Family<AsyncValue<GetMangaDetailModel>> {
/// See also [getMangaDetail].
GetMangaDetailProvider call({
required String image,
required String imageUrl,
required String url,
required String name,
String lang = '',
required String lang,
required String source,
}) {
return GetMangaDetailProvider(
image: image,
imageUrl: imageUrl,
url: url,
name: name,
lang: lang,
@ -62,7 +62,7 @@ class GetMangaDetailFamily extends Family<AsyncValue<GetMangaDetailModel>> {
covariant GetMangaDetailProvider provider,
) {
return call(
image: provider.image,
imageUrl: provider.imageUrl,
url: provider.url,
name: provider.name,
lang: provider.lang,
@ -90,15 +90,15 @@ class GetMangaDetailProvider
extends AutoDisposeFutureProvider<GetMangaDetailModel> {
/// See also [getMangaDetail].
GetMangaDetailProvider({
required this.image,
required this.imageUrl,
required this.url,
required this.name,
this.lang = '',
required this.lang,
required this.source,
}) : super.internal(
(ref) => getMangaDetail(
ref,
image: image,
imageUrl: imageUrl,
url: url,
name: name,
lang: lang,
@ -115,7 +115,7 @@ class GetMangaDetailProvider
GetMangaDetailFamily._allTransitiveDependencies,
);
final String image;
final String imageUrl;
final String url;
final String name;
final String lang;
@ -124,7 +124,7 @@ class GetMangaDetailProvider
@override
bool operator ==(Object other) {
return other is GetMangaDetailProvider &&
other.image == image &&
other.imageUrl == imageUrl &&
other.url == url &&
other.name == name &&
other.lang == lang &&
@ -134,7 +134,7 @@ class GetMangaDetailProvider
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, image.hashCode);
hash = _SystemHash.combine(hash, imageUrl.hashCode);
hash = _SystemHash.combine(hash, url.hashCode);
hash = _SystemHash.combine(hash, name.hashCode);
hash = _SystemHash.combine(hash, lang.hashCode);

View file

@ -20,10 +20,11 @@ Future<GetMangaModel> getPopularManga(GetPopularMangaRef ref,
List<String?> url = [];
List<String?> name = [];
List<String?> image = [];
source = source.toLowerCase();
//mangahere
if (source == "mangahere") {
final dom = await httpResToDom(url: 'https://www.mangahere.cc/ranking/');
final dom = await httpResToDom(
url: 'https://www.mangahere.cc/ranking/', headers: {});
if (dom
.querySelectorAll(
'body > div.container.weekrank.ranking > div > div > ul > li > a')

View file

@ -6,7 +6,7 @@ part of 'get_popular_manga.dart';
// RiverpodGenerator
// **************************************************************************
String _$getPopularMangaHash() => r'4ab45f760fe457710bb9d09c72faef2fc09261e2';
String _$getPopularMangaHash() => r'0b6445ff81dbbe1d337f37ed46544f5834805088';
/// Copied from Dart SDK
class _SystemHash {

View file

@ -1,7 +1,8 @@
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));
Future<Document> httpResToDom(
{required String url, required Map<String, String>? headers}) async {
final response = await http.get(Uri.parse(url), headers: headers);
return Document.html(response.body);
}

View file

@ -18,7 +18,7 @@ class _SourcesScreenState extends State<SourcesScreen> {
onTap: () {
context.push('/mangaHome',
extra: MangaType(
isFullData: true, lang: 'en', source: 'mangahere'));
isFullData: true, lang: 'en', source: 'MangaHere'));
},
leading: Container(
height: 37,

View file

@ -0,0 +1,371 @@
import 'package:blur/blur.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:draggable_home/draggable_home.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/views/manga/detail/readmore.dart';
class CardSliverAppBar extends ConsumerStatefulWidget {
final double? height;
final Function(bool) isExtended;
final double? appBarHeight = 65;
final Text? title;
final Widget? titleDescription;
final List<Color>? backButtonColors;
final Widget? action;
final bool? isManga;
final String? description;
final ModelManga? modelManga;
final List<String>? genre;
final List<String>? chapterTitle;
const CardSliverAppBar({
super.key,
required this.isExtended,
required this.isManga,
required this.height,
required this.title,
this.titleDescription,
this.backButtonColors,
this.action,
required this.description,
required this.genre,
required this.chapterTitle,
required this.modelManga,
});
@override
ConsumerState<CardSliverAppBar> createState() => _CardSliverAppBarState();
}
class _CardSliverAppBarState extends ConsumerState<CardSliverAppBar> {
bool _reverse = false;
@override
Widget build(BuildContext context) {
return NotificationListener<UserScrollNotification>(
onNotification: (notification) {
if (notification.direction == ScrollDirection.forward) {
widget.isExtended(true);
}
if (notification.direction == ScrollDirection.reverse) {
widget.isExtended(false);
}
return true;
},
child: DraggableHome(
centerTitle: false,
leading: const BackButton(),
headerExpandedHeight: mediaHeight(context, 1) <= 600 ? 0.50 : 0.35,
title: Text(widget.modelManga!.name!),
actions: [
IconButton(
icon: _reverse
? const Icon(FontAwesomeIcons.arrowDownShortWide)
: const Icon(FontAwesomeIcons.arrowUpShortWide),
iconSize: 25,
onPressed: () {
setState(() {
_reverse = !_reverse;
});
},
),
],
headerWidget: Stack(
children: [
_backgroundConstructor(),
SafeArea(
child: Stack(
children: [
_titleConstructor(),
_cardConstructor(),
if (widget.action != null) _actionConstructor(),
_backButtonConstructor(),
_filterConstructor(),
],
),
)
],
),
body: [
_bodyContainer(),
_listView(),
const SizedBox(
height: 50,
)
],
fullyStretchable: false,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBarColor: Theme.of(context).scaffoldBackgroundColor,
),
);
}
ListView _listView() {
return ListView.builder(
// controller: _scrollController,
padding: const EdgeInsets.only(top: 0),
physics: const NeverScrollableScrollPhysics(),
reverse: _reverse,
itemCount: widget.modelManga!.chapterTitle!.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
key: ObjectKey(widget.modelManga!.chapterUrl),
onTap: () {},
trailing: const Icon(FontAwesomeIcons.circleDown),
subtitle: widget.isManga == true
? Text(
widget.modelManga!.chapterDate![index],
style: const TextStyle(fontSize: 13),
)
: const SizedBox(
height: 10,
),
title: Text(
widget.modelManga!.chapterTitle![index],
style: const TextStyle(fontSize: 15),
),
);
});
}
Widget _backButtonConstructor() {
return Positioned(
top: 7,
left: 5,
child: Column(
children: [
IconButton(
icon: const Icon(Icons.arrow_back),
iconSize: 25,
onPressed: () {
Navigator.of(context).pop();
},
)
],
),
);
}
Widget _filterConstructor() {
return Positioned(
top: 7,
right: 0,
child: IconButton(
icon: _reverse
? const Icon(FontAwesomeIcons.arrowDownShortWide)
: const Icon(FontAwesomeIcons.arrowUpShortWide),
iconSize: 25,
onPressed: () {
setState(() {
_reverse = !_reverse;
});
},
),
);
}
Widget _bodyContainer() {
return Container(
key: const Key("widget_body"),
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Wrap(
children: [
for (var i = 0; i < widget.genre!.length; i++)
GestureDetector(
onTap: () {},
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(
left: 2, right: 2, bottom: 5),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(width: 1, color: Colors.white),
),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 2, horizontal: 8),
child: Text(
widget.genre![i],
style: const TextStyle(fontSize: 12),
),
),
),
),
],
),
)
],
),
),
// log
Column(
children: [
//Description
if (widget.description != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: ReadMoreWidget(
text: widget.description!,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(
'${widget.chapterTitle!.length.toString()} chapter(s)',
style: const TextStyle(fontWeight: FontWeight.bold),
),
)
],
),
),
],
),
],
),
// _body
// ,
);
}
Widget _cardConstructor() {
return Positioned(
key: const Key("widget_card"),
top: widget.height! - (widget.appBarHeight! * 1.8),
left: 20,
child: GestureDetector(
onTap: () {},
child: SizedBox(
width: widget.appBarHeight! * 1.5,
height: widget.appBarHeight! * 2.3,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)),
image: DecorationImage(
image: CachedNetworkImageProvider(widget.modelManga!.imageUrl!),
fit: BoxFit.cover,
),
),
),
),
),
);
}
Widget _backgroundConstructor() {
return SizedBox(
key: const Key("widget_background"),
height: widget.height! + 400,
width: MediaQuery.of(context).size.width,
// color: Colors.black,
child: Blur(
blur: 10,
blurColor: Theme.of(context).primaryColor,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(5)),
image: DecorationImage(
image: CachedNetworkImageProvider(widget.modelManga!.imageUrl!),
fit: BoxFit.cover,
),
),
),
),
);
}
Widget _titleConstructor() {
return Positioned(
key: const Key("widget_title"),
top: widget.height! - widget.appBarHeight!,
left: 30,
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 100),
width: MediaQuery.of(context).size.width,
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.3),
height: 70,
child: _titleDescriptionHandler(),
),
);
}
Widget _titleDescriptionHandler() {
var titleContainer = Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(
bottom: 50,
),
child: widget.title,
);
var titleDescriptionContainer = Opacity(
opacity: 1.0,
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(
top: 25,
),
child: widget.titleDescription,
),
);
return Stack(
alignment: Alignment.centerLeft,
children: [
titleContainer,
titleDescriptionContainer,
],
);
}
Widget _actionConstructor() {
return Positioned(
key: const Key("widget_action"),
top: widget.height! - widget.appBarHeight! - 45,
right: 10,
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.all(Radius.circular(50)),
boxShadow: const [
BoxShadow(color: Colors.black54, blurRadius: 3.0)
]),
child: widget.action,
),
const SizedBox(
width: 5,
),
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: const BorderRadius.all(Radius.circular(50)),
boxShadow: const [
BoxShadow(color: Colors.black54, blurRadius: 3.0)
]),
child: IconButton(
onPressed: () async {},
icon: const Icon(Icons.travel_explore)),
),
],
));
}
}

View file

@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/services/get_manga_detail.dart';
import 'package:mangayomi/views/manga/detail/manga_details_view.dart';
class MangaReaderDetail extends ConsumerStatefulWidget {
final ModelManga modelManga;
const MangaReaderDetail({super.key, required this.modelManga});
@override
ConsumerState<MangaReaderDetail> createState() => _MangaReaderDetailState();
}
class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
bool _isFavorite = false;
@override
void initState() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
super.initState();
}
@override
void dispose() {
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: () async {
if (_isFavorite) {
bool isOk = false;
ref
.watch(getMangaDetailProvider(
imageUrl: '',
lang: widget.modelManga.lang!,
name: widget.modelManga.name!,
source: widget.modelManga.source!,
url: widget.modelManga.link!)
.future)
.then((value) {
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
chapterDate: value.chapterDate,
chapterTitle: value.chapterTitle,
chapterUrl: value.chapterUrl,
description: widget.modelManga.description,
status: value.status,
favorite: _isFavorite,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang);
ref.watch(hiveBoxManga).put(widget.modelManga.link!, model);
setState(() {
isOk = true;
});
});
await Future.doWhile(() async {
await Future.delayed(const Duration(seconds: 1));
if (isOk == true) {
return false;
}
return true;
});
}
},
child: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxManga).listenable(),
builder: (context, value, child) {
final entries = value.values
.where((element) => element.link == widget.modelManga.link)
.toList();
if (entries.isNotEmpty) {
return MangaDetailsView(
modelManga: entries[0],
isFavorite: (value) {
setState(() {
_isFavorite = value;
});
},
isManga: true,
);
}
return MangaDetailsView(
modelManga: widget.modelManga,
isFavorite: (value) {
setState(() {
_isFavorite = value;
});
},
isManga: true,
);
},
),
),
);
}
}

View file

@ -0,0 +1,67 @@
import 'package:expandable_text/expandable_text.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/utils/media_query.dart';
class ReadMoreWidget extends StatefulWidget {
const ReadMoreWidget({Key? key, required this.text}) : super(key: key);
final String text;
@override
ReadMoreWidgetState createState() => ReadMoreWidgetState();
}
class ReadMoreWidgetState extends State<ReadMoreWidget>
with TickerProviderStateMixin {
bool expanded = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
ExpandableText(
animationDuration: const Duration(milliseconds: 500),
onExpandedChanged: (ok) {
setState(() => expanded = ok);
},
expandOnTextTap: true,
widget.text,
expandText: '',
maxLines: 3,
expanded: false,
onPrefixTap: () {
setState(() => expanded = !expanded);
},
linkColor: Theme.of(context).scaffoldBackgroundColor,
animation: true,
collapseOnTextTap: true,
prefixText: '',
),
Container(
color: Theme.of(context).cardColor.withOpacity(0.15),
width: mediaWidth(context, 1),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
expanded
? Row(
children: const [
Text(
"Show less",
),
Icon(Icons.keyboard_arrow_up_sharp)
],
)
: Row(
children: const [
Text(
"Show more",
),
Icon(Icons.keyboard_arrow_down_sharp)
],
),
],
))
],
);
}
}

View file

@ -150,11 +150,11 @@ class _MangaHomeImageCardState extends ConsumerState<MangaHomeImageCard>
Widget build(BuildContext context) {
super.build(context);
final getMangaDetail = ref.watch(getMangaDetailProvider(
source: widget.source,
image: widget.image,
name: widget.name,
url: widget.url,
));
source: widget.source,
imageUrl: widget.image,
name: widget.name,
url: widget.url,
lang: widget.lang));
return getMangaDetail.when(
data: (data) {

View file

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/models/model_manga.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';
@ -27,14 +29,32 @@ class _MangaImageCardWidgetState extends ConsumerState<MangaImageCardWidget> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () async {},
onTap: () async {
final modelManga = ModelManga(
imageUrl: widget.getMangaDetailModel!.imageUrl,
name: widget.getMangaDetailModel!.name,
genre: widget.getMangaDetailModel!.genre,
author: widget.getMangaDetailModel!.author,
chapterDate: widget.getMangaDetailModel!.chapterDate,
chapterTitle: widget.getMangaDetailModel!.chapterTitle,
chapterUrl: widget.getMangaDetailModel!.chapterUrl,
status: widget.getMangaDetailModel!.status,
description: widget.getMangaDetailModel!.description,
favorite: false,
link: widget.getMangaDetailModel!.url,
source: widget.getMangaDetailModel!.source,
lang: widget.lang);
if (mounted) {
context.push('/manga-reader/detail', extra: modelManga);
}
},
child: CoverViewWidget(children: [
cachedNetworkImage(
headers: {
"Referer": "https://www.mangahere.cc/",
"Cookie": "isAdult=1"
},
imageUrl: widget.getMangaDetailModel!.image!,
imageUrl: widget.getMangaDetailModel!.imageUrl!,
width: 200,
height: 270,
fit: BoxFit.cover),

View file

@ -41,6 +41,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.10.0"
blur:
dependency: "direct main"
description:
name: blur
sha256: fd23f1247faee4a7d1a3efb6b7c3cea134f3b939d72e5f8d45233deb0776259f
url: "https://pub.dev"
source: hosted
version: "3.1.0"
boolean_selector:
dependency: transitive
description:
@ -241,6 +249,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
draggable_home:
dependency: "direct main"
description:
name: draggable_home
sha256: "4fa3c71f7784bb9a0ab722bb6245e071c261dcf2a3e1e004ee681bda60cfbbbb"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
expandable_text:
dependency: "direct main"
description:
name: expandable_text
sha256: "7d03ea48af6987b20ece232678b744862aa3250d4a71e2aaf1e4af90015d76b1"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
fake_async:
dependency: transitive
description:
@ -328,6 +352,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
font_awesome_flutter:
dependency: "direct main"
description:
name: font_awesome_flutter
sha256: "959ef4add147753f990b4a7c6cccb746d5792dbdc81b1cde99e62e7edb31b206"
url: "https://pub.dev"
source: hosted
version: "10.4.0"
freezed_annotation:
dependency: transitive
description:

View file

@ -41,6 +41,10 @@ dependencies:
riverpod_annotation: ^2.0.2
html: ^0.15.2
flutter_js: ^0.6.0
font_awesome_flutter: ^10.1.0
blur: ^3.1.0
draggable_home: ^1.0.4
expandable_text: ^2.3.0
# The following adds the Cupertino Icons font to your application.