@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
10
10
import 'package:photo_manager/photo_manager.dart' ;
11
11
import 'package:photo_manager_image_provider/photo_manager_image_provider.dart' ;
12
12
import 'package:video_player/video_player.dart' ;
13
+ import 'package:visibility_detector/visibility_detector.dart' ;
13
14
import 'package:wechat_picker_library/wechat_picker_library.dart' ;
14
15
15
16
import '../../constants/constants.dart' ;
@@ -179,29 +180,45 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
179
180
final _showVideo = ValueNotifier <bool >(false );
180
181
late final _controller = widget.controller;
181
182
183
+ ScrollNotificationObserverState ? _scrollNotificationObserver;
184
+ bool _scrolling = false ;
185
+
182
186
@override
183
187
void initState () {
184
188
super .initState ();
185
- WidgetsBinding .instance.addPostFrameCallback ((_) {
186
- _controller.play ().then ((_) {
187
- HapticFeedback .lightImpact ();
188
- _showVideo.value = true ;
189
- });
190
- });
191
189
_controller.addListener (_notify);
192
190
}
193
191
192
+ @override
193
+ void didChangeDependencies () {
194
+ super .didChangeDependencies ();
195
+ _scrollNotificationObserver? .removeListener (_handleScrollNotification);
196
+ _scrollNotificationObserver = ScrollNotificationObserver .maybeOf (context);
197
+ _scrollNotificationObserver? .addListener (_handleScrollNotification);
198
+ }
199
+
194
200
@override
195
201
void dispose () {
202
+ _scrollNotificationObserver? .removeListener (_handleScrollNotification);
196
203
_controller.pause ();
197
204
_controller.removeListener (_notify);
198
205
super .dispose ();
199
206
}
200
207
201
- Future <void > continuePlay () async {
202
- if (_showVideo.value && _controller.value.position != Duration .zero) {
203
- HapticFeedback .lightImpact ();
204
- await _controller.play ();
208
+ void _handleScrollNotification (ScrollNotification notification) {
209
+ if (notification is ScrollStartNotification ) {
210
+ _scrolling = true ;
211
+ } else if (notification is ScrollEndNotification ) {
212
+ _scrolling = false ;
213
+ }
214
+ }
215
+
216
+ void _onVisibilityChanged (VisibilityInfo info) {
217
+ final fraction = info.visibleFraction;
218
+ if (fraction == 1 && ! _showVideo.value && ! _scrolling) {
219
+ _showVideoAndPlay ();
220
+ } else if (fraction < 1 && _showVideo.value) {
221
+ _hideVideoAndStop ();
205
222
}
206
223
}
207
224
@@ -223,75 +240,89 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
223
240
}
224
241
225
242
Future <void > _hideVideoAndStop () async {
243
+ _showVideo.value = false ;
244
+ await Future .delayed (kThemeChangeDuration);
226
245
await _controller.pause ();
227
246
await _controller.seekTo (Duration .zero);
228
- _showVideo.value = false ;
229
247
}
230
248
231
249
@override
232
250
Widget build (BuildContext context) {
233
- return GestureDetector (
234
- onLongPress: () {
235
- _showVideoAndPlay ();
236
- },
237
- onLongPressUp: () {
238
- _hideVideoAndStop ();
239
- },
240
- child: ExtendedImageGesture (
241
- widget.state,
242
- imageBuilder: (
243
- Widget image, {
244
- ExtendedImageGestureState ? imageGestureState,
245
- }) {
246
- return ValueListenableBuilder (
247
- valueListenable: _showVideo,
248
- builder: (context, showVideo, child) {
249
- if (imageGestureState == null ||
250
- widget.state.extendedImageInfo == null ) {
251
- return child! ;
252
- }
253
- final size = MediaQuery .sizeOf (context);
254
- final rect = GestureWidgetDelegateFromState .getRectFormState (
255
- Offset .zero & size,
256
- imageGestureState,
257
- width: _controller.value.size.width,
258
- height: _controller.value.size.height,
259
- copy: true ,
260
- );
261
- return Stack (
262
- children: < Widget > [
263
- imageGestureState.wrapGestureWidget (
264
- FittedBox (
265
- fit: BoxFit .cover,
266
- clipBehavior: Clip .hardEdge,
267
- child: SizedBox (
268
- width: rect.width,
269
- height: rect.height,
270
- child: VideoPlayer (_controller),
251
+ return VisibilityDetector (
252
+ key: ValueKey (widget.state),
253
+ onVisibilityChanged: _onVisibilityChanged,
254
+ child: GestureDetector (
255
+ onLongPress: () {
256
+ _showVideoAndPlay ();
257
+ },
258
+ onLongPressUp: () {
259
+ _hideVideoAndStop ();
260
+ },
261
+ child: ExtendedImageGesture (
262
+ widget.state,
263
+ imageBuilder: (
264
+ Widget image, {
265
+ ExtendedImageGestureState ? imageGestureState,
266
+ }) {
267
+ return ValueListenableBuilder (
268
+ valueListenable: _showVideo,
269
+ builder: (context, showVideo, child) {
270
+ if (imageGestureState == null ||
271
+ widget.state.extendedImageInfo == null ) {
272
+ return child! ;
273
+ }
274
+ final scaled = imageGestureState.gestureDetails? .totalScale !=
275
+ imageGestureState.imageGestureConfig? .initialScale;
276
+ final size = MediaQuery .sizeOf (context);
277
+ final imageRect =
278
+ GestureWidgetDelegateFromState .getRectFormState (
279
+ Offset .zero & size,
280
+ imageGestureState,
281
+ copy: true ,
282
+ );
283
+ final videoRect =
284
+ GestureWidgetDelegateFromState .getRectFormState (
285
+ Offset .zero & size,
286
+ imageGestureState,
287
+ width: _controller.value.size.width,
288
+ height: _controller.value.size.height,
289
+ copy: true ,
290
+ );
291
+ return Stack (
292
+ children: < Widget > [
293
+ imageGestureState.wrapGestureWidget (
294
+ FittedBox (
295
+ fit: BoxFit .cover,
296
+ clipBehavior: Clip .hardEdge,
297
+ child: SizedBox (
298
+ width: videoRect.width,
299
+ height: videoRect.height,
300
+ child: VideoPlayer (_controller),
301
+ ),
271
302
),
272
303
),
273
- ),
274
- Positioned . fill (
275
- child : AnimatedOpacity (
276
- duration : kThemeChangeDuration ,
277
- opacity : showVideo ? 0.0 : 1.0 ,
278
- child : child ! ,
304
+ Positioned . fill (
305
+ child : AnimatedOpacity (
306
+ duration : kThemeChangeDuration,
307
+ opacity : showVideo ? 0.0 : 1.0 ,
308
+ child : child ! ,
309
+ ) ,
279
310
),
280
- ),
281
- Positioned . fromRect (
282
- rect : rect,
283
- child : AnimatedOpacity (
284
- duration : kThemeChangeDuration ,
285
- opacity : showVideo ? 0.0 : 1.0 ,
286
- child : _buildLivePhotoIndicator (context ),
311
+ Positioned . fromRect (
312
+ rect : imageRect,
313
+ child : AnimatedOpacity (
314
+ duration : kThemeChangeDuration,
315
+ opacity : showVideo || scaled ? 0.0 : 1.0 ,
316
+ child : _buildLivePhotoIndicator (context) ,
317
+ ),
287
318
),
288
- ) ,
289
- ],
290
- );
291
- } ,
292
- child : image,
293
- );
294
- } ,
319
+ ] ,
320
+ );
321
+ },
322
+ child : image ,
323
+ );
324
+ },
325
+ ) ,
295
326
),
296
327
);
297
328
}
0 commit comments