Chewie는 Cupertino 및 Material 디자인 스타일용 내장 재생 컨트롤을 갖춘 사용자 정의 가능한 비디오 플레이어를 제공하는 Flutter 패키지입니다. 하위 비디오 플레이어 엔진을 래핑하여 재생 컨트롤, 진행률 표시기, 여러 플랫폼에서의 전체 화면 지원을 포함한 직관적인 인터페이스를 제공합니다. 통합이 간편하도록 설계되어 최소한의 설정으로 Flutter 앱의 비디오 재생 경험을 향상시킵니다. Android, iOS, Web, macOS를 지원하므로 일관된 비디오 재생 기능이 필요한 크로스플랫폼 애플리케이션에 적합합니다.
사용 사례
모바일 앱에 비디오 삽입하기
사용자 정의 UI를 갖춘 미디어 플레이어 만들기
교육 앱에서 비디오 콘텐츠 지원
스트리밍 또는 튜토리얼 애플리케이션 구축
웹 앱에 전체 화면 비디오 재생 추가하기
주요 기능
Cupertino 및 Material 디자인 컨트롤
전체 화면 지원
사용자 정의 재생 컨트롤
크로스 플랫폼 호환성
기존 비디오 플레이어와의 간편한 통합
적합한 대상
영상 중심의 Flutter 앱을 개발하는 개발자
플랫폼 일관성 있는 UI 컴포넌트가 필요한 팀
간단하지만 정교한 비디오 재생이 필요한 앱
교육 또는 엔터테인먼트 플랫폼
Flutter를 사용하는 웹 및 모바일 앱 개발자
고려 사항
호환되는 비디오 소스(예: HTTP URL 또는 로컬 파일)가 필요합니다
기본 UI 요소를 초과한 사용자 정의는 제한적
하위 비디오 플레이어 구현에 따라 다릅니다
자막과 같은 고급 기능을 사용하려면 추가 구성이 필요할 수 있습니다
복잡한 비디오 편집 워크플로우에 적합하지 않습니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
chewie
The video player for Flutter with a heart of gold.
The video_player plugin provides low-level
access to video playback.
Chewie uses the video_player under the hood and wraps it in a friendly Material or Cupertino UI!
This library is NOT responsible for any issues caused by video_player, since it's merely a UI
layer on top of it.
In other words, if you see any PlatformExceptions being thrown in your app due to video playback,
they are exclusive to the video_player library.
Instead, please raise an issue related to it with the Flutter Team.
🔀 Flutter Version Compatibility
This library will at the very least make a solid effort to support the second most recent version
of Flutter released. In other words, it will adopt N-1 version support at
the bare minimum.
However, this cannot be guaranteed either due to major changes between Flutter versions or
this library's dependencies. Should that occur, future updates will be released as major or minor
versions as needed.
🖼️ Preview
MaterialControls
MaterialDesktopControls
CupertinoControls
⬇️ Installation
In your pubspec.yaml file within your Flutter Project add chewie and video_player under dependencies:
Chewie has some options which control the video. These options appear by default in a showModalBottomSheet (similar to YT). By default, Chewie passes Playback speed and Subtitles options as an OptionItem.
To add additional options, just add these lines to your ChewieController:
Your additionalOptions are already included here (if you provided additionalOptions)!
pauseOnBackgroundTap (Tap Anywhere to Pause/Play)
The pauseOnBackgroundTap feature allows the video to be paused or played by tapping anywhere on the video player area.
This eliminates the need for users to tap the play/pause icon in the center of the video.
This feature can be enabled by setting the pauseOnBackgroundTap property in the ChewieController.
When enabled, tapping anywhere on the video will toggle between play and pause states.
final chewieController = ChewieController(
videoPlayerController: videoPlayerController,
pauseOnBackgroundTap: true, // Allow tapping anywhere on the video to pause/play
);
Chewie allows you to enhance the video playback experience with text overlays. You can add a List<Subtitle> to your ChewieController, restyle the default subtitle box with subtitleStyle, or replace it entirely with the subtitleBuilder function.
Showing Subtitles by Default
Chewie provides the showSubtitles flag, allowing you to control whether subtitles are displayed automatically when the video starts. By default, this flag is set to false.
Adding Subtitles
Here’s an example of how to add subtitles to your ChewieController:
Cue text extracted from WebVTT or SubRip files often carries inline markup, and Chewie renders it for you:
Subtitle(
index: 0,
start: Duration.zero,
end: constDuration(seconds: 10),
text: '<i>The law is the law, Mr. Hancock.</i>',
),
<b>, <i>, <u> and <font color="#rrggbb"> are applied on top of your text style. The other WebVTT cue tags — <c.class>, <v Speaker>, <lang xx>, <ruby>/<rt> and timestamp tags — are dropped while their text is kept, and escapes such as & are decoded. A tag that opens on one line and closes on the next works too.
Parsing is lenient, so cue text is never mangled: 5 < 10 and <3 are shown as written, an unclosed tag simply runs to the end of the cue, and a stray closing tag is ignored. If you would rather show cue text exactly as it arrives, set subtitleStyle: SubtitleStyle(renderMarkup: false).
Styling Subtitles
subtitleStyle changes how the default subtitle box looks without giving up markup rendering:
Leaving textStyle.color unset inherits the colour from the surrounding DefaultTextStyle, which is what Chewie does by default.
Customizing Subtitles
Reach for subtitleBuilder when you need to build the whole widget yourself. It receives the cue exactly as you supplied it — markup and all — and Chewie's own rendering, including subtitleStyle, is skipped. Run the cue through parseSubtitleMarkup to keep markup working:
Pass a list of ChewieChapters, sorted by ascending start time, to draw the progress bar as YouTube-style chapter segments. Hovering or scrubbing the bar shows the pointed chapter's title above it:
When chapters is empty (the default), the progress bar is rendered as a single continuous segment, as before.
📺 Casting
Chewie can hand playback over to a cast receiver — a Chromecast, an Android TV,
anything you can talk to — and drive it from the same controls.
What Chewie provides is the UI and the handover: a cast button in every skin, a
device picker, an overlay in place of the video while a session is live, and the
local↔remote transitions in both directions. What it deliberately does not
provide is a sender. A pure-Flutter video player package has no business
dragging the Google Cast SDK, its native dependencies and its permission
requirements into every app that just wants play controls — so you supply the
backend, and Chewie stays agnostic about which one.
Wiring it up
Implement ChewieCastController against the sender of your choice, then hand it
to the ChewieController along with the media the receiver should fetch:
castMedia is required whenever castController is set. Chewie cannot derive
it from the VideoPlayerController: the receiver fetches the stream itself, so
the URL has to be reachable from the TV, and a file:// or asset source has
no remote equivalent at all.
What happens on connect
When your controller moves its connectionState to
CastConnectionState.connected, Chewie:
pauses the local player and notes where it was,
calls load(castMedia, startAt: <that position>, autoPlay: <was it playing>),
replaces the video surface with the casting overlay,
routes play, pause, seek, volume and the progress bar to the receiver.
On disconnected it runs the same trip in reverse, seeking the local player to
wherever the receiver got to and resuming if it was playing. Chewie listens to
your controller but never disposes it, so one instance can serve many videos.
Implementing a backend
ChewieCastController is a ChangeNotifier with three groups of members:
discovery (devices, isDiscovering, startDiscovery, stopDiscovery),
session (connectionState, connectedDevice, connect, disconnect) and
remote playback (value, load, play, pause, seekTo, setVolume,
setPlaybackSpeed). Call notifyListeners() whenever any of them change —
every piece of cast UI rebuilds off those notifications.
value is a VideoPlayerValue, the same type the local player reports, which is
what lets one set of controls render a remote session identically to a local
one. Only duration, position, buffered, isPlaying, isBuffering,
isInitialized, volume and playbackSpeed are read; leave the rest at their
defaults.
The example app ships a DemoCastController that fakes discovery, a connection
delay and a ticking receiver, so you can see the whole flow without a
Chromecast on the desk.
Options
Option
Description
castController
Your backend. Casting is off entirely when this is null.
castMedia
What the receiver should play. Required alongside castController.
allowCasting
Hides the cast button without tearing the backend out. Defaults to true.
castTranslations
Strings for the button, picker and overlay.
castOverlayBuilder
Replaces the default "Casting to …" overlay.
additionalControls
Extra widgets for the control bar itself, for controls that cannot be an options-sheet row.
Extra control-bar buttons
additionalOptions adds rows to the options sheet. When a control has to be a
widget rather than a menu entry — a platform view, say — put it in the bar
itself:
They sit alongside the built-in buttons and inherit the bar's show/hide
behaviour, so they fade with the rest of the controls instead of floating over
the video.
The motivating case is AirPlay: Apple exposes no way to select a route
programmatically, so the button must be UIKit's own AVRoutePickerView.
🧪 Example
Please run the app in the example/ folder to start playing!
⏪ Migrating from Chewie < 0.9.0
Instead of passing the VideoPlayerController and your options to the Chewie widget you now pass them to the ChewieController and pass that later to the Chewie widget.
final playerWidget = Chewie(
videoPlayerController,
autoPlay: true,
looping: true,
);
becomes
final chewieController = ChewieController(
videoPlayerController: videoPlayerController,
autoPlay: true,
looping: true,
);
final playerWidget = Chewie(
controller: chewieController,
);
🗺️ Roadmap
[x] MaterialUI
[x] MaterialDesktopUI
[x] CupertinoUI
[x] Options with translations
[x] Subtitles
[x] CustomControls
[x] Auto-Rotate on FullScreen depending on Source Aspect-Ratio
[x] Live-Stream and UI
[x] AutoPlay
[x] Placeholder
[x] Looping
[x] Start video at
[x] Custom Progress-Bar colors
[x] Custom Overlay
[x] Allow Sleep (Wakelock)
[x] Playbackspeed Control
[x] Custom Route-Pagebuilder
[x] Custom Device-Orientation and SystemOverlay before and after fullscreen
[x] Custom ErrorBuilder
[ ] Support different resolutions of video
[ ] Re-design State-Manager with Provider
[x] Screen-Mirroring / Casting (backend-agnostic; see Casting)
⚠️ Android warning
There is an open issue that the buffering state of a video is not reported correctly. With this, the loading state is always triggered, hiding controls to play, pause or seek the video. A workaround was implemented until this is fixed, however it can't be perfect and still hides controls if seeking backwards while the video is paused, as a result of lack of correct buffering information (see #912).
Add the following to partly fix this behavior:
// Your init code can be above
videoController.addListener(yourListeningMethod);
// ...bool wasPlayingBefore = false;
voidyourListeningMethod() {
if (!videoController.value.isPlaying && !wasPlayingBefore) {
// -> Workaround if seekTo another position while it was paused before.// On Android this might lead to infinite loading, so just play the// video again.
videoController.play();
}
wasPlayingBefore = videoController.value.isPlaying;
// ...
}
You can also disable the loading spinner entirely to fix this problem in a more complete way, however will remove the loading indicator if a video is buffering.
The video_player plugin used by chewie will only work in iOS simulators if you are on flutter 1.26.0 or above. You may need to switch to the beta channel flutter channel beta
Please refer to this issue.