17

I have looked through the Flutter documentation to try and find an event, callback or even a state that I could hook into when the FlexibleSpaceBar is collapsed or expanded.

return new FlexibleSpaceBar(
  title: new Column(
    crossAxisAlignment: CrossAxisAlignment.end,
    mainAxisAlignment: MainAxisAlignment.end,
    children: <Widget>[
        new Text(_name, style: textTheme.headline),
        new Text(_caption, style: textTheme.caption)
    ]),
  centerTitle: false,
  background: getImage());`

When the FlexibleSpaceBar is snapped in (collapsed), I want to hide the _caption text and only display the _name text. When it is expanded fully, I obviously want to display both _name & _caption.

How do I go about doing that?

Im new to flutter, so I am somewhat lost on this.

Also reported at https://github.com/flutter/flutter/issues/18567

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Jakes
  • 369
  • 1
  • 3
  • 8

6 Answers6

15

It's not hard to create your own FlexibleSpaceBar.

enter image description here

import 'dart:math' as math;

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: SafeArea(
          child: MyHomePage(),
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ScrollController controller = ScrollController();
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      physics: ClampingScrollPhysics(),
      controller: controller,
      slivers: [
        SliverAppBar(
          expandedHeight: 220.0,
          floating: true,
          pinned: true,
          elevation: 50,
          backgroundColor: Colors.pink,
          leading: IconButton(
            icon: Icon(Icons.menu),
            onPressed: () {},
          ),
          flexibleSpace: _MyAppSpace(),
        ),
        SliverList(
          delegate: SliverChildListDelegate(
            List.generate(
              200,
              (index) => Card(
                child: Padding(
                  padding: EdgeInsets.all(10),
                  child: Text('text $index'),
                ),
              ),
            ),
          ),
        )
      ],
    );
  }
}

class _MyAppSpace extends StatelessWidget {
  const _MyAppSpace({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, c) {
        final settings = context
            .dependOnInheritedWidgetOfExactType<FlexibleSpaceBarSettings>();
        final deltaExtent = settings.maxExtent - settings.minExtent;
        final t =
            (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent)
                .clamp(0.0, 1.0) as double;
        final fadeStart = math.max(0.0, 1.0 - kToolbarHeight / deltaExtent);
        const fadeEnd = 1.0;
        final opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t);

        return Stack(
          children: [
            Center(
              child: Opacity(
                  opacity: 1 - opacity,
                  child: getTitle(
                    'Collapsed Title',
                  )),
            ),
            Opacity(
              opacity: opacity,
              child: Stack(
                alignment: Alignment.bottomCenter,
                children: [
                  getImage(),
                  getTitle(
                    'Expended Title',
                  )
                ],
              ),
            ),
          ],
        );
      },
    );
  }

  Widget getImage() {
    return Container(
      width: double.infinity,
      child: Image.network(
        'https://source.unsplash.com/daily?code',
        fit: BoxFit.cover,
      ),
    );
  }

  Widget getTitle(String text) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Text(
        text,
        textAlign: TextAlign.center,
        style: TextStyle(
          color: Colors.white,
          fontSize: 26.0,
          fontWeight: FontWeight.bold,
        ),
      ),
    );
  }
}
Kherel
  • 14,882
  • 5
  • 50
  • 80
9

You can use AnimatedOpacity class.

 flexibleSpace:  LayoutBuilder(
                    builder: (BuildContext context, BoxConstraints constraints) {
                     var top = constraints.biggest.height;
                      return FlexibleSpaceBar(
                          title: AnimatedOpacity(
                              duration: Duration(milliseconds: 300),
                               //opacity: top > 71 && top < 91 ? 1.0 : 0.0,                           
                              child: Text(
                               top > 71 && top < 91 ? "Collapse" : "Expanded",
                                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                              )),
                          background: Image.network(
                            "https://images.ctfassets.net/pjshm78m9jt4/383122_header/d79a41045d07d114941f7641c83eea6d/importedImage383122_header",
                            fit: BoxFit.cover,
                          ));
                    }),

Can check original answer from this link
https://stackoverflow.com/a/53380630/9719695

Wai Han Ko
  • 760
  • 5
  • 16
7

It can be done like this :

inside your initState method add the scroll listener like that :

  ScrollController _controller;
  bool silverCollapsed = false;
  String myTitle = "default title";

  @override
  void initState() {
    super.initState();

    _controller = ScrollController();

    _controller.addListener(() {
       if (_controller.offset > 220 && !_controller.position.outOfRange) {
          if(!silverCollapsed){

            // do what ever you want when silver is collapsing !

            myTitle = "silver collapsed !";
            silverCollapsed = true;
            setState(() {});
          }
       }
       if (_controller.offset <= 220 && !_controller.position.outOfRange) {
         if(silverCollapsed){

            // do what ever you want when silver is expanding !

            myTitle = "silver expanded !";
            silverCollapsed = false;
            setState(() {});
         }
       }
    });
 }

then wrap your silverAppBar inside CustomScrollView and add the controller to this CustomScrollView like that :

 @override
 Widget build(BuildContext context) {
    return Scaffold(
       backgroundColor: Colors.white,
       body: CustomScrollView(
          controller: _controller,
          slivers: <Widget>[
             SliverAppBar(
                expandedHeight: 300,
                title: myTitle,
                flexibleSpace: FlexibleSpaceBar(),
             ),
             SliverList(
                delegate: SliverChildListDelegate(<Widget>[
                   // your widgets inside here !
                ]),
             ),
          ],
       ),
    );
}

finally change the condition value _controller.offset > 220 to fit your need !

elhoucine ayoub
  • 949
  • 2
  • 12
  • 19
2

FlexibleSpaceBar per se won't be enough. You need to wrap it into CustomScrollView and SliverAppBar. These widgets must be controller by a ScrollController, which will fire an event whenever scroll offset changes. Based on it, you can find out if app bar is collapsed or expanded, and change the content accordingly. Here you will find a working example.

Alexey
  • 1,177
  • 2
  • 14
  • 23
  • You can also see this question for more details. https://stackoverflow.com/questions/49942791/how-to-customized-the-sliverappbar-app-bar – Alexey Aug 20 '18 at 08:43
1

Give an height in padding in FlexibleSpaceBar

flexibleSpace: FlexibleSpaceBar(
          titlePadding: EdgeInsets.only(
            top: 100, // give the value
          title: Text(
            "Test"
        ),
Vishnu Suresh
  • 69
  • 1
  • 5
0

Follow up to Vishnu Suresh answer:

flexibleSpace: FlexibleSpaceBar(
      titlePadding: EdgeInsets.only(
        top: kToolbarHeight, // give the value
      title: Text(
        "Test"
    ),

This will use the appbar height for the padding.

Harish
  • 649
  • 5
  • 11