hm_video_player 0.4.2 copy "hm_video_player: ^0.4.2" to clipboard
hm_video_player: ^0.4.2 copied to clipboard

跨平台视频播放器插件,支持全屏、画中画、投屏、倍速等功能。

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:hm_video_player/hm_video_player.dart';

import 'cast_page.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const DemoApp());
}

class DemoApp extends StatelessWidget {
  const DemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: DemoHomePage(),
    );
  }
}

class DemoHomePage extends StatefulWidget {
  const DemoHomePage({super.key});

  @override
  State<DemoHomePage> createState() => _DemoHomePageState();
}

class _DemoHomePageState extends State<DemoHomePage> {
  final controller = HmVideoPlayerController();

  static const url =
      'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4';

  @override
  void initState() {
    super.initState();
    controller.events.listen((event) {
      if (!mounted) return;
      debugPrint('hm player event: $event');
      if (event is CastRequestedEvent) {
        Navigator.of(context).push(
          MaterialPageRoute<void>(
            builder: (_) => CastPage(
              controller: controller,
              initialContext: CastRequestContext(
                mediaUrl: event.mediaUrl,
                title: event.title,
                position: event.position,
                playing: event.playing,
                supportedProtocols: event.supportedProtocols,
                currentSession: event.currentSession,
              ),
            ),
          ),
        );
      }
      if (event is CastStateChangedEvent) {
        debugPrint(
          'cast state: ${event.state} protocol=${event.protocol} '
          'device=${event.deviceName} message=${event.message}',
        );
      }
      if (event is QualitySelectedEvent) {
        // 应用层:根据 qualities[event.index].value 请求新播放地址,再调用 setSource。
        // controller.setSource(newUrl, position: currentPosition);
      }
      if (event is PipOpenedEvent) {
        debugPrint('pip opened');
      }
      if (event is PipClosedEvent) {
        debugPrint('pip closed');
      }
    });
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xfff7f7f7),
      appBar: AppBar(
        title: const Text('HM Video Player'),
        backgroundColor: Colors.white,
        surfaceTintColor: Colors.white,
      ),
      body: ListView(
        children: [
          AspectRatio(
            aspectRatio: 16 / 9,
            child: HmVideoPlayer(
              controller: controller,
              videoUrl: url,
              title: '莫离 第01集',
              cover: false,
              looping: true,
              autoPlay: true,
              autoRotateFullscreen: true,
              syncSystemGestureDirection: true,
              showNativeControls: true,
              inlineControls: NativeControlsConfig.inlinePreset,
              fullscreenControls: NativeControlsConfig.fullscreenPreset,
              qualities: const [
                VideoQuality(
                  name: '臻彩MAX',
                  subName: 'AI增强',
                  value: '6',
                  vip: true,
                  vipDesc: 'VIP尊享',
                  fullWidth: true,
                ),
                VideoQuality(
                  name: '4K',
                  subName: '超高清 SDR',
                  value: '5',
                  vip: true,
                  vipDesc: 'VIP尊享',
                  fullWidth: true,
                ),
                VideoQuality(
                  name: '1080P',
                  subName: 'HDR Vivid',
                  value: '4',
                  fullWidth: true,
                ),
                VideoQuality(
                  name: '1080P',
                  subName: '高清 SDR',
                  value: '3',
                ),
                VideoQuality(
                  name: '720P',
                  subName: '准高清',
                  value: '2',
                ),
                VideoQuality(
                  name: '480P',
                  subName: '标清',
                  value: '1',
                ),
              ],
              currentQualityIndex: 4,
              currentEpisodeIndex: 2,
              episodes: [
                for (var i = 1; i <= 100; i++)
                  VideoEpisode(
                    title: switch (i) {
                      1 => '沾喜气!精分孤女轮椅战神王府大婚',
                      2 => '王爷初遇莫离',
                      3 => '轮椅上的秘密',
                      4 => '王府夜宴风波',
                      5 => '莫离展露锋芒',
                      6 => '暗流涌动',
                      7 => '危机四伏',
                      8 => '联手破局',
                      9 => '真相浮现',
                      10 => '情感纠葛',
                      11 => '绝地反击',
                      12 => '大婚前夕',
                      _ => '第 $i 集',
                    },
                    url: url,
                    cover: 'https://picsum.photos/seed/hm_episode_$i/320/180',
                  ),
              ],
              danmakuItems: const [
                DanmakuItem(text: '原生小窗播放器', at: Duration(seconds: 1)),
                DanmakuItem(
                  text: '同一个原生播放器进入全屏',
                  at: Duration(seconds: 2),
                ),
              ],
              speeds: const [3.0, 2.0, 1.5, 1.25, 1.0, 0.5],
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(16),
            child: FilledButton(
              onPressed: controller.enterPictureInPicture,
              child: const Text('进入画中画'),
            ),
          ),
          // Padding(
          //   padding: const EdgeInsets.all(16),
          //   child: Wrap(
          //     spacing: 12,
          //     runSpacing: 12,
          //     children: [
          //       FilledButton(
          //         onPressed: controller.enterFullscreen,
          //         child: const Text('进入全屏'),
          //       ),
          //       OutlinedButton(
          //         onPressed: () => controller.setSpeed(1.5),
          //         child: const Text('1.5x'),
          //       ),
          //       OutlinedButton(
          //         onPressed: () =>
          //             controller.seekTo(const Duration(seconds: 5)),
          //         child: const Text('跳到 5 秒'),
          //       ),
          //     ],
          //   ),
          // ),
          // const _InfoCard(
          //   title: '架构说明',
          //   body:
          //       'Flutter 与原生通过 Pigeon 生成的类型安全 API 通信。小窗和全屏都是原生播放器,避免 Flutter texture 与原生画面交接问题。',
          // ),
          // const _InfoCard(
          //   title: '控制栏',
          //   body:
          //       '小屏/全屏控制栏均为原生实现,默认布局参考腾讯视频。可通过 inlineControls / fullscreenControls 配置各按钮显隐,controlButtonTapped 事件回传 Flutter。',
          // ),
          // const _InfoCard(
          //   title: '测试重点',
          //   body:
          //       '1. 点击全屏按钮进入全屏。\n2. 旋转手机进入全屏。\n3. 全屏中转回竖屏自动退出。\n4. 全屏中左右横屏切换。\n5. 退出时是否还有双层画面或底部残留。',
          // ),
          // const SizedBox(height: 800),
        ],
      ),
    );
  }
}

class _InfoCard extends StatelessWidget {
  const _InfoCard({required this.title, required this.body});

  final String title;
  final String body;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.fromLTRB(16, 8, 16, 8),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.04),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          Text(body, style: const TextStyle(height: 1.45)),
        ],
      ),
    );
  }
}
1
likes
0
points
180
downloads

Publisher

unverified uploader

Weekly Downloads

跨平台视频播放器插件,支持全屏、画中画、投屏、倍速等功能。

Repository (GitHub)
View/report issues

Topics

#video-player #video #cast #picture-in-picture #fullscreen

License

unknown (license)

Dependencies

flutter

More

Packages that depend on hm_video_player

Packages that implement hm_video_player