<h1 id="how-to-blur-background-widgets-using-backdropfilter-in-flutter">How to Blur Background Widgets Using BackdropFilter in Flutter</h1> <p>Creating a frosted glass effect or blurred UI components has become a popular design trend in modern applications. Flutter provides the <code>BackdropFilter</code> widget to easily achieve this effect by applying various filters to the content behind a widget. In this tutorial, we'll explore how to create beautiful blur effects in your Flutter applications.</p> <h2 id="understanding-backdropfilter">Understanding BackdropFilter</h2> <p>The <code>BackdropFilter</code> widget in Flutter applies a filter to all the widgets that are painted before it in the same layer. It's commonly used with the <code>ImageFilter.blur</code> filter to create frosted glass or blur effects.</p> <p>Here's the basic structure:</p> <pre>BackdropFilter( filter: ImageFilter.blur( sigmaX: 5.0, sigmaY: 5.0, ), child: Container( // Your content here ), ) </pre> <p>The <code>sigmaX</code> and <code>sigmaY</code> parameters control the amount of blur in the horizontal and vertical directions.</p> <p><img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8IS0tIEJhY2tncm91bmQgLS0+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjUwMCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiNmYWZhZmEiLz4KICAKICA8IS0tIENvbG9yZnVsIEJhY2tncm91bmQgQ2lyY2xlcyAtLT4KICA8Y2lyY2xlIGN4PSIxMDAiIGN5PSIxNTAiIHI9IjcwIiBmaWxsPSJyZ2JhKDc3LCAxODIsIDI1NSwgMC43KSIvPgogIDxjaXJjbGUgY3g9IjIwMCIgY3k9IjEwMCIgcj0iNjAiIGZpbGw9InJnYmEoMjU1LCA5OSwgMTMyLCAwLjcpIi8+CiAgPGNpcmNsZSBjeD0iMzAwIiBjeT0iMTgwIiByPSI4MCIgZmlsbD0icmdiYSgyNTUsIDE5OSwgMCwgMC41KSIvPgogIAogIDwhLS0gRGl2aWRlciAtLT4KICA8bGluZSB4MT0iMjUwIiB5MT0iMCIgeDI9IjI1MCIgeTI9IjMwMCIgc3Ryb2tlPSIjYWFhIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1kYXNoYXJyYXk9IjUsIDUiLz4KICAKICA8IS0tIExhYmVscyAtLT4KICA8dGV4dCB4PSIxMjUiIHk9IjI4MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjE0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5PcmlnaW5hbDwvdGV4dD4KICA8dGV4dCB4PSIzNzUiIHk9IjI4MCIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjE0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5XaXRoIEJhY2tkcm9wRmlsdGVyIEJsdXI8L3RleHQ+CiAgCiAgPCEtLSBCbHVycmVkIFNpZGUgKFNpbXVsYXRlZCBCbHVyKSAtLT4KICA8ZGVmcz4KICAgIDxmaWx0ZXIgaWQ9ImJsdXJGaWx0ZXIiPgogICAgICA8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSI1IiAvPgogICAgPC9maWx0ZXI+CiAgPC9kZWZzPgogIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI1MCwgMCkiPgogICAgPGcgZmlsdGVyPSJ1cmwoI2JsdXJGaWx0ZXIpIj4KICAgICAgPGNpcmNsZSBjeD0iMTAwIiBjeT0iMTUwIiByPSI3MCIgZmlsbD0icmdiYSg3NywgMTgyLCAyNTUsIDAuNykiLz4KICAgICAgPGNpcmNsZSBjeD0iMjAwIiBjeT0iMTAwIiByPSI2MCIgZmlsbD0icmdiYSgyNTUsIDk5LCAxMzIsIDAuNykiLz4KICAgICAgPGNpcmNsZSBjeD0iMzAwIiBjeT0iMTgwIiByPSI4MCIgZmlsbD0icmdiYSgyNTUsIDE5OSwgMCwgMC41KSIvPgogICAgPC9nPgogICAgPCEtLSBHbGFzc3kgQ29udGFpbmVyIC0tPgogICAgPHJlY3QgeD0iNzUiIHk9IjExMCIgd2lkdGg9IjE1MCIgaGVpZ2h0PSI4MCIgcng9IjEwIiByeT0iMTAiIGZpbGw9InJnYmEoMjU1LCAyNTUsIDI1NSwgMC4zKSIgc3Ryb2tlPSJyZ2JhKDI1NSwgMjU1LCAyNTUsIDAuNSkiIHN0cm9rZS13aWR0aD0iMSIvPgogICAgPHRleHQgeD0iMTUwIiB5PSIxNTUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxNCIgZmlsbD0id2hpdGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPkZyb3N0ZWQgR2xhc3MgRWZmZWN0PC90ZXh0PgogIDwvZz4KICAKICA8IS0tIE9yaWdpbmFsIENvbnRhaW5lciAtLT4KICA8cmVjdCB4PSI3NSIgeT0iMTEwIiB3aWR0aD0iMTUwIiBoZWlnaHQ9IjgwIiByeD0iMTAiIHJ5PSIxMCIgZmlsbD0icmdiYSgyNTUsIDI1NSwgMjU1LCAwLjcpIiBzdHJva2U9IiNjY2MiIHN0cm9rZS13aWR0aD0iMSIvPgogIDx0ZXh0IHg9IjE1MCIgeT0iMTU1IiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTQiIGZpbGw9IiMzMzMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPlJlZ3VsYXIgQ29udGFpbmVyPC90ZXh0PgogIAogIDwhLS0gQ29kZSBBbm5vdGF0aW9ucyAtLT4KICA8dGV4dCB4PSIxNTAiIHk9IjIyMCIgZm9udC1mYW1pbHk9Im1vbm9zcGFjZSIgZm9udC1zaXplPSIxMCIgZmlsbD0iIzY2NiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+Q29udGFpbmVyKGNvbG9yOiBDb2xvcnMud2hpdGUud2l0aE9wYWNpdHkoMC43KSk8L3RleHQ+CiAgCiAgPHRleHQgeD0iMzc1IiB5PSIyMjAiIGZvbnQtZmFtaWx5PSJtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTAiIGZpbGw9IiM2NjYiIHRleHQtYW5jaG9yPSJtaWRkbGUiPkJhY2tkcm9wRmlsdGVyKGZpbHRlcjogSW1hZ2VGaWx0ZXIuYmx1cihzaWdtYVg6IDUsIHNpZ21hWTogNSkpPC90ZXh0PgogIDx0ZXh0IHg9IjM3NSIgeT0iMjM1IiBmb250LWZhbWlseT0ibW9ub3NwYWNlIiBmb250LXNpemU9IjEwIiBmaWxsPSIjNjY2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5Db250YWluZXIoY29sb3I6IENvbG9ycy53aGl0ZS53aXRoT3BhY2l0eSgwLjMpKTwvdGV4dD4KICAKICA8IS0tIFNtYWxsIEluZGljYXRvcnMgLS0+CiAgPGNpcmNsZSBjeD0iMTUwIiBjeT0iNjUiIHI9IjMiIGZpbGw9IiM2NjYiLz4KICA8cGF0aCBkPSJNIDE1MCA2NSBMIDE1MCAxMDAiIHN0cm9rZT0iIzY2NiIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtZGFzaGFycmF5PSIyLDIiLz4KICAKICA8Y2lyY2xlIGN4PSI0MDAiIGN5PSI2NSIgcj0iMyIgZmlsbD0iIzY2NiIvPgogIDxwYXRoIGQ9Ik0gNDAwIDY1IEwgNDAwIDEwMCIgc3Ryb2tlPSIjNjY2IiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1kYXNoYXJyYXk9IjIsMiIvPgo8L3N2Zz4=" alt="Blur Effect Visualization" /></p> <h2 id="basic-implementation">Basic Implementation</h2> <p>Let's start with a simple example of how to implement a blur effect:</p> <pre>import 'package:flutter/material.dart'; import 'dart:ui';
class BlurExample extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Backdrop Filter Example'), ), body: Stack( children: [ // Background Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage('https://picsum.photos/id/1015/1000/800'), fit: BoxFit.cover, ), ), ),
// Blurred Container
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
width: 300,
height: 200,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.white.withOpacity(0.2),
width: 1.5,
),
),
child: Center(
child: Text(
&#39;Frosted Glass Effect&#39;,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
),
),
),
],
),
);
} } </pre> <p>In this example:</p> <ol> <li>We create a <code>Stack</code> to layer our components</li> <li>The background is a full-screen image</li> <li>We wrap the <code>BackdropFilter</code> with <code>ClipRRect</code> to constrain the blur effect to our container's boundaries</li> <li>The semi-transparent white container creates the frosted glass appearance</li> </ol> <h2 id="important-notes-about-backdropfilter">Important Notes About BackdropFilter</h2> <p>There are a few important things to understand about how <code>BackdropFilter</code> works:</p> <ol> <li><p><strong>It Affects Widgets Below It</strong>: <code>BackdropFilter</code> applies its filter to all widgets that are painted before it in the same layer.</p> </li> <li><p><strong>Clipping is Important</strong>: Always wrap your <code>BackdropFilter</code> with a clipping widget (like <code>ClipRect</code> or <code>ClipRRect</code>) to constrain the filter effect to a specific area.</p> </li> <li><p><strong>Performance Considerations</strong>: Blur operations are computationally expensive. Use them judiciously to avoid performance issues.</p> </li> </ol> <h2 id="creating-a-blurred-app-bar">Creating a Blurred App Bar</h2> <p>Let's create a customized blurred app bar:</p> <pre>class BlurredAppBar extends StatelessWidget { final String title;
const BlurredAppBar({Key? key, required this.title}) : super(key: key);
@override Widget build(BuildContext context) { return ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: AppBar( backgroundColor: Colors.transparent, elevation: 0, title: Text(title), flexibleSpace: Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), ), ), ), ), ); } } </pre> <p>Usage:</p> <pre>Scaffold( extendBodyBehindAppBar: true, appBar: PreferredSize( preferredSize: Size.fromHeight(kToolbarHeight), child: BlurredAppBar(title: 'Blurred App Bar'), ), body: Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage('https://picsum.photos/id/1018/1000/800'), fit: BoxFit.cover, ), ), ), ) </pre> <h2 id="creating-a-blurred-bottom-navigation-bar">Creating a Blurred Bottom Navigation Bar</h2> <p>Similarly, we can create a blurred bottom navigation bar:</p> <pre>class BlurredBottomNavBar extends StatelessWidget { final int currentIndex; final Function(int) onTap;
const BlurredBottomNavBar({ Key? key, required this.currentIndex, required this.onTap, }) : super(key: key);
@override Widget build(BuildContext context) { return ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), border: Border( top: BorderSide( color: Colors.white.withOpacity(0.2), width: 1.0, ), ), ), child: BottomNavigationBar( backgroundColor: Colors.transparent, elevation: 0, currentIndex: currentIndex, onTap: onTap, selectedItemColor: Colors.white, unselectedItemColor: Colors.white70, type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( icon: Icon(Icons.home), label: 'Home', ), BottomNavigationBarItem( icon: Icon(Icons.search), label: 'Search', ), BottomNavigationBarItem( icon: Icon(Icons.favorite), label: 'Favorites', ), BottomNavigationBarItem( icon: Icon(Icons.person), label: 'Profile', ), ], ), ), ), ); } } </pre> <h2 id="creating-a-blurred-modal-bottom-sheet">Creating a Blurred Modal Bottom Sheet</h2> <p>A blurred modal bottom sheet can add an elegant touch to your app:</p> <pre>void showBlurredBottomSheet(BuildContext context) { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (context) { return ClipRRect( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.vertical(top: Radius.circular(20)), border: Border.all( color: Colors.white.withOpacity(0.2), width: 1.5, ), ), height: 300, child: Column( children: [ Container( width: 40, height: 5, margin: EdgeInsets.only(bottom: 20), decoration: BoxDecoration( color: Colors.white.withOpacity(0.5), borderRadius: BorderRadius.circular(2.5), ), ), Text( 'Blurred Bottom Sheet', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 15), Text( 'This bottom sheet uses BackdropFilter to create a beautiful blur effect.', style: TextStyle( color: Colors.white.withOpacity(0.8), fontSize: 16, ), textAlign: TextAlign.center, ), Spacer(), ElevatedButton( onPressed: () => Navigator.pop(context), child: Text('Close'), style: ElevatedButton.styleFrom( primary: Colors.white.withOpacity(0.3), onPrimary: Colors.white, shadowColor: Colors.transparent, ), ), ], ), ), ), ); }, ); } </pre> <h2 id="creating-a-reusable-frosted-glass-container">Creating a Reusable Frosted Glass Container</h2> <p>Let's create a reusable frosted glass container widget:</p> <pre>class FrostedGlassBox extends StatelessWidget { final double width; final double height; final Widget child; final double blur; final double borderRadius; final Color borderColor; final Color backgroundColor;
const FrostedGlassBox({ Key? key, required this.width, required this.height, required this.child, this.blur = 10, this.borderRadius = 20, this.borderColor = Colors.white30, this.backgroundColor = Colors.white30, }) : super(key: key);
@override Widget build(BuildContext context) { return ClipRRect( borderRadius: BorderRadius.circular(borderRadius), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur), child: Container( width: width, height: height, decoration: BoxDecoration( color: backgroundColor, borderRadius: BorderRadius.circular(borderRadius), border: Border.all( color: borderColor, width: 1.5, ), ), child: child, ), ), ); } } </pre> <p>Usage:</p> <pre>FrostedGlassBox( width: 300, height: 200, child: Center( child: Text( 'Reusable Frosted Glass', style: TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), ), ), ) </pre> <h2 id="performance-optimization">Performance Optimization</h2> <p>Blur operations can be computationally expensive. Here are some tips to optimize performance:</p> <ol> <li><p><strong>Limit the Size</strong>: Apply blur to smaller areas rather than the entire screen.</p> </li> <li><p><strong>Reduce Blur Complexity</strong>: Lower sigma values require less computation.</p> </li> <li><p><strong>Avoid Animating Blur Values</strong>: Changing blur values in animations can be very expensive.</p> </li> <li><p><strong>Consider Using Static Blurred Images</strong>: For static backgrounds, pre-blur the image instead of using <code>BackdropFilter</code>.</p> </li> <li><p><strong>Use RepaintBoundary</strong>: Wrap widgets that don't change often with <code>RepaintBoundary</code> to reduce unnecessary repainting.</p> </li> </ol> <pre>RepaintBoundary( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container(/* ... */), ), ) </pre> <h2 id="common-use-cases-for-backdrop-filter">Common Use Cases for Backdrop Filter</h2> <h3 id="photo-gallery-overlays">1. Photo Gallery Overlays</h3> <pre>Stack( children: [ Image.network('https://example.com/image.jpg'), Positioned( bottom: 0, left: 0, right: 0, child: ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), child: Container( padding: EdgeInsets.all(16), color: Colors.black.withOpacity(0.3), child: Text( 'Image Caption', style: TextStyle(color: Colors.white), ), ), ), ), ), ], ) </pre> <h3 id="blurred-login-background">2. Blurred Login Background</h3> <pre>class BlurredLoginScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ // Background image Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage('assets/background.jpg'), fit: BoxFit.cover, ), ), ),
// Blurred background
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
child: Container(
color: Colors.black.withOpacity(0.1),
),
),
// Login form
Center(
child: FrostedGlassBox(
width: 300,
height: 400,
borderRadius: 15,
backgroundColor: Colors.white.withOpacity(0.15),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
&#39;Login&#39;,
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 30),
TextField(
decoration: InputDecoration(
hintText: &#39;Email&#39;,
filled: true,
fillColor: Colors.white.withOpacity(0.3),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
),
),
SizedBox(height: 15),
TextField(
obscureText: true,
decoration: InputDecoration(
hintText: &#39;Password&#39;,
filled: true,
fillColor: Colors.white.withOpacity(0.3),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
),
),
SizedBox(height: 30),
ElevatedButton(
onPressed: () {},
child: Text(&#39;Login&#39;),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
horizontal: 50,
vertical: 15,
),
primary: Colors.white.withOpacity(0.3),
),
),
],
),
),
),
),
],
),
);
} } </pre> <h2 id="troubleshooting-common-issues">Troubleshooting Common Issues</h2> <h3 id="backdropfilter-is-not-blurring-anything">1. "BackdropFilter is not blurring anything"</h3> <p>Ensure that:</p> <ul> <li>The <code>BackdropFilter</code> is actually positioned on top of other widgets in a Stack</li> <li>You've wrapped it with a clipping widget like <code>ClipRect</code></li> <li>There's visual content underneath to be blurred</li> </ul> <h3 id="blur-effect-is-applying-to-the-entire-screen">2. "Blur effect is applying to the entire screen"</h3> <p>Always wrap your <code>BackdropFilter</code> with a clipping widget to constrain its effect:</p> <pre>ClipRect( child: BackdropFilter( // Your filter ), ) </pre> <h3 id="performance-is-slow-when-using-blur">3. "Performance is slow when using blur"</h3> <p>Reduce the blur complexity or limit its use:</p> <ul> <li>Use lower sigma values</li> <li>Apply blur to smaller areas</li> <li>Consider pre-blurred images for static content</li> </ul> <h2 id="conclusion">Conclusion</h2> <p>The <code>BackdropFilter</code> widget is a powerful tool in Flutter that allows you to create stunning blur effects and frosted glass interfaces. By combining it with other widgets like <code>ClipRRect</code> and semi-transparent containers, you can create modern, visually appealing UIs.</p> <p>Remember to use blur effects judiciously to maintain good performance, especially on less powerful devices. For static backgrounds, consider pre-blurring images rather than using real-time blurs.</p> <p>By following the techniques and examples in this guide, you'll be able to implement beautiful blur effects in your Flutter applications, enhancing the visual appeal and user experience of your app.</p> <p>Happy coding!</p>