- Flutter Silver’s Lifetime Enemy (ScrollView)
- Flutter silver lifetime enemies (ExtendedList)
- Flutter Sliver You want waterfall flow little sister
- Flutter silver locks your beauty
preface
Sliver it’s been 8 months since you asked for your little sister waterfall flow. Writing bugs takes time. Sliver says it works, but there are a few minor flaws.
- SliverPersistentHeader
For anyone who has used this, the question is why do you have to set minextents and maxextents? If I don’t know the height of the Widget in advance, for example, if it’s a piece of text, I don’t know the length, I don’t know the height in advance (of course you can use TextPainter). It’s really disgusting, because when I was writing code a year ago, I had to do it in advance, low.
- SliverToBoxAdapter
I want to keep the CustomScrollView pinned to a Widget, I think everyone’s first reaction is to use SliverPersistentHeader and set minExtent and maxExtent to the same. What if I don’t know the height of the Widget in advance?
- SliverAppbar
The inside is made with a SliverPersistentHeader that exposes an expandedHeight. Again, you want me to set the height ahead of time.
How to lock you gracefully
As usual, the first step is to look at the source code.
SliverPinnedPersistentHeader
See source SliverPersistentHeader
Here, according to pinned and floating differences, the official data are divided into the following 4 situations.
if (floating && pinned)
return _SliverFloatingPinnedPersistentHeader(delegate: delegate);
if (pinned)
return _SliverPinnedPersistentHeader(delegate: delegate);
if (floating)
return _SliverFloatingPersistentHeader(delegate: delegate);
return _SliverScrollingPersistentHeader(delegate: delegate);
Copy the code
I’m going to assume that you’ve seen the previous article, so I’m going to go straight to the final impact on the layout
RenderSliverScrollingPersistentHeader extends RenderSliverPersistentHeader
RenderSliverPinnedPersistentHeader extends RenderSliverPersistentHeader
RenderSliverFloatingPersistentHeader extends RenderSliverPersistentHeader
RenderSliverFloatingPinnedPersistentHeader extends RenderSliverFloatingPersistentHeader
Can see they are ultimately RenderSliverPersistentHeader, there will be the same paint process, and their two main different is that performLayout and childMainAxisPosition method.
We will only focus on pinned:true here.
RenderSliverPinnedPersistentHeader I left the key code here.
@override
void performLayout() {
final SliverConstraints constraints = this.constraints;
/ / the delegate maxExtent
final double maxExtent = this.maxExtent;
// Whether to overlap with other slivers
final bool overlapsContent = constraints.overlap > 0.0;
// layoutChild calls the delegate. Build method here
layoutChild(constraints.scrollOffset, maxExtent, overlapsContent: overlapsContent);
// The rest of the draw area
final double effectiveRemainingPaintExtent = math.max(0, constraints.remainingPaintExtent - constraints.overlap);
/ / layout area
final double layoutExtent = (maxExtent - constraints.scrollOffset).clamp(0.0, effectiveRemainingPaintExtent) as double;
final doublestretchOffset = stretchConfiguration ! =null ?
constraints.overlap.abs() :
0.0;
geometry = SliverGeometry(
scrollExtent: maxExtent,
paintOrigin: constraints.overlap,
paintExtent: math.min(childExtent, effectiveRemainingPaintExtent),
layoutExtent: layoutExtent,
maxPaintExtent: maxExtent + stretchOffset,
maxScrollObstructionExtent: minExtent,
cacheExtent: layoutExtent > 0.0 ? -constraints.cacheOrigin + layoutExtent : layoutExtent,
hasVisualOverflow: true.// Conservatively say we do have overflow to avoid complexity.
);
}
// Affects where the final child is drawn in the paint method.
@override
double childMainAxisPosition(RenderBox child) => 0.0;
Copy the code
The above things should be more familiar, the Sliver series inside the compulsory knowledge. We just need to calculate minExtent and maxExtent in the performLayout process.
How do you compute that with the WidgetminExtent
和 maxExtent
呢
Convert the Widget to RenderBox
We all know that Widget <=> Element <=> RenderOjbect, the Element is attached to the parent in the mount, At this point we can use the updateChild method to convert the Widget to the corresponding Element, and get the Widget’s RenderOjbect(RenderBox) from the insertChildRenderObject.
The important code is as follows:
Element _minExtentPrototype;
static final Object _minExtentPrototypeSlot = Object(a);Element _maxExtentPrototype;
static final Object _maxExtentPrototypeSlot = Object(a);@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
renderObject.element = this;
_minExtentPrototype = updateChild(_minExtentPrototype,
// Widget corresponding to minExtent
widget.delegate.minExtentProtoType, _minExtentPrototypeSlot);
_maxExtentPrototype = updateChild(_maxExtentPrototype,
// Widget corresponding to maxExtent
widget.delegate.maxExtentProtoType, _maxExtentPrototypeSlot);
}
@override
void insertChildRenderObject(covariant RenderBox child, dynamic slot) {
assert(renderObject.debugValidateChild(child));
assert(child is RenderBox);
// Assign child to RenderOject according to slot
if (slot == _minExtentPrototypeSlot) {
renderObject.minProtoType = child;
} else if (slot == _maxExtentPrototypeSlot) {
renderObject.maxProtoType = child;
} else{ renderObject.child = child; }}Copy the code
The stack information is shown below
Calculate by renderBox.layoutminExtent
和 maxExtent
The logic is the same as the official logic, except that minProtoType and maxProtoType are used to calculate minExtent and maxProtoType
RenderBox _minProtoType;
RenderBox get minProtoType => _minProtoType;
set minProtoType(RenderBox value) {
if(_minProtoType ! =null) {
dropChild(_minProtoType);
}
_minProtoType = value;
if(_minProtoType ! =null) {
adoptChild(_minProtoType);
}
markNeedsLayout();
}
RenderBox _maxProtoType;
RenderBox get maxProtoType => _maxProtoType;
set maxProtoType(RenderBox value) {
if(_maxProtoType ! =null) {
dropChild(_maxProtoType);
}
_maxProtoType = value;
if(_maxProtoType ! =null) {
adoptChild(_maxProtoType);
}
markNeedsLayout();
}
double get minExtent => getChildExtend(minProtoType, constraints);
double get maxExtent => getChildExtend(maxProtoType, constraints);
double getChildExtend(RenderBox child, SliverConstraints constraints) {
if (child == null) {
return 0.0;
}
assert(child.hasSize);
assert(constraints.axis ! =null);
switch (constraints.axis) {
case Axis.vertical:
return child.size.height;
case Axis.horizontal:
return child.size.width;
}
return null;
}
@override
void performLayout() {
final SliverConstraints constraints = this.constraints;
minProtoType.layout(constraints.asBoxConstraints(), parentUsesSize: true);
maxProtoType.layout(constraints.asBoxConstraints(), parentUsesSize: true);
final bool overlapsContent = constraints.overlap > 0.0;
excludeFromSemanticsScrolling =
overlapsContent || (constraints.scrollOffset > maxExtent - minExtent);
layoutChild(constraints.scrollOffset, maxExtent,
overlapsContent: overlapsContent);
final double effectiveRemainingPaintExtent =
math.max(0, constraints.remainingPaintExtent - constraints.overlap);
final double layoutExtent = (maxExtent - constraints.scrollOffset)
.clamp(0.0, effectiveRemainingPaintExtent) as double;
geometry = SliverGeometry(
scrollExtent: maxExtent,
paintOrigin: constraints.overlap,
paintExtent: math.min(childExtent, effectiveRemainingPaintExtent),
layoutExtent: layoutExtent,
maxPaintExtent: maxExtent,
maxScrollObstructionExtent: minExtent,
cacheExtent: layoutExtent > 0.0
? -constraints.cacheOrigin + layoutExtent
: layoutExtent,
hasVisualOverflow:
true.// Conservatively say we do have overflow to avoid complexity.
);
}
Copy the code
SliverPinnedToBoxAdapter
See source SliverToBoxAdapter
The SliverToBoxAdapter source code is relatively simple. It is drawn in the paint method using ParentData to set the drawing start point
class RenderSliverToBoxAdapter extends RenderSliverSingleBoxAdapter {
/// Creates a [RenderSliver] that wraps a [RenderBox].
RenderSliverToBoxAdapter({
RenderBox child,
}) : super(child: child);
@override
void performLayout() {
if (child == null) {
geometry = SliverGeometry.zero;
return;
}
child.layout(constraints.asBoxConstraints(), parentUsesSize: true);
double childExtent;
switch (constraints.axis) {
case Axis.horizontal:
childExtent = child.size.width;
break;
case Axis.vertical:
childExtent = child.size.height;
break;
}
assert(childExtent ! =null);
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,
hasVisualOverflow: childExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0,); setChildParentData(child, constraints, geometry); }}Copy the code
We said at the beginning that a SliverToBoxAdapter can be pinned to the data side and remain pinned to the data side. True) and minExtent = maxExtent = child’s extent. So all is well solved, we can get the performLayout RenderSliverPinnedPersistentHeader code directly to use, and calculate the good drawing the starting point to ParentData can.
@override
void performLayout() {
if (child == null) {
geometry = SliverGeometry.zero;
return;
}
child.layout(constraints.asBoxConstraints(), parentUsesSize: true);
assert(childExtent ! =null);
final double effectiveRemainingPaintExtent =
math.max(0, constraints.remainingPaintExtent - constraints.overlap);
final double layoutExtent = (childExtent - constraints.scrollOffset)
.clamp(0.0, effectiveRemainingPaintExtent) as double;
geometry = SliverGeometry(
scrollExtent: childExtent,
paintOrigin: constraints.overlap,
paintExtent: math.min(childExtent, effectiveRemainingPaintExtent),
layoutExtent: layoutExtent,
maxPaintExtent: childExtent,
maxScrollObstructionExtent: childExtent,
cacheExtent: layoutExtent > 0.0
? -constraints.cacheOrigin + layoutExtent
: layoutExtent,
hasVisualOverflow:
true.// Conservatively say we do have overflow to avoid complexity.
);
setChildParentData(child, constraints, geometry);
}
@override
void setChildParentData(RenderObject child, SliverConstraints constraints,
SliverGeometry geometry) {
final SliverPhysicalParentData childParentData =
child.parentData as SliverPhysicalParentData;
assert(constraints.axisDirection ! =null);
assert(constraints.growthDirection ! =null);
Offset offset = Offset.zero;
switch (applyGrowthDirectionToAxisDirection(
constraints.axisDirection, constraints.growthDirection)) {
case AxisDirection.up:
offset += Offset(
0.0,
geometry.paintExtent -
childMainAxisPosition(child as RenderBox) -
childExtent);
break;
case AxisDirection.down:
offset += Offset(0.0, childMainAxisPosition(child as RenderBox));
break;
case AxisDirection.left:
offset += Offset(
geometry.paintExtent -
childMainAxisPosition(child as RenderBox) -
childExtent,
0.0);
break;
case AxisDirection.right:
offset += Offset(childMainAxisPosition(child as RenderBox), 0.0);
break;
}
childParentData.paintOffset = offset;
assert(childParentData.paintOffset ! =null);
}
@override
double childMainAxisPosition(RenderBox child) => 0.0;
Copy the code
ExtendedSliverAppbar
This is not to look at the code, very simple, it is made of SliverPinnedPersistentHeader, of course, is also the reference for the official SliverAppbar `, just as complicated as there is no official, believes that seen in front of me a few silver related article, can literally change.
How do I use the Silver Extension library
Add reference
Add the reference to dependencies under pubspec.yaml
dependencies:
extended_sliver: latest-version
Copy the code
Execute the flutter Packages get download
SliverPinnedPersistentHeader
Same as the official SliverPersistentHeader(pinned: true), the difference is that you don’t have to set minExtent and maxExtent.
It calculates minextents and maxextents by setting minExtentProtoType and maxExtentProtoType.
This is a very useful component when you don’t know the actual size of the Widget before it has a layout.
SliverPinnedPersistentHeader(
delegate: MySliverPinnedPersistentHeaderDelegate(
minExtentProtoType: Container(
height: 120.0,
color: Colors.red.withOpacity(0.5),
child: FlatButton(
child: const Text('minProtoType'),
onPressed: () {
print('minProtoType');
},
),
alignment: Alignment.topCenter,
),
maxExtentProtoType: Container(
height: 200.0,
color: Colors.blue,
child: FlatButton(
child: const Text('maxProtoType'),
onPressed: () {
print('maxProtoType');
},
),
alignment: Alignment.bottomCenter,
),
),
)
Copy the code
SliverPinnedToBoxAdapter
You can easily create a locked Sliver.
This is a very useful component when you have no way of knowing the actual size of the child before it has a layout.
SliverPinnedToBoxAdapter(
child: Container(
padding: const EdgeInsets.all(20),
color: Colors.blue.withOpacity(0.5),
child: Column(
children: <Widget>[
const Text(
'[love]Extended text help you to build rich text quickly. any special text you will have with extended text. '
'\n\nIt\'s my pleasure to invite you to join \$FlutterCandies\$ if you want to improve flutter .[love]'
'\n\nif you meet any problem, please let me konw @zmtzawqlp .[sun_glasses]'),
FlatButton(
child: const Text('I\'m button. click me! '),
onPressed: () {
debugPrint('click'); },),],),)Copy the code
ExtendedSliverAppbar
You can create a SliverAppbar without setting an expandedHeight.
return CustomScrollView(
slivers: <Widget>[
ExtendedSliverAppbar(
title: const Text(
'ExtendedSliverAppbar',
style: TextStyle(color: Colors.white),
),
leading: const BackButton(
onPressed: null,
color: Colors.white,
),
background: Image.asset(
'assets/cypridina.jpeg',
fit: BoxFit.cover,
),
actions: Padding(
padding: const EdgeInsets.all(10.0),
child: Icon(
Icons.more_horiz,
color: Colors.white,
),
),
),
],
);
Copy the code
Complex example
Example address, including the following features.
- Random length of text, no need to write maxExtent
- The drop-down refresh
- Controls the visibility of buttons in the Toolbar based on their location
conclusion
2020 is going to be a busy year and a fast growth year for Flutter, web performance has been improved, MacOS, Linux are all in beta, and UWP is on the way. All I need is you. What are you looking at? You? ! Put extended_sliver on, if you have any good Sliver effects, welcome pr; If you need anything new, you’re welcome.
Welcome to joinFlutter CandiesAnd produce cute little Flutter candies (QQ group: 181398081)
Finally put the Flutter on the bucket. It smells good.