更新 devtools_options.yaml 以启用 riverpod 和 shared_preferences 扩展;更新 AuthApi 枚举以添加 playerRegistrationList API;重构 Auth 页面以在用户已登录时直接导航到扫码页面;重构 EventInfoPage 以使用新的状态管理和加载赛事报名信息;更新扫码页面以导航到 EventInfoPage。
This commit is contained in:
@@ -1,64 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
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/recording/model/model_recording_context.dart';
|
||||
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
||||
import 'package:recording_tool/features/events/state/state_event_info.dart';
|
||||
import 'package:recording_tool/features/events/view_model/view_model_event_info.dart';
|
||||
import 'package:recording_tool/features/recording/pages/page_record.dart';
|
||||
import 'package:recording_tool/shared/widgets/widgets.dart';
|
||||
|
||||
class EventInfoPage extends StatelessWidget {
|
||||
const EventInfoPage({super.key});
|
||||
|
||||
class EventInfoPage extends ConsumerStatefulWidget {
|
||||
const EventInfoPage({super.key, required this.playerId});
|
||||
final String playerId;
|
||||
static const mockRtmpUrl =
|
||||
'rtmp://192.168.1.245:19090/蔡依婷vs夏志豪_空中格斗赛_高中组/蔡依婷vs夏志豪_空中格斗赛_高中组';
|
||||
|
||||
static const _profile = EventProfile(
|
||||
avatarLabel: '头像',
|
||||
name: '王东方',
|
||||
phone: '199966666527',
|
||||
eventTitle: '全国青少年无人机大赛',
|
||||
);
|
||||
@override
|
||||
ConsumerState<EventInfoPage> createState() => _EventInfoPageState();
|
||||
}
|
||||
|
||||
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) {
|
||||
class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
ref.read(eventInfoProvider.notifier).loadRegistrationList();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> onItemTap(EventRegistrationItem item) async {
|
||||
debugPrint('item tapped: ${item.name}');
|
||||
await ref.read(eventInfoProvider.notifier).requestStreamKey(item);
|
||||
if (!mounted) return;
|
||||
final profile = ref.read(eventInfoProvider).profile;
|
||||
AppNavigator.push(
|
||||
RecordingPage(
|
||||
recordingContext: item.toRecordingContext(profile: _profile),
|
||||
streamUrl: mockRtmpUrl,
|
||||
recordingContext: item.toRecordingContext(profile: profile),
|
||||
streamUrl: EventInfoPage.mockRtmpUrl,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = ref.watch(eventInfoProvider);
|
||||
final profile = state.profile;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
@@ -71,10 +58,10 @@ class EventInfoPage extends StatelessWidget {
|
||||
padding: EdgeInsets.fromLTRB(62.w, 8.h, 62.w, 40.h),
|
||||
child: Column(
|
||||
children: [
|
||||
const _ProfileSection(profile: _profile),
|
||||
SizedBox(height: 82.h),
|
||||
_ProfileSection(profile: profile),
|
||||
SizedBox(height: 20.h),
|
||||
Text(
|
||||
_profile.eventTitle,
|
||||
state.eventTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 20.sp,
|
||||
height: 1.2,
|
||||
@@ -85,12 +72,12 @@ class EventInfoPage extends StatelessWidget {
|
||||
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]),
|
||||
),
|
||||
child: _ScheduleList(
|
||||
state: state,
|
||||
onRetry: () => ref
|
||||
.read(eventInfoProvider.notifier)
|
||||
.loadRegistrationList(),
|
||||
onItemTap: onItemTap,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -104,52 +91,6 @@ class EventInfoPage extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
RecordingContext toRecordingContext({required EventProfile profile}) {
|
||||
return RecordingContext(
|
||||
eventTitle: profile.eventTitle,
|
||||
matchName: name,
|
||||
group: group,
|
||||
venue: venue,
|
||||
time: time,
|
||||
playerName: profile.name,
|
||||
playerPhone: profile.phone,
|
||||
laneNo: laneNo,
|
||||
status: status,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Header extends StatelessWidget {
|
||||
const _Header({required this.onBack});
|
||||
|
||||
@@ -184,18 +125,10 @@ class _ProfileSection extends StatelessWidget {
|
||||
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)),
|
||||
),
|
||||
),
|
||||
profile.avatarUrl.isEmpty
|
||||
? AppAvatar(size: 50.r)
|
||||
: AppAvatar(size: 50.r, imageUrl: profile.avatarUrl),
|
||||
|
||||
SizedBox(width: 28.w),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
@@ -233,10 +166,54 @@ class _ProfileSection extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ScheduleList extends StatelessWidget {
|
||||
const _ScheduleList({
|
||||
required this.state,
|
||||
required this.onRetry,
|
||||
required this.onItemTap,
|
||||
});
|
||||
|
||||
final EventInfoState state;
|
||||
final VoidCallback onRetry;
|
||||
final ValueChanged<EventRegistrationItem> onItemTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (state.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (state.items.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
state.errorMessage ?? '暂无赛事报名信息',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18.sp, color: const Color(0xFF2F2F2F)),
|
||||
),
|
||||
SizedBox(height: 18.h),
|
||||
TextButton(onPressed: onRetry, child: const Text('重新加载')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: state.items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = state.items[index];
|
||||
return _ScheduleCard(item: item, onTap: () => onItemTap(item));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ScheduleCard extends StatelessWidget {
|
||||
const _ScheduleCard({required this.item, required this.onTap});
|
||||
|
||||
final EventScheduleItem item;
|
||||
final EventRegistrationItem item;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
@@ -259,7 +236,9 @@ class _ScheduleCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${item.name} (${item.group})',
|
||||
item.group.isEmpty
|
||||
? item.name
|
||||
: '${item.name} (${item.group})',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
@@ -314,7 +293,7 @@ class _ScheduleCard extends StatelessWidget {
|
||||
class _ScheduleBadge extends StatelessWidget {
|
||||
const _ScheduleBadge({required this.item});
|
||||
|
||||
final EventScheduleItem item;
|
||||
final EventRegistrationItem item;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
Reference in New Issue
Block a user