Flutter自定义卡片底部圆角边框模糊效果实现求助
Flutter自定义卡片底部圆角模糊效果问题
我正在Flutter中制作自定义卡片,希望底部区域带有圆角的模糊效果,但边框处的效果始终无法达到预期。以下是我实现的Widget代码:
Container( height: widget.height.h, width: 100.w, decoration: BoxDecoration( //color: Colors.blue, // You can change this color borderRadius: BorderRadius.circular(20), ), child: Stack( children: [ SizedBox( height: 65.h, width: 100.w, child: PageView.builder( itemCount: widget.event.assets.length, onPageChanged: (value) { setState(() { pageIndex = value; }); if (videoPlayerController != null) { togglePlayPause(isPlay: false); } if (widget.event.assets[value].type == MediaEnum.video.name) { initPlayer = true; initVideoPlayer( videoUrl: widget.event.assets[value].url, isAutoPlay: true, ); } }, itemBuilder: (context, index) { return AspectRatio( aspectRatio: 2 / 3, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: widget.event.assets[index].type == MediaEnum.video.name ? _buildVideoContent(index) : _buildImageContent(index), ), ), ); }, ), ), Positioned( bottom: 0, child: Stack( children: [ // Shadow container Container( height: 11.5.h, // 12.h,//110, width: 96.w, decoration: BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 10, // Increase blur for more fade spreadRadius: 10, // Increase spread for wider fade offset: Offset(0, -1), // Center the shadow ), ], ), ), // Main content with blur ClipRRect( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20), ), child: BackdropFilter( blendMode: BlendMode.src, filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( height: 11.5.h, //110 width: 100.w, padding: EdgeInsets.symmetric(horizontal: 3.w), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent.withOpacity(0.1), Colors.black.withOpacity(0.0), Colors.black.withOpacity(0.1), Colors.black.withOpacity(0.15), Colors.black.withOpacity(0.2), ], stops: [0.0, 0.1, 0.3, 0.7, 1.0], ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(20), bottomRight: Radius.circular(50), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 1.5.h, ), Row( children: [ SizedBox( width: 89.w, child: Text( widget.event.name, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 19.5.sp, fontWeight: FontWeight.w700, color: colorScheme.background, ), ), ) ], ), Text( StringExtension.formatDateForNewEventCard( widget.event.startDate), style: TextStyle( fontSize: 15.sp, fontWeight: FontWeight.w700, color: colorScheme.background.withOpacity(0.8), ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [ Text( "${widget.event.priceRangeStart.toIndianRupeeString()}", style: TextStyle( color: colorScheme.background, fontSize: 16.sp, fontWeight: FontWeight.bold, ), ), SizedBox( width: 55.w, child: Text( " onwards, (${widget.event.coverChargeEnabled ? 'Cover Charge Applicable' : 'Free'})", overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 15.5.sp, fontWeight: FontWeight.w500, color: colorScheme.background .withOpacity(0.8), ), ), ), ]), if (widget.isInListing) Padding( padding: EdgeInsets.only(right: 2.h), child: SvgPicture.asset( AssetConstants.longArrowRight, width: 30, )) ], ) ], ), ), ), ), ], ), ), Positioned( bottom: 110, right: 10, child: widget.event.assets[pageIndex].type == MediaEnum.video.name && isPlaying ? ValueListenableBuilder( valueListenable: widget.isMutedNotifier, builder: (context, isMuted, child) { return GestureDetector( onTap: () { if (widget.isInListing) { toggleMuteUnmuteForValueListener(isMute); } else { // print("is mute ${isMute}"); toggleMuteUnmute(); } }, child: Container( color: Colors.transparent, height: 12.5.h, width: 12.5.h, child: Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ SvgPicture.asset( widget.isInListing ? (isMuted ? AssetConstants.muteFillIcon : AssetConstants.unmuteFillIcon) : (isMute ? AssetConstants.muteFillIcon : AssetConstants.unmuteFillIcon), color: Colors.white, ), ], ), ), ); }) : SizedBox( width: 4.5.w, height: 3.h, ), ), Positioned( bottom: 100, left: 0, right: 0, child: Container( // height: 4.h, padding: EdgeInsets.only( left: 1.5.h, right: widget.isInListing ? 1.8.h : 0, bottom: 2.h), width: 95.w, child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ //play pause button widget.isInListing ? SizedBox() : widget.event.assets[pageIndex].type == MediaEnum.video.name && widget.isInListing ? GestureDetector( onTap: () { print("is playingMain ${isPlaying}"); togglePlayPause(isPlay: !isPlaying); }, child: Center( child: CircleAvatar( radius: 3.5.w, backgroundColor: Theme.of(context).colorScheme.shadow, child: Icon( isPlaying ? Icons.pause_rounded : Icons.play_arrow_rounded, color: Colors.white, size: 4.5.w, ), ), ), ) : SizedBox( width: 4.5.w, height: 3.h, ), //page indicator widget.event.assets.length <= 1 ? const SizedBox() : Center( child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ ...List.generate( widget.event.assets.length, (dotIndex) => Padding( padding: dotIndex == widget.event.assets.length - 1 ? EdgeInsets.zero : EdgeInsets.only(right: 2.0.w), child: Container( height: 1.9.w, width: 1.9.w, decoration: BoxDecoration( shape: BoxShape.circle, color: pageIndex == dotIndex ? Theme.of(context) .colorScheme .background : Theme.of(context) .colorScheme .secondaryContainer), ), )), ], ), ), // mute button ], ), ), ), if (widget.isInListing) Positioned( top: 0, child: SizedBox( height: 105.w, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (widget.event.pub != null) Padding( padding: EdgeInsets.symmetric( horizontal: 3.w, vertical: 2.h), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 80.w, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( onTap: () { AnalyticsService().logEvent( eventName: 'view_club', paras: { 'club_id': widget .event.pub!.id .toString(), }); navigator<NavigationService>() .navigateTo( UserRoutes.clubProfileRoute, queryParams: { 'id': widget.event.pub!.id .toString() }); }, child: CircleAvatar( radius: 4.5.w, backgroundImage: CachedNetworkImageProvider( CustomImageProvider.getImageUrl( widget.event.pub?.logo ?? "", // widget.event.pub?.coverImageUrl ?? '', ImageType.profile)), ), ), SizedBox( width: 2.w, ), Expanded( child: InkWell( onTap: () { navigator<NavigationService>() .navigateTo( UserRoutes .clubProfileRoute, queryParams: { 'id': widget.event.pub!.id .toString() }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, children: [ GestureDetector( onTap: () { AnalyticsService().logEvent( eventName: 'view_club', paras: { 'club_id': widget .event.pub!.id .toString(), }); navigator< NavigationService>() .navigateTo( UserRoutes .clubProfileRoute, queryParams: { 'id': widget .event.pub!.id .toString() }); }, child: Text( widget.event.pub!.fullName, overflow: TextOverflow.ellipsis, maxLines: 1, style: themeData .textTheme.bodyMedium! .copyWith( color: themeData .colorScheme .background, fontWeight: FontWeight.w600, fontSize: 16.5.sp), ), ), Text( widget.event.address ?.vicinity ?? '', maxLines: 1, overflow: TextOverflow.ellipsis, style: themeData .textTheme.bodySmall! .copyWith( fontWeight: FontWeight.w400, fontSize: 14.sp, color: themeData .colorScheme.background, ), ), ], ), ), ), ], ), ), ]), ) ], ), ), ) ], ), )
效果对比
实际效果:

期望效果:

已尝试方法
- 使用ClipRRect结合BackdropFilter添加模糊效果
- 添加渐变和阴影优化外观
内容的提问来源于stack exchange,提问作者Anshuman Sharma
相关产品推荐
相关产品推荐

