redesign manga reader view

This commit is contained in:
kodjodevf 2023-04-04 15:22:49 +01:00
parent 4bea922334
commit 299460e2be
7 changed files with 616 additions and 640 deletions

View file

@ -1,377 +0,0 @@
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/manga_reader.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: () {
pushMangaReaderView(
context: context,
modelManga: widget.modelManga!,
index: index);
},
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,351 @@
import 'dart:developer';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:draggable_scrollbar/draggable_scrollbar.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/manga_reader.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/views/manga/detail/readmore.dart';
class MangaDetailView extends ConsumerStatefulWidget {
final Function(bool) isExtended;
final int listLength;
final Widget? titleDescription;
final List<Color>? backButtonColors;
final Widget? action;
final ModelManga? modelManga;
const MangaDetailView({
super.key,
required this.isExtended,
this.titleDescription,
this.backButtonColors,
this.action,
required this.modelManga,
required this.listLength,
});
@override
ConsumerState<MangaDetailView> createState() => _MangaDetailViewState();
}
class _MangaDetailViewState extends ConsumerState<MangaDetailView> {
@override
void initState() {
_scrollController = ScrollController()
..addListener(() {
ref.read(offetProvider.notifier).state = _scrollController.offset;
});
super.initState();
}
final offetProvider = StateProvider((ref) => 0.0);
bool _reverse = false;
ScrollController _scrollController = ScrollController();
@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: Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: Size.fromHeight(AppBar().preferredSize.height),
child: Consumer(
builder: (context, ref, child) {
return AppBar(
title: ref.watch(offetProvider) > 200
? Text(widget.modelManga!.name!)
: null,
backgroundColor: ref.watch(offetProvider) == 0.0
? Colors.transparent
: Theme.of(context).scaffoldBackgroundColor,
actions: [
IconButton(
splashRadius: 20,
onPressed: () {},
icon: Icon(Icons.download_outlined,
color: Theme.of(context).hintColor)),
IconButton(
splashRadius: 20,
onPressed: () {},
icon: Icon(Icons.filter_list_sharp,
color: Theme.of(context).hintColor)),
PopupMenuButton(
color: Theme.of(context).hintColor,
itemBuilder: (context) {
return [
const PopupMenuItem<int>(
value: 0,
child: Text("1"),
),
const PopupMenuItem<int>(
value: 1,
child: Text("2"),
),
const PopupMenuItem<int>(
value: 2,
child: Text("3"),
),
];
},
onSelected: (value) {
if (value == 0) {
} else if (value == 1) {
} else if (value == 2) {}
}),
],
);
},
)),
body: _listView(),
));
}
_listView() {
return DraggableScrollbar.rrect(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.only(top: 0),
reverse: _reverse,
itemCount: widget.listLength,
itemBuilder: (context, index) {
int finalIndex = index - 1;
if (index == 0) {
return _bodyContainer();
}
return ListTile(
key: ObjectKey(widget.modelManga!.chapterUrl),
onTap: () {
pushMangaReaderView(
context: context,
modelManga: widget.modelManga!,
index: finalIndex);
},
trailing: const Icon(FontAwesomeIcons.circleDown),
subtitle: Text(
widget.modelManga!.chapterDate![finalIndex],
style: const TextStyle(fontSize: 13),
),
title: Text(
widget.modelManga!.chapterTitle![finalIndex],
style: const TextStyle(fontSize: 15),
),
);
}));
}
Widget _bodyContainer() {
return Stack(
children: [
Positioned(top: 0, child: _backgroundConstructor()),
Container(
height: 300,
decoration: BoxDecoration(
color: Theme.of(context).scaffoldBackgroundColor,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Theme.of(context).scaffoldBackgroundColor.withOpacity(0.6),
Color(Theme.of(context).scaffoldBackgroundColor.value)
],
stops: const [0, .8],
),
),
),
Column(
children: [
SizedBox(
height: AppBar().preferredSize.height,
),
SizedBox(
height: 180,
child: Stack(
children: [
_titleConstructor(),
_cardConstructor(),
],
),
),
_actionConstructor(),
Container(
key: const Key("widget_body"),
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.modelManga!.description != null)
Padding(
padding: const EdgeInsets.all(8.0),
child: ReadMoreWidget(
text: widget.modelManga!.description!,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Wrap(
children: [
for (var i = 0;
i < widget.modelManga!.genre!.length;
i++)
Padding(
padding: const EdgeInsets.only(
left: 2, right: 2, bottom: 5),
child: SizedBox(
height: 30,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: BeveledRectangleBorder(
borderRadius:
BorderRadius.circular(3))),
onPressed: () {},
child: Text(
widget.modelManga!.genre![i],
style: const TextStyle(fontSize: 12),
),
),
),
),
],
),
),
// log
Column(
children: [
//Description
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8),
child: Text(
'${widget.modelManga!.chapterTitle!.length.toString()} chapter(s)',
style: const TextStyle(
fontWeight: FontWeight.bold),
),
)
],
),
),
],
),
],
),
),
],
),
],
);
}
Widget _cardConstructor() {
return Positioned(
key: const Key("widget_card"),
top: 20,
left: 20,
child: GestureDetector(
onTap: () {},
child: SizedBox(
width: 65 * 1.5,
height: 65 * 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 cachedNetworkImage(
imageUrl: widget.modelManga!.imageUrl!,
width: mediaWidth(context, 1),
height: 300,
fit: BoxFit.cover);
}
Widget _titleConstructor() {
return Positioned(
key: const Key("widget_title"),
top: 60,
left: 30,
child: Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 100),
width: MediaQuery.of(context).size.width,
height: 70,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.modelManga!.name!,
style:
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
widget.titleDescription!,
],
),
),
);
}
Widget _actionConstructor() {
return Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
widget.action!,
const SizedBox(
width: 5,
),
SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {},
child: Column(
children: const [
Icon(
Icons.travel_explore,
size: 25,
),
SizedBox(
height: 4,
),
Text('WebView')
],
),
),
)
],
),
),
);
}
}

View file

@ -5,21 +5,19 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/media_query.dart';
import 'package:mangayomi/views/manga/detail/card_sliver_app_bar.dart';
import 'package:mangayomi/views/manga/detail/manga_detail_view.dart';
final isExtended = StateProvider.autoDispose<bool>((ref) {
return true;
});
class MangaDetailsView extends ConsumerStatefulWidget {
final bool isManga;
final ModelManga modelManga;
final Function(bool) isFavorite;
const MangaDetailsView({
super.key,
required this.isFavorite,
required this.modelManga,
required this.isManga,
});
@override
@ -56,233 +54,242 @@ class _MangaDetailsViewState extends ConsumerState<MangaDetailsView> {
Widget build(BuildContext context) {
final manga = ref.watch(hiveBoxManga);
return Scaffold(
floatingActionButton: widget.isManga
? widget.modelManga.chapterTitle!.isNotEmpty
? ValueListenableBuilder<Box>(
valueListenable: ref.watch(hiveBoxMangaInfo).listenable(),
builder: (context, value, child) {
final entries = value.get(
"${widget.modelManga.source}/${widget.modelManga.name}-chapter_index",
defaultValue: '');
if (entries.isNotEmpty) {
return Consumer(builder: (context, ref, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
height: 50,
width: !ref.watch(isExtended)
? 63
: mediaWidth(context, 0.4),
duration: const Duration(milliseconds: 400),
curve: Curves.easeIn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20))),
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
curve: Curves.easeIn,
width: !ref.watch(isExtended)
? 0
: mediaWidth(context, 0.2),
duration:
const Duration(milliseconds: 400),
child: Text(
widget.modelManga.chapterTitle![
int.parse(entries.toString())],
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white, fontSize: 13),
),
),
const Icon(
Icons.play_arrow,
color: Colors.white,
)
],
floatingActionButton: widget.modelManga.chapterTitle!.isNotEmpty
? ValueListenableBuilder<Box>(
valueListenable: ref.watch(hiveBoxMangaInfo).listenable(),
builder: (context, value, child) {
final entries = value.get(
"${widget.modelManga.source}/${widget.modelManga.name}-chapter_index",
defaultValue: '');
if (entries.isNotEmpty) {
return Consumer(builder: (context, ref, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
height: 50,
width: !ref.watch(isExtended)
? 63
: mediaWidth(context, 0.3),
duration: const Duration(milliseconds: 400),
curve: Curves.easeIn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15))),
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
curve: Curves.easeIn,
width: !ref.watch(isExtended)
? 0
: mediaWidth(context, 0.15),
duration: const Duration(milliseconds: 400),
child: const Text(
"Continue",
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 13),
),
),
const Icon(
Icons.play_arrow,
)
],
),
),
),
],
);
});
}
return Consumer(builder: (context, ref, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
height: 50,
width: !ref.watch(isExtended)
? 60
: mediaWidth(context, 0.25),
duration: const Duration(milliseconds: 500),
curve: Curves.easeIn,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15))),
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
curve: Curves.easeIn,
width: !ref.watch(isExtended) ? 0 : 30,
duration: const Duration(milliseconds: 500),
child: const Text(
"Read",
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 13),
),
),
),
],
);
});
}
return Consumer(builder: (context, ref, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
height: 50,
width: !ref.watch(isExtended) ? null : null,
duration: const Duration(microseconds: 500),
curve: Curves.elasticOut,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20))),
onPressed: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedContainer(
curve: Curves.elasticOut,
width: !ref.watch(isExtended) ? 0 : null,
duration: const Duration(microseconds: 500),
child: Text(
"Read",
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white, fontSize: 13),
),
),
AnimatedContainer(
curve: Curves.elasticOut,
width: !ref.watch(isExtended) ? 0 : 10,
duration: const Duration(microseconds: 500),
),
const Icon(
Icons.play_arrow,
color: Colors.white,
)
],
AnimatedContainer(
curve: Curves.easeIn,
width: !ref.watch(isExtended) ? 0 : 10,
duration: const Duration(milliseconds: 500),
),
),
),
],
);
});
},
)
: Container()
: Container(),
body: CardSliverAppBar(
height: 200,
title: Text(widget.modelManga.name!,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold)),
titleDescription: Row(
children: [
Flexible(
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return index == 0
? Row(
children: [
CircleAvatar(
backgroundColor: Theme.of(context).cardColor,
radius: 12,
child: const Icon(
FontAwesomeIcons.clock,
size: 13,
)),
const SizedBox(
width: 2,
),
Text(widget.modelManga.status!)
],
)
: Padding(
padding: const EdgeInsets.only(left: 5),
child: Row(
children: [
CircleAvatar(
backgroundColor: Theme.of(context).cardColor,
radius: 12,
child: const Icon(
FontAwesomeIcons.user,
size: 13,
)),
const SizedBox(
width: 2,
),
Text(widget.modelManga.author!)
const Icon(
Icons.play_arrow,
)
],
),
);
},
),
),
),
],
);
});
},
)
: null,
body: MangaDetailView(
titleDescription: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.modelManga.author!,
style: const TextStyle(fontWeight: FontWeight.w500),
),
Row(
children: [
Text(widget.modelManga.status!),
const Text(''),
Text(widget.modelManga.source!)
],
)
],
),
action: widget.isManga
? 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) {
if (entries[0].favorite == true) {
_checkFavorite(true);
action: 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) {
if (entries[0].favorite == true) {
_checkFavorite(true);
return IconButton(
onPressed: () {
_setFavorite(false);
manga.delete(widget.modelManga.link);
},
icon: const Icon(Icons.favorite));
} else {
_checkFavorite(false);
return IconButton(
onPressed: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
chapterDate: widget.modelManga.chapterDate,
chapterTitle: widget.modelManga.chapterTitle,
chapterUrl: widget.modelManga.chapterUrl,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang);
manga.put(widget.modelManga.link, model);
},
icon: const Icon(Icons.favorite_border_rounded));
}
}
return IconButton(
onPressed: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
chapterDate: widget.modelManga.chapterDate,
chapterTitle: widget.modelManga.chapterTitle,
chapterUrl: widget.modelManga.chapterUrl,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang);
manga.put(widget.modelManga.link, model);
},
icon: const Icon(Icons.favorite_border_rounded));
return SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
_setFavorite(false);
manga.delete(widget.modelManga.link);
},
child: Column(
children: const [
Icon(
Icons.favorite,
size: 25,
),
SizedBox(
height: 4,
),
Text('In library')
],
),
),
);
} else {
_checkFavorite(false);
return SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
chapterDate: widget.modelManga.chapterDate,
chapterTitle: widget.modelManga.chapterTitle,
chapterUrl: widget.modelManga.chapterUrl,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang);
manga.put(widget.modelManga.link, model);
},
child: Column(
children: const [
Icon(
Icons.favorite_border_rounded,
size: 25,
),
SizedBox(
height: 4,
),
Text('Add to library')
],
),
),
);
}
}
return SizedBox(
width: mediaWidth(context, 0.4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
elevation: 0),
onPressed: () {
_setFavorite(true);
final model = ModelManga(
imageUrl: widget.modelManga.imageUrl,
name: widget.modelManga.name,
genre: widget.modelManga.genre,
author: widget.modelManga.author,
status: widget.modelManga.status,
chapterDate: widget.modelManga.chapterDate,
chapterTitle: widget.modelManga.chapterTitle,
chapterUrl: widget.modelManga.chapterUrl,
description: widget.modelManga.description,
favorite: true,
link: widget.modelManga.link,
source: widget.modelManga.source,
lang: widget.modelManga.lang);
manga.put(widget.modelManga.link, model);
},
)
: Container(),
chapterTitle: widget.modelManga.chapterTitle,
genre: widget.modelManga.genre,
isManga: widget.isManga,
child: Column(
children: const [
Icon(
Icons.favorite_border_rounded,
size: 25,
),
SizedBox(
height: 4,
),
Text('Add to library')
],
),
),
);
},
),
modelManga: widget.modelManga,
description: widget.modelManga.description,
listLength: widget.modelManga.chapterUrl!.length + 1,
isExtended: (value) {
ref.read(isExtended.notifier).update((state) => value);
ref.read(isExtended.notifier).state = value;
},
),
);

View file

@ -90,7 +90,6 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
_isFavorite = value;
});
},
isManga: true,
);
}
return MangaDetailsView(
@ -100,7 +99,6 @@ class _MangaReaderDetailState extends ConsumerState<MangaReaderDetail> {
_isFavorite = value;
});
},
isManga: true,
);
},
),

View file

@ -18,49 +18,39 @@ class ReadMoreWidgetState extends State<ReadMoreWidget>
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: '',
Stack(
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: '',
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: expanded
? const Icon(Icons.keyboard_arrow_up_sharp)
: const Icon(Icons.keyboard_arrow_down_sharp))
],
),
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)
],
),
],
))
color: Theme.of(context).cardColor.withOpacity(0.15),
width: mediaWidth(context, 1),
)
],
);
}

View file

@ -257,6 +257,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
draggable_scrollbar:
dependency: "direct main"
description:
name: draggable_scrollbar
sha256: a906e27fc1ee056e2942d66989dd0cb5b70a361e7d44af566cfa1b584054eac3
url: "https://pub.dev"
source: hosted
version: "0.1.0"
expandable_text:
dependency: "direct main"
description:

View file

@ -42,14 +42,13 @@ dependencies:
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
flex_color_scheme: ^7.0.0
scrollable_positioned_list: ^0.3.5
extended_image: ^7.0.2
photo_view: ^0.14.0
modal_bottom_sheet: ^3.0.0-pre
draggable_scrollbar: ^0.1.0
# The following adds the Cupertino Icons font to your application.