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,
                        ),
                      ],
                    )
                  ],
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}
相关推荐
悟空瞎说1 天前
Flutter 架构详解:新手必懂底层原理
flutter
SoaringHeart1 天前
Flutter最佳实践:IM聊天文字链接自动识别跳转
前端·flutter
恋猫de小郭1 天前
KMP / CMP 鸿蒙版本 Beta 发布,他有什么特别之处?
android·前端·flutter
风华圆舞2 天前
Flutter + 鸿蒙 Intents Kit:页面直达能力的完整接入方案
flutter·ui·华为·harmonyos
韩曙亮2 天前
【Flutter】Flutter 组件 ④ ( 组件渲染 的 三棵树理论 | Widget 树 → Element 树 → RenderObject 树 )
flutter·element·widget·renderobject
恋猫de小郭2 天前
Android 17 正式版发布,全新 AI 和各种破坏性更新
android·前端·flutter
kingbal2 天前
Windows:flutter环境搭建
windows·flutter
911hzh2 天前
Flutter MethodChannel 跨端通信框架 zh_native_channel:快速入门、优势分析与 Pigeon 对比
flutter
911hzh2 天前
Flutter 快速搭建新项目:用 Flutter Foundation Kit 一条命令生成带基础架构的 App 模板
flutter