|
|
|
/*
|
|
|
|
* Author: Jpeng
|
|
|
|
* Email: peng8350@gmail.com
|
|
|
|
* Time: 2019-07-11 12:23
|
|
|
|
*/
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
|
/*
|
|
|
|
aim to implements expand all the free empty place when viewport is not full
|
|
|
|
,but this can not correction offset,due to _minScrollExtent,_maxScrollExtent private in RenderViewport
|
|
|
|
,no idea how to do. without doing this,chat list (top when not full && reverse = true) can not be done.
|
|
|
|
in my plugin similar issue:#127,# 118
|
|
|
|
in flutter similar issue:#12650,#33399,#17444
|
|
|
|
*/
|
|
|
|
|
|
|
|
class ExpandedViewport extends Viewport {
|
|
|
|
ExpandedViewport({
|
|
|
|
Key? key,
|
|
|
|
AxisDirection? axisDirection = AxisDirection.down,
|
|
|
|
AxisDirection? crossAxisDirection,
|
|
|
|
double? anchor = 0.0,
|
|
|
|
ViewportOffset? offset,
|
|
|
|
Key? center,
|
|
|
|
double? cacheExtent = 1,
|
|
|
|
List<Widget>? slivers = const <Widget>[],
|
|
|
|
}) : super(
|
|
|
|
key: key,
|
|
|
|
slivers: slivers!,
|
|
|
|
axisDirection: axisDirection!,
|
|
|
|
crossAxisDirection: crossAxisDirection,
|
|
|
|
anchor: anchor!,
|
|
|
|
offset: offset!,
|
|
|
|
center: center,
|
|
|
|
cacheExtent: cacheExtent);
|
|
|
|
|
|
|
|
@override
|
|
|
|
RenderViewport createRenderObject(BuildContext context) {
|
|
|
|
// TODO: implement createRenderObject
|
|
|
|
return _RenderExpandedViewport(
|
|
|
|
axisDirection: axisDirection,
|
|
|
|
crossAxisDirection: crossAxisDirection ??
|
|
|
|
Viewport.getDefaultCrossAxisDirection(context, axisDirection),
|
|
|
|
anchor: anchor,
|
|
|
|
offset: offset,
|
|
|
|
cacheExtent: cacheExtent!,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _RenderExpandedViewport extends RenderViewport {
|
|
|
|
_RenderExpandedViewport({
|
|
|
|
AxisDirection axisDirection = AxisDirection.down,
|
|
|
|
@required AxisDirection? crossAxisDirection,
|
|
|
|
@required ViewportOffset? offset,
|
|
|
|
double anchor = 0.0,
|
|
|
|
List<RenderSliver>? children,
|
|
|
|
RenderSliver? center,
|
|
|
|
double? cacheExtent,
|
|
|
|
}) : super(
|
|
|
|
axisDirection: axisDirection,
|
|
|
|
crossAxisDirection: crossAxisDirection!,
|
|
|
|
offset: offset!,
|
|
|
|
anchor: anchor,
|
|
|
|
children: children,
|
|
|
|
center: center,
|
|
|
|
cacheExtent: cacheExtent);
|
|
|
|
|
|
|
|
@override
|
|
|
|
void performLayout() {
|
|
|
|
// TODO: implement performLayout
|
|
|
|
super.performLayout();
|
|
|
|
RenderSliver? expand;
|
|
|
|
RenderSliver? p = firstChild!;
|
|
|
|
double totalLayoutExtent = 0;
|
|
|
|
double frontExtent = 0.0;
|
|
|
|
while (p != null) {
|
|
|
|
totalLayoutExtent += p.geometry!.scrollExtent;
|
|
|
|
if (p is _RenderExpanded) {
|
|
|
|
expand = p;
|
|
|
|
frontExtent = totalLayoutExtent;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = childAfter(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size.height > totalLayoutExtent) {
|
|
|
|
_attemptLayout(expand!, size.height, size.width,
|
|
|
|
offset.pixels - frontExtent - (size.height - totalLayoutExtent));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// _minScrollExtent private in super,no setter method
|
|
|
|
double _attemptLayout(RenderSliver expandPosition, double mainAxisExtent,
|
|
|
|
double crossAxisExtent, double correctedOffset) {
|
|
|
|
assert(!mainAxisExtent.isNaN);
|
|
|
|
assert(mainAxisExtent >= 0.0);
|
|
|
|
assert(crossAxisExtent.isFinite);
|
|
|
|
assert(crossAxisExtent >= 0.0);
|
|
|
|
assert(correctedOffset.isFinite);
|
|
|
|
|
|
|
|
// centerOffset is the offset from the leading edge of the RenderViewport
|
|
|
|
// to the zero scroll offset (the line between the forward slivers and the
|
|
|
|
// reverse slivers).
|
|
|
|
final double centerOffset = mainAxisExtent * anchor - correctedOffset;
|
|
|
|
final double reverseDirectionRemainingPaintExtent =
|
|
|
|
centerOffset.clamp(0.0, mainAxisExtent);
|
|
|
|
|
|
|
|
final double forwardDirectionRemainingPaintExtent =
|
|
|
|
(mainAxisExtent - centerOffset).clamp(0.0, mainAxisExtent);
|
|
|
|
final double fullCacheExtent = mainAxisExtent + 2 * cacheExtent!;
|
|
|
|
final double centerCacheOffset = centerOffset + cacheExtent!;
|
|
|
|
final double forwardDirectionRemainingCacheExtent =
|
|
|
|
(fullCacheExtent - centerCacheOffset).clamp(0.0, fullCacheExtent);
|
|
|
|
|
|
|
|
final RenderSliver? leadingNegativeChild = childBefore(center!);
|
|
|
|
// positive scroll offsets
|
|
|
|
return layoutChildSequence(
|
|
|
|
child: expandPosition,
|
|
|
|
scrollOffset: math.max(0.0, -centerOffset),
|
|
|
|
overlap:
|
|
|
|
leadingNegativeChild == null ? math.min(0.0, -centerOffset) : 0.0,
|
|
|
|
layoutOffset: centerOffset >= mainAxisExtent
|
|
|
|
? centerOffset
|
|
|
|
: reverseDirectionRemainingPaintExtent,
|
|
|
|
remainingPaintExtent: forwardDirectionRemainingPaintExtent,
|
|
|
|
mainAxisExtent: mainAxisExtent,
|
|
|
|
crossAxisExtent: crossAxisExtent,
|
|
|
|
growthDirection: GrowthDirection.forward,
|
|
|
|
advance: childAfter,
|
|
|
|
remainingCacheExtent: forwardDirectionRemainingCacheExtent,
|
|
|
|
cacheOrigin: centerOffset.clamp(-cacheExtent!, 0.0),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//tag
|
|
|
|
class SliverExpanded extends SingleChildRenderObjectWidget {
|
|
|
|
SliverExpanded() : super(child: Container());
|
|
|
|
|
|
|
|
@override
|
|
|
|
RenderSliver createRenderObject(BuildContext context) {
|
|
|
|
// TODO: implement createRenderObject
|
|
|
|
return _RenderExpanded();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _RenderExpanded extends RenderSliver
|
|
|
|
with RenderObjectWithChildMixin<RenderBox> {
|
|
|
|
@override
|
|
|
|
void performLayout() {
|
|
|
|
// TODO: implement performLayout
|
|
|
|
geometry = SliverGeometry.zero;
|
|
|
|
}
|
|
|
|
}
|