Flutter中如何创建带圆角、带模糊效果的自定义裁剪形状?
Absolutely! Flutter gives you all the tools you need to clip a custom shape, add a blur effect, and define rounded corners—let’s break down how to pull this off step by step:
Key Components You’ll Use
ClipPath: Clips any widget to a custom shape defined by aCustomClipper<Path>.BackdropFilter: Applies a blur effect to the content behind the widget (perfect for that frosted glass look).CustomClipper<Path>: Lets you define your exact desired shape, including rounded corners where you need them.
Step 1: Build Your Custom Clipper with Rounded Corners
First, create a CustomClipper class to outline your target shape. We’ll use Path.arcToPoint() to add smooth rounded corners to each edge.
For example, here’s a clipper for a marker-style shape with rounded edges:
class CustomMarkerClipper extends CustomClipper<Path> { final double borderRadius; CustomMarkerClipper({this.borderRadius = 16.0}); @override Path getClip(Size size) { final path = Path(); // Start at top-left rounded corner path.moveTo(borderRadius, 0); // Draw top edge to top-right rounded corner path.lineTo(size.width - borderRadius, 0); path.arcToPoint( Offset(size.width, borderRadius), radius: Radius.circular(borderRadius), clockwise: true, ); // Draw slanted right edge to bottom-right rounded corner path.lineTo(size.width * 0.8, size.height - borderRadius); path.arcToPoint( Offset(size.width * 0.8 - borderRadius, size.height), radius: Radius.circular(borderRadius), clockwise: true, ); // Draw bottom edge to bottom-left rounded corner path.lineTo(borderRadius, size.height); path.arcToPoint( Offset(0, size.height - borderRadius), radius: Radius.circular(borderRadius), clockwise: true, ); // Draw left edge back to starting point path.lineTo(0, borderRadius); path.arcToPoint( Offset(borderRadius, 0), radius: Radius.circular(borderRadius), clockwise: true, ); return path; } @override bool shouldReclip(covariant CustomMarkerClipper oldClipper) { return oldClipper.borderRadius != borderRadius; } }
Step 2: Combine Clipping and Blur
Wrap your content (or a semi-transparent backdrop) with ClipPath and BackdropFilter to get the blurred, clipped shape. A semi-transparent fill will help the blur effect stand out against the background:
ClipPath( clipper: CustomMarkerClipper(borderRadius: 20), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Container( width: 300, height: 150, decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), // Soft semi-transparent fill border: Border.all(color: Colors.white.withOpacity(0.3)), ), child: const Center( child: Text( "Blurred Custom Shape", style: TextStyle(color: Colors.white, fontSize: 18), ), ), ), ), )
Step 3: Full Working Example
Here’s a complete screen that uses a background image to showcase the blur effect clearly:
import 'dart:ui'; import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Stack( children: [ // Background image to show blur effect Image.network( 'https://picsum.photos/800/1200', fit: BoxFit.cover, width: double.infinity, height: double.infinity, ), // Blurred custom shape positioned in the center Center( child: ClipPath( clipper: CustomMarkerClipper(borderRadius: 20), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12), child: Container( width: 320, height: 160, decoration: BoxDecoration( color: Colors.blueGrey.withOpacity(0.15), border: Border.all(color: Colors.white.withOpacity(0.4)), ), child: const Padding( padding: EdgeInsets.all(16.0), child: Text( "This is a custom rounded blurred shape!", style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.w500, ), textAlign: TextAlign.center, ), ), ), ), ), ), ], ), ), ); } } // Include the CustomMarkerClipper class from Step 1 here class CustomMarkerClipper extends CustomClipper<Path> { final double borderRadius; CustomMarkerClipper({this.borderRadius = 16.0}); @override Path getClip(Size size) { final path = Path(); path.moveTo(borderRadius, 0); path.lineTo(size.width - borderRadius, 0); path.arcToPoint( Offset(size.width, borderRadius), radius: Radius.circular(borderRadius), clockwise: true, ); path.lineTo(size.width * 0.8, size.height - borderRadius); path.arcToPoint( Offset(size.width * 0.8 - borderRadius, size.height), radius: Radius.circular(borderRadius), clockwise: true, ); path.lineTo(borderRadius, size.height); path.arcToPoint( Offset(0, size.height - borderRadius), radius: Radius.circular(borderRadius), clockwise: true, ); path.lineTo(0, borderRadius); path.arcToPoint( Offset(borderRadius, 0), radius: Radius.circular(borderRadius), clockwise: true, ); return path; } @override bool shouldReclip(covariant CustomMarkerClipper oldClipper) { return oldClipper.borderRadius != borderRadius; } }
Quick Tips
- Adjust Blur Strength: Tweak
sigmaXandsigmaYinImageFilter.blur()—higher values mean a more intense blur. - Optimize Performance:
BackdropFiltercan be resource-heavy. Limit its size to only the area you need, and avoid nesting multiple instances. - Customize the Shape: Modify the
getClip()method inCustomMarkerClipperto match your exact design. UsePathmethods likequadraticBezierTo()orarcTo()to create any curve or angle.
内容的提问来源于stack exchange,提问作者Rafiqul Hasan

