Search code examples
flutterdartflutter-layoutgaussianblur

Gradient on top of blurred image (flutter)


I want a achieve a design like the one in the second screenshot with a faded background. So the upper half of the blurred background image is already fine, just the bottom half is missing a gradient into a fixed color (like black or white).

For clarification: I want only the background to fade into black without affecting the top layer (in this case the untouched image). I tried quite long now to get a suitable solution but unfortunately I did not find any.

Current state:

current state

Goal:

goal

Current code (first screenshot):

Container(
 color: Colors.white,
 child: Container(
  decoration: BoxDecoration(
    image: DecorationImage(
      image: CachedNetworkImageProvider(widget.storyData.coverURL),
      fit: BoxFit.cover,
    ),
  ),
  child: BackdropFilter(
    filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
    child: Container(
      child: Padding(
        padding: const EdgeInsets.all(80.0),
        child: Hero(
          tag: widget.heroTagID,
          child: CachedNetworkImage(
            imageUrl: widget.storyData.coverURL,
            placeholder: (context, url) => Padding(
              padding: EdgeInsets.all(25),
              child: Icon(Ionicons.image_outline),
            ),
          ),
        ),
      ),
    ),
  )),
);

Solution

  • You can achieve that using Stack and LinearGradient.

    I've replaced CachedNetworkImageProvider with simple NetworkImage so that the code can work on https://dartpad.dev/. The principle remains the same:

    import 'dart:ui';
    import 'package:flutter/material.dart';
    
    final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: MyWidget(),
            ),
          ),
        );
      }
    }
    
    class MyWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Stack(
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: NetworkImage(
                    'https://picsum.photos/id/255/1440/3200',
                  ),
                  fit: BoxFit.cover,
                ),
              ),
            ),
            Container(
              constraints: BoxConstraints.expand(),
              decoration: BoxDecoration(
                  color: Colors.white,
                  gradient: LinearGradient(
                      begin: FractionalOffset.topCenter,
                      end: FractionalOffset.bottomCenter,
                      colors: [Colors.black.withOpacity(0.0), Colors.black],
                      stops: [0.0, 1.0]
                  )
              ),
            ),
            BackdropFilter(
                filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
                child: Center(
                  child: Padding(
                    padding: const EdgeInsets.all(80.0),
                    child: Hero(
                        tag: 'heroTag',
                        child: Image.network(
                          'https://picsum.photos/id/255/1440/3200',
                        )
                    ),
                  ),
                )
            ),
          ],
        );
      }
    }
    

    You can tweak the colors and stops to get it exactly how you want it.

    End result:

    end_result_image