Flutter视频播放插件flutter_nimplayer的使用

Flutter视频播放插件flutter_nimplayer的使用

Flutter 版的网易云信点播播放器 SDK,支持 Android & iOS。

开始使用

添加依赖:

dependencies:
  flutter_nimplayer: ^{latest_version}

用法

第一步,创建播放器

player = FlutterNimplayerFactory.createPlayer();

第二步,添加播放器视图

NimplayerView(
  onCreated: (viewId) {
    initAndPlay(viewId);
  },
  x: 0,
  y: 0,
  width: viewWidth,
  height: viewHeight,
);

第三步,设置播放源并播放

void initAndPlay(int viewId) {
  // 建立连接
  player?.setPlayerView(viewId);
  // 设置播放链接
  player?.setUrl(url);
  // 准备播放
  player?.prepare();
}

具体示例请查看项目的 example 文件夹,更多播放器接口以及播放回调请查看项目源码。

完整示例代码

import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_nimplayer/flutter_nimplayer.dart';
import 'package:flutter_nimplayer_example/ext/int.dart';
import 'package:flutter_nimplayer_example/widget/translucent_container.dart';

void main() {
  runApp(const MyApp());
}

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

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  /// 播放 url
  String url =
      'http://jdvodma74obxu.vod.126.net/jdvodma74obxu/elyOEqRN_4973406720_sd.m3u8?'
      'wsSecret=b316d80189ffc977e11dd1f79eaa645d&'
      'wsTime=1667546938';

  /// 是否以全屏方式播放
  bool fullscreenMode = false;

  /// region 播放相关
  FlutterNimplayer? player;
  int _videoViewId = -1;
  bool videoPlaying = false;
  bool videoLoading = false;
  bool videoEnded = false;

  // endregion

  /// region 进度条相关
  Timer? videoControlTimer;
  bool videoControlVisible = false;
  int _videoLength = 0;
  String videoTotalTime = '00.00';
  String videoPlayedTime = '00:00';
  double videoPosition = 0.0;
  bool _slidingInProgress = false;

  // endregion

  [@override](/user/override)
  void initState() {
    super.initState();

    player = FlutterNimplayerFactory.createPlayer();
    player?.setOnPrepared((playerId) async {
      videoPlaying = true;
      _showVideoControlBar();

      var size = await player?.getVideoSize();
      if (size != null && size is Map) {
        var width = size['width'];
        var height = size['height'];
        print('flutter_nimplayer: 视频宽度 = $width, 高度 = $height');
      }

      _videoLength = await player?.getDuration();
      setState(() {
        videoTotalTime = _videoLength.millisecondsToTimeString();
      });

      player!.setOnLoadingStatusListener(loadingBegin: (playerId) {
        setState(() {
          videoLoading = true;
        });
      }, loadingProgress: (playerId, percent) {
        // 播放进度回调仅支持安卓。取决于视频长度,有可能当加载进度大于 30 时就可以播放了
        if (percent > 70) {
          videoLoading = false;
        }
      }, loadingEnd: (playerId) {
        videoLoading = false;
      });

      player!.setOnCompletion((playerId, code, extra) {
        setState(() {
          videoPosition = 1;
          videoEnded = true;
          videoPlaying = false;
          videoControlVisible = false;
        });

        player!.setOnError((playerId, code, extra) {
          print(
              'flutter_nimplayer: playerId = $playerId, code = $code, extra = $extra');
        });
      });
    });

    // 使用定时器获取播放进度,安卓上支持小于一秒内的刷新,iOS 上只支持秒级刷新
    Timer.periodic(const Duration(milliseconds: 200), (timer) async {
      int pos = 0;
      if (!_slidingInProgress && videoPlaying) {
        pos = await player?.getCurrentPosition() ?? 0;
        double newPosition = pos / _videoLength;
        if (newPosition < 0) {
          newPosition = 0;
        } else if (newPosition > 1) {
          newPosition = 1;
        }
        setState(() {
          videoPosition = newPosition;
        });

        String newTime = pos.millisecondsToTimeString();
        if (newTime != videoPlayedTime) {
          setState(() {
            videoPlayedTime = newTime;
          });
        }
      }
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Builder(builder: (context) {
          var width = MediaQuery.of(context).size.width;
          var height = MediaQuery.of(context).size.height;
          if (!fullscreenMode) {
            height = width * 9 / 16;
          }
          // 如果是全屏则旋转屏幕并反向设置宽高
          return SafeArea(
            child: RotatedBox(
              quarterTurns: fullscreenMode ? 1 : 0,
              child: SizedBox(
                width: fullscreenMode ? height : width,
                height: fullscreenMode ? width : height,
                child: Stack(
                  children: [
                    _buildPlayerView(width, height),
                    _buildGestureDetector(),
                    _buildLoadingIndicator(),
                    _buildControlBar(),
                    _buildReplayButton(),
                  ],
                ),
              ),
            ),
          );
        }),
      ),
    );
  }

  Widget _buildPlayerView(double width, double height) {
    return NimplayerView(
      onCreated: (viewId) {
        initAndPlay(viewId);
      },
      x: 0,
      y: 0,
      width: fullscreenMode ? height : width,
      height: fullscreenMode ? width : height,
    );
  }

  Widget _buildGestureDetector() {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () {
        toggleVideoControlBar();
      },
    );
  }

  Widget _buildLoadingIndicator() {
    if (videoLoading) {
      return Center(
        child: TranslucentContainer(
          padding: const EdgeInsets.all(8),
          child: Theme(
              data: ThemeData(
                  cupertinoOverrideTheme:
                      const CupertinoThemeData(brightness: Brightness.dark)),
              child: const CupertinoActivityIndicator()),
        ),
      );
    }
    return Container();
  }

  Widget _buildControlBar() {
    if (videoControlVisible) {
      return Positioned(
        left: fullscreenMode ? 50 : 0,
        right: fullscreenMode ? 50 : 0,
        bottom: 0,
        child: Container(
          color: Colors.black38,
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              IconButton(
                  onPressed: () {
                    toggleVideoPlayAndPause();
                  },
                  icon: Icon(videoPlaying ? Icons.pause : Icons.play_arrow,
                      color: Colors.white, size: 24)),
              SizedBox(
                width: 35,
                child: Text(
                  videoPlayedTime,
                  style: const TextStyle(color: Colors.white, fontSize: 11),
                ),
              ),
              SizedBox(
                width: 40,
                child: Text(
                  '/ $videoTotalTime',
                  style: TextStyle(color: Colors.grey[400], fontSize: 11),
                ),
              ),
              Expanded(
                child: SliderTheme(
                    data: const SliderThemeData(
                      trackHeight: 2,
                      thumbColor: Colors.white,
                      thumbShape: RoundSliderThumbShape(
                        enabledThumbRadius: 5,
                        pressedElevation: 2,
                      ),
                    ),
                    child: Slider(
                      value: videoPosition,
                      activeColor: Colors.blue,
                      inactiveColor: const Color(0xFF929BA2),
                      onChanged: (value) {
                        slideTo(value);
                      },
                      onChangeEnd: (double value) {
                        seekTo(value);
                      },
                    )),
              ),
            ],
          ),
        ),
      );
    }
    return Container();
  }

  Widget _buildReplayButton() {
    if (videoEnded) {
      return Center(
        child: GestureDetector(
          onTap: () {
            replayVideo();
          },
          child: TranslucentContainer(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: const [
                Icon(
                  Icons.replay,
                  color: Colors.white,
                  size: 30,
                ),
                SizedBox(height: 4),
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: 20),
                  child: Text('重播', style: TextStyle(color: Colors.white)),
                ),
              ],
            ),
          ),
        ),
      );
    }
    return Container();
  }

  void initAndPlay(int viewId) {
    _videoViewId = viewId;

    player?.setPlayerView(viewId);
    player?.setUrl(url);
    // player?.setScalingMode(1);
    player?.prepare();
  }

  void toggleVideoControlBar() {
    if (videoEnded) return;

    if (videoControlVisible) {
      setState(() {
        videoControlVisible = false;
      });
    } else {
      _showVideoControlBar();
    }
  }

  void _showVideoControlBar() {
    setState(() {
      videoControlVisible = true;
    });

    if (videoControlTimer != null && videoControlTimer!.isActive) {
      videoControlTimer!.cancel();
    }
    videoControlTimer = Timer(const Duration(seconds: 5), () {
      setState(() {
        videoControlVisible = false;
      });
    });
  }

  void toggleVideoPlayAndPause() {
    _showVideoControlBar();

    if (videoPlaying) {
      player!.pause();
    } else {
      player!.play();
    }

    setState(() {
      videoPlaying = !videoPlaying;
    });
  }

  void seekTo(double value) {
    slideTo(value);

    player!.seekTo((value * _videoLength).toInt(), true);
    setState(() {
      videoLoading = false;
      _slidingInProgress = false;
    });
  }

  void slideTo(double value) {
    _showVideoControlBar();

    setState(() {
      videoPosition = value;
      _slidingInProgress = true;
    });
  }

  void replayVideo() {
    setState(() {
      videoPosition = 0;
      videoPlayedTime = '00:00';
      videoEnded = false;
    });

    toggleVideoPlayAndPause();
  }

  [@override](/user/override)
  void dispose() {
    player?.destroy();
    videoControlTimer?.cancel();

    super.dispose();
  }
}

更多关于Flutter视频播放插件flutter_nimplayer的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频播放插件flutter_nimplayer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_nimplayer 是一个用于在 Flutter 应用中播放视频的插件。它基于 NativePlayer,支持 Android 和 iOS 平台。以下是如何在你的 Flutter 项目中使用 flutter_nimplayer 的步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 flutter_nimplayer 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_nimplayer: ^0.0.1  # 请检查最新版本

然后运行 flutter pub get 来获取依赖。

2. 在代码中使用 flutter_nimplayer

在你的 Flutter 代码中导入 flutter_nimplayer,并使用它来播放视频。

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: VideoPlayerScreen(),
    );
  }
}

class VideoPlayerScreen extends StatefulWidget {
  @override
  _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  late NativePlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = NativePlayerController();
    _controller.initialize().then((_) {
      // 初始化完成后,可以开始播放视频
      _controller.play('https://www.example.com/sample.mp4');
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter NimPlayer Example'),
      ),
      body: Center(
        child: NativePlayer(_controller),
      ),
    );
  }
}

3. 配置 Android 和 iOS 平台

Android

android/app/build.gradle 文件中,确保 minSdkVersion 至少为 21:

defaultConfig {
    minSdkVersion 21
    targetSdkVersion 30
    // 其他配置
}

iOS

ios/Podfile 中,确保 platform :ios 的版本至少为 9.0:

platform :ios, '9.0'

然后在终端中运行 pod install 来更新 iOS 项目的依赖。

4. 运行应用

确保你的设备或模拟器已连接,然后运行 flutter run 来启动应用。你应该能够看到视频播放器,并且视频开始播放。

5. 其他功能

flutter_nimplayer 还支持其他功能,例如暂停、停止、快进、快退等。你可以通过 NativePlayerController 来控制这些行为。

例如:

_controller.pause();  // 暂停播放
_controller.seekTo(Duration(seconds: 10));  // 快进到 10 秒
_controller.setVolume(0.5);  // 设置音量

6. 处理错误

你还可以监听播放器的错误事件:

_controller.setErrorListener((error) {
  print('An error occurred: $error');
});
回到顶部