自习室优化ok

This commit is contained in:
zhutao
2025-11-28 13:31:23 +08:00
parent 4ecb0c35d6
commit 57305c5804
57 changed files with 2500 additions and 597 deletions

View File

@@ -1,8 +1,8 @@
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:app/config/config.dart';
import 'package:app/providers/user_store.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import '../viewmodel/tch_room_vm.dart';
import 'student_item.dart';
@@ -27,11 +27,11 @@ class _ContentViewState extends State<ContentView> {
@override
void dispose() {
super.dispose();
WakelockPlus.disable();
_dispose();
}
void _initRtc() async {
UserStore userStore = context.read<UserStore>();
final vm = context.read<TchRoomVM>();
_engine = createAgoraRtcEngine();
//初始化 RtcEngine设置频道场景为 channelProfileLiveBroadcasting直播场景
@@ -41,39 +41,33 @@ class _ContentViewState extends State<ContentView> {
channelProfile: ChannelProfileType.channelProfileCommunication,
),
);
//添加回调
_engine!.registerEventHandler(
RtcEngineEventHandler(
// 成功加入频道回调
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
setState(() {});
},
// 远端用户或主播加入当前频道回调
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {},
// 远端用户或主播离开当前频道回调
onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) {},
),
);
//启动视频模块
// 启用视频模块
await _engine!.enableVideo();
//加入频道
await _engine!.joinChannel(
token: vm.rtcToken!.token,
channelId: vm.rtcToken!.channel,
uid: userStore.userInfo!.id,
options: ChannelMediaOptions(
// 自动订阅所有视频流
autoSubscribeVideo: true,
// 自动订阅所有音频流
autoSubscribeAudio: true,
// 发布摄像头采集的视频
publishCameraTrack: true,
// 发布麦克风采集的音频
publishMicrophoneTrack: true,
// 设置用户角色为 clientRoleBroadcaster主播或 clientRoleAudience观众
clientRoleType: ClientRoleType.clientRoleBroadcaster,
),
);
// 开启本地预览
await _engine!.startPreview();
final status = await _engine!.getConnectionState();
WakelockPlus.enable();
if (status == ConnectionStateType.connectionStateDisconnected) {
//加入频道
await _engine!.joinChannel(
token: vm.rtcToken!.token,
channelId: vm.rtcToken!.channel,
uid: vm.rtcToken!.uid,
options: ChannelMediaOptions(
// 自动订阅所有视频流
autoSubscribeVideo: true,
// 自动订阅所有音频流
autoSubscribeAudio: true,
// 发布摄像头采集的视频
publishCameraTrack: true,
// 发布麦克风采集的音频
publishMicrophoneTrack: true,
// 设置用户角色为 clientRoleBroadcaster主播或 clientRoleAudience观众
clientRoleType: ClientRoleType.clientRoleBroadcaster,
),
);
}
}
//销毁
@@ -89,8 +83,11 @@ class _ContentViewState extends State<ContentView> {
return Consumer<TchRoomVM>(
builder: (context, vm, _) {
if (vm.students.isEmpty) {
return Center(
child: Text('准备中'),
return Align(
child: Text(
'学生还没入场',
style: TextStyle(color: Colors.white),
),
);
}
//选中的学生
@@ -105,9 +102,30 @@ class _ContentViewState extends State<ContentView> {
spacing: 15,
children: [
Expanded(
child: StudentItem(
user: activeStudent,
engine: _engine,
child: Stack(
children: [
StudentItem(
user: activeStudent,
engine: _engine,
),
Positioned(
top: 0,
left: 0,
child: Container(
width: 150,
color: Colors.black,
child: AspectRatio(
aspectRatio: 1 / 1.2,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine!,
canvas: const VideoCanvas(uid: 0),
),
),
),
),
),
],
),
),
SizedBox(

View File

@@ -1,11 +1,11 @@
import 'dart:async';
import 'package:app/utils/time.dart';
import 'package:app/widgets/base/dialog/config_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../../../../widgets/room/core/count_down_vm.dart';
import 'content_view.dart';
import '../viewmodel/tch_room_vm.dart';
@@ -17,40 +17,12 @@ class StatusView extends StatefulWidget {
}
class _StatusViewState extends State<StatusView> {
int _seconds = 0;
Timer? _timer;
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _startCountDown(DateTime startTime) {
// 避免重复计时器
if (_timer != null) return;
final now = DateTime.now();
int diff = startTime.difference(now).inSeconds;
if (diff <= 0) {
return;
}
setState(() {
_seconds = diff;
});
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (!mounted) return;
setState(() {
_seconds--;
});
if (_seconds <= 0) {
_timer?.cancel();
_timer = null;
}
});
void initState() {
super.initState();
final countVM = context.read<CountDownVM>();
countVM.removeListener(_onCountDownEnd);
countVM.addListener(_onCountDownEnd);
}
///开播中返回拦截弹窗
@@ -72,55 +44,65 @@ class _StatusViewState extends State<StatusView> {
);
}
///监听会议室倒计时结束的时候
void _onCountDownEnd() {
final countVM = context.read<CountDownVM>();
if (countVM.endCountDown == 0) {
EasyLoading.showToast("自习室已到结束时间,请记得关闭会议室");
countVM.removeListener(_onCountDownEnd);
}
}
@override
Widget build(BuildContext context) {
final vm = context.watch<TchRoomVM>();
final tchVM = context.watch<TchRoomVM>();
var roomStatus = tchVM.roomInfo.roomStatus;
/// 1. 未加载
if (vm.roomStatus == -1) {
if (roomStatus == -1) {
return const Align(
child: Text("加载中", style: TextStyle(color: Colors.white)),
);
}
/// 2. 未开始的房间
if (vm.roomStatus == 0) {
if (vm.canEnterRoom) {
// 到时间了 → 自动开播
WidgetsBinding.instance.addPostFrameCallback((_) {
vm.toggleRoom(isOpen: true);
});
} else {
// 没到时间 → 启动倒计时
_startCountDown(parseTime(vm.roomInfo.startTime));
}
return Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"未到开播时间,到点后自动开播",
style: TextStyle(color: Colors.white),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 10),
child: Text(
formatSeconds(_seconds),
style: const TextStyle(
color: Colors.white,
fontSize: 26,
fontWeight: FontWeight.bold,
),
if (roomStatus == 0) {
return Consumer<CountDownVM>(
builder: (_, countVM, __) {
if (countVM.canEnterRoom) {
tchVM.toggleRoom(isOpen: true);
return SizedBox();
} else {
countVM.startStartCountdown();
return Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"未到开播时间,到点后自动开播",
style: TextStyle(color: Colors.white),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 10),
child: Text(
formatSeconds(countVM.startCountDown),
style: const TextStyle(
color: Colors.white,
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
),
);
}
},
);
}
/// 3. 已开播
if (vm.roomStatus == 1) {
if (roomStatus == 1) {
return PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, _) {

View File

@@ -2,6 +2,7 @@ import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:app/pages/teacher/room/viewmodel/type.dart';
import 'package:app/request/dto/room/room_user_dto.dart';
import 'package:app/widgets/room/file_drawer.dart';
import 'package:app/widgets/room/other_widget.dart';
import 'package:app/widgets/room/video_surface.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@@ -26,7 +27,11 @@ class StudentItem extends StatefulWidget {
class _StudentItemState extends State<StudentItem> {
///打开文件列表
void _openFileList() {
showFileDialog(context, isUpload: false);
showFileDialog(
context,
isUpload: false,
files: widget.user.filesList,
);
}
@override
@@ -40,7 +45,6 @@ class _StudentItemState extends State<StudentItem> {
///声音是否开启
bool isSpeakerOpen = widget.user.speekerStatus == 1;
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Container(
@@ -51,15 +55,18 @@ class _StudentItemState extends State<StudentItem> {
child: SizedBox(
width: double.infinity,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
if (widget.engine != null)
AgoraVideoView(
controller: VideoViewController(
rtcEngine: widget.engine!,
canvas: VideoCanvas(uid: widget.user.rtcUid),
VideoSurface(
user: widget.user,
child: AgoraVideoView(
controller: VideoViewController(
rtcEngine: widget.engine!,
canvas: VideoCanvas(uid: widget.user.rtcUid),
),
),
),
// VideoSurface(),
Positioned(
bottom: 0,
left: 0,
@@ -79,6 +86,8 @@ class _StudentItemState extends State<StudentItem> {
),
),
),
///右上角选中
if (widget.user.userId != vm.activeSId)
Positioned(
right: 5,
@@ -99,6 +108,17 @@ class _StudentItemState extends State<StudentItem> {
),
),
),
///举手
if (widget.user.handup == 1)
Positioned(
bottom: 40,
child: HandRaiseButton(
onTap: () {
vm.clearHandUp(widget.user.userId);
},
),
),
],
),
),
@@ -106,6 +126,7 @@ class _StudentItemState extends State<StudentItem> {
ColoredBox(
color: Color(0xFF232426),
child: Row(
spacing: 1,
children: [
_actionItem(
icon: isCameraOpen ? RemixIcons.video_on_fill : RemixIcons.video_off_fill,