Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Allow GridView to overlap SliverAppBar

I am trying to reproduce the following example from the earlier Material design specifications (open for animated demo):

Google Notes

Until now I was able to produce the scrolling effect, but the overlap of the content is still missing. I couldn't find out how to do this properly.

import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            title: Text('Title'),
            expandedHeight: 200.0,
            primary: true,
            pinned: true,
          ),
          SliverFixedExtentList(
            itemExtent: 30.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int i) => Text('Item $i')
            ),
          ),
        ],
      ),
    );
  }
}
like image 990
Philip Giuliani Avatar asked Dec 10 '18 16:12

Philip Giuliani


2 Answers

I managed to get this functionality, using the ScrollController and a couple of tricks:

Demo GIF

Here's the code:

  ScrollController _scrollController;
  static const kHeaderHeight = 235.0;

  double get _headerOffset {
    if (_scrollController.hasClients) if (_scrollController.offset > kHeaderHeight)
      return -1 * (kHeaderHeight + 50.0);
    else
      return -1 * (_scrollController.offset * 1.5);

    return 0.0;
  }

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

    _scrollController = ScrollController()..addListener(() => setState(() {}));
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return StackWithAllChildrenReceiveEvents(
      alignment: AlignmentDirectional.topCenter,
      children: [
        Positioned(
          top: _headerOffset,
          child: Container(
            height: kHeaderHeight,
            width: MediaQuery.of(context).size.width,
            color: Colors.blue,
          ),
        ),
        Padding(
          padding: EdgeInsets.only(left: 20.0, right: 20.0),
          child: Feed(controller: _scrollController, headerHeight: kHeaderHeight),
        ),
      ],
    );
  }

To make the Feed() not overlap the blue container, I simply made the first child of it a SizedBox with the required height property.

Note that I am using a modified Stack class. That is in order to let the first Widget in the stack (the blue container) to detect presses, so it will fit my uses; unfortunately at this point the default Stack widget has an issue with that, you can read more about it over https://github.com/flutter/flutter/issues/18450.

The StackWithAllChildrenReceiveEvents code can be found over https://github.com/flutter/flutter/issues/18450#issuecomment-575447316.

like image 164
Jon Avatar answered Nov 15 '22 14:11

Jon


I had the same problem and could not solve it with slivers. This example from another stackoverflow question solved my problem.

flutter - App bar scrolling with overlapping content in Flexible space

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Scroll demo',
      home: new Scaffold(
        appBar: new AppBar(elevation: 0.0),
        body: new CustomScroll(),
      ),
    );
  }
}

class CustomScroll extends StatefulWidget {
  @override
  State createState() => new CustomScrollState();
}

class CustomScrollState extends State<CustomScroll> {
  ScrollController scrollController;
  double offset = 0.0;
  static const double kEffectHeight = 100.0;

  @override
  Widget build(BuildContext context) {
    return new Stack(
      alignment: AlignmentDirectional.topCenter,
      children: <Widget> [
        new Container(
          color: Colors.blue,
          height: (kEffectHeight - offset * 0.5).clamp(0.0, kEffectHeight),
        ),
        new Positioned(
          child: new Container(
            width: 200.0,
            child: new ListView.builder(
              itemCount: 100,
              itemBuilder: buildListItem,
              controller: scrollController,
            ),
          ),
        ),
      ],
    );
  }

  Widget buildListItem(BuildContext context, int index) {
    return new Container(
      color: Colors.white,
      child: new Text('Item $index')
    );
  }

  void updateOffset() {
    setState(() {
      offset = scrollController.offset;
    }); 
  }

  @override
  void initState() {
    super.initState();
    scrollController = new ScrollController();
    scrollController.addListener(updateOffset);
  }

  @override
  void dispose() {
    super.dispose();
    scrollController.removeListener(updateOffset);
  }
}

Change the list to a grid and its what you want

like image 26
m1416 Avatar answered Nov 15 '22 14:11

m1416