Skip to content

Commit 3a1add1

Browse files
committed
Merge branch 'master' into IK_share
2 parents ea018a9 + 86df1ea commit 3a1add1

File tree

5 files changed

+165
-37
lines changed

5 files changed

+165
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import 'package:devour/presentation/widgets/platform/platform_icon_button.dart';
2+
import 'package:flutter/cupertino.dart';
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter/widgets.dart';
5+
import 'package:lottie/lottie.dart';
6+
import 'package:octo_image/octo_image.dart';
7+
8+
/// A widget to show image from network or cache with correct size
9+
class FeedImage extends StatelessWidget {
10+
/// Construct FeedImag
11+
const FeedImage({
12+
Key? key,
13+
this.constraints,
14+
required this.imageProvider,
15+
this.onRefreshPressed,
16+
}) : super(key: key);
17+
18+
final BoxConstraints? constraints;
19+
final ImageProvider imageProvider;
20+
/// Callback fired, when user taps on error builder, when image is failed to load
21+
final VoidCallback? onRefreshPressed;
22+
23+
@override
24+
Widget build(BuildContext context) {
25+
return ConstrainedBox(
26+
constraints: BoxConstraints(
27+
minHeight: 200,
28+
maxWidth: constraints?.maxWidth ?? double.infinity,
29+
),
30+
child: OctoImage(
31+
// key: widget.key,
32+
image: imageProvider,
33+
fit: BoxFit.fitWidth,
34+
progressIndicatorBuilder: (ctx, prgrs) => SizedBox(
35+
height: 200,
36+
child: Lottie.asset(
37+
'lib/assets/animations/space_loader.json',
38+
),
39+
),
40+
errorBuilder: (ctx, error, stack) => SizedBox(
41+
height: 200,
42+
child: Center(
43+
child: PlatformIconButton(
44+
icon: Icons.refresh,
45+
onPressed: onRefreshPressed,
46+
color: CupertinoColors.white,
47+
size: 24,
48+
text: 'failed to load 🤬',
49+
),
50+
),
51+
),
52+
),
53+
);
54+
}
55+
}

lib/presentation/screens/feed/feed_widget.dart

+18-16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import 'dart:math';
21
import 'dart:ui';
32

43
import 'package:cached_network_image/cached_network_image.dart';
54
import 'package:devour/application/feed/bloc/feed_bloc.dart';
65
import 'package:devour/injection.dart';
6+
import 'package:devour/presentation/screens/feed/feed_image_widget.dart';
77
import 'package:devour/presentation/screens/feed/feed_scroll_physics.dart';
88
import 'package:devour/presentation/screens/feed/post_widget.dart';
9+
import 'package:devour/presentation/widgets/animations/animated_opacity_reverse.dart';
910
import 'package:flutter/cupertino.dart';
1011
import 'package:flutter/material.dart';
1112
import 'package:flutter/rendering.dart';
@@ -14,7 +15,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
1415
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
1516
import 'package:fpdart/fpdart.dart' show Option;
1617
import 'package:lottie/lottie.dart';
17-
import 'package:octo_image/octo_image.dart';
1818
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
1919
import 'package:collection/collection.dart';
2020

@@ -111,7 +111,6 @@ class _FeedWidgetState extends State<FeedWidget> {
111111
state.memes[index].imageLink,
112112
cacheManager: cacheManager,
113113
);
114-
115114
final key = renderedMemesKeys.putIfAbsent(
116115
index,
117116
() => GlobalKey(),
@@ -126,20 +125,22 @@ class _FeedWidgetState extends State<FeedWidget> {
126125
renderedMemes[index] = Size(cnstr.maxWidth, 200);
127126
}
128127

129-
return ConstrainedBox(
130-
constraints: BoxConstraints(
131-
minHeight: 200,
132-
// maxHeight: maxHeight,
133-
maxWidth: cnstr.maxWidth,
134-
),
135-
child: OctoImage(
128+
// Using [AnimatedVisibilityWithReversedDuration] to
129+
// hide with animation selected post and show immediately
130+
// if its not. Its because of postWidget, it will re-render
131+
// any error images, so they will reload, and this and post
132+
// widgets will have different state, but overlaping each other
133+
return AnimatedVisibilityWithReversedDuration(
134+
duration: const Duration(milliseconds: 300),
135+
reversedDuration: Duration.zero,
136+
visibility: index != state.iterator,
137+
child: FeedImage(
136138
key: key,
137-
image: imageProvider,
138-
fit: BoxFit.fitWidth,
139-
progressIndicatorBuilder: (context, progress) =>
140-
Lottie.asset(
141-
'lib/assets/animations/space_loader.json',
142-
),
139+
imageProvider: imageProvider,
140+
constraints: cnstr,
141+
onRefreshPressed: () => setState(() {
142+
renderedMemesKeys[index] = GlobalKey();
143+
}),
143144
),
144145
);
145146
},
@@ -215,6 +216,7 @@ class _FeedWidgetState extends State<FeedWidget> {
215216
WidgetsBinding.instance!.addPostFrameCallback(
216217
(_) => setState(() {
217218
renderedMemes[index] = size;
219+
print('index $index is $size');
218220
imageProvider
219221
.resolve(ImageConfiguration.empty)
220222
.removeListener(listener);

lib/presentation/screens/feed/post_widget.dart

+23-20
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ import 'package:devour/application/feed/bloc/feed_bloc.dart';
55
import 'package:devour/domain/meme/abstract_meme_model.dart';
66
import 'package:devour/domain/misc/helper.dart';
77
import 'package:devour/injection.dart';
8+
import 'package:devour/presentation/screens/feed/feed_image_widget.dart';
89
import 'package:devour/presentation/widgets/platform/platform_icon_button.dart';
910
import 'package:devour/presentation/widgets/platform/platform_theme.dart';
1011
import 'package:flutter/cupertino.dart';
1112
import 'package:flutter/material.dart';
1213
import 'package:flutter/widgets.dart';
1314
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
14-
import 'package:octo_image/octo_image.dart';
1515
import 'package:share_plus/share_plus.dart';
1616

17-
class PostWidget extends StatelessWidget {
17+
/// Widget to show post information like description and likes and image,
18+
/// to create illusion of selecting meme from list.
19+
class PostWidget extends StatefulWidget {
20+
/// Constructs PostWidget
1821
const PostWidget(
1922
this.state, {
2023
required this.constraints,
@@ -24,13 +27,19 @@ class PostWidget extends StatelessWidget {
2427
final FeedState state;
2528
final BoxConstraints constraints;
2629

30+
@override
31+
State<PostWidget> createState() => _PostWidgetState();
32+
}
33+
34+
class _PostWidgetState extends State<PostWidget> {
2735
@override
2836
Widget build(BuildContext context) {
29-
if (state.isLoading) {
37+
if (widget.state.isLoading) {
3038
return Container();
3139
}
3240

33-
final key = state.currentMemeWidget.toNullable();
41+
final key = widget.state.currentMemeWidget.toNullable();
42+
// key.currentWidget
3443
final box = key?.currentContext?.findRenderObject() as RenderBox?;
3544
final pos = box?.localToGlobal(Offset.zero) ?? Offset.zero;
3645

@@ -53,21 +62,15 @@ class PostWidget extends StatelessWidget {
5362
],
5463
);
5564
},
56-
child: ConstrainedBox(
57-
key: Key(state.currentMemeModel.imageLink),
58-
constraints: BoxConstraints(
59-
minWidth: constraints.maxWidth,
60-
),
61-
child: OctoImage(
62-
image: CachedNetworkImageProvider(
63-
state.currentMemeModel.imageLink,
64-
errorListener: () =>
65-
print('error (${state.currentMemeModel.imageLink})'),
66-
cacheManager: serviceLocator<CacheManager>(),
67-
),
68-
fit: BoxFit.fitWidth,
69-
width: constraints.maxWidth,
65+
child: FeedImage(
66+
key: Key(widget.state.currentMemeModel.imageLink),
67+
imageProvider: CachedNetworkImageProvider(
68+
widget.state.currentMemeModel.imageLink,
69+
errorListener: () => print(
70+
'error (${widget.state.currentMemeModel.imageLink})'),
71+
cacheManager: serviceLocator<CacheManager>(),
7072
),
73+
constraints: widget.constraints,
7174
),
7275
),
7376
),
@@ -77,7 +80,7 @@ class PostWidget extends StatelessWidget {
7780
child: Row(
7881
mainAxisAlignment: MainAxisAlignment.end,
7982
children: [
80-
PostActionsWidget(currentPost: state.currentMemeModel),
83+
PostActionsWidget(currentPost: widget.state.currentMemeModel),
8184
],
8285
),
8386
),
@@ -86,7 +89,7 @@ class PostWidget extends StatelessWidget {
8689
bottom: 100,
8790
width: 400,
8891
child: PostDescriptionWidget(
89-
currentPost: state.currentMemeModel,
92+
currentPost: widget.state.currentMemeModel,
9093
),
9194
),
9295
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:flutter/material.dart';
2+
3+
/// Like [AnimatedOpacity], but with control over [reversedDuration]
4+
class AnimatedVisibilityWithReversedDuration extends StatefulWidget {
5+
/// Constructs AnimatedVisibilityWithReversedDuration
6+
const AnimatedVisibilityWithReversedDuration({
7+
Key? key,
8+
required this.duration,
9+
required this.visibility,
10+
required this.child,
11+
Duration? reversedDuration,
12+
}) : reversedDuration = reversedDuration ?? duration,
13+
super(key: key);
14+
15+
final Duration duration;
16+
/// Reversed duration. If null - [duration] will be used
17+
final Duration reversedDuration;
18+
final bool visibility;
19+
final Widget child;
20+
21+
@override
22+
State<AnimatedVisibilityWithReversedDuration> createState() =>
23+
_AnimatedVisibilityWithReversedDurationState();
24+
}
25+
26+
class _AnimatedVisibilityWithReversedDurationState
27+
extends State<AnimatedVisibilityWithReversedDuration>
28+
with TickerProviderStateMixin {
29+
late AnimationController controller;
30+
31+
@override
32+
void initState() {
33+
super.initState();
34+
controller = AnimationController(
35+
vsync: this,
36+
duration: widget.reversedDuration,
37+
reverseDuration: widget.duration,
38+
value: 1.0,
39+
);
40+
}
41+
42+
@override
43+
void didUpdateWidget(
44+
covariant AnimatedVisibilityWithReversedDuration oldWidget,
45+
) {
46+
if (oldWidget.visibility != widget.visibility) {
47+
if (widget.visibility) {
48+
controller.forward();
49+
} else {
50+
controller.reverse();
51+
}
52+
}
53+
super.didUpdateWidget(oldWidget);
54+
}
55+
56+
@override
57+
Widget build(BuildContext context) => AnimatedBuilder(
58+
animation: controller,
59+
child: widget.child,
60+
builder: (context, child) {
61+
return Opacity(
62+
opacity: controller.value,
63+
child: child,
64+
);
65+
},
66+
);
67+
}

lib/presentation/widgets/platform/platform_icon_button.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class PlatformIconButton
1717
final double size;
1818
final Color color;
1919
final String text;
20-
final VoidCallback onPressed;
20+
final VoidCallback? onPressed;
2121

2222
@override
2323
CupertinoButton buildCupertino(BuildContext context) {
@@ -54,6 +54,7 @@ class PlatformIconButton
5454
];
5555

5656
return Column(
57+
mainAxisSize: MainAxisSize.min,
5758
children: [
5859
Text(
5960
String.fromCharCode(icon.codePoint),

0 commit comments

Comments
 (0)