Improve UI

This commit is contained in:
2025-01-15 18:24:38 +08:00
parent d874920c88
commit 399b9abaee
9 changed files with 358 additions and 227 deletions

View File

@@ -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,
),
);
}
}

View File

@@ -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,
),
),
);

View File

@@ -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);
}