Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add header spacing & stuckAmount as optional #80

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ class Example2 extends StatelessWidget {
itemBuilder: (context, index) {
return StickyHeaderBuilder(
controller: controller, // Optional
builder: (BuildContext context, double stuckAmount) {
stuckAmount = 1.0 - stuckAmount.clamp(0.0, 1.0);
builder: (BuildContext context, double? stuckAmount) {
stuckAmount = 1.0 - (stuckAmount ?? 0.0).clamp(0.0, 1.0);
return Container(
height: 50.0,
color: Color.lerp(Colors.blue[700], Colors.red[700], stuckAmount),
Expand Down Expand Up @@ -201,8 +201,8 @@ class Example3 extends StatelessWidget {
return StickyHeaderBuilder(
overlapHeaders: true,
controller: controller, // Optional
builder: (BuildContext context, double stuckAmount) {
stuckAmount = 1.0 - stuckAmount.clamp(0.0, 1.0);
builder: (BuildContext context, double? stuckAmount) {
stuckAmount = 1.0 - (stuckAmount ?? 0.0).clamp(0.0, 1.0);
return Container(
height: 50.0,
color: Colors.grey.shade900.withOpacity(0.6 + stuckAmount * 0.4),
Expand Down
19 changes: 15 additions & 4 deletions lib/sticky_headers/render.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:flutter/widgets.dart';
/// Called every layout to provide the amount of stickyness a header is in.
/// This lets the widgets animate their content and provide feedback.
///
typedef RenderStickyHeaderCallback = void Function(double stuckAmount);
typedef RenderStickyHeaderCallback = void Function(double? stuckAmount);

/// RenderObject for StickyHeader widget.
///
Expand All @@ -27,16 +27,19 @@ class RenderStickyHeader extends RenderBox
RenderStickyHeaderCallback? _callback;
ScrollPosition _scrollPosition;
bool _overlapHeaders;
double _headerSpacing;

RenderStickyHeader({
required ScrollPosition scrollPosition,
RenderStickyHeaderCallback? callback,
bool overlapHeaders = false,
double headerSpacing = 0.0,
RenderBox? header,
RenderBox? content,
}) : _scrollPosition = scrollPosition,
_callback = callback,
_overlapHeaders = overlapHeaders {
_overlapHeaders = overlapHeaders,
_headerSpacing = headerSpacing {
if (content != null) add(content);
if (header != null) add(header);
}
Expand Down Expand Up @@ -70,6 +73,14 @@ class RenderStickyHeader extends RenderBox
markNeedsLayout();
}

set headerSpacing(double newValue) {
if (_headerSpacing == newValue) {
return;
}
_headerSpacing = newValue;
markNeedsLayout();
}

@override
void attach(PipelineOwner owner) {
super.attach(owner);
Expand Down Expand Up @@ -125,7 +136,7 @@ class RenderStickyHeader extends RenderBox
headerParentData.offset = Offset(0.0, max(0.0, min(-stuckOffset, maxOffset)));

// report to widget how much the header is stuck.
if (_callback != null) {
if (_callback != null && stuckOffset != 0) {
final stuckAmount = max(min(headerHeight, stuckOffset), -headerHeight) / headerHeight;
_callback!(stuckAmount);
}
Expand All @@ -135,7 +146,7 @@ class RenderStickyHeader extends RenderBox
final scrollBox = _scrollPosition.context.notificationContext!.findRenderObject();
if (scrollBox?.attached ?? false) {
try {
return localToGlobal(Offset.zero, ancestor: scrollBox).dy;
return localToGlobal(Offset(0, -(_headerSpacing)), ancestor: scrollBox).dy;
} catch (e) {
// ignore and fall-through and return 0.0
}
Expand Down
23 changes: 17 additions & 6 deletions lib/sticky_headers/widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import './render.dart';
/// -1.0 >= value >= 0.0: past stuck
/// ```
///
typedef StickyHeaderWidgetBuilder = Widget Function(BuildContext context, double stuckAmount);
typedef StickyHeaderWidgetBuilder = Widget Function(BuildContext context, double? stuckAmount);

/// Stick Header Widget
///
Expand All @@ -33,6 +33,7 @@ class StickyHeader extends MultiChildRenderObjectWidget {
Key? key,
required this.header,
required this.content,
this.headerSpacing = 0.0,
this.overlapHeaders = false,
this.controller,
this.callback,
Expand All @@ -48,6 +49,9 @@ class StickyHeader extends MultiChildRenderObjectWidget {
/// Content to be shown below the header.
final Widget content;

/// Spacing between sticky header and start position.
final double headerSpacing;

/// If true, the header will overlap the Content.
final bool overlapHeaders;

Expand All @@ -60,21 +64,23 @@ class StickyHeader extends MultiChildRenderObjectWidget {

@override
RenderStickyHeader createRenderObject(BuildContext context) {
final scrollPosition = controller?.position ?? Scrollable.of(context)!.position;
final scrollPosition = controller?.position ?? Scrollable.of(context).position;
return RenderStickyHeader(
scrollPosition: scrollPosition,
callback: callback,
overlapHeaders: overlapHeaders,
headerSpacing: headerSpacing,
);
}

@override
void updateRenderObject(BuildContext context, RenderStickyHeader renderObject) {
final scrollPosition = controller?.position ?? Scrollable.of(context)!.position;
final scrollPosition = controller?.position ?? Scrollable.of(context).position;
renderObject
..scrollPosition = scrollPosition
..callback = callback
..overlapHeaders = overlapHeaders;
..overlapHeaders = overlapHeaders
..headerSpacing = headerSpacing;
}
}

Expand All @@ -91,6 +97,7 @@ class StickyHeaderBuilder extends StatefulWidget {
Key? key,
required this.builder,
required this.content,
this.headerSpacing = 0.0,
this.overlapHeaders = false,
this.controller,
}) : super(key: key);
Expand All @@ -102,6 +109,9 @@ class StickyHeaderBuilder extends StatefulWidget {
/// Content to be shown below the header.
final Widget content;

/// Spacing between sticky header and start position.
final double headerSpacing;

/// If true, the header will overlap the Content.
final bool overlapHeaders;

Expand All @@ -119,12 +129,13 @@ class _StickyHeaderBuilderState extends State<StickyHeaderBuilder> {
Widget build(BuildContext context) {
return StickyHeader(
overlapHeaders: widget.overlapHeaders,
headerSpacing: widget.headerSpacing,
header: LayoutBuilder(
builder: (context, _) => widget.builder(context, _stuckAmount ?? 0.0),
builder: (context, _) => widget.builder(context, _stuckAmount),
),
content: widget.content,
controller: widget.controller,
callback: (double stuckAmount) {
callback: (double? stuckAmount) {
if (_stuckAmount != stuckAmount) {
_stuckAmount = stuckAmount;
WidgetsBinding.instance.endOfFrame.then((_) {
Expand Down