Anonymous
Как мне получить размытие этого слоя на границах?
Сообщение
Anonymous » 03 дек 2024, 12:28
Я создаю пользовательскую карту во 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 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()
.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()
.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,
),
),
],
),
),
),
],
),
),
]),
)
],
),
),
)
],
),
)
полученный результат:
[!actual](
https://i.sstatic.net/cukz2pgY.png )
желаемый результат, который я хочу:
[!desired](
https://i.sstatic.net/JfN8aie2.png )
Что я пробовал:
Использовал BackdropFilter с ClipRRect для применения эффекта размытия.
Добавлены градиент и тень для улучшения внешнего вида.
Подробнее здесь:
https://stackoverflow.com/questions/792 ... he-borders
1733218095
Anonymous
Я создаю пользовательскую карту во Flutter и хочу, чтобы нижняя область имела эффект размытия с закругленными углами. Однако я изо всех сил пытаюсь добиться желаемого вида границ. Вот виджет, который я реализовал: [code]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 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() .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() .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, ), ), ], ), ), ), ], ), ), ]), ) ], ), ), ) ], ), ) [/code] полученный результат: [!actual](https://i.sstatic.net/cukz2pgY.png) желаемый результат, который я хочу: [!desired](https://i.sstatic.net/JfN8aie2.png) Что я пробовал: Использовал BackdropFilter с ClipRRect для применения эффекта размытия. Добавлены градиент и тень для улучшения внешнего вида. Подробнее здесь: [url]https://stackoverflow.com/questions/79246988/how-should-i-obtain-this-layer-blur-at-the-borders[/url]