Created with <3 with dartpad.dev.
Last active
September 4, 2022 11:35
-
-
Save jogboms/21f82a71d4890d92436ca7fd38a895b3 to your computer and use it in GitHub Desktop.
Parallax Header
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
void main() => runApp( | |
const MaterialApp( | |
debugShowCheckedModeBanner: false, | |
home: ParallaxHeaderDemo(), | |
), | |
); | |
class ParallaxHeaderDemo extends StatefulWidget { | |
const ParallaxHeaderDemo({super.key}); | |
@override | |
State<ParallaxHeaderDemo> createState() => _ParallaxHeaderDemoState(); | |
} | |
class _ParallaxHeaderDemoState extends State<ParallaxHeaderDemo> { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: ParallaxHeaderScrollView( | |
header: AppBar( | |
title: const Text('Parallax Header'), | |
actions: [ | |
IconButton(onPressed: () {}, icon: const Icon(Icons.add)), | |
], | |
), | |
slivers: const [ | |
SliverFillRemaining(child: ColoredBox(color: Colors.greenAccent)), | |
SliverFillRemaining(child: ColoredBox(color: Colors.redAccent)), | |
], | |
), | |
); | |
} | |
} | |
class ParallaxHeaderScrollView extends ScrollView { | |
const ParallaxHeaderScrollView({ | |
super.key, | |
required this.header, | |
required this.slivers, | |
}) : super(center: _centerKey); | |
final Widget header; | |
final List<Widget> slivers; | |
static const Key _centerKey = Key('center'); | |
@override | |
List<Widget> buildSlivers(BuildContext context) => [ | |
SliverParallaxHeader(child: header), | |
KeyedSubtree(key: _centerKey, child: slivers.first), | |
...slivers.sublist(1), | |
]; | |
} | |
class SliverParallaxHeader extends SingleChildRenderObjectWidget { | |
const SliverParallaxHeader({super.key, required super.child}); | |
@override | |
RenderObject createRenderObject(BuildContext context) => RenderSliverParallaxHeader(); | |
} | |
class RenderSliverParallaxHeader extends RenderSliverSingleBoxAdapter { | |
RenderSliverParallaxHeader({super.child}); | |
@override | |
void performLayout() { | |
if (child == null) { | |
geometry = SliverGeometry.zero; | |
return; | |
} | |
final SliverConstraints constraints = this.constraints; | |
child!.layout(constraints.asBoxConstraints(), parentUsesSize: true); | |
final double childExtent = child!.size.height; | |
final double paintedChildSize = calculatePaintOffset(constraints, from: 0.0, to: childExtent); | |
final double cacheExtent = calculateCacheOffset(constraints, from: 0.0, to: childExtent); | |
assert(paintedChildSize.isFinite); | |
assert(paintedChildSize >= 0.0); | |
geometry = SliverGeometry( | |
scrollExtent: childExtent, | |
paintExtent: paintedChildSize, | |
cacheExtent: cacheExtent, | |
maxPaintExtent: childExtent, | |
hitTestExtent: paintedChildSize, | |
scrollOffsetCorrection: geometry == null ? childExtent : null, | |
hasVisualOverflow: childExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0, | |
); | |
setChildParentData(child!, constraints, geometry!); | |
} | |
@override | |
void paint(PaintingContext context, Offset offset) { | |
if (child != null && geometry!.visible) { | |
context.paintChild(child!, offset); | |
// For when you want it fixed to the top when you over scroll on iOS | |
// context.paintChild(child!, Offset.zero); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment