Created
September 26, 2023 01:35
-
-
Save HansMuller/39251348d70e79385031bd8266bcd63e to your computer and use it in GitHub Desktop.
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 'dart:math' as math; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/rendering.dart'; | |
class Item extends StatelessWidget { | |
const Item({ super.key, required this.title, required this.color }); | |
final String title; | |
final Color color; | |
@override | |
Widget build(BuildContext context) { | |
return Card( | |
color: color, | |
child: ListTile( | |
textColor: Colors.white, | |
title: Text(title), | |
), | |
); | |
} | |
} | |
// A placeholder SliverList of 50 items. | |
class ItemList extends StatelessWidget { | |
const ItemList({ | |
super.key, | |
required this.startColor, | |
required this.endColor, | |
this.itemCount = 50, | |
}); | |
final Color startColor; | |
final Color endColor; | |
final int itemCount; | |
@override | |
Widget build(BuildContext context) { | |
return SliverList( | |
delegate: SliverChildBuilderDelegate( | |
(BuildContext context, int index) { | |
return Item( | |
title: 'Item $index', | |
color: Color.lerp(startColor, endColor, index / itemCount)! | |
); | |
}, | |
childCount: itemCount, | |
), | |
); | |
} | |
} | |
class FloatingHeaderSliver extends StatefulWidget { | |
const FloatingHeaderSliver({ super.key, required this.child }); | |
final Widget child; | |
@override | |
State<FloatingHeaderSliver> createState() => _FloatingHeaderSliverState(); | |
} | |
class _FloatingHeaderSliverState extends State<FloatingHeaderSliver> with SingleTickerProviderStateMixin { | |
@override | |
Widget build(BuildContext context) { | |
return _FloatingHeaderSliver( | |
child: widget.child, | |
); | |
} | |
} | |
class _FloatingHeaderSliver extends SingleChildRenderObjectWidget { | |
const _FloatingHeaderSliver({ | |
super.child, | |
}); | |
@override | |
_RenderFloatingHeaderSliver createRenderObject(BuildContext context) { | |
return _RenderFloatingHeaderSliver(); | |
} | |
@override | |
void updateRenderObject(BuildContext context, _RenderFloatingHeaderSliver renderObject) { | |
} | |
} | |
class _RenderFloatingHeaderSliver extends RenderSliverSingleBoxAdapter { | |
_RenderFloatingHeaderSliver(); | |
ScrollDirection scrollDirection = ScrollDirection.idle; | |
double? effectiveScrollOffset; | |
double? scrollAnchor; | |
double get childExtent { | |
if (child == null) { | |
return 0.0; | |
} | |
assert(child!.hasSize); | |
return switch (constraints.axis) { | |
Axis.vertical => child!.size.height, | |
Axis.horizontal => child!.size.width, | |
}; | |
} | |
@override | |
double childMainAxisPosition(covariant RenderObject child) => 0; | |
@override | |
void performLayout() { | |
final SliverConstraints constraints = this.constraints; | |
child?.layout(constraints.asBoxConstraints(), parentUsesSize: true); | |
late final double scrollOffset; | |
late final double paintExtent; | |
print(constraints.userScrollDirection); | |
if (constraints.userScrollDirection == ScrollDirection.forward && (scrollAnchor != null || constraints.scrollOffset >= childExtent)) { | |
scrollAnchor ??= constraints.scrollOffset; | |
final double scrollAnchorDelta = scrollAnchor! - constraints.scrollOffset; | |
scrollOffset = clampDouble(childExtent - scrollAnchorDelta, 0, constraints.remainingPaintExtent); | |
paintExtent = childExtent - scrollOffset; | |
} else { | |
scrollAnchor = null; | |
paintExtent = clampDouble(childExtent - constraints.scrollOffset, 0, constraints.remainingPaintExtent); | |
scrollOffset = clampDouble(constraints.scrollOffset, 0, childExtent); | |
} | |
geometry = SliverGeometry( | |
scrollExtent: childExtent, | |
paintOrigin: math.max(-scrollOffset, -childExtent), | |
paintExtent: paintExtent, | |
layoutExtent: paintExtent, | |
maxPaintExtent: childExtent, | |
maxScrollObstructionExtent: childExtent, | |
cacheExtent: calculateCacheOffset(constraints, from: 0.0, to: childExtent), | |
hasVisualOverflow: true, // Conservatively say we do have overflow to avoid complexity. | |
); | |
} | |
} | |
class AppBarParts extends StatefulWidget { | |
const AppBarParts({ super.key }); | |
@override | |
State<AppBarParts> createState() => _AppBarPartsState(); | |
} | |
class _AppBarPartsState extends State<AppBarParts> { | |
late final ScrollController scrollController; | |
@override | |
void initState() { | |
super.initState(); | |
scrollController = ScrollController(); | |
} | |
@override | |
void dispose() { | |
scrollController.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
const EdgeInsets horizontalPadding = EdgeInsets.symmetric(horizontal: 8); | |
return Scaffold( | |
body: SafeArea( | |
child: Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 4), | |
child: CustomScrollView( | |
controller: scrollController, | |
slivers: const <Widget>[ | |
SliverPadding( | |
padding: horizontalPadding, | |
sliver: ItemList( | |
startColor: Colors.blue, | |
endColor: Colors.red, | |
itemCount: 3, | |
), | |
), | |
SliverPadding( | |
padding: horizontalPadding, | |
sliver: FloatingHeaderSliver( | |
child: SizedBox( | |
height: 128, | |
child: Item( | |
title: 'FloatingHeaderSliver', | |
color: Colors.orange, | |
), | |
), | |
), | |
), | |
SliverPadding( | |
padding: horizontalPadding, | |
sliver: ItemList( | |
startColor: Colors.blue, | |
endColor: Colors.red, | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
class AppBarPartsApp extends StatelessWidget { | |
const AppBarPartsApp({ super.key }); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
theme: ThemeData(useMaterial3: true), | |
home: const AppBarParts(), | |
); | |
} | |
} | |
void main() { | |
runApp(const AppBarPartsApp()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment