Want to overlap SliverList on a SliverAppBar. You can use the following widget which uses Stack and scroll listeners to achieve something like the screenshots.
The Widgets:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | import 'package:flutter/material.dart'; class DetailScaffold extends StatefulWidget { final ScrollController controller; final ScrollPhysics physics; final List<Widget> slivers; final double expandedHeight; /// Changes edge behavior to account for [SliverAppBar.pinned]. /// /// Hides the edge when the [ScrollController.offset] reaches the collapsed /// height of the [SliverAppBar] to prevent it from overlapping the app bar. final bool hasPinnedAppBar; DetailScaffold({ @required this.expandedHeight, this.controller, this.physics, this.slivers, this.hasPinnedAppBar = false, }) { assert(expandedHeight != null); assert(hasPinnedAppBar != null); } @override _DetailScaffoldState createState() => _DetailScaffoldState(); } class _DetailScaffoldState extends State<DetailScaffold> { ScrollController ctrl; @override void initState() { super.initState(); ctrl = widget.controller ?? ScrollController(); ctrl.addListener(() => setState(() {})); } @override void dispose() { if (widget.controller == null) { ctrl.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { return Stack( children: <Widget>[ CustomScrollView( controller: ctrl, physics: widget.physics, slivers: widget.slivers, ), _buildEdge(), ], ); } _buildEdge() { var edgeHeight = 12.0; var paddingTop = MediaQuery.of(context).padding.top; var defaultOffset = (paddingTop + widget.expandedHeight) - edgeHeight; var top = defaultOffset; var edgeSize = edgeHeight; if (ctrl.hasClients) { double offset = ctrl.offset; top -= offset > 0 ? offset : 0; if (widget.hasPinnedAppBar) { // Hide edge to prevent overlapping the toolbar during scroll. var breakpoint = widget.expandedHeight - kToolbarHeight - edgeHeight; if (offset >= breakpoint) { edgeSize = edgeHeight - (offset - breakpoint); if (edgeSize < 0) { edgeSize = 0; } top += (edgeHeight - edgeSize); } } } return Positioned( top: top, left: 0, right: 0, child: Container( height: edgeSize, decoration: BoxDecoration( color: Theme.of(context).canvasColor, borderRadius: BorderRadius.vertical( top: Radius.circular(12), ), ), ), ); } } |
Using it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Home extends StatelessWidget { @override Widget build(BuildContext context) { var expandedHeight = 128.0; return DetailScaffold( expandedHeight: expandedHeight, slivers: <Widget>[ SliverAppBar( expandedHeight: expandedHeight, flexibleSpace: FlexibleSpaceBar( background: Container(color: Colors.purple), ), ), SliverList( delegate: SliverChildBuilderDelegate((_, i) { return ListTile(title: Text('Item $i')); }, childCount: 50), ), ], ); } } |
If you like this question & answer and want to contribute, then write your question & answer and email to freewebmentor[@]gmail.com. Your question and answer will appear on FreeWebMentor.com and help other developers.