mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
Improve UI
This commit is contained in:
@@ -1,5 +1,27 @@
|
||||
part of 'components.dart';
|
||||
|
||||
ImageProvider? _findImageProvider(Comic comic) {
|
||||
ImageProvider image;
|
||||
if (comic is LocalComic) {
|
||||
image = LocalComicImageProvider(comic);
|
||||
} else if (comic is History) {
|
||||
image = HistoryImageProvider(comic);
|
||||
} else if (comic.sourceKey == 'local') {
|
||||
var localComic = LocalManager().find(comic.id, ComicType.local);
|
||||
if (localComic == null) {
|
||||
return null;
|
||||
}
|
||||
image = FileImage(localComic.coverFile);
|
||||
} else {
|
||||
image = CachedImageProvider(
|
||||
comic.cover,
|
||||
sourceKey: comic.sourceKey,
|
||||
cid: comic.id,
|
||||
);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
class ComicTile extends StatelessWidget {
|
||||
const ComicTile(
|
||||
{super.key,
|
||||
@@ -27,8 +49,14 @@ class ComicTile extends StatelessWidget {
|
||||
onTap!();
|
||||
return;
|
||||
}
|
||||
App.mainNavigatorKey?.currentContext
|
||||
?.to(() => ComicPage(id: comic.id, sourceKey: comic.sourceKey));
|
||||
App.mainNavigatorKey?.currentContext?.to(
|
||||
() => ComicPage(
|
||||
id: comic.id,
|
||||
sourceKey: comic.sourceKey,
|
||||
cover: comic.cover,
|
||||
title: comic.title,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onLongPressed(context) {
|
||||
@@ -61,8 +89,14 @@ class ComicTile extends StatelessWidget {
|
||||
icon: Icons.chrome_reader_mode_outlined,
|
||||
text: 'Details'.tl,
|
||||
onClick: () {
|
||||
App.mainNavigatorKey?.currentContext
|
||||
?.to(() => ComicPage(id: comic.id, sourceKey: comic.sourceKey));
|
||||
App.mainNavigatorKey?.currentContext?.to(
|
||||
() => ComicPage(
|
||||
id: comic.id,
|
||||
sourceKey: comic.sourceKey,
|
||||
cover: comic.cover,
|
||||
title: comic.title,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
MenuEntry(
|
||||
@@ -161,23 +195,9 @@ class ComicTile extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget buildImage(BuildContext context) {
|
||||
ImageProvider image;
|
||||
if (comic is LocalComic) {
|
||||
image = LocalComicImageProvider(comic as LocalComic);
|
||||
} else if (comic is History) {
|
||||
image = HistoryImageProvider(comic as History);
|
||||
} else if (comic.sourceKey == 'local') {
|
||||
var localComic = LocalManager().find(comic.id, ComicType.local);
|
||||
if (localComic == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
image = FileImage(localComic.coverFile);
|
||||
} else {
|
||||
image = CachedImageProvider(
|
||||
comic.cover,
|
||||
sourceKey: comic.sourceKey,
|
||||
cid: comic.id,
|
||||
);
|
||||
var image = _findImageProvider(comic);
|
||||
if (image == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return AnimatedImage(
|
||||
image: image,
|
||||
@@ -199,15 +219,25 @@ class ComicTile extends StatelessWidget {
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 24, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: height * 0.68,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
Hero(
|
||||
tag: "cover${comic.id}${comic.sourceKey}",
|
||||
child: Container(
|
||||
width: height * 0.68,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.colorScheme.outlineVariant,
|
||||
blurRadius: 1,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: buildImage(context),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: buildImage(context),
|
||||
),
|
||||
SizedBox.fromSize(
|
||||
size: const Size(16, 5),
|
||||
@@ -248,20 +278,23 @@ class ComicTile extends StatelessWidget {
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.toOpacity(0.2),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
child: Hero(
|
||||
tag: "cover${comic.id}${comic.sourceKey}",
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.toOpacity(0.2),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: buildImage(context),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: buildImage(context),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
@@ -1400,7 +1433,7 @@ class _RatingWidgetState extends State<RatingWidget> {
|
||||
}
|
||||
if (full < widget.count) {
|
||||
children.add(ClipRect(
|
||||
clipper: SMClipper(rating: star() * widget.size),
|
||||
clipper: _SMClipper(rating: star() * widget.size),
|
||||
child: Icon(
|
||||
Icons.star,
|
||||
size: widget.size,
|
||||
@@ -1449,10 +1482,10 @@ class _RatingWidgetState extends State<RatingWidget> {
|
||||
}
|
||||
}
|
||||
|
||||
class SMClipper extends CustomClipper<Rect> {
|
||||
class _SMClipper extends CustomClipper<Rect> {
|
||||
final double rating;
|
||||
|
||||
SMClipper({required this.rating});
|
||||
_SMClipper({required this.rating});
|
||||
|
||||
@override
|
||||
Rect getClip(Size size) {
|
||||
@@ -1460,7 +1493,52 @@ class SMClipper extends CustomClipper<Rect> {
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(SMClipper oldClipper) {
|
||||
bool shouldReclip(_SMClipper oldClipper) {
|
||||
return rating != oldClipper.rating;
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleComicTile extends StatelessWidget {
|
||||
const SimpleComicTile({super.key, required this.comic, this.onTap});
|
||||
|
||||
final Comic comic;
|
||||
|
||||
final void Function()? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var image = _findImageProvider(comic);
|
||||
|
||||
var child = image == null
|
||||
? const SizedBox()
|
||||
: AnimatedImage(
|
||||
image: image,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
filterQuality: FilterQuality.medium,
|
||||
);
|
||||
|
||||
return AnimatedTapRegion(
|
||||
borderRadius: 8,
|
||||
onTap: onTap ?? () {
|
||||
context.to(
|
||||
() => ComicPage(
|
||||
id: comic.id,
|
||||
sourceKey: comic.sourceKey,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 92,
|
||||
height: 114,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -41,39 +41,44 @@ class AnimatedTapRegion extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AnimatedTapRegionState extends State<AnimatedTapRegion> {
|
||||
bool isScaled = false;
|
||||
|
||||
bool isHovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) {
|
||||
isHovered = true;
|
||||
if (!isScaled) {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
if (isHovered) {
|
||||
setState(() => isScaled = true);
|
||||
}
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
isHovered = true;
|
||||
});
|
||||
},
|
||||
onExit: (_) {
|
||||
isHovered = false;
|
||||
if(isScaled) {
|
||||
setState(() => isScaled = false);
|
||||
}
|
||||
setState(() {
|
||||
isHovered = false;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: AnimatedScale(
|
||||
duration: _fastAnimationDuration,
|
||||
scale: isScaled ? 1.1 : 1,
|
||||
child: widget.child,
|
||||
child: AnimatedContainer(
|
||||
duration: _fastAnimationDuration,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(widget.borderRadius),
|
||||
boxShadow: isHovered
|
||||
? [
|
||||
BoxShadow(
|
||||
color: context.colorScheme.outline,
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
]
|
||||
: [
|
||||
BoxShadow(
|
||||
color: context.colorScheme.outlineVariant,
|
||||
blurRadius: 1,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: widget.child,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@@ -200,15 +200,17 @@ class NaviPaneState extends State<NaviPane>
|
||||
}
|
||||
|
||||
Widget buildMainView() {
|
||||
return Navigator(
|
||||
observers: [widget.observer],
|
||||
key: widget.navigatorKey,
|
||||
onGenerateRoute: (settings) => AppPageRoute(
|
||||
preventRebuild: false,
|
||||
isRootRoute: true,
|
||||
builder: (context) {
|
||||
return _NaviMainView(state: this);
|
||||
},
|
||||
return HeroControllerScope(
|
||||
controller: MaterialApp.createMaterialHeroController(),
|
||||
child: Navigator(
|
||||
observers: [widget.observer],
|
||||
key: widget.navigatorKey,
|
||||
onGenerateRoute: (settings) => AppPageRoute(
|
||||
preventRebuild: false,
|
||||
builder: (context) {
|
||||
return _NaviMainView(state: this);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -362,16 +364,14 @@ class _SideNaviWidget extends StatelessWidget {
|
||||
color: enabled ? colorScheme.primaryContainer : null,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: showTitle ? Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 12),
|
||||
Text(entry.label)
|
||||
],
|
||||
) : Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: icon,
|
||||
),
|
||||
child: showTitle
|
||||
? Row(
|
||||
children: [icon, const SizedBox(width: 12), Text(entry.label)],
|
||||
)
|
||||
: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
).paddingVertical(4);
|
||||
}
|
||||
@@ -395,16 +395,14 @@ class _PaneActionWidget extends StatelessWidget {
|
||||
duration: const Duration(milliseconds: 180),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
height: 38,
|
||||
child: showTitle ? Row(
|
||||
children: [
|
||||
icon,
|
||||
const SizedBox(width: 12),
|
||||
Text(entry.label)
|
||||
],
|
||||
) : Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: icon,
|
||||
),
|
||||
child: showTitle
|
||||
? Row(
|
||||
children: [icon, const SizedBox(width: 12), Text(entry.label)],
|
||||
)
|
||||
: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
).paddingVertical(4);
|
||||
}
|
||||
|
Reference in New Issue
Block a user