flutter实现观看直播评论抽奖功能

推流实现

https://blog.csdn.net/weixin_52901235/article/details/161480071?fromshare=blogdetail&sharetype=blogdetail&sharerId=161480071&sharerefer=PC&sharesource=weixin_52901235&sharefrom=from_link

安装web_socket_channel

flutter pub add web_socket_channel

Dart 复制代码
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart'; // Flutter官方视频播放插件
import 'package:web_socket_channel/web_socket_channel.dart'; // 模拟实时弹幕

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '直播互动抽奖',
      theme: ThemeData.dark(), // 直播间通常使用深色主题
      home: const LiveRoomPage(),
    );
  }
}

class LiveRoomPage extends StatefulWidget {
  const LiveRoomPage({super.key});
  @override
  State<LiveRoomPage> createState() => _LiveRoomPageState();
}

class _LiveRoomPageState extends State<LiveRoomPage> {
  // 1. 视频播放控制器
  VideoPlayerController? _videoController;
  bool _isVideoInitialized = false;
  bool _isReconnecting = false;

  // 2. 实时评论相关
  final TextEditingController _commentController = TextEditingController();
  final ScrollController _scrollController = ScrollController();
  final List<String> _comments = [];
  WebSocketChannel? _channel;

  // 3. 抽奖相关
  final Set<String> _lotteryPool = {}; // 参与抽奖的用户池(去重)
  String? _winner;
  int _countdown = 0;
  Timer? _countdownTimer;
  bool _isDrawing = false;

  @override
  void initState() {
    super.initState();
    _initLiveStream();
    _connectDanmaku();
  }

  // --- 1. 直播画面与中断重连逻辑 ---
  void _initLiveStream() {
    // 这里使用一个公开的测试直播流(HLS格式),实际项目中替换为你的直播推流地址
    const testLiveUrl = 'https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8';
    
    _videoController = VideoPlayerController.networkUrl(Uri.parse(testLiveUrl))
      ..initialize().then((_) {
        setState(() { _isVideoInitialized = true; });
        _videoController!.play();
        _videoController!.setLooping(true); // 直播流通常设为循环或持续拉流
      });

    // 监听视频播放状态,实现断线重连
    _videoController!.addListener(() {
      if (_videoController!.value.hasError && !_isReconnecting) {
        print("直播流中断,正在尝试重连...");
        _reconnectLiveStream();
      }
    });
  }

  void _reconnectLiveStream() {
    setState(() { _isReconnecting = true; });
    // 延迟3秒后重新初始化视频控制器
    Future.delayed(const Duration(seconds: 3), () {
      if (mounted) {
        _videoController?.dispose();
        _initLiveStream();
        setState(() { _isReconnecting = false; });
      }
    });
  }

  // --- 2. 实时评论与发送 ---
  void _connectDanmaku() {
    // 模拟连接弹幕服务器(实际项目中这里应替换为 TUILiveKit 的弹幕回调或第三方平台WebSocket)
    // 这里仅做UI逻辑演示,真实环境需替换为实际的弹幕接收逻辑
    _addComment("系统", "欢迎来到直播间!发送评论即可参与抽奖~");
  }

  void _addComment(String user, String msg) {
    setState(() {
      _comments.add('$user: $msg');
      _lotteryPool.add(user); // 发送评论自动加入抽奖池
    });
    // 自动滚动到底部
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (_scrollController.hasClients) {
        _scrollController.animateTo(
          _scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeOut,
        );
      }
    });
  }

  void _sendComment() {
    if (_commentController.text.trim().isEmpty) return;
    final myComment = _commentController.text;
    _addComment("我", myComment); // 实际项目中需调用 SDK 发送弹幕接口
    _commentController.clear();
  }

  // --- 3. 倒计时抽奖功能 ---
  void _startLottery() {
    if (_lotteryPool.isEmpty || _isDrawing) return;
    
    setState(() {
      _isDrawing = true;
      _winner = null;
      _countdown = 5; // 5秒倒计时
    });

    // 倒计时逻辑
    _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (_countdown > 1) {
        setState(() { _countdown--; });
      } else {
        timer.cancel();
        _drawWinner(); // 倒计时结束,抽取中奖者
      }
    });
  }

  void _drawWinner() {
    final random = Random();
    final poolList = _lotteryPool.toList();
    final luckyUser = poolList[random.nextInt(poolList.length)];
    
    setState(() {
      _winner = "🎉 恭喜用户 [$luckyUser] 中奖!";
      _isDrawing = false;
    });
  }

  @override
  void dispose() {
    _videoController?.dispose();
    _commentController.dispose();
    _scrollController.dispose();
    _countdownTimer?.cancel();
    _channel?.sink.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Stack(
        children: [
          // 底层:直播画面
          Center(
            child: _isVideoInitialized
                ? AspectRatio(
                    aspectRatio: _videoController!.value.aspectRatio,
                    child: VideoPlayer(_videoController!),
                  )
                : const CircularProgressIndicator(color: Colors.white),
          ),
          if (_isReconnecting)
            const Center(child: Text("直播中断,重连中...", style: TextStyle(color: Colors.red, fontSize: 18))),

          // 顶层:UI交互层
          Column(
            children: [
              // 顶部抽奖按钮与结果
              Padding(
                padding: const EdgeInsets.only(top: 40, left: 16, right: 16),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    ElevatedButton(
                      onPressed: _isDrawing ? null : _startLottery,
                      style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),
                      child: Text(_isDrawing ? "倒计时 $_countdown 秒" : "开始抽奖 (当前池子: ${_lotteryPool.length}人)"),
                    ),
                    if (_winner != null)
                      Container(
                        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
                        color: Colors.red.withOpacity(0.8),
                        child: Text(_winner!, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold)),
                      ),
                  ],
                ),
              ),
              
              const Spacer(), // 将评论区挤到底部

              // 底部评论区与输入框
              Container(
                padding: const EdgeInsets.all(10),
                color: Colors.black.withOpacity(0.6),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    // 评论列表
                    SizedBox(
                      height: 150,
                      child: ListView.builder(
                        controller: _scrollController,
                        itemCount: _comments.length,
                        itemBuilder: (context, index) {
                          return Padding(
                            padding: const EdgeInsets.symmetric(vertical: 2),
                            child: Text(_comments[index], style: const TextStyle(color: Colors.white, fontSize: 14)),
                          );
                        },
                      ),
                    ),
                    // 输入框
                    Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: _commentController,
                            style: const TextStyle(color: Colors.white),
                            decoration: const InputDecoration(
                              hintText: "发送评论参与抽奖...",
                              hintStyle: TextStyle(color: Colors.white54),
                              border: OutlineInputBorder(),
                            ),
                          ),
                        ),
                        IconButton(
                          icon: const Icon(Icons.send, color: Colors.blue),
                          onPressed: _sendComment,
                        ),
                      ],
                    )
                  ],
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}
相关推荐
●VON12 小时前
鸿蒙Flutter实战:自定义SearchDelegate应用内搜索
flutter·华为·harmonyos·鸿蒙
韩曙亮12 小时前
【错误记录】Flutter 编译 Android APK 文件安装包报错 ( 国内镜像源设置 )
android·flutter
李宏伟~13 小时前
flutter实现支付宝支付
flutter
●VON13 小时前
鸿蒙Flutter实战:待办事项三态筛选器
flutter·华为·harmonyos·鸿蒙
李宏伟~13 小时前
flutter实现直播推流端
flutter
●VON14 小时前
鸿蒙Flutter实战:多选批量删除模式的实现
flutter·华为·harmonyos·鸿蒙
坚果的博客14 小时前
Flutter 三方库(Flutter-New-Badge)适配开源鸿蒙教程
flutter·开源·harmonyos
二蛋和他的大花14 小时前
高德地图 Flutter 插件:跨 Android / iOS / HarmonyOS 的完整实现
android·flutter·ios
不爱吃糖的程序媛15 小时前
鸿蒙Flutter 三方库 country_codes 的 适配实战
flutter·华为·harmonyos