397 lines
14 KiB
Dart
397 lines
14 KiB
Dart
import 'dart:ui';
|
||
|
||
import 'package:cached_network_image/cached_network_image.dart';
|
||
import 'package:flip_card/flip_card.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:get/get.dart';
|
||
import 'package:gradient_borders/box_borders/gradient_box_border.dart';
|
||
import 'package:photo_view/photo_view.dart';
|
||
import 'package:photo_view/photo_view_gallery.dart';
|
||
import 'package:wallpaperx/common/components/view_state_widget.dart';
|
||
import 'package:wallpaperx/entity/image_model.dart';
|
||
import 'package:wallpaperx/generated/assets.dart';
|
||
|
||
import 'wallpaper_detail_controller.dart';
|
||
|
||
class WallpaperDetailView extends GetView<WallpaperDetailController> {
|
||
const WallpaperDetailView({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: Colors.black,
|
||
extendBodyBehindAppBar: true,
|
||
extendBody: true,
|
||
body: FlipCard(
|
||
flipOnTouch: false,
|
||
controller: controller.flipCardController,
|
||
fill: Fill.fillBack,
|
||
side: CardSide.FRONT,
|
||
direction: FlipDirection.HORIZONTAL,
|
||
front: Stack(
|
||
fit: StackFit.expand,
|
||
children: [
|
||
_buildPhotoView(),
|
||
_buildBottomOption(context),
|
||
],
|
||
),
|
||
back: _buildImageInfo(context),
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 图片PageView
|
||
Widget _buildPhotoView() {
|
||
return Obx(() {
|
||
if (controller.wallpaperList.isEmpty) {
|
||
return Container();
|
||
}
|
||
return PhotoViewGallery.builder(
|
||
itemCount: controller.wallpaperList.length,
|
||
scrollPhysics: const BouncingScrollPhysics(),
|
||
wantKeepAlive: true,
|
||
gaplessPlayback: true,
|
||
builder: (context, index) {
|
||
return PhotoViewGalleryPageOptions.customChild(
|
||
child: PhotoView(
|
||
enableRotation: true,
|
||
imageProvider: CachedNetworkImageProvider(
|
||
controller.wallpaperList[index].imageUrl,
|
||
),
|
||
initialScale: PhotoViewComputedScale.covered,
|
||
minScale: PhotoViewComputedScale.contained * 0.5,
|
||
maxScale: PhotoViewComputedScale.covered * 3,
|
||
loadingBuilder: (context, event) => loadingView(),
|
||
errorBuilder: (BuildContext context, Object error,
|
||
StackTrace? stackTrace) =>
|
||
errorView,
|
||
),
|
||
);
|
||
},
|
||
pageController: controller.pageController,
|
||
onPageChanged: (index) => controller.onPageChanged(index),
|
||
);
|
||
});
|
||
}
|
||
|
||
/// 底部选项
|
||
Widget _buildBottomOption(context) {
|
||
return Positioned(
|
||
bottom: 0,
|
||
left: 0,
|
||
right: 0,
|
||
child: Column(
|
||
children: [
|
||
ClipRect(
|
||
child: BackdropFilter(
|
||
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||
child: Container(
|
||
padding: EdgeInsets.only(
|
||
bottom: MediaQuery.of(context).padding.bottom),
|
||
width: double.infinity,
|
||
height: 70.h,
|
||
decoration: const BoxDecoration(
|
||
color: Color.fromRGBO(0, 0, 0, 0.65),
|
||
),
|
||
clipBehavior: Clip.hardEdge,
|
||
child: Row(
|
||
children: [
|
||
16.horizontalSpace,
|
||
_buildOptionItem(
|
||
Assets.iconBackBig,
|
||
'',
|
||
controller.showAd,
|
||
),
|
||
Expanded(child: Container()),
|
||
Obx(() => _buildOptionItem(
|
||
controller.isFavorite.value
|
||
? Assets.iconFavorite
|
||
: Assets.iconUnFavorite,
|
||
'Favorite',
|
||
controller.collectionImage,
|
||
)),
|
||
24.horizontalSpace,
|
||
_buildOptionItem(
|
||
Assets.iconDownload,
|
||
'',
|
||
controller.downloadImg,
|
||
),
|
||
24.horizontalSpace,
|
||
_buildOptionItem(
|
||
Assets.iconImgInfo,
|
||
'',
|
||
controller.flipCard,
|
||
),
|
||
16.horizontalSpace,
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
/// 图片详情
|
||
Widget _buildImageInfo(context) {
|
||
return GetBuilder<WallpaperDetailController>(
|
||
id: 'buildImageInfo',
|
||
builder: (logic) {
|
||
ImageModel model = controller.wallpaperList[controller.position];
|
||
return Container(
|
||
clipBehavior: Clip.hardEdge,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(12).r,
|
||
image: DecorationImage(
|
||
fit: BoxFit.cover,
|
||
image: CachedNetworkImageProvider(
|
||
controller.wallpaperList[controller.position].imageUrl,
|
||
),
|
||
),
|
||
),
|
||
child: BackdropFilter(
|
||
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||
child: Container(
|
||
width: double.infinity,
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xff000000).withOpacity(.8),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
63.verticalSpace,
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Stack(
|
||
clipBehavior: Clip.none,
|
||
children: [
|
||
Image.asset(
|
||
Assets.iconImgDelTip,
|
||
width: 52.w,
|
||
height: 52.w,
|
||
),
|
||
Positioned(
|
||
left: -15.w,
|
||
top: 15.w,
|
||
child: Image.asset(
|
||
Assets.iconStarSymbolS,
|
||
width: 20.w,
|
||
),
|
||
),
|
||
Positioned(
|
||
left: -8.w,
|
||
top: 10.w,
|
||
child: Image.asset(
|
||
Assets.iconStarSymbolM,
|
||
width: 20.w,
|
||
),
|
||
),
|
||
Positioned(
|
||
right: -12.w,
|
||
bottom: -2.w,
|
||
child: Image.asset(
|
||
Assets.iconStarSymbolL,
|
||
width: 25.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
37.verticalSpace,
|
||
Expanded(
|
||
child: ListView(
|
||
padding: const EdgeInsets.symmetric(horizontal: 30).w,
|
||
children: [
|
||
Wrap(
|
||
spacing: 8.w,
|
||
runSpacing: 8.w,
|
||
children: model.tags!
|
||
.map((e) => tagItem(e.taglabel))
|
||
.toList(),
|
||
),
|
||
32.verticalSpace,
|
||
genInfo(
|
||
Image.asset(
|
||
Assets.iconSampler,
|
||
width: 28.w,
|
||
),
|
||
"Sampler:",
|
||
model.generateInfo!.samplingMethod ?? "",
|
||
),
|
||
23.verticalSpace,
|
||
genInfo(
|
||
Image.asset(
|
||
Assets.iconCfgScale,
|
||
width: 28.w,
|
||
),
|
||
"CFG scale:",
|
||
model.generateInfo!.cfgScale.toString(),
|
||
),
|
||
23.verticalSpace,
|
||
genInfo(
|
||
Image.asset(
|
||
Assets.iconSteps,
|
||
width: 28.w,
|
||
),
|
||
"Steps:",
|
||
model.generateInfo!.samplingStep.toString(),
|
||
),
|
||
23.verticalSpace,
|
||
genInfo(
|
||
Image.asset(
|
||
Assets.iconSeed,
|
||
width: 28.w,
|
||
),
|
||
"Seed:",
|
||
model.generateInfo!.seed.toString(),
|
||
),
|
||
20.verticalSpace,
|
||
Text(
|
||
"Prompt",
|
||
textAlign: TextAlign.start,
|
||
style:
|
||
TextStyle(color: Colors.white, fontSize: 16.sp),
|
||
),
|
||
12.verticalSpace,
|
||
Text(
|
||
model.generateInfo?.prompt ?? "",
|
||
textAlign: TextAlign.start,
|
||
style: TextStyle(
|
||
color: const Color(0xffA1A9B3), fontSize: 14.sp),
|
||
),
|
||
20.verticalSpace,
|
||
Text(
|
||
"Negative Prompt",
|
||
textAlign: TextAlign.start,
|
||
style:
|
||
TextStyle(color: Colors.white, fontSize: 16.sp),
|
||
),
|
||
12.verticalSpace,
|
||
Text(
|
||
model.generateInfo?.negativePrompt ?? "",
|
||
textAlign: TextAlign.start,
|
||
style: TextStyle(
|
||
color: const Color(0xffA1A9B3), fontSize: 14.sp),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
24.verticalSpace,
|
||
Container(
|
||
height: 70.h,
|
||
width: double.infinity,
|
||
padding: EdgeInsets.only(
|
||
left: 16,
|
||
right: 16,
|
||
bottom: MediaQuery.of(context).padding.bottom,
|
||
).w,
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
_buildOptionItem(
|
||
Assets.iconBackBig,
|
||
'',
|
||
controller.showAd,
|
||
),
|
||
GestureDetector(
|
||
onTap: () =>
|
||
controller.copyPrompt(model.generateInfo),
|
||
child: Container(
|
||
width: 200.w,
|
||
height: 40.h,
|
||
alignment: Alignment.center,
|
||
decoration: BoxDecoration(
|
||
color: Colors.white.withOpacity(.2),
|
||
borderRadius: BorderRadius.circular(50).r,
|
||
),
|
||
child: Text(
|
||
"Copy Prompt",
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(
|
||
color: Colors.white, fontSize: 14.sp),
|
||
),
|
||
),
|
||
),
|
||
_buildOptionItem(
|
||
Assets.iconImgInfo,
|
||
'',
|
||
controller.flipCard,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
|
||
Widget genInfo(icon, title, content) {
|
||
return Row(
|
||
children: [
|
||
icon,
|
||
16.horizontalSpace,
|
||
SizedBox(
|
||
width: 250.w,
|
||
child: Text(
|
||
title + content,
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
textAlign: TextAlign.start,
|
||
style: TextStyle(color: Colors.white, fontSize: 16.sp),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget tagItem(tag) {
|
||
return GestureDetector(
|
||
onTap: () => controller.toCategoryItem(tag),
|
||
child: Container(
|
||
decoration: BoxDecoration(
|
||
border: const GradientBoxBorder(
|
||
gradient: LinearGradient(colors: [
|
||
Color(0xffBEEF32),
|
||
Color(0xff2795E5),
|
||
Color(0xff8041FD),
|
||
]),
|
||
width: 1,
|
||
),
|
||
borderRadius: BorderRadius.circular(50.r),
|
||
),
|
||
child: Container(
|
||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12).w,
|
||
child: Text(
|
||
"#$tag",
|
||
style: TextStyle(color: Colors.white, fontSize: 14.sp),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildOptionItem(String iconName, String option, Function() onTap) {
|
||
return Material(
|
||
color: Colors.transparent,
|
||
child: InkWell(
|
||
onTap: onTap,
|
||
child: Image.asset(
|
||
iconName,
|
||
width: 24.w,
|
||
height: 24.w,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|