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 { 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: false, imageProvider: CachedNetworkImageProvider( controller.wallpaperList[index].imageUrl, ), initialScale: PhotoViewComputedScale.contained, 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, '', Get.back, ), 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.flipCardAndShowAd, ), 16.horizontalSpace, ], ), ), ), ), ], ), ); } /// 图片详情 Widget _buildImageInfo(context) { return GetBuilder( 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, color: Colors.white, 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, color: Colors.white, width: 28.w, ), "Sampler:", model.generateInfo!.samplingMethod ?? "", ), 23.verticalSpace, genInfo( Image.asset( Assets.iconCfgScale, color: Colors.white, width: 28.w, ), "CFG scale:", model.generateInfo!.cfgScale.toString(), ), 23.verticalSpace, genInfo( Image.asset( Assets.iconSteps, color: Colors.white, width: 28.w, ), "Steps:", model.generateInfo!.samplingStep.toString(), ), 23.verticalSpace, genInfo( Image.asset( Assets.iconSeed, color: Colors.white, 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, '', Get.back, ), 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, ), ), ); } }