Skip to content

Commit 14976b9

Browse files
committed
*fix scroll physics centering (center along active area)
+load widget on octoImage (#8)
1 parent 7abeb8d commit 14976b9

File tree

3 files changed

+119
-101
lines changed

3 files changed

+119
-101
lines changed

lib/presentation/screens/feed/feed_scroll_physics.dart

+10-5
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,31 @@ class FeedScrollPhysics extends ScrollPhysics {
3030
FeedScrollPhysics(
3131
this.elementsSizes,
3232
this.activeViewportHeight, {
33+
this.topPadding = 0.0,
3334
ScrollPhysics? parent,
3435
}) : super(parent: parent);
3536

3637
/// Sizes of elements, to calculate correct item positions
37-
Map<int, Size> elementsSizes;
38+
final Map<int, Size> elementsSizes;
3839

39-
/// Height of visible viewport, to centrate element
40-
double activeViewportHeight;
40+
/// Height of visible viewport, to center element
41+
final double activeViewportHeight;
42+
43+
/// Height of top safe area, to adjust centred element
44+
final double topPadding;
4145

4246
@override
4347
FeedScrollPhysics applyTo(ScrollPhysics? ancestor) {
4448
return FeedScrollPhysics(
4549
elementsSizes,
4650
activeViewportHeight,
51+
topPadding: topPadding,
4752
parent: buildParent(ancestor),
4853
);
4954
}
5055

5156
double _getElement(ScrollMetrics position) {
52-
final viewportHeight = activeViewportHeight;
57+
final viewportHeight = position.viewportDimension;
5358
// Target position
5459
final viewportCenter = position.pixels + (viewportHeight / 2.0);
5560

@@ -85,7 +90,7 @@ class FeedScrollPhysics extends ScrollPhysics {
8590
}
8691
final elementCenter = sum + (elementsSizes[elementIndex]!.height / 2.0);
8792

88-
return elementCenter - viewportCenter;
93+
return (elementCenter - viewportCenter) - topPadding;
8994
}
9095

9196
double _getTargetPixels(

lib/presentation/screens/feed/feed_widget.dart

+99-94
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class _FeedWidgetState extends State<FeedWidget> {
3737
super.initState();
3838

3939
listener.itemPositions.addListener(updateSelectedMeme);
40+
// cacheManager.emptyCache();
4041
}
4142

4243
@override
@@ -79,73 +80,86 @@ class _FeedWidgetState extends State<FeedWidget> {
7980
}
8081

8182
return LayoutBuilder(
82-
builder: (BuildContext context, BoxConstraints cnstr) => Stack(
83-
children: [
84-
ImageFiltered(
85-
imageFilter: ImageFilter.compose(
86-
outer: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
87-
inner: ColorFilter.mode(
88-
Colors.black.withOpacity(.15),
89-
BlendMode.darken,
83+
builder: (BuildContext context, BoxConstraints cnstr) {
84+
// We can use safe area here, but it will create more problems
85+
final maxHeight = cnstr.maxHeight -
86+
(MediaQuery.of(context).padding.bottom +
87+
MediaQuery.of(context).padding.top);
88+
89+
return Stack(
90+
children: [
91+
ImageFiltered(
92+
imageFilter: ImageFilter.compose(
93+
outer: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
94+
inner: ColorFilter.mode(
95+
Colors.black.withOpacity(.15),
96+
BlendMode.darken,
97+
),
9098
),
91-
),
92-
child: ScrollablePositionedList.builder(
93-
physics: FeedScrollPhysics(
94-
renderedMemes,
95-
// sbustract bottom tab navbar size, because we cant see anything
96-
// behide it (unlike top unsafe area)
97-
cnstr.maxHeight - MediaQuery.of(context).padding.bottom,
98-
),
99-
itemCount: state.memes.length,
100-
itemPositionsListener: listener,
101-
itemBuilder: (BuildContext context, int index) {
102-
final imageProvider = CachedNetworkImageProvider(
103-
state.memes[index].imageLink,
104-
cacheManager: cacheManager,
105-
);
106-
// We can use safe area here, but it will create more problems
107-
final maxHeight = cnstr.maxHeight -
108-
MediaQuery.of(context).padding.bottom -
109-
MediaQuery.of(context).padding.top;
110-
final key = renderedMemesKeys.putIfAbsent(
111-
index,
112-
() => GlobalKey(),
113-
);
114-
if (renderedMemes[index] == null) {
115-
propogateSizeToMap(
116-
imageProvider,
117-
cnstr,
118-
maxHeight,
99+
child: ScrollablePositionedList.builder(
100+
physics: FeedScrollPhysics(
101+
renderedMemes,
102+
// sbustract bottom tab navbar size, because we cant see anything
103+
// behide it (unlike top unsafe area)
104+
maxHeight,
105+
topPadding: MediaQuery.of(context).padding.top,
106+
),
107+
itemCount: state.memes.length,
108+
itemPositionsListener: listener,
109+
itemBuilder: (BuildContext context, int index) {
110+
final imageProvider = CachedNetworkImageProvider(
111+
state.memes[index].imageLink,
112+
cacheManager: cacheManager,
113+
);
114+
115+
final key = renderedMemesKeys.putIfAbsent(
119116
index,
117+
() => GlobalKey(),
118+
);
119+
if (renderedMemes[index] == null) {
120+
propogateSizeToMap(
121+
imageProvider,
122+
cnstr,
123+
maxHeight,
124+
index,
125+
);
126+
renderedMemes[index] = Size(cnstr.maxWidth, 200);
127+
}
128+
129+
return ConstrainedBox(
130+
constraints: BoxConstraints(
131+
minHeight: 200,
132+
// maxHeight: maxHeight,
133+
maxWidth: cnstr.maxWidth,
134+
),
135+
child: OctoImage(
136+
key: key,
137+
image: imageProvider,
138+
fit: BoxFit.fitWidth,
139+
progressIndicatorBuilder: (context, progress) =>
140+
Lottie.asset(
141+
'lib/assets/animations/space_loader.json',
142+
),
143+
),
120144
);
121-
renderedMemes[index] = Size(cnstr.maxWidth, 200);
122-
}
123-
124-
return ConstrainedBox(
125-
constraints: BoxConstraints(
126-
minHeight: 200,
127-
minWidth: 200,
128-
maxHeight: maxHeight,
129-
),
130-
child: OctoImage(
131-
key: key,
132-
image: imageProvider,
133-
),
145+
},
146+
),
147+
),
148+
buildVignette(),
149+
// Build overlay with actions and description, with listenable of current
150+
// scroll position
151+
ValueListenableBuilder(
152+
valueListenable: listener.itemPositions,
153+
builder: (_, __, ___) {
154+
return PostWidget(
155+
state,
156+
constraints: cnstr,
134157
);
135158
},
136-
),
137-
),
138-
buildVignette(),
139-
// Build overlay with actions and description, with listenable of current
140-
// scroll position
141-
ValueListenableBuilder(
142-
valueListenable: listener.itemPositions,
143-
builder: (_, __, ___) {
144-
return PostWidget(state);
145-
},
146-
)
147-
],
148-
),
159+
)
160+
],
161+
);
162+
},
149163
);
150164
}
151165

@@ -185,39 +199,30 @@ class _FeedWidgetState extends State<FeedWidget> {
185199
double maxHeight,
186200
int index,
187201
) {
202+
late ImageStreamListener listener;
203+
listener = ImageStreamListener((info, _) {
204+
// Calculating correct size of resulting image
205+
Size size = Size(
206+
info.image.width.toDouble(),
207+
info.image.height.toDouble(),
208+
);
209+
size = Size(
210+
constraints.maxWidth,
211+
constraints.maxWidth / size.aspectRatio,
212+
);
213+
214+
// For updating UI after fetching image (without it overlayed image will be smol)
215+
WidgetsBinding.instance!.addPostFrameCallback(
216+
(_) => setState(() {
217+
renderedMemes[index] = size;
218+
imageProvider
219+
.resolve(ImageConfiguration.empty)
220+
.removeListener(listener);
221+
}),
222+
);
223+
});
188224
imageProvider.resolve(ImageConfiguration.empty).addListener(
189-
ImageStreamListener((info, _) {
190-
// Calculating correct size of resulting image
191-
Size size = Size(
192-
info.image.width.toDouble(),
193-
info.image.height.toDouble(),
225+
listener,
194226
);
195-
if (size.width > constraints.maxWidth) {
196-
size = Size(
197-
constraints.maxWidth,
198-
constraints.maxWidth / size.aspectRatio,
199-
);
200-
}
201-
if (size.height > maxHeight) {
202-
size = Size(
203-
maxHeight * size.aspectRatio,
204-
maxHeight,
205-
);
206-
}
207-
if (size.height < 200) {
208-
size = Size(
209-
200 * size.aspectRatio,
210-
200,
211-
);
212-
}
213-
214-
// For updating UI after fetching image (without it overlayed image will be smol)
215-
WidgetsBinding.instance!.addPostFrameCallback(
216-
(_) => setState(() {
217-
renderedMemes[index] = size;
218-
}),
219-
);
220-
}),
221-
);
222227
}
223228
}

lib/presentation/screens/feed/post_widget.dart

+10-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ import 'package:octo_image/octo_image.dart';
1515
class PostWidget extends StatelessWidget {
1616
const PostWidget(
1717
this.state, {
18+
required this.constraints,
1819
Key? key,
1920
}) : super(key: key);
2021

2122
final FeedState state;
23+
final BoxConstraints constraints;
2224

2325
@override
2426
Widget build(BuildContext context) {
@@ -49,14 +51,20 @@ class PostWidget extends StatelessWidget {
4951
],
5052
);
5153
},
52-
child: Container(
54+
child: ConstrainedBox(
5355
key: Key(state.currentMemeModel.imageLink),
56+
constraints: BoxConstraints(
57+
minWidth: constraints.maxWidth,
58+
),
5459
child: OctoImage(
5560
image: CachedNetworkImageProvider(
5661
state.currentMemeModel.imageLink,
57-
errorListener: () => print('error (${state.currentMemeModel.imageLink})'),
62+
errorListener: () =>
63+
print('error (${state.currentMemeModel.imageLink})'),
5864
cacheManager: serviceLocator<CacheManager>(),
5965
),
66+
fit: BoxFit.fitWidth,
67+
width: constraints.maxWidth,
6068
),
6169
),
6270
),

0 commit comments

Comments
 (0)