新增赛事信息页

This commit is contained in:
2026-07-09 14:57:09 +08:00
parent c38f6d69c8
commit 75c42ea168
4 changed files with 365 additions and 7 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recording_tool/app/router/app_navigator.dart';
import 'package:recording_tool/features/auth/view_model_auth/view_model_auth.dart';
import 'package:recording_tool/features/scan_qrcode/pages/page_scan_qrcode.dart';
import 'package:recording_tool/features/events/pages/page_event_info.dart';
import 'package:recording_tool/shared/widgets/widgets.dart';
class AuthPageWidget extends ConsumerStatefulWidget {
@@ -62,7 +62,7 @@ class _AuthPageWidgetState extends ConsumerState<AuthPageWidget> {
.auth(code ?? '');
if (!mounted) return;
if (success) {
AppNavigator.push(const ScanQrCodePage());
AppNavigator.push(const EventInfoPage());
return;
}
final message = ref.read(authProvider).errorMessage;
@@ -0,0 +1,359 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recording_tool/app/router/app_navigator.dart';
import 'package:recording_tool/features/recording/pages/page_record.dart';
class EventInfoPage extends StatelessWidget {
const EventInfoPage({super.key});
static const _profile = EventProfile(
avatarLabel: '头像',
name: '王东方',
phone: '199966666527',
eventTitle: '全国青少年无人机大赛',
);
static const _items = [
EventScheduleItem(
name: '个人飞行赛',
group: '小学组',
venue: '场地 1',
time: '7月1日 12:00-15:00',
laneNo: '10号',
),
EventScheduleItem(
name: '空中足球赛',
group: '小学组',
venue: '场地 1',
time: '7月2日 12:00-15:00',
status: '已完成',
),
EventScheduleItem(
name: '穿越竞速赛',
group: '小学组',
venue: '场地 2',
time: '7月3日 09:00-11:30',
laneNo: '06号',
),
EventScheduleItem(
name: '编队飞行赛',
group: '小学组',
venue: '场地 3',
time: '7月3日 14:00-16:00',
status: '待开始',
),
];
void onItemTap(EventScheduleItem item) {
debugPrint('item tapped: ${item.name}');
AppNavigator.push(const RecordingPage());
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_Header(onBack: () => AppNavigator.pop(context: context)),
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.fromLTRB(62.w, 8.h, 62.w, 40.h),
child: Column(
children: [
const _ProfileSection(profile: _profile),
SizedBox(height: 82.h),
Text(
_profile.eventTitle,
style: TextStyle(
fontSize: 20.sp,
height: 1.2,
color: const Color(0xFF2F2F2F),
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 24.h),
SizedBox(
height: 400.h,
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) => _ScheduleCard(
item: _items[index],
onTap: () => onItemTap(_items[index]),
),
),
),
],
),
),
),
],
),
),
);
}
}
class EventProfile {
const EventProfile({
required this.avatarLabel,
required this.name,
required this.phone,
required this.eventTitle,
});
final String avatarLabel;
final String name;
final String phone;
final String eventTitle;
}
class EventScheduleItem {
const EventScheduleItem({
required this.name,
required this.group,
required this.venue,
required this.time,
this.laneNo,
this.status,
});
final String name;
final String group;
final String venue;
final String time;
final String? laneNo;
final String? status;
}
class _Header extends StatelessWidget {
const _Header({required this.onBack});
final VoidCallback onBack;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 52.h,
child: Align(
alignment: Alignment.centerLeft,
child: IconButton(
onPressed: onBack,
icon: Icon(Icons.arrow_back_ios_new, size: 32.r),
color: Colors.black,
tooltip: '返回',
padding: EdgeInsets.only(left: 16.w),
constraints: BoxConstraints(minWidth: 56.w, minHeight: 52.h),
),
),
);
}
}
class _ProfileSection extends StatelessWidget {
const _ProfileSection({required this.profile});
final EventProfile profile;
@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
// width: 225.w,
// height: 184.h,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(color: const Color(0xFF7A7A7A)),
),
child: Text(
profile.avatarLabel,
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF2F2F2F)),
),
),
SizedBox(width: 28.w),
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 30.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
profile.name,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 22.sp,
height: 1.2,
color: const Color(0xFF2F2F2F),
),
),
SizedBox(height: 36.h),
Text(
profile.phone,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 22.sp,
height: 1.2,
color: const Color(0xFF2F2F2F),
),
),
],
),
),
),
],
);
}
}
class _ScheduleCard extends StatelessWidget {
const _ScheduleCard({required this.item, required this.onTap});
final EventScheduleItem item;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Material(
color: Colors.white,
child: InkWell(
onTap: onTap,
child: Container(
constraints: BoxConstraints(minHeight: 138.h),
padding: EdgeInsets.fromLTRB(10.w, 14.h, 14.w, 0),
decoration: BoxDecoration(
border: Border.all(color: const Color(0xFF7A7A7A)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${item.name} ${item.group}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 20.sp,
height: 1.2,
color: const Color(0xFF2F2F2F),
fontWeight: FontWeight.w500,
),
),
SizedBox(height: 26.h),
Text(
item.venue,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 20.sp,
height: 1.2,
color: const Color(0xFF2F2F2F),
),
),
SizedBox(height: 26.h),
Text(
item.time,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 20.sp,
height: 1.2,
color: const Color(0xFF2F2F2F),
),
),
],
),
),
SizedBox(width: 12.w),
SizedBox(
width: 148.w,
height: 120.h,
child: Align(
alignment: Alignment.center,
child: _ScheduleBadge(item: item),
),
),
],
),
),
),
);
}
}
class _ScheduleBadge extends StatelessWidget {
const _ScheduleBadge({required this.item});
final EventScheduleItem item;
@override
Widget build(BuildContext context) {
if (item.status != null) {
return CustomPaint(
painter: _CutCornerBorderPainter(color: const Color(0xFF7A7A7A)),
child: SizedBox(
width: 148.w,
height: 90.h,
child: Center(
child: Text(
item.status!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF2F2F2F)),
),
),
),
);
}
return Text(
item.laneNo ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 34.sp,
height: 1,
color: const Color(0xFF2F2F2F),
fontWeight: FontWeight.w700,
),
);
}
}
class _CutCornerBorderPainter extends CustomPainter {
const _CutCornerBorderPainter({required this.color});
final Color color;
@override
void paint(Canvas canvas, Size size) {
final side = 12.r;
final path = Path()
..moveTo(side, 0)
..lineTo(size.width - side, 0)
..lineTo(size.width, side)
..lineTo(size.width, size.height - side)
..lineTo(size.width - side, size.height)
..lineTo(side, size.height)
..lineTo(0, size.height - side)
..lineTo(0, side)
..close();
final paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1
..color = color;
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant _CutCornerBorderPainter oldDelegate) {
return oldDelegate.color != color;
}
}
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recording_tool/app/router/app_navigator.dart';
import 'package:recording_tool/features/recording/pages/page_record.dart';
import 'package:recording_tool/features/scan_qrcode/pages/page_push_test.dart';
import 'package:recording_tool/shared/widgets/app_bar.dart';
import 'package:recording_tool/shared/widgets/app_button.dart';
import 'package:recording_tool/shared/widgets/app_qr_scanner_dialog.dart';
+4 -4
View File
@@ -250,16 +250,16 @@ class WebviewPageState extends State<WebviewPage> {
final ok = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text("提示"),
content: const Text("是否返回上一页?"),
title: const Text('提示'),
content: const Text('是否返回上一页?'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text("取消"),
child: const Text('取消'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text("确定"),
child: const Text('确定'),
),
],
),