FLUTTER ECOSYSTEM

albemala/native_video_player

一个 Flutter 小部件,使用原生实现,在 iOS 和 Android 上播放视频。

原生视频播放器 项目封面
Stars
46
Forks
41
最近推送(UTC)
2026年6月29日
项目状态
未归档
albemala GitHub avatar
GITHUB User

albemala ↗

Software engineer & wannabe UI designer. Maker of hexee.app, exabox.app, and many others. All my projects at projects.albemala.me

语言SwiftDartKotlinRubyShellObjective-C

此仓库发布的插件

使用的依赖

依赖清单 7 项
  • flutter{"sdk":"flutter"}
  • path_provider^2.1.5
  • path^1.9.0
  • flutter_test开发依赖{"sdk":"flutter"}
  • very_good_analysis开发依赖^7.0.0
  • build_runner开发依赖^2.4.13
  • pigeon开发依赖{"git":{"url":"https://github.com/feduke-nukem/packages.git","ref":"f9a3a92e9853484bd91cf29b6b306607b0c4eb35","path":"packages/pigeon"}}

原始 README

以下为英文项目原文快照,最新内容请访问 GitHub。

展开 / 收起项目 README

native_video_player

Pub

A Flutter widget to play videos on iOS, macOS and Android using a native implementation.

Perfect for building video-centric apps like TikTok, Instagram Reels, or YouTube Shorts, as well as general video playback needs.

Android iOS macOS
Support 16+ 9.0+ 10.11+
https://raw.githubusercontent.com/albemala/native_video_player/main/screenshots/1.gif https://raw.githubusercontent.com/albemala/native_video_player/main/screenshots/2.gif https://raw.githubusercontent.com/albemala/native_video_player/main/screenshots/3.gif
Implementation

Usage

Loading a video
@override
Widget build(BuildContext context) {
  return NativeVideoPlayerView(
    onViewReady: (controller) async {
      await controller.loadVideo(
        VideoSource(
          path: 'path/to/file',
          type: VideoSourceType.asset,
        ),
      );
    },
  );
}
Listen to events
StreamSubscription<void>? _eventsSubscription;

_eventsSubscription = controller.events.listen((event) {
  switch (event) {
    case PlaybackStatusChangedEvent():
      // Emitted when playback status changes (playing, paused, or stopped)
      final playbackStatus = controller.playbackStatus;
    case PlaybackPositionChangedEvent():
      // Emitted when playback position changes
      final position = controller.playbackPosition;
    case PlaybackReadyEvent():
      // Emitted when video is loaded and ready to play
      final height = controller.videoInfo?.height;
      final width = controller.videoInfo?.width;
      final duration = controller.videoInfo?.duration;
    case PlaybackEndedEvent():
      // Emitted when video playback ends
    case PlaybackSpeedChangedEvent():
      // Emitted when playback speed changes
    case VolumeChangedEvent():
      // Emitted when volume changes
    case PlaybackErrorEvent():
      // Emitted when an error occurs
      print('Playback error: ${event.errorMessage}');
  }
});

// Don't forget to dispose the subscription
@override
void dispose() {
  _eventsSubscription?.cancel();
  super.dispose();
}
Autoplay
bool isAutoplayEnabled = false;

Future<void> _loadVideoSource() async {
  await controller.loadVideo(videoSource);
  if (isAutoplayEnabled) {
    await controller.play();
  }
}
Playback loop
bool isPlaybackLoopEnabled = false;

void _onPlaybackEnded() {
  if (isPlaybackLoopEnabled) {
    controller.stop();
    controller.play();
  }
}

// Set up event listener
_eventsSubscription = controller.events.listen((event) {
  switch (event) {
    case PlaybackEndedEvent():
      _onPlaybackEnded();
    // ... handle other events ...
  }
});
Controller disposal
class VideoPlayerState extends State<VideoPlayer> {
  NativeVideoPlayerController? _controller;
  StreamSubscription<void>? _eventsSubscription;

  Future<void> _initController(NativeVideoPlayerController controller) async {
    _controller = controller;
    _eventsSubscription = _controller?.events.listen((event) {
      // ... handle events ...
    });
  }

  @override
  void dispose() {
    // Cancel any event subscriptions
    _eventsSubscription?.cancel();
    // Dispose of the controller
    _controller?.dispose();
    _controller = null;
    super.dispose();
  }
}

It's important to properly dispose of both the controller and any event subscriptions when you're done with them to prevent memory leaks. This typically happens in the dispose() method of your widget's State.

Advanced usage

See the example app for a complete usage example.

Support this project

Sponsors:

Thank you to all sponsors for supporting this project! ❤️

Other projects

All my projects

Credits

Created by @albemala (Twitter)