Skip to content

Instantly share code, notes, and snippets.

@AlexV525
Last active October 14, 2021 06:39
Show Gist options
  • Save AlexV525/f3321ee7580336f9c4a20d905b162510 to your computer and use it in GitHub Desktop.
Save AlexV525/f3321ee7580336f9c4a20d905b162510 to your computer and use it in GitHub Desktop.
DecoratedSliver
///
/// [Author] Alex (https://github.com/AlexV525)
/// [Date] 2021/10/13 14:36
///
import 'dart:math' as math;
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
@immutable
class DecoratedSliver extends RenderObjectWidget {
const DecoratedSliver({
Key? key,
required this.sliver,
required this.decoration,
}) : super(key: key);
final Widget sliver;
final BoxDecoration decoration;
@override
_RenderSliverDecoration createRenderObject(BuildContext context) =>
_RenderSliverDecoration();
@override
_DecoratedSliverElement createElement() => _DecoratedSliverElement(this);
@override
void updateRenderObject(
BuildContext context,
_RenderSliverDecoration renderObject,
) =>
renderObject;
}
class _DecoratedSliverElement extends RenderObjectElement {
/// Creates an element that uses the given widget as its configuration.
_DecoratedSliverElement(DecoratedSliver widget) : super(widget);
@override
DecoratedSliver get widget => super.widget as DecoratedSliver;
Element? _decoration;
Element? _sliver;
Widget get _decoratedBox => Container(decoration: widget.decoration);
@override
void visitChildren(ElementVisitor visitor) {
if (_decoration != null) {
visitor(_decoration!);
}
if (_sliver != null) {
visitor(_sliver!);
}
}
@override
void forgetChild(Element child) {
super.forgetChild(child);
if (child == _decoration) {
_decoration = null;
}
if (child == _sliver) {
_sliver = null;
}
}
@override
void mount(Element? parent, dynamic newSlot) {
super.mount(parent, newSlot);
_decoration = updateChild(_decoration, _decoratedBox, 0);
_sliver = updateChild(_sliver, widget.sliver, 1);
}
@override
void update(DecoratedSliver newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_decoration = updateChild(_decoration, _decoratedBox, 0);
_sliver = updateChild(_sliver, widget.sliver, 1);
}
@override
void insertRenderObjectChild(RenderObject child, int? slot) {
final _RenderSliverDecoration renderObject =
this.renderObject as _RenderSliverDecoration;
if (slot == 0) {
renderObject.decoration = child as RenderBox?;
}
if (slot == 1) {
renderObject.child = child as RenderSliver?;
}
assert(renderObject == this.renderObject);
}
@override
void moveRenderObjectChild(
RenderObject child,
Object? slot,
Object? newSlot,
) {
assert(false);
}
@override
void removeRenderObjectChild(RenderObject child, Object? slot) {
final _RenderSliverDecoration renderObject =
this.renderObject as _RenderSliverDecoration;
if (renderObject.decoration == child) {
renderObject.decoration = null;
}
if (renderObject.child == child) {
renderObject.child = null;
}
assert(renderObject == this.renderObject);
}
}
class _RenderSliverDecoration extends RenderSliver with RenderSliverHelpers {
_RenderSliverDecoration({
RenderObject? decoration,
RenderSliver? child,
}) {
this.decoration = decoration as RenderBox?;
this.child = child;
}
/// The render object's decoration.
RenderBox? get decoration => _decoration;
RenderBox? _decoration;
set decoration(RenderBox? value) {
if (_decoration != null) {
dropChild(_decoration!);
}
_decoration = value;
if (_decoration != null) {
adoptChild(_decoration!);
}
}
/// The render object's unique child
RenderSliver? get child => _child;
RenderSliver? _child;
set child(RenderSliver? value) {
if (_child != null) {
dropChild(_child!);
}
_child = value;
if (_child != null) {
adoptChild(_child!);
}
}
@override
void setupParentData(RenderObject child) {
if (child.parentData is! SliverPhysicalParentData)
child.parentData = SliverPhysicalParentData();
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
if (_decoration != null) {
_decoration!.attach(owner);
}
if (_child != null) {
_child!.attach(owner);
}
}
@override
void detach() {
super.detach();
if (_decoration != null) {
_decoration!.detach();
}
if (_child != null) {
_child!.detach();
}
}
@override
void redepthChildren() {
if (_decoration != null) {
redepthChild(_decoration!);
}
if (_child != null) {
redepthChild(_child!);
}
}
@override
void visitChildren(RenderObjectVisitor visitor) {
if (_decoration != null) {
visitor(_decoration!);
}
if (_child != null) {
visitor(_child!);
}
}
@override
List<DiagnosticsNode> debugDescribeChildren() {
return <DiagnosticsNode>[
if (decoration != null) decoration!.toDiagnosticsNode(name: 'decoration'),
if (child != null) child!.toDiagnosticsNode(name: 'child'),
];
}
@override
void performLayout() {
if (child == null) {
geometry = SliverGeometry.zero;
return;
}
child!.layout(constraints, parentUsesSize: true);
final SliverGeometry childLayoutGeometry = child!.geometry!;
geometry = childLayoutGeometry;
if (decoration != null) {
decoration!.layout(
constraints.asBoxConstraints(
maxExtent: childLayoutGeometry.maxPaintExtent,
crossAxisExtent: constraints.crossAxisExtent,
),
parentUsesSize: true,
);
}
if (child == null) {
geometry = SliverGeometry(
hasVisualOverflow: constraints.scrollOffset > 0.0,
);
} else {
child!.layout(
constraints.copyWith(
scrollOffset: math.max(0.0, constraints.scrollOffset),
cacheOrigin: math.min(0.0, constraints.cacheOrigin),
overlap: 0.0,
remainingPaintExtent: constraints.remainingPaintExtent,
remainingCacheExtent: constraints.remainingCacheExtent,
),
parentUsesSize: true,
);
final SliverGeometry childLayoutGeometry = child!.geometry!;
if (childLayoutGeometry.scrollOffsetCorrection != null) {
geometry = SliverGeometry(
scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection,
);
return;
}
final double paintExtent = math.min(
math.max(
childLayoutGeometry.paintExtent,
childLayoutGeometry.layoutExtent,
),
constraints.remainingPaintExtent,
);
geometry = SliverGeometry(
scrollExtent: childLayoutGeometry.scrollExtent,
paintExtent: paintExtent,
layoutExtent: math.min(
childLayoutGeometry.layoutExtent,
paintExtent,
),
cacheExtent: math.min(
childLayoutGeometry.cacheExtent,
constraints.remainingCacheExtent,
),
maxPaintExtent: childLayoutGeometry.maxPaintExtent,
hitTestExtent: math.max(
childLayoutGeometry.paintExtent,
childLayoutGeometry.hitTestExtent,
),
hasVisualOverflow: childLayoutGeometry.hasVisualOverflow,
);
}
}
@override
bool hitTestChildren(
SliverHitTestResult result, {
required double mainAxisPosition,
required double crossAxisPosition,
}) {
assert(geometry!.hitTestExtent > 0.0);
if (child != null && child!.geometry!.hitTestExtent > 0.0) {
return child!.hitTest(
result,
mainAxisPosition: mainAxisPosition - childMainAxisPosition(child),
crossAxisPosition: crossAxisPosition,
);
}
return false;
}
@override
double childMainAxisPosition(RenderObject? child) => 0;
@override
void applyPaintTransform(RenderObject child, Matrix4 transform) {
final SliverPhysicalParentData childParentData =
child.parentData as SliverPhysicalParentData;
childParentData.applyPaintTransform(transform);
}
@override
void paint(PaintingContext context, Offset offset) {
if (geometry!.visible) {
if (decoration != null) {
final SliverPhysicalParentData childParentData =
decoration!.parentData as SliverPhysicalParentData;
context.paintChild(decoration!, offset + childParentData.paintOffset);
}
if (child != null && child!.geometry!.visible) {
final SliverPhysicalParentData childParentData =
child!.parentData as SliverPhysicalParentData;
context.paintChild(child!, offset + childParentData.paintOffset);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment