初始化
This commit is contained in:
154
lib/pages/common/auth/login_page.dart
Normal file
154
lib/pages/common/auth/login_page.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:app/widgets/base/button/index.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
import 'widgets/login_agree.dart';
|
||||
import 'widgets/login_input.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
|
||||
@override
|
||||
State<LoginPage> createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
///协议
|
||||
bool _agree = false;
|
||||
|
||||
///输入框
|
||||
final TextEditingController _telController = TextEditingController();
|
||||
final TextEditingController _codeController = TextEditingController();
|
||||
|
||||
///验证码倒计时
|
||||
var _countDown = 0;
|
||||
Timer? _timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
///点击发送验证码
|
||||
void _sendCode() async {
|
||||
RegExp regExp = RegExp(r'^1\d{10}$');
|
||||
if (!regExp.hasMatch(_telController.text)) {
|
||||
EasyLoading.showToast("请填写正确的手机号");
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_countDown = 60;
|
||||
});
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
setState(() {
|
||||
_countDown--;
|
||||
});
|
||||
if (_countDown <= 0) {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
///提交登录
|
||||
void _handSubmit() async {
|
||||
RegExp regExp = RegExp(r'^1\d{10}$');
|
||||
if (!regExp.hasMatch(_telController.text) || _codeController.text.isEmpty) {
|
||||
EasyLoading.showToast("请填写完整手机号或验证码");
|
||||
return;
|
||||
}
|
||||
context.go(RoutePaths.sHome);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//是否已发送验证码
|
||||
bool codeOk = _countDown == 0;
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 20, right: 20, top: 0.08.sh, bottom: 40),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin: EdgeInsets.only(bottom: 40),
|
||||
child: Text("登陆学光自习室", style: Theme.of(context).textTheme.titleLarge),
|
||||
),
|
||||
LoginInput(
|
||||
hintText: "请输入手机号",
|
||||
controller: _telController,
|
||||
suffix: Visibility(
|
||||
visible: _telController.text.isNotEmpty,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
_telController.clear();
|
||||
},
|
||||
child: Icon(RemixIcons.close_circle_fill, size: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
spacing: 20,
|
||||
children: [
|
||||
Expanded(
|
||||
child: LoginInput(
|
||||
hintText: "输入验证码",
|
||||
controller: _codeController,
|
||||
maxLength: 4,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 45,
|
||||
child: Button(
|
||||
text: codeOk ? "发送验证码" : "$_countDown 秒后发送",
|
||||
radius: BorderRadius.circular(10),
|
||||
disabled: !codeOk,
|
||||
onPressed: _sendCode,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 40),
|
||||
height: 50,
|
||||
child: Button(text: "登 录", onPressed: _handSubmit),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
margin: EdgeInsets.only(top: 20),
|
||||
alignment: Alignment.center,
|
||||
child: LoginAgree(
|
||||
value: _agree,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_agree = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
71
lib/pages/common/auth/widgets/login_agree.dart
Normal file
71
lib/pages/common/auth/widgets/login_agree.dart
Normal file
@@ -0,0 +1,71 @@
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
|
||||
class LoginAgree extends StatelessWidget {
|
||||
final ValueChanged<bool?>? onChanged;
|
||||
final bool value;
|
||||
|
||||
const LoginAgree({
|
||||
super.key,
|
||||
this.onChanged,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 25,
|
||||
child: Transform.scale(
|
||||
scale: 0.8,
|
||||
child: Checkbox(
|
||||
value: value,
|
||||
shape: CircleBorder(),
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "注册或登录即表示您了解并同意",
|
||||
),
|
||||
TextSpan(
|
||||
text: "服务条款",
|
||||
style: TextStyle(color: Theme.of(context).primaryColor),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => context.push(
|
||||
RoutePaths.agreement,
|
||||
extra: {
|
||||
"title": "Terms of Service",
|
||||
"url": "https://support.curain.ai/privacy/foodcura/terms_service.html",
|
||||
},
|
||||
),
|
||||
),
|
||||
TextSpan(text: " 和 "),
|
||||
TextSpan(
|
||||
text: "隐私协议",
|
||||
style: TextStyle(color: Theme.of(context).primaryColor),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => context.push(
|
||||
RoutePaths.agreement,
|
||||
extra: {
|
||||
"title": "Privacy",
|
||||
"url": "https://support.curain.ai/privacy/foodcura/privacy_policy.html",
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/pages/common/auth/widgets/login_input.dart
Normal file
49
lib/pages/common/auth/widgets/login_input.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class LoginInput extends StatelessWidget {
|
||||
final String hintText;
|
||||
final int maxLength;
|
||||
final TextEditingController controller;
|
||||
final Widget? suffix;
|
||||
|
||||
const LoginInput({
|
||||
super.key,
|
||||
required this.hintText,
|
||||
this.maxLength = 11,
|
||||
required this.controller,
|
||||
this.suffix,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
maxLength: maxLength,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly, // 只允许 0-9
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: Theme.of(context).textTheme.labelLarge?.copyWith(fontSize: 16),
|
||||
counterText: '',
|
||||
filled: true,
|
||||
fillColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
borderSide: BorderSide.none, // 去掉边框
|
||||
),
|
||||
isCollapsed: true,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 14, horizontal: 20),
|
||||
suffixIconConstraints: BoxConstraints(minWidth: 0, minHeight: 0),
|
||||
suffixIcon: suffix != null
|
||||
? Container(
|
||||
padding: EdgeInsets.only(right: 20),
|
||||
child: suffix,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
30
lib/pages/common/splash/splash_page.dart
Normal file
30
lib/pages/common/splash/splash_page.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class SplashPage extends StatefulWidget {
|
||||
const SplashPage({super.key});
|
||||
|
||||
@override
|
||||
State<SplashPage> createState() => _SplashPageState();
|
||||
}
|
||||
|
||||
class _SplashPageState extends State<SplashPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initPermission();
|
||||
}
|
||||
|
||||
///权限效验初始化
|
||||
void initPermission() async {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
context.go(RoutePaths.login);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold();
|
||||
}
|
||||
}
|
||||
36
lib/pages/student/home/s_home_page.dart
Normal file
36
lib/pages/student/home/s_home_page.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:app/config/theme/base/app_theme_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'today/s_today_card.dart';
|
||||
import 'widgets/user_header.dart';
|
||||
|
||||
class SHomePage extends StatefulWidget {
|
||||
const SHomePage({super.key});
|
||||
|
||||
@override
|
||||
State<SHomePage> createState() => _SHomePageState();
|
||||
}
|
||||
|
||||
class _SHomePageState extends State<SHomePage> {
|
||||
///刷新状态
|
||||
Future<void> _refresh() async {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
appBar: UserHeader(),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: _refresh,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(context.pagePadding),
|
||||
children: [
|
||||
STodayCard(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
85
lib/pages/student/home/today/banner_info.dart
Normal file
85
lib/pages/student/home/today/banner_info.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'package:app/config/theme/base/app_theme_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///banner
|
||||
class BannerInfo extends StatelessWidget {
|
||||
const BannerInfo({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.network(
|
||||
"https://images.unsplash.com/photo-1505209487757-5114235191e5?w=800",
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).primaryColor,
|
||||
Theme.of(context).primaryColor.withValues(alpha: 0.5),
|
||||
],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.all(context.pagePadding),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
margin: EdgeInsets.only(bottom: 30),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black26,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 5,
|
||||
children: [
|
||||
Container(
|
||||
width: 15,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
color: context.success,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"进行中",
|
||||
style: TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"高中数学专场",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 5),
|
||||
child: Text(
|
||||
"专业老师在线陪伴,专注高效学习",
|
||||
style: TextStyle(color: Colors.white70, fontSize: 14),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
127
lib/pages/student/home/today/s_today_card.dart
Normal file
127
lib/pages/student/home/today/s_today_card.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'package:app/config/theme/base/app_theme_ext.dart';
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:app/widgets/base/button/index.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
import 'banner_info.dart';
|
||||
import 'teacher_info.dart';
|
||||
|
||||
class STodayCard extends StatefulWidget {
|
||||
const STodayCard({super.key});
|
||||
|
||||
@override
|
||||
State<STodayCard> createState() => _STodayCardState();
|
||||
}
|
||||
|
||||
class _STodayCardState extends State<STodayCard> {
|
||||
|
||||
///进入自习室
|
||||
void _handleEnterRoom() {
|
||||
context.push(RoutePaths.sRoom);
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
BannerInfo(),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.symmetric(horizontal: context.pagePadding, vertical: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Column(
|
||||
spacing: 30,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TeacherInfo(),
|
||||
Row(
|
||||
spacing: 20,
|
||||
children: [
|
||||
InfoItem(
|
||||
label: "自习时间",
|
||||
value: "19:00-21:00",
|
||||
icon: RemixIcons.time_line,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
InfoItem(
|
||||
label: "在线人数",
|
||||
value: "8/12 人",
|
||||
icon: RemixIcons.time_line,
|
||||
color: context.success,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 50,
|
||||
child: Button(
|
||||
text: "进入自习室",
|
||||
onPressed: _handleEnterRoom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///信息item
|
||||
class InfoItem extends StatelessWidget {
|
||||
final String label;
|
||||
final String value;
|
||||
final IconData icon;
|
||||
final Color color;
|
||||
|
||||
const InfoItem({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.icon,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(context.pagePadding),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(icon, color: color),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: Theme.of(context).textTheme.labelMedium),
|
||||
Text(value, style: TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
54
lib/pages/student/home/today/teacher_info.dart
Normal file
54
lib/pages/student/home/today/teacher_info.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
//老师信息
|
||||
import 'package:app/config/theme/base/app_theme_ext.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TeacherInfo extends StatelessWidget {
|
||||
const TeacherInfo({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.all(context.pagePadding),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xffeef2ff),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 15,
|
||||
children: [
|
||||
Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.white,
|
||||
width: 3,
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: 'https://doaf.asia/api/assets/1/图/62865798_p0.jpg',
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("张老师"),
|
||||
Text(
|
||||
"资深数学教师 · 10年教学经验",
|
||||
style: Theme.of(context).textTheme.labelLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
52
lib/pages/student/home/widgets/feature_static.dart
Normal file
52
lib/pages/student/home/widgets/feature_static.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class FeatureStatic extends StatelessWidget {
|
||||
const FeatureStatic({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<FeatureItem> items = [
|
||||
FeatureItem("视频陪学", "老师全程在线监督", RemixIcons.video_on_ai_line),
|
||||
FeatureItem("举手提问", "实时互动解答疑惑", RemixIcons.hand),
|
||||
FeatureItem("拍照题目", "快速上传问题截图", RemixIcons.camera_2_line),
|
||||
FeatureItem("文件共享", "支持PDF等多种格式", RemixIcons.upload_2_line),
|
||||
];
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 15,
|
||||
children: [
|
||||
Text("核心功能", style: TextStyle(fontSize: 18)),
|
||||
GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
mainAxisSpacing: 15,
|
||||
crossAxisSpacing: 15,
|
||||
mainAxisExtent: 120
|
||||
),
|
||||
itemBuilder: (_, index) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: items.length,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureItem {
|
||||
final String title;
|
||||
final String desc;
|
||||
final IconData icon;
|
||||
|
||||
FeatureItem(this.title, this.desc, this.icon);
|
||||
}
|
||||
44
lib/pages/student/home/widgets/user_header.dart
Normal file
44
lib/pages/student/home/widgets/user_header.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class UserHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
const UserHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
title: const Text('学光自习室'),
|
||||
actions: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xffffb900), Color(0xffff8904)],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 5,
|
||||
children: [
|
||||
const Icon(
|
||||
RemixIcons.vip_crown_line,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
Text(
|
||||
"会员至 2025-03-12",
|
||||
style: TextStyle(color: Colors.white, fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
92
lib/pages/student/room/controls/bottom_bar.dart
Normal file
92
lib/pages/student/room/controls/bottom_bar.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
import 'package:app/widgets/room/file_drawer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
|
||||
class BottomBar extends StatefulWidget {
|
||||
const BottomBar({super.key});
|
||||
|
||||
@override
|
||||
State<BottomBar> createState() => _BottomBarState();
|
||||
}
|
||||
|
||||
class _BottomBarState extends State<BottomBar> {
|
||||
///显示文件
|
||||
void _handShowFile() {
|
||||
showFileDialog(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xff232426),
|
||||
),
|
||||
height: 70,
|
||||
child: Row(
|
||||
children: [
|
||||
BarItem(
|
||||
title: "摄像头",
|
||||
icon: RemixIcons.video_on_fill,
|
||||
),
|
||||
BarItem(
|
||||
title: "麦克风",
|
||||
icon: RemixIcons.mic_off_fill,
|
||||
),
|
||||
BarItem(
|
||||
title: "已静音",
|
||||
icon: RemixIcons.volume_mute_fill,
|
||||
isOff: true,
|
||||
),
|
||||
BarItem(
|
||||
title: "举手",
|
||||
icon: RemixIcons.hand,
|
||||
),
|
||||
BarItem(
|
||||
title: "拍照",
|
||||
icon: RemixIcons.upload_2_fill,
|
||||
onTap: _handShowFile,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BarItem extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final bool isOff;
|
||||
final void Function()? onTap;
|
||||
|
||||
const BarItem({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
this.isOff = false,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
color: isOff ? Colors.red : null,
|
||||
child: Column(
|
||||
spacing: 3,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, color: Colors.white),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(color: Colors.white70, fontSize: 12),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
37
lib/pages/student/room/controls/top_bar.dart
Normal file
37
lib/pages/student/room/controls/top_bar.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class TopBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
final bool showOther;
|
||||
final void Function()? onOther;
|
||||
|
||||
const TopBar({super.key, this.showOther = false, this.onOther});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppBar(
|
||||
foregroundColor: Colors.white,
|
||||
titleTextStyle: TextStyle(color: Colors.white, fontSize: 18),
|
||||
backgroundColor: Color(0xff232426),
|
||||
centerTitle: true,
|
||||
title: Column(
|
||||
children: [
|
||||
Text("会议"),
|
||||
Text(
|
||||
"01:12",
|
||||
style: TextStyle(fontSize: 12, color: Colors.white24),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: onOther,
|
||||
icon: Icon(showOther ? RemixIcons.team_fill : RemixIcons.team_line),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
86
lib/pages/student/room/s_room_page.dart
Normal file
86
lib/pages/student/room/s_room_page.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'package:app/widgets/base/transition/slide_hide.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'controls/bottom_bar.dart';
|
||||
import 'controls/top_bar.dart';
|
||||
import 'video/student_video_list.dart';
|
||||
import 'video/teacher_video.dart';
|
||||
|
||||
class SRoomPage extends StatefulWidget {
|
||||
const SRoomPage({super.key});
|
||||
|
||||
@override
|
||||
State<SRoomPage> createState() => _SRoomPageState();
|
||||
}
|
||||
|
||||
class _SRoomPageState extends State<SRoomPage> {
|
||||
/// 显示控制栏
|
||||
bool _controlsVisible = true;
|
||||
|
||||
///显示其他学生画面
|
||||
bool _showOtherStudent = true;
|
||||
|
||||
///切换显示控制栏
|
||||
void _toggleOverlay() {
|
||||
setState(() {
|
||||
_controlsVisible = !_controlsVisible;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
//底部控制显示
|
||||
GestureDetector(
|
||||
onTap: _toggleOverlay,
|
||||
child: Container(color: Color(0xff2c3032)),
|
||||
),
|
||||
|
||||
//老师视频画面
|
||||
TeacherVideo(),
|
||||
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Visibility(
|
||||
visible: _showOtherStudent,
|
||||
child: StudentVideoList(),
|
||||
),
|
||||
),
|
||||
|
||||
///控制栏
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: SlideHide(
|
||||
direction: SlideDirection.up,
|
||||
hide: !_controlsVisible,
|
||||
child: TopBar(
|
||||
showOther: _showOtherStudent,
|
||||
onOther: () {
|
||||
setState(() {
|
||||
_showOtherStudent = !_showOtherStudent;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: SlideHide(
|
||||
direction: SlideDirection.down,
|
||||
hide: !_controlsVisible,
|
||||
child: BottomBar(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
64
lib/pages/student/room/video/student_video_list.dart
Normal file
64
lib/pages/student/room/video/student_video_list.dart
Normal file
@@ -0,0 +1,64 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StudentVideoList extends StatefulWidget {
|
||||
const StudentVideoList({super.key});
|
||||
|
||||
@override
|
||||
State<StudentVideoList> createState() => _StudentVideoListState();
|
||||
}
|
||||
|
||||
class _StudentVideoListState extends State<StudentVideoList> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
width: 250,
|
||||
padding: EdgeInsets.only(bottom: 30),
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.all(10),
|
||||
itemCount: 8,
|
||||
itemBuilder: (context, index) {
|
||||
return VideoItem();
|
||||
},
|
||||
separatorBuilder: (context, index) => SizedBox(height: 15),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class VideoItem extends StatelessWidget {
|
||||
const VideoItem({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1.5 / 1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xff373c3e),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 5,
|
||||
left: 5,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black26,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: Text(
|
||||
"小红",
|
||||
style: TextStyle(fontSize: 12, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
22
lib/pages/student/room/video/teacher_video.dart
Normal file
22
lib/pages/student/room/video/teacher_video.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TeacherVideo extends StatefulWidget {
|
||||
const TeacherVideo({super.key});
|
||||
|
||||
@override
|
||||
State<TeacherVideo> createState() => _TeacherVideoState();
|
||||
}
|
||||
|
||||
class _TeacherVideoState extends State<TeacherVideo> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Align(
|
||||
child: Text(
|
||||
"画面准备中",
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
28
lib/pages/teacher/home/t_home_page.dart
Normal file
28
lib/pages/teacher/home/t_home_page.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:app/config/theme/base/app_theme_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'widgets/header.dart';
|
||||
import 'widgets/today_card.dart';
|
||||
|
||||
class THomePage extends StatefulWidget {
|
||||
const THomePage({super.key});
|
||||
|
||||
@override
|
||||
State<THomePage> createState() => _THomePageState();
|
||||
}
|
||||
|
||||
class _THomePageState extends State<THomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||
appBar: Header(),
|
||||
body: ListView(
|
||||
padding: EdgeInsets.symmetric(vertical: 20, horizontal: context.pagePadding),
|
||||
children: [
|
||||
TodayCard(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
49
lib/pages/teacher/home/widgets/header.dart
Normal file
49
lib/pages/teacher/home/widgets/header.dart
Normal file
@@ -0,0 +1,49 @@
|
||||
import 'package:app/config/theme/base/app_theme_ext.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class Header extends StatelessWidget implements PreferredSizeWidget {
|
||||
const Header({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: context.pagePadding),
|
||||
height: preferredSize.height,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"学光自习室",
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
Text(
|
||||
"在线陪伴、用心教学",
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: Icon(RemixIcons.user_line),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
128
lib/pages/teacher/home/widgets/today_card.dart
Normal file
128
lib/pages/teacher/home/widgets/today_card.dart
Normal file
@@ -0,0 +1,128 @@
|
||||
import 'package:app/router/route_paths.dart';
|
||||
import 'package:app/widgets/base/button/index.dart';
|
||||
import 'package:app/widgets/base/card/g_card.dart';
|
||||
import 'package:app/widgets/base/config/config.dart';
|
||||
import 'package:app/widgets/base/tag/index.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class TodayCard extends StatelessWidget {
|
||||
const TodayCard({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
/// item
|
||||
Widget item({
|
||||
required String title,
|
||||
required String value,
|
||||
required IconData icon,
|
||||
required Color color,
|
||||
}) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Container(
|
||||
width: 45,
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: Theme.of(context).textTheme.labelLarge),
|
||||
Text(value),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return GCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Text("高三数学冲刺班"),
|
||||
Tag(text: "待开始"),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 5),
|
||||
child: Text(
|
||||
"和学生们一起专注学习、共同进步",
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Icon(RemixIcons.arrow_right_s_line, size: 30),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 30),
|
||||
child: Row(
|
||||
spacing: 15,
|
||||
children: [
|
||||
item(
|
||||
title: "开始时间",
|
||||
value: "14:00",
|
||||
icon: RemixIcons.time_line,
|
||||
color: Color(0xff2b7efd),
|
||||
),
|
||||
item(
|
||||
title: "学生人数",
|
||||
value: "8 名",
|
||||
icon: RemixIcons.group_line,
|
||||
color: Color(0xff00c74f),
|
||||
),
|
||||
item(
|
||||
title: "时长",
|
||||
value: "120 分钟",
|
||||
icon: RemixIcons.book_open_line,
|
||||
color: Color(0xffac45fd),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(top: 30),
|
||||
height: 45,
|
||||
child: Button(
|
||||
text: "开始自习室",
|
||||
type: ThemeType.success,
|
||||
onPressed: () {
|
||||
context.push(RoutePaths.tRoom);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
74
lib/pages/teacher/room/controls/top_bar.dart
Normal file
74
lib/pages/teacher/room/controls/top_bar.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class TopBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const TopBar({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//标题子显示内容
|
||||
Widget infoItem({required String title, required IconData icon}) {
|
||||
return Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
Icon(icon, color: Colors.white54, size: 14),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 12, color: Colors.white54),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
//操作按钮
|
||||
Widget actionButton({required IconData icon, required String title}) {
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
margin: EdgeInsets.only(right: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xff4a4f4f),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Icon(icon, size: 16),
|
||||
Text(title, style: TextStyle(fontSize: 14)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: Color(0xff373c3e),
|
||||
foregroundColor: Colors.white,
|
||||
title: Column(
|
||||
spacing: 5,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("高三数学重置版", style: TextStyle(color: Colors.white, fontSize: 18)),
|
||||
Row(
|
||||
spacing: 15,
|
||||
children: [
|
||||
infoItem(title: "剩余 1小时23分钟", icon: RemixIcons.time_line),
|
||||
infoItem(title: "8 名学生", icon: RemixIcons.group_line),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
actionButton(
|
||||
icon: RemixIcons.video_on_ai_line,
|
||||
title: "关闭全部",
|
||||
),
|
||||
actionButton(
|
||||
icon: RemixIcons.volume_up_line,
|
||||
title: "全部静音",
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
}
|
||||
46
lib/pages/teacher/room/t_room_page.dart
Normal file
46
lib/pages/teacher/room/t_room_page.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'controls/top_bar.dart';
|
||||
import 'view/student_item.dart';
|
||||
import 'view/waiting_start.dart';
|
||||
|
||||
class TRoomPage extends StatefulWidget {
|
||||
const TRoomPage({super.key});
|
||||
|
||||
@override
|
||||
State<TRoomPage> createState() => _TRoomPageState();
|
||||
}
|
||||
|
||||
class _TRoomPageState extends State<TRoomPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return 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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
105
lib/pages/teacher/room/view/student_item.dart
Normal file
105
lib/pages/teacher/room/view/student_item.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:app/widgets/room/file_drawer.dart';
|
||||
import 'package:app/widgets/room/video_surface.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:remixicon/remixicon.dart';
|
||||
|
||||
class StudentItem extends StatefulWidget {
|
||||
const StudentItem({super.key});
|
||||
|
||||
@override
|
||||
State<StudentItem> createState() => _StudentItemState();
|
||||
}
|
||||
|
||||
class _StudentItemState extends State<StudentItem> {
|
||||
///打开文件列表
|
||||
void _openFileList(){
|
||||
showFileDialog(context,isUpload: false);
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Container(
|
||||
color: Color(0xFF404649),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Stack(
|
||||
children: [
|
||||
VideoSurface(
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0x0b050505), Color(0x54050505)],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
"李明辉",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
ColoredBox(
|
||||
color: Color(0xFF232426),
|
||||
child: Row(
|
||||
children: [
|
||||
_actionItem(icon: RemixIcons.video_on_fill, isActive: false),
|
||||
_actionItem(
|
||||
icon: RemixIcons.mic_off_fill,
|
||||
),
|
||||
_actionItem(
|
||||
icon: RemixIcons.volume_mute_fill,
|
||||
),
|
||||
_actionItem(
|
||||
icon: RemixIcons.file_list_3_fill,
|
||||
onTap: _openFileList
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
Widget _actionItem({
|
||||
required IconData icon,
|
||||
bool isActive = true,
|
||||
void Function()? onTap,
|
||||
}) {
|
||||
Color offColor = Color(0xFFE75B61);
|
||||
return Expanded(
|
||||
child: Container(
|
||||
height: 50,
|
||||
color: isActive ? null : offColor.withValues(alpha: 0.3),
|
||||
child: IconButton(
|
||||
onPressed: onTap,
|
||||
icon: Icon(
|
||||
icon,
|
||||
color: isActive ? Colors.white : offColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
80
lib/pages/teacher/room/view/waiting_start.dart
Normal file
80
lib/pages/teacher/room/view/waiting_start.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user