Search code examples
flutterflutter-layoutflutter-sliver

Set background image height, width, position in Flutter


I have a background image in my SliverAppBar. I've tried the BoxFit.contain, BoxFit.fill...etc, but none of them work for what I'd like to do.

Here is what I can get:

not good

But here's what I want:

good!

I see there is BoxFit.values but I cannot find any documentation showing how to use this (if it's the right thing?)

Here's my code:

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:my_app/Theme.dart' as MyTheme;
import 'package:my_app/ui/rule_section_details/RuleRow.dart';

@override
class SliverHeaderTest extends StatelessWidget {
  final DocumentSnapshot ruleGroup;

  SliverHeaderTest(this.ruleGroup);

  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(slivers: <Widget>[
        SliverAppBar(
          floating: true,
          backgroundColor: Color(int.parse(ruleGroup['color'])),
          expandedHeight: 200.0,
          flexibleSpace: FlexibleSpaceBar(
            // background: Image.asset('assets/img/circular-image.png',
            // fit: BoxFit.contain),
            background: new Image(
              image: new AssetImage(ruleGroup['image']),
              height: MyTheme.Dimens.ruleGroupListIconHeight,
              width: MyTheme.Dimens.ruleGroupListIconWidth,
            ),
            title: Text(ruleGroup['name'],
                style: MyTheme.TextStyles.ruleSectionPageTitle),
            centerTitle: true,
          ),
          actions: <Widget>[
            IconButton(
              icon: const Icon(Icons.share),
              tooltip: 'Share',
              onPressed: () {/* ... */},
            ),
          ],
        ),
        StreamBuilder(
            stream: Firestore.instance
                .collection('rules')
                .where("section", isEqualTo: ruleGroup['id'])
                .orderBy("subsection")
                .orderBy("subsubsection")
                .orderBy("subsubsubsection")
                .snapshots(),
            builder: (context, snapshot) {
              if (!snapshot.hasData) {
                return SliverList(
                  delegate: SliverChildListDelegate(
                    [
                      Container(
                        child: new Center(child: new Text('Loading...')),
                      )
                    ],
                  ),
                );
              }
              return SliverPadding(
                  padding: EdgeInsets.only(top: 16.0),
                  sliver: SliverList(
                      delegate: SliverChildBuilderDelegate((context, index) {
                    return new RuleRow(snapshot.data.documents[index]);
                  }, childCount: snapshot.data.documents.length)));
            })
      ]),
    );
  }
}

Solution

  • It is the Desired behavior of background: property of FlexibleSpaceBar - its Suppose to fill all the background area of the Appbar, now title here is not separate element to render below background, but a foreground widget of the FlexibleSpaceBar to show on top of background:

    If You really need to separate the title & Image here you can't use background & title property, but Instead you need to use Column or ListView instead of FlexibleSpaceBar.

    You can try the Following Code with Possible options:

    Recommended Solution:

    SliverAppBar(
                backgroundColor: Colors.blue,
                expandedHeight: 200.0,
                floating: true,
                //  pinned: true,
                flexibleSpace: FlexibleSpaceBar(
                    centerTitle: true,
                    title: Text("Collapsing Toolbar",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 16.0,
                        )),
                    background: Row(
                      children: <Widget>[
                        Spacer(),
                        CircleAvatar(
                          radius: 54.0,
                          backgroundImage: NetworkImage(
                            "https://placeimg.com/640/480/animals",
                          ),
                        ),
                        Spacer(),
                      ],
                    )),
              ),
    

    This Image is with radius: 68.0,.

    enter image description here

    Following are using fixed Margins, might cause issue in responsive design, but still works.

    With ClipOval:

    SliverAppBar(
                backgroundColor: Colors.blue,
                expandedHeight: 200.0,
                floating: true,
                //  pinned: true,
                flexibleSpace: FlexibleSpaceBar(
                    centerTitle: true,
                    title: Text("Collapsing Toolbar",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 16.0,
                        )),
                    background: Container(
                      margin:
                          EdgeInsets.symmetric(horizontal: 125.0, vertical: 50.0),
                      child: ClipOval(
                        child: Image.network(
                          "https://placeimg.com/640/480/animals",
                        ),
                      ),
                    )),
              ),
    

    enter image description here

    with CircleAvatar

    SliverAppBar(
                backgroundColor: Colors.blue,
                expandedHeight: 200.0,
                floating: true,
                //  pinned: true,
                flexibleSpace: FlexibleSpaceBar(
                    centerTitle: true,
                    title: Text("Collapsing Toolbar",
                        style: TextStyle(
                          color: Colors.white,
                          fontSize: 16.0,
                        )),
                    background: Container(
                      margin:
                          EdgeInsets.symmetric(horizontal: 125.0, vertical: 50.0),
                      child: CircleAvatar(
                        radius: 30.0,
                        backgroundImage: NetworkImage(
                          "https://placeimg.com/640/480/animals",
                        ),
                      ),
                    )),
              ),
    

    enter image description here

    Update:

    with ListView Option. Note: AppBar height is Determined by expandedHeight: property & will not increase in case of increase in Image Radius.

    SliverAppBar(
                backgroundColor: Colors.blue,
                expandedHeight: 200.0,
                floating: true,
                //  pinned: true,
                flexibleSpace: Center(
                  child: ListView(
                    shrinkWrap: true,
                    children: <Widget>[
                      Row(
                        children: <Widget>[
                          Spacer(),
                          CircleAvatar(
                            radius: 68.0,
                            backgroundImage: NetworkImage(
                              "https://placeimg.com/640/480/animals",
                            ),
                          ),
                          Spacer(),
                        ],
                      ),
                      Center(
                        child: Text("Collapsing Toolbar",
                            style: TextStyle(
                              color: Colors.white,
                              fontSize: 22.0,
                            )),
                      ),
                    ],
                  ),
                ),
              ),