1
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'controls/top_bar.dart';
|
||||
import 'view/student_item.dart';
|
||||
import 'view/waiting_start.dart';
|
||||
import 'widgets/status_view.dart';
|
||||
import 'viewmodel/students_view_model.dart';
|
||||
|
||||
class TRoomPage extends StatefulWidget {
|
||||
const TRoomPage({super.key});
|
||||
final int roomId;
|
||||
final String startTime;
|
||||
|
||||
const TRoomPage({
|
||||
super.key,
|
||||
required this.roomId,
|
||||
required this.startTime,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TRoomPage> createState() => _TRoomPageState();
|
||||
@@ -17,37 +23,15 @@ class _TRoomPageState extends State<TRoomPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider<StudentsViewModel>(
|
||||
create: (BuildContext context) {
|
||||
return StudentsViewModel();
|
||||
return StudentsViewModel(
|
||||
roomId: widget.roomId,
|
||||
start: widget.startTime,
|
||||
);
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: Color(0xff2c3032),
|
||||
appBar: TopBar(),
|
||||
body: true
|
||||
? WaitingStart()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
spacing: 15,
|
||||
children: [
|
||||
Expanded(
|
||||
child: StudentItem(),
|
||||
),
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
return SizedBox(
|
||||
height: 250,
|
||||
child: StudentItem(),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => SizedBox(height: 15),
|
||||
itemCount: 7,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: StatusView(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app/utils/time.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class WaitingStart extends StatefulWidget {
|
||||
const WaitingStart({super.key});
|
||||
|
||||
@override
|
||||
State<WaitingStart> createState() => _WaitingStartState();
|
||||
}
|
||||
|
||||
class _WaitingStartState extends State<WaitingStart> {
|
||||
///剩余秒
|
||||
int _seconds = 0;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
startCountDown();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
///开始倒计时
|
||||
void startCountDown() {
|
||||
//当前时间
|
||||
DateTime now = DateTime.now();
|
||||
//远端时间
|
||||
DateTime remote = DateTime.parse("2025-11-19 17:10:00".replaceFirst(' ', 'T'));
|
||||
setState(() {
|
||||
_seconds = remote.difference(now).inSeconds;
|
||||
});
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
_seconds--;
|
||||
});
|
||||
if (_seconds <= 0) {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
_start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///倒计时结束开始
|
||||
void _start() {}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"未到开播时间,到点后自动开播",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
formatSeconds(_seconds),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,78 @@
|
||||
import 'package:app/data/models/student.dart';
|
||||
import 'package:app/websocket/room_websocket.dart';
|
||||
import 'package:app/request/dto/room/room_user_dto.dart';
|
||||
import 'package:app/request/websocket/room_protocol.dart';
|
||||
import 'package:app/request/websocket/room_websocket.dart';
|
||||
import 'package:app/utils/time.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class StudentsViewModel extends ChangeNotifier {
|
||||
///学生摄像头列表
|
||||
List<Student> _students = [];
|
||||
|
||||
///房间的基础信息,房间id、房间开始时间
|
||||
final int roomId;
|
||||
late final DateTime startTime;
|
||||
|
||||
StudentsViewModel({required this.roomId, String? start}) {
|
||||
startTime = parseTime(start!);
|
||||
_startRoom();
|
||||
}
|
||||
|
||||
List<Student> get students => _students;
|
||||
|
||||
///是否能开始自习室
|
||||
bool get canEnterRoom {
|
||||
final now = DateTime.now();
|
||||
|
||||
// 如果到了开始时间,则可以进入房间
|
||||
if (now.isAfter(startTime)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///websocket管理
|
||||
late RoomWebSocket _ws;
|
||||
|
||||
StudentsViewModel() {
|
||||
_startRoom();
|
||||
}
|
||||
|
||||
///开始链接房间
|
||||
void _startRoom() {
|
||||
void _startRoom() async {
|
||||
_ws = RoomWebSocket();
|
||||
_ws.connect();
|
||||
//如果socket的token没有,先初始化
|
||||
if (_ws.wsToken.isEmpty) {
|
||||
await _ws.initToken(roomId);
|
||||
}
|
||||
//启动连接
|
||||
await _ws.connect();
|
||||
//进入房间命令
|
||||
_ws.send(RoomCommand.joinRoom);
|
||||
|
||||
//监听各种ws事件
|
||||
_ws.stream.listen((msg) {
|
||||
_handleMessage();
|
||||
if (msg.event == RoomEvent.changeUser) {
|
||||
final list = msg.data['user_list'].map((x) => RoomUserDto.fromJson(x)).toList();
|
||||
onStudentChange(list);
|
||||
}
|
||||
});
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
///发送命令
|
||||
void _handleMessage() {
|
||||
print("监听webscoket传来的事件");
|
||||
///自习室的开关
|
||||
/// - [isOpen]: 是否开启
|
||||
void toggleRoom({required bool isOpen}) {
|
||||
if (isOpen) {
|
||||
_ws.send(RoomCommand.openRoom);
|
||||
} else {
|
||||
_ws.send(RoomCommand.closeRoom);
|
||||
}
|
||||
}
|
||||
|
||||
///学生人员变化事件,(如加入、退出、掉线)
|
||||
void onStudentChange(List<RoomUserDto> list) {}
|
||||
|
||||
//销毁
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_ws.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
35
lib/pages/teacher/room/widgets/content_view.dart
Normal file
35
lib/pages/teacher/room/widgets/content_view.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'student_item.dart';
|
||||
|
||||
class ContentView extends StatelessWidget {
|
||||
const ContentView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
spacing: 15,
|
||||
children: [
|
||||
Expanded(
|
||||
child: StudentItem(),
|
||||
),
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
return SizedBox(
|
||||
height: 250,
|
||||
child: StudentItem(),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => SizedBox(height: 15),
|
||||
itemCount: 7,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
114
lib/pages/teacher/room/widgets/status_view.dart
Normal file
114
lib/pages/teacher/room/widgets/status_view.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app/utils/time.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'content_view.dart';
|
||||
import '../viewmodel/students_view_model.dart';
|
||||
|
||||
class StatusView extends StatefulWidget {
|
||||
const StatusView({super.key});
|
||||
|
||||
@override
|
||||
State<StatusView> createState() => _StatusViewState();
|
||||
}
|
||||
|
||||
class _StatusViewState extends State<StatusView> {
|
||||
///房间状态
|
||||
RoomStatus status = RoomStatus.loading;
|
||||
|
||||
///剩余秒
|
||||
int _seconds = 0;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_init();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
void _init() {
|
||||
final vm = context.read<StudentsViewModel>();
|
||||
//如果房间可以开始
|
||||
if (vm.canEnterRoom) {
|
||||
status = RoomStatus.start;
|
||||
} else {
|
||||
status = RoomStatus.waiting;
|
||||
startCountDown();
|
||||
}
|
||||
}
|
||||
|
||||
///开始倒计时
|
||||
void startCountDown() {
|
||||
final vm = context.read<StudentsViewModel>();
|
||||
//当前时间
|
||||
DateTime now = DateTime.now();
|
||||
//远端时间
|
||||
setState(() {
|
||||
_seconds = vm.startTime.difference(now).inSeconds;
|
||||
});
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
_seconds--;
|
||||
});
|
||||
if (_seconds <= 0) {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
setState(() {
|
||||
status = RoomStatus.start;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///开启自习室
|
||||
void openRoom() {
|
||||
final vm = context.read<StudentsViewModel>();
|
||||
vm.toggleRoom(isOpen: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (status == RoomStatus.waiting) {
|
||||
return Align(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"未到开播时间,到点后自动开播",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text(
|
||||
formatSeconds(_seconds),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else if (status == RoomStatus.start) {
|
||||
return ContentView();
|
||||
}
|
||||
return SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
enum RoomStatus {
|
||||
loading, // 加载中
|
||||
waiting, //房间倒计时等待中
|
||||
start, //房间开始中
|
||||
}
|
||||
Reference in New Issue
Block a user