我正在Flutter中创建一个自定义卡片,我希望底部区域具有圆角的模糊效果。然而,我正在努力实现所需的边界外观。这是我实现的小部件:
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,
),
),
],
),
),
),
],
),
),
]),
)
],
),
),
)
],
),
)
我尝试过的: 使用 BackdropFilter 和 ClipRRect 来应用模糊效果。 添加渐变和阴影以增强外观。
我已经用 LinearGradient 尝试过了。希望有帮助
Column(
children: [
Stack(alignment: Alignment.bottomCenter, children: [
Image.network(
'image_url',
fit: BoxFit.fill,
),
Container(
alignment: Alignment.bottomCenter,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("One More Light",style: TextStyle(fontSize: 30,color: Colors.white),),
Text("Starts from...",style: TextStyle(fontSize: 30,color: Colors.white),),
],
),
width: MediaQuery.of(context).size.width,
height: 100,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent, // Start with transparent
Colors.black.withOpacity(0.7), // Light black color with transparency
],
stops: [0.0, 1.0],
),
),
),
]),
],
),
根据您的要求调整容器的高度。