Skip to content

Commit d8cac57

Browse files
authoredJan 17, 2022
Merge pull request #14 from IgorKhramtsov/IK_slivers
Rewriting feed list to slivers
2 parents a4fc7f8 + 9a3641a commit d8cac57

File tree

7 files changed

+276
-123
lines changed

7 files changed

+276
-123
lines changed
 

‎ios/Podfile.lock

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PODS:
55
- FMDB/standard (2.7.5)
66
- path_provider (0.0.1):
77
- Flutter
8+
- share_plus (0.0.1):
9+
- Flutter
810
- sqflite (0.0.2):
911
- Flutter
1012
- FMDB (>= 2.7.5)
@@ -14,6 +16,7 @@ PODS:
1416
DEPENDENCIES:
1517
- Flutter (from `Flutter`)
1618
- path_provider (from `.symlinks/plugins/path_provider/ios`)
19+
- share_plus (from `.symlinks/plugins/share_plus/ios`)
1720
- sqflite (from `.symlinks/plugins/sqflite/ios`)
1821
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
1922

@@ -26,6 +29,8 @@ EXTERNAL SOURCES:
2629
:path: Flutter
2730
path_provider:
2831
:path: ".symlinks/plugins/path_provider/ios"
32+
share_plus:
33+
:path: ".symlinks/plugins/share_plus/ios"
2934
sqflite:
3035
:path: ".symlinks/plugins/sqflite/ios"
3136
url_launcher:
@@ -35,6 +40,7 @@ SPEC CHECKSUMS:
3540
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
3641
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
3742
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
43+
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
3844
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
3945
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
4046

‎lib/presentation/screens/feed/feed_scroll_physics.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extension PositionRetriever on GlobalKey<State<StatefulWidget>> {
2525
}
2626
}
2727

28-
/// By element scroll physics
28+
/// Scroll physics with snaping to elements (based on PageScrollPhysics)
2929
class FeedScrollPhysics extends ScrollPhysics {
3030
FeedScrollPhysics(
3131
this.elementsSizes,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import 'dart:math';
2+
import 'dart:ui';
3+
4+
import 'package:flutter/cupertino.dart';
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter/rendering.dart';
7+
import 'package:flutter/widgets.dart';
8+
9+
/// [SliverList] with bluring and fading of non-center elements based of how far they are.
10+
class FeedSliverList extends SliverList {
11+
/// [SliverList] with bluring and fading of non-center elements based of how far they are.
12+
const FeedSliverList({
13+
Key? key,
14+
required SliverChildDelegate delegate,
15+
this.listener,
16+
}) : super(key: key, delegate: delegate);
17+
final CurrentElementListener? listener;
18+
19+
@override
20+
SliverMultiBoxAdaptorElement createElement() =>
21+
SliverMultiBoxAdaptorElement(this, replaceMovedChildren: true);
22+
23+
@override
24+
RenderSliverList createRenderObject(BuildContext context) {
25+
final SliverMultiBoxAdaptorElement element =
26+
context as SliverMultiBoxAdaptorElement;
27+
return RenderFeedSliverList(childManager: element, listener: listener);
28+
}
29+
}
30+
31+
// ignore: public_member_api_docs
32+
class RenderFeedSliverList extends RenderSliverList {
33+
// ignore: public_member_api_docs
34+
RenderFeedSliverList({
35+
required RenderSliverBoxChildManager childManager,
36+
this.listener,
37+
}) : super(childManager: childManager);
38+
39+
final CurrentElementListener? listener;
40+
41+
/// Calculating how far is center of element from center of viewport in percents (0.0 - 1.0).
42+
double getDistanceToViewportCenter(RenderBox child) {
43+
final viewportCenter = constraints.viewportMainAxisExtent / 2.0;
44+
45+
final double mainAxisDelta = childMainAxisPosition(child);
46+
final childCenter = mainAxisDelta + (paintExtentOf(child) / 2.0);
47+
return (viewportCenter - childCenter).abs() /
48+
constraints.viewportMainAxisExtent;
49+
}
50+
51+
@override
52+
void performLayout() {
53+
super.performLayout();
54+
if (firstChild == null) return;
55+
56+
var minDistanceChild = firstChild!;
57+
RenderBox? child = childAfter(minDistanceChild);
58+
while (child != null) {
59+
final distance = getDistanceToViewportCenter(child);
60+
if (distance < getDistanceToViewportCenter(minDistanceChild)) {
61+
minDistanceChild = child;
62+
} else {
63+
break; // If we start having larger number - we cant get minimal distance anymore.
64+
}
65+
66+
child = childAfter(child);
67+
}
68+
final minDistanceChildIndex = indexOf(minDistanceChild);
69+
70+
listener?.currentIndex.value = minDistanceChildIndex;
71+
}
72+
73+
/// Copy of original function with bluring side elements.
74+
@override
75+
void paint(PaintingContext context, Offset offset) {
76+
if (firstChild == null) return;
77+
// offset is to the top-left corner, regardless of our axis direction.
78+
// originOffset gives us the delta from the real origin to the origin in the axis direction.
79+
final Offset mainAxisUnit, crossAxisUnit, originOffset;
80+
final bool addExtent;
81+
switch (applyGrowthDirectionToAxisDirection(
82+
constraints.axisDirection, constraints.growthDirection)) {
83+
case AxisDirection.up:
84+
mainAxisUnit = const Offset(0.0, -1.0);
85+
crossAxisUnit = const Offset(1.0, 0.0);
86+
originOffset = offset + Offset(0.0, geometry!.paintExtent);
87+
addExtent = true;
88+
break;
89+
case AxisDirection.right:
90+
mainAxisUnit = const Offset(1.0, 0.0);
91+
crossAxisUnit = const Offset(0.0, 1.0);
92+
originOffset = offset;
93+
addExtent = false;
94+
break;
95+
case AxisDirection.down:
96+
mainAxisUnit = const Offset(0.0, 1.0);
97+
crossAxisUnit = const Offset(1.0, 0.0);
98+
originOffset = offset;
99+
addExtent = false;
100+
break;
101+
case AxisDirection.left:
102+
mainAxisUnit = const Offset(-1.0, 0.0);
103+
crossAxisUnit = const Offset(0.0, 1.0);
104+
originOffset = offset + Offset(geometry!.paintExtent, 0.0);
105+
addExtent = true;
106+
break;
107+
}
108+
RenderBox? child = firstChild;
109+
while (child != null) {
110+
final double mainAxisDelta = childMainAxisPosition(child);
111+
final double crossAxisDelta = childCrossAxisPosition(child);
112+
Offset childOffset = Offset(
113+
originOffset.dx +
114+
mainAxisUnit.dx * mainAxisDelta +
115+
crossAxisUnit.dx * crossAxisDelta,
116+
originOffset.dy +
117+
mainAxisUnit.dy * mainAxisDelta +
118+
crossAxisUnit.dy * crossAxisDelta,
119+
);
120+
if (addExtent) childOffset += mainAxisUnit * paintExtentOf(child);
121+
122+
// If the child's visible interval (mainAxisDelta, mainAxisDelta + paintExtentOf(child))
123+
// does not intersect the paint extent interval (0, constraints.remainingPaintExtent), it's hidden.
124+
if (mainAxisDelta < constraints.remainingPaintExtent &&
125+
mainAxisDelta + paintExtentOf(child) > 0) {
126+
var fadeStrength = getDistanceToViewportCenter(child);
127+
// Special non-linear function, to blur faster
128+
fadeStrength =
129+
(pow(fadeStrength + 0.4, 4.0) + pow(fadeStrength, 2.0)).toDouble();
130+
131+
// ImageFilter with blur and blackout effect
132+
final filter = ImageFilter.compose(
133+
outer: ImageFilter.blur(
134+
sigmaX: 6.0 * fadeStrength,
135+
sigmaY: 6.0 * fadeStrength,
136+
),
137+
inner: ColorFilter.mode(
138+
Colors.black.withOpacity(
139+
max(min(1.0, .45 * fadeStrength), 0.0), // bound to 0.0 - 1.0
140+
),
141+
BlendMode.darken,
142+
),
143+
);
144+
145+
// ClipRect is required to prevent blur from being applied to entire screen
146+
context.pushClipRect(
147+
needsCompositing,
148+
childOffset,
149+
Rect.fromLTWH(0, 0, child.size.width, child.size.height),
150+
(context, offset) {
151+
context.pushLayer(
152+
ImageFilterLayer(imageFilter: filter),
153+
(context, o) {
154+
context.paintChild(child!, o);
155+
},
156+
offset,
157+
);
158+
},
159+
);
160+
}
161+
162+
child = childAfter(child);
163+
}
164+
}
165+
}
166+
167+
class CurrentElementListener {
168+
final ValueNotifier<int> currentIndex = ValueNotifier(0);
169+
}

0 commit comments

Comments
 (0)