Does anyone know how to add a gradient background to CupertinoSliverAppBar?

Issue

I am trying to add a gradient background to a CupertinoSliverAppBar in a Flutter app but I cannot seem to figure out how to do it. The SliverAppBar has a flexibleSpace property that would accept a gradient but the CupertinoSliverAppBar only has a backgroundColor property.

Alternatively, if it were possible to move the title in the flexibleSpace more to the left, I could go with that. But I cannot figure that out either.

I read these:

And this issue opened on the Flutter repository: https://github.com/flutter/flutter/issues/25144

@rmtmckenzie does the trick! It is worth noting this also works with CupertinoSliverNavigationBar. Also, all transition animations are preserved except for that of the background for which you will bee seeing the backgroundColor property being animated. You can cheat by using one of the gradient’s colors as the backgroundColor but it is not perfect. The gradient is indeed rendered below the content. See below:

enter image description here

Solution

This is actually a bit difficult – as you mentioned the CupertinoSliverAppBar doesn’t support ‘flexibleSpace’ or anything like it, so you’re stuck with trying to use the backgroundColor which doesn’t do what you need either.

I’d recommend opening an issue on the Flutter repo stating that it isn’t possible and why you’d like to do it. They probably won’t take action on it right away but if they’re aware that people would like to do it it, it might happen.

Not all hope is lost for an immediate fix though, we can cheat! (although I’m hoping you’ll use a less terifyingly ugly gradient when you write your version)

screenshot showing cupertinoappbar with gradientscreenshot showing cupertionoappbar collapsed with gradient

What I’ve done is subclassed Border, and then overridden what it draws so that it draws a gradient you pass in before drawing the actual border. This works because it’s given a paint context that covers the entire app bar, and draws over the background of the appbar but below its content (hopefully – it seems to be below the title at least so I assume it’s below everything else too). The background color is still drawn under the appbar so if your gradient is somewhat transparent you’ll probably want to set the backgroundColor to Colors.transparent.

Here’s the code:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() => runApp(GradientAppBar());

class GradientCheatingBorder extends Border {
  const GradientCheatingBorder({
    this.gradient,
    BorderSide top = BorderSide.none,
    BorderSide right = BorderSide.none,
    BorderSide bottom = BorderSide.none,
    BorderSide left = BorderSide.none,
  }) : super(top: top, right: right, bottom: bottom, left: left);

  const GradientCheatingBorder.fromBorderSide(BorderSide side, {this.gradient})
      : super.fromBorderSide(side);

  factory GradientCheatingBorder.all({
    Color color = const Color(0xFF000000),
    double width = 1.0,
    BorderStyle style = BorderStyle.solid,
    Gradient gradient,
  }) {
    final BorderSide side =
        BorderSide(color: color, width: width, style: style);
    return GradientCheatingBorder.fromBorderSide(side, gradient: gradient);
  }

  final Gradient gradient;

  @override
  void paint(
    Canvas canvas,
    Rect rect, {
    TextDirection textDirection,
    BoxShape shape = BoxShape.rectangle,
    BorderRadius borderRadius,
  }) {
    if (gradient != null) {
      canvas.drawRect(
        rect,
        Paint()
          ..shader = gradient.createShader(rect)
          ..style = PaintingStyle.fill,
      );
    }

    super.paint(
      canvas,
      rect,
      textDirection: textDirection,
      shape: shape,
      borderRadius: borderRadius,
    );
  }
}

class GradientAppBar extends StatefulWidget {
  @override
  _GradientAppBarState createState() => _GradientAppBarState();
}

class _GradientAppBarState extends State<GradientAppBar> {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      home: CustomScrollView(
        slivers: <Widget>[
          CupertinoSliverNavigationBar(
            largeTitle: Text("Title"),
            border: GradientCheatingBorder.fromBorderSide(
              BorderSide.none,
              gradient: LinearGradient(colors: [Colors.black, Colors.white]),
            ),
          ),
          SliverList(
            delegate: SliverChildListDelegate(
              [
                Container(
                  color: Colors.blue,
                  height: 500,
                ),
                Divider(),
                Container(
                  color: Colors.black12,
                  height: 500,
                ),
                Divider(),
                Container(
                  color: Colors.lightBlue,
                  height: 500,
                ),
                Divider(),
                Container(
                  color: Colors.lightGreen,
                  height: 500,
                ),
                Divider(),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Answered By – rmtmckenzie

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.