Skip to content

Commit

Permalink
🚀 Use visibility_detector and scroll observer to improve media play…
Browse files Browse the repository at this point in the history
…ing experiences
  • Loading branch information
AlexV525 committed Sep 24, 2024
1 parent ea06ee2 commit 91557a1
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 79 deletions.
14 changes: 7 additions & 7 deletions lib/src/delegates/asset_picker_builder_delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2433,15 +2433,15 @@ class DefaultAssetPickerBuilderDelegate
}
});
}
return AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
child: Theme(
data: theme,
return Theme(
data: theme,
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: overlayStyle,
child: CNP<DefaultAssetPickerProvider>.value(
value: provider,
builder: (BuildContext context, _) => Material(
color: theme.canvasColor,
child: Stack(
builder: (BuildContext context, _) => Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
body: Stack(
fit: StackFit.expand,
children: <Widget>[
if (isAppleOS(context))
Expand Down
6 changes: 3 additions & 3 deletions lib/src/delegates/asset_picker_viewer_builder_delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1010,9 +1010,9 @@ class DefaultAssetPickerViewerBuilderDelegate
(themeData.effectiveBrightness.isDark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark),
child: Material(
color: themeData.scaffoldBackgroundColor,
child: Stack(
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Stack(
children: <Widget>[
Positioned.fill(child: _pageViewBuilder(context)),
if (isWeChatMoment && hasVideo) ...<Widget>[
Expand Down
160 changes: 91 additions & 69 deletions lib/src/widget/builder/image_page_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:photo_manager_image_provider/photo_manager_image_provider.dart';
import 'package:video_player/video_player.dart';
import 'package:visibility_detector/visibility_detector.dart';
import 'package:wechat_picker_library/wechat_picker_library.dart';

import '../../constants/constants.dart';
Expand Down Expand Up @@ -174,29 +175,45 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
final _showVideo = ValueNotifier<bool>(false);
late final _controller = widget.controller;

ScrollNotificationObserverState? _scrollNotificationObserver;
bool _scrolling = false;

@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_controller.play().then((_) {
HapticFeedback.lightImpact();
_showVideo.value = true;
});
});
_controller.addListener(_notify);
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_scrollNotificationObserver?.removeListener(_handleScrollNotification);
_scrollNotificationObserver = ScrollNotificationObserver.maybeOf(context);
_scrollNotificationObserver?.addListener(_handleScrollNotification);
}

@override
void dispose() {
_scrollNotificationObserver?.removeListener(_handleScrollNotification);
_controller.pause();
_controller.removeListener(_notify);
super.dispose();
}

Future<void> continuePlay() async {
if (_showVideo.value && _controller.value.position != Duration.zero) {
HapticFeedback.lightImpact();
await _controller.play();
void _handleScrollNotification(ScrollNotification notification) {
if (notification is ScrollStartNotification) {
_scrolling = true;
} else if (notification is ScrollEndNotification) {
_scrolling = false;
}
}

void _onVisibilityChanged(VisibilityInfo info) {
final fraction = info.visibleFraction;
if (fraction == 1 && !_showVideo.value && !_scrolling) {
_showVideoAndPlay();
} else if (fraction < 1 && _showVideo.value) {
_hideVideoAndStop();
}
}

Expand All @@ -218,75 +235,80 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
}

Future<void> _hideVideoAndStop() async {
_showVideo.value = false;
await Future.delayed(kThemeChangeDuration);
await _controller.pause();
await _controller.seekTo(Duration.zero);
_showVideo.value = false;
}

@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
_showVideoAndPlay();
},
onLongPressUp: () {
_hideVideoAndStop();
},
child: ExtendedImageGesture(
widget.state,
imageBuilder: (
Widget image, {
ExtendedImageGestureState? imageGestureState,
}) {
return ValueListenableBuilder(
valueListenable: _showVideo,
builder: (context, showVideo, child) {
if (imageGestureState == null ||
widget.state.extendedImageInfo == null) {
return child!;
}
final size = MediaQuery.sizeOf(context);
final rect = GestureWidgetDelegateFromState.getRectFormState(
Offset.zero & size,
imageGestureState,
width: _controller.value.size.width,
height: _controller.value.size.height,
copy: true,
);
return Stack(
children: <Widget>[
imageGestureState.wrapGestureWidget(
FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: rect.width,
height: rect.height,
child: VideoPlayer(_controller),
return VisibilityDetector(
key: ValueKey(widget.state),
onVisibilityChanged: _onVisibilityChanged,
child: GestureDetector(
onLongPress: () {
_showVideoAndPlay();
},
onLongPressUp: () {
_hideVideoAndStop();
},
child: ExtendedImageGesture(
widget.state,
imageBuilder: (
Widget image, {
ExtendedImageGestureState? imageGestureState,
}) {
return ValueListenableBuilder(
valueListenable: _showVideo,
builder: (context, showVideo, child) {
if (imageGestureState == null ||
widget.state.extendedImageInfo == null) {
return child!;
}
final size = MediaQuery.sizeOf(context);
final rect = GestureWidgetDelegateFromState.getRectFormState(
Offset.zero & size,
imageGestureState,
width: _controller.value.size.width,
height: _controller.value.size.height,
copy: true,
);
return Stack(
children: <Widget>[
imageGestureState.wrapGestureWidget(
FittedBox(
fit: BoxFit.cover,
clipBehavior: Clip.hardEdge,
child: SizedBox(
width: rect.width,
height: rect.height,
child: VideoPlayer(_controller),
),
),
),
),
Positioned.fill(
child: AnimatedOpacity(
duration: kThemeChangeDuration,
opacity: showVideo ? 0.0 : 1.0,
child: child!,
Positioned.fill(
child: AnimatedOpacity(
duration: kThemeChangeDuration,
opacity: showVideo ? 0.0 : 1.0,
child: child!,
),
),
),
Positioned.fromRect(
rect: rect,
child: AnimatedOpacity(
duration: kThemeChangeDuration,
opacity: showVideo ? 0.0 : 1.0,
child: _buildLivePhotoIndicator(context),
Positioned.fromRect(
rect: rect,
child: AnimatedOpacity(
duration: kThemeChangeDuration,
opacity: showVideo ? 0.0 : 1.0,
child: _buildLivePhotoIndicator(context),
),
),
),
],
);
},
child: image,
);
},
],
);
},
child: image,
);
},
),
),
);
}
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies:
photo_manager_image_provider: ^2.0.0
provider: ^6.0.5
video_player: ^2.7.0
visibility_detector: ^0.4.0

dev_dependencies:
flutter_lints: any
Expand Down

0 comments on commit 91557a1

Please sign in to comment.