1.新增参赛队伍页面交互

2.新增人工处理功能交互
This commit is contained in:
2026-07-20 09:29:53 +08:00
parent a4333d3bd1
commit 4e0a675198
8 changed files with 1014 additions and 1 deletions
@@ -0,0 +1,101 @@
class CompetitionPlayer {
const CompetitionPlayer({required this.id, required this.name});
final String id;
final String name;
}
class CompetitionTeam {
const CompetitionTeam({
required this.id,
required this.name,
required this.players,
});
final String id;
final String name;
final List<CompetitionPlayer> players;
String get playerNames => players.map((player) => player.name).join('');
}
class CompetitionMatchup {
const CompetitionMatchup({
required this.id,
required this.teamA,
required this.teamB,
this.winnerTeamId,
});
final String id;
final CompetitionTeam teamA;
final CompetitionTeam teamB;
final String? winnerTeamId;
CompetitionMatchup copyWith({String? winnerTeamId}) {
return CompetitionMatchup(
id: id,
teamA: teamA,
teamB: teamB,
winnerTeamId: winnerTeamId ?? this.winnerTeamId,
);
}
}
class CompetitionTeamListItem {
const CompetitionTeamListItem({
required this.id,
required this.eventName,
required this.itemName,
required this.groupName,
required this.matchPlace,
required this.matchStartTime,
required this.matchEndTime,
required this.matchups,
this.completed = false,
});
final String id;
final String eventName;
final String itemName;
final String groupName;
final String matchPlace;
final String matchStartTime;
final String matchEndTime;
final List<CompetitionMatchup> matchups;
final bool completed;
String get title => groupName.isEmpty ? itemName : '$itemName $groupName';
String get scheduleTime => '$matchStartTime-$matchEndTime';
CompetitionTeamListItem copyWith({List<CompetitionMatchup>? matchups}) {
return CompetitionTeamListItem(
id: id,
eventName: eventName,
itemName: itemName,
groupName: groupName,
matchPlace: matchPlace,
matchStartTime: matchStartTime,
matchEndTime: matchEndTime,
matchups: matchups ?? this.matchups,
completed: completed,
);
}
}
class CompetitionTeamPageResult {
const CompetitionTeamPageResult({
required this.items,
required this.total,
required this.page,
required this.pageSize,
});
final List<CompetitionTeamListItem> items;
final int total;
final int page;
final int pageSize;
bool get hasMore => page * pageSize < total;
}
@@ -0,0 +1,249 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart';
import 'package:recording_tool/features/competition_teams/view_model/view_model_competition_teams.dart';
import 'package:recording_tool/features/competition_teams/widgets/widget_manual_winner_dialog.dart';
import 'package:recording_tool/shared/widgets/app_empty_view.dart';
import 'package:recording_tool/shared/widgets/app_toast.dart';
class CompetitionTeamDetailPage extends ConsumerWidget {
const CompetitionTeamDetailPage({super.key, required this.itemId});
final String itemId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final items = ref.watch(
competitionTeamsProvider.select((state) => state.items),
);
CompetitionTeamListItem? item;
for (final candidate in items) {
if (candidate.id == itemId) {
item = candidate;
break;
}
}
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: Text(item?.title ?? '参赛队伍')),
body: item == null
? const AppEmptyView(message: '未找到对阵信息')
: ListView.separated(
padding: EdgeInsets.fromLTRB(20.w, 24.h, 20.w, 36.h),
itemCount: item.matchups.length,
separatorBuilder: (_, _) => SizedBox(height: 22.h),
itemBuilder: (context, index) {
final matchup = item!.matchups[index];
return _MatchupCard(
matchup: matchup,
index: index,
onManualProcess: () => _handleManualProcess(
context,
ref,
itemId: item!.id,
matchup: matchup,
),
);
},
),
);
}
Future<void> _handleManualProcess(
BuildContext context,
WidgetRef ref, {
required String itemId,
required CompetitionMatchup matchup,
}) async {
final winnerTeamId = await ManualWinnerDialog.show(
context,
matchup: matchup,
);
if (winnerTeamId == null || !context.mounted) return;
ref
.read(competitionTeamsProvider.notifier)
.selectWinner(
itemId: itemId,
matchupId: matchup.id,
winnerTeamId: winnerTeamId,
);
final winnerName = winnerTeamId == matchup.teamA.id
? matchup.teamA.name
: matchup.teamB.name;
AppToast.show('已设置$winnerName直接获胜');
}
}
class _MatchupCard extends StatelessWidget {
const _MatchupCard({
required this.matchup,
required this.index,
required this.onManualProcess,
});
final CompetitionMatchup matchup;
final int index;
final VoidCallback onManualProcess;
@override
Widget build(BuildContext context) {
return Container(
key: ValueKey('competition-matchup-${matchup.id}'),
padding: EdgeInsets.fromLTRB(16.w, 10.h, 16.w, 18.h),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: const Color(0xFFC7CCD4)),
borderRadius: BorderRadius.circular(12.r),
),
child: Column(
children: [
Row(
children: [
Text(
'${index + 1}',
style: TextStyle(
fontSize: 14.sp,
color: const Color(0xFF7A828E),
),
),
const Spacer(),
TextButton(
key: ValueKey('manual-process-${matchup.id}'),
onPressed: onManualProcess,
child: Text(
'人工处理',
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF078AF2),
),
),
),
],
),
SizedBox(height: 8.h),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: _TeamPanel(
team: matchup.teamA,
alignment: CrossAxisAlignment.start,
winner: matchup.winnerTeamId == matchup.teamA.id,
accentColor: const Color(0xFFFF6B75),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Text(
'VS',
style: TextStyle(
fontSize: 21.sp,
fontWeight: FontWeight.w700,
color: const Color(0xFF303640),
),
),
),
Expanded(
child: _TeamPanel(
team: matchup.teamB,
alignment: CrossAxisAlignment.end,
textAlign: TextAlign.end,
winner: matchup.winnerTeamId == matchup.teamB.id,
accentColor: const Color(0xFF20BFA9),
),
),
],
),
],
),
);
}
}
class _TeamPanel extends StatelessWidget {
const _TeamPanel({
required this.team,
required this.alignment,
required this.winner,
required this.accentColor,
this.textAlign = TextAlign.start,
});
final CompetitionTeam team;
final CrossAxisAlignment alignment;
final TextAlign textAlign;
final bool winner;
final Color accentColor;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: alignment,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 180),
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: winner
? accentColor.withValues(alpha: 0.14)
: Colors.transparent,
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Text(
team.name,
textAlign: textAlign,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF282E37),
),
),
),
if (winner) ...[
SizedBox(width: 5.w),
Icon(Icons.emoji_events, size: 17.r, color: accentColor),
],
],
),
),
SizedBox(height: 10.h),
Text(
team.playerNames,
textAlign: textAlign,
style: TextStyle(
fontSize: 15.sp,
height: 1.45,
color: const Color(0xFF4E5662),
),
),
if (winner) ...[
SizedBox(height: 8.h),
Container(
key: ValueKey('winner-${team.id}'),
padding: EdgeInsets.symmetric(horizontal: 9.w, vertical: 3.h),
decoration: BoxDecoration(
color: accentColor,
borderRadius: BorderRadius.circular(10.r),
),
child: Text(
'胜方',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
),
],
],
);
}
}
@@ -0,0 +1,189 @@
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/competition_teams/model/model_competition_team.dart';
import 'package:recording_tool/features/competition_teams/pages/page_competition_team_detail.dart';
import 'package:recording_tool/features/competition_teams/view_model/view_model_competition_teams.dart';
import 'package:recording_tool/shared/widgets/app_empty_view.dart';
import 'package:recording_tool/shared/widgets/app_error_view.dart';
import 'package:recording_tool/shared/widgets/app_loading_view.dart';
import 'package:recording_tool/shared/widgets/app_refresh_list.dart';
class CompetitionTeamListPage extends ConsumerStatefulWidget {
const CompetitionTeamListPage({super.key});
@override
ConsumerState<CompetitionTeamListPage> createState() =>
_CompetitionTeamListPageState();
}
class _CompetitionTeamListPageState
extends ConsumerState<CompetitionTeamListPage> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(competitionTeamsProvider.notifier).loadInitial();
});
}
@override
Widget build(BuildContext context) {
final state = ref.watch(competitionTeamsProvider);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(title: const Text('参赛队伍')),
body: SafeArea(
top: false,
child: Builder(
builder: (context) {
if (state.isInitialLoading && state.items.isEmpty) {
return const AppLoadingView(message: '正在加载参赛队伍...');
}
if (state.errorMessage != null && state.items.isEmpty) {
return AppErrorView(
message: state.errorMessage!,
onRetry: () =>
ref.read(competitionTeamsProvider.notifier).loadInitial(),
);
}
return AppRefreshList<CompetitionTeamListItem>(
items: state.items,
onRefresh: ref.read(competitionTeamsProvider.notifier).refresh,
onLoadMore: ref.read(competitionTeamsProvider.notifier).loadMore,
enablePullUp: state.hasMore,
padding: EdgeInsets.fromLTRB(20.w, 18.h, 20.w, 28.h),
separator: SizedBox(height: 14.h),
empty: const AppEmptyView(message: '暂无参赛队伍'),
itemBuilder: (context, item, index) {
return _CompetitionScheduleCard(
item: item,
onTap: () => AppNavigator.push(
CompetitionTeamDetailPage(itemId: item.id),
context: context,
),
);
},
);
},
),
),
);
}
}
class _CompetitionScheduleCard extends StatelessWidget {
const _CompetitionScheduleCard({required this.item, required this.onTap});
final CompetitionTeamListItem item;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Material(
key: ValueKey('competition-team-item-${item.id}'),
color: Colors.white,
borderRadius: BorderRadius.circular(14.r),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(14.r),
child: Container(
constraints: BoxConstraints(minHeight: 142.h),
padding: EdgeInsets.all(16.r),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.r),
border: Border.all(color: const Color(0xFFD7DBE2)),
boxShadow: const [
BoxShadow(
color: Color(0x0F1A2230),
blurRadius: 16,
offset: Offset(0, 6),
),
],
),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 19.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF20242B),
),
),
SizedBox(height: 18.h),
_InfoLine(
icon: Icons.location_on_outlined,
text: item.matchPlace,
),
SizedBox(height: 12.h),
_InfoLine(
icon: Icons.schedule_outlined,
text: item.scheduleTime,
),
],
),
),
SizedBox(width: 14.w),
Container(
width: 72.w,
height: 72.h,
alignment: Alignment.center,
decoration: BoxDecoration(
color: item.completed
? const Color(0xFFF1F3F6)
: const Color(0xFFEAF3FF),
borderRadius: BorderRadius.circular(16.r),
),
child: Text(
item.completed ? '已完成' : item.matchPlace,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: item.completed ? 14.sp : 20.sp,
fontWeight: FontWeight.w700,
color: item.completed
? const Color(0xFF69717D)
: const Color(0xFF147FEA),
),
),
),
],
),
),
),
);
}
}
class _InfoLine extends StatelessWidget {
const _InfoLine({required this.icon, required this.text});
final IconData icon;
final String text;
@override
Widget build(BuildContext context) {
return Row(
children: [
Icon(icon, size: 18.r, color: const Color(0xFF7B8491)),
SizedBox(width: 8.w),
Expanded(
child: Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15.sp, color: const Color(0xFF525B68)),
),
),
],
);
}
}
@@ -0,0 +1,118 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart';
final competitionTeamsServerProvider = Provider<CompetitionTeamsServer>((ref) {
return const CompetitionTeamsServer();
});
class CompetitionTeamsServer {
const CompetitionTeamsServer();
static const totalCount = 10;
Future<CompetitionTeamPageResult> fetchPage({
required int page,
required int pageSize,
}) async {
final start = (page - 1) * pageSize;
if (start >= totalCount) {
return CompetitionTeamPageResult(
items: const [],
total: totalCount,
page: page,
pageSize: pageSize,
);
}
final end = (start + pageSize).clamp(0, totalCount);
final items = List.generate(
end - start,
(index) => _buildItem(start + index),
);
return CompetitionTeamPageResult(
items: items,
total: totalCount,
page: page,
pageSize: pageSize,
);
}
CompetitionTeamListItem _buildItem(int index) {
final number = index + 1;
final group = index.isEven ? '小学组' : '初中组';
final hour = 9 + index ~/ 2;
return CompetitionTeamListItem(
id: 'competition-$number',
eventName: '全国青少年无人机大赛',
itemName: index % 3 == 0 ? '空中足球赛' : '空中格斗赛',
groupName: group,
matchPlace: '场地 ${index % 4 + 1}',
matchStartTime: '${hour.toString().padLeft(2, '0')}:00',
matchEndTime: '${hour.toString().padLeft(2, '0')}:30',
completed: index == totalCount - 1,
matchups: [
_buildMatchup(itemNumber: number, matchNumber: 1),
_buildMatchup(itemNumber: number, matchNumber: 2),
],
);
}
CompetitionMatchup _buildMatchup({
required int itemNumber,
required int matchNumber,
}) {
final matchupId = 'match-$itemNumber-$matchNumber';
final teamAIndex = (itemNumber + matchNumber - 2) % _teamNames.length;
final teamBIndex = (teamAIndex + 1) % _teamNames.length;
return CompetitionMatchup(
id: matchupId,
teamA: _buildTeam(
id: '$matchupId-team-a',
name: _teamNames[teamAIndex],
playerOffset: teamAIndex * 3,
),
teamB: _buildTeam(
id: '$matchupId-team-b',
name: _teamNames[teamBIndex],
playerOffset: teamBIndex * 3,
),
);
}
CompetitionTeam _buildTeam({
required String id,
required String name,
required int playerOffset,
}) {
return CompetitionTeam(
id: id,
name: name,
players: List.generate(3, (index) {
final playerIndex = (playerOffset + index) % _playerNames.length;
return CompetitionPlayer(
id: '$id-player-$index',
name: _playerNames[playerIndex],
);
}),
);
}
static const _teamNames = ['王多鱼队', '钱多多队', '逐风少年队', '蓝翼飞行队', '星火队'];
static const _playerNames = [
'王伟',
'李庆超',
'王大亮',
'韩宁政',
'阮晴桦',
'刘美玲',
'陈子航',
'周白芷',
'林培伦',
'蔡依婷',
'夏志豪',
'赵云飞',
'孙雨泽',
'吴佳琪',
'郑凯文',
];
}
@@ -0,0 +1,42 @@
import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart';
class CompetitionTeamsState {
const CompetitionTeamsState({
this.items = const [],
this.page = 0,
this.hasMore = true,
this.isInitialLoading = false,
this.isRefreshing = false,
this.isLoadingMore = false,
this.errorMessage,
});
final List<CompetitionTeamListItem> items;
final int page;
final bool hasMore;
final bool isInitialLoading;
final bool isRefreshing;
final bool isLoadingMore;
final String? errorMessage;
CompetitionTeamsState copyWith({
List<CompetitionTeamListItem>? items,
int? page,
bool? hasMore,
bool? isInitialLoading,
bool? isRefreshing,
bool? isLoadingMore,
String? errorMessage,
bool clearError = false,
}) {
return CompetitionTeamsState(
items: items ?? this.items,
page: page ?? this.page,
hasMore: hasMore ?? this.hasMore,
isInitialLoading: isInitialLoading ?? this.isInitialLoading,
isRefreshing: isRefreshing ?? this.isRefreshing,
isLoadingMore: isLoadingMore ?? this.isLoadingMore,
errorMessage: clearError ? null : errorMessage ?? this.errorMessage,
);
}
}
@@ -0,0 +1,98 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/legacy.dart';
import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart';
import 'package:recording_tool/features/competition_teams/server/server_competition_teams.dart';
import 'package:recording_tool/features/competition_teams/state/state_competition_teams.dart';
final competitionTeamsProvider =
StateNotifierProvider<CompetitionTeamsViewModel, CompetitionTeamsState>((
ref,
) {
return CompetitionTeamsViewModel(ref);
});
class CompetitionTeamsViewModel extends StateNotifier<CompetitionTeamsState> {
CompetitionTeamsViewModel(this._ref) : super(const CompetitionTeamsState());
static const pageSize = 5;
final Ref _ref;
Future<void> loadInitial() async {
state = state.copyWith(
isInitialLoading: true,
isLoadingMore: false,
isRefreshing: false,
clearError: true,
);
await _loadPage(page: 1, replace: true);
}
Future<void> refresh() async {
if (state.isRefreshing) return;
state = state.copyWith(isRefreshing: true, clearError: true);
await _loadPage(page: 1, replace: true);
}
Future<void> loadMore() async {
if (!state.hasMore || state.isLoadingMore || state.isInitialLoading) return;
state = state.copyWith(isLoadingMore: true, clearError: true);
await _loadPage(page: state.page + 1, replace: false);
}
void selectWinner({
required String itemId,
required String matchupId,
required String winnerTeamId,
}) {
final updatedItems = state.items
.map((item) {
if (item.id != itemId) return item;
final updatedMatchups = item.matchups
.map((matchup) {
if (matchup.id != matchupId) return matchup;
final validWinner =
winnerTeamId == matchup.teamA.id ||
winnerTeamId == matchup.teamB.id;
return validWinner
? matchup.copyWith(winnerTeamId: winnerTeamId)
: matchup;
})
.toList(growable: false);
return item.copyWith(matchups: updatedMatchups);
})
.toList(growable: false);
state = state.copyWith(items: updatedItems);
}
CompetitionTeamListItem? findItem(String itemId) {
for (final item in state.items) {
if (item.id == itemId) return item;
}
return null;
}
Future<void> _loadPage({required int page, required bool replace}) async {
try {
final result = await _ref
.read(competitionTeamsServerProvider)
.fetchPage(page: page, pageSize: pageSize);
state = state.copyWith(
items: replace ? result.items : [...state.items, ...result.items],
page: result.page,
hasMore: result.hasMore,
isInitialLoading: false,
isRefreshing: false,
isLoadingMore: false,
clearError: true,
);
} catch (error) {
state = state.copyWith(
isInitialLoading: false,
isRefreshing: false,
isLoadingMore: false,
errorMessage: '参赛队伍加载失败,请重试',
);
}
}
}
@@ -0,0 +1,210 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recording_tool/features/competition_teams/model/model_competition_team.dart';
import 'package:recording_tool/shared/widgets/app_button.dart';
class ManualWinnerDialog extends StatefulWidget {
const ManualWinnerDialog({super.key, required this.matchup});
final CompetitionMatchup matchup;
static Future<String?> show(
BuildContext context, {
required CompetitionMatchup matchup,
}) {
return showDialog<String>(
context: context,
barrierDismissible: false,
builder: (_) => ManualWinnerDialog(matchup: matchup),
);
}
@override
State<ManualWinnerDialog> createState() => _ManualWinnerDialogState();
}
class _ManualWinnerDialogState extends State<ManualWinnerDialog> {
String? _selectedTeamId;
@override
void initState() {
super.initState();
_selectedTeamId = widget.matchup.winnerTeamId;
}
@override
Widget build(BuildContext context) {
return AlertDialog(
insetPadding: EdgeInsets.symmetric(horizontal: 22.w),
contentPadding: EdgeInsets.zero,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18.r)),
content: SizedBox(
width: 320.w,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
'assets/images/image_dialog_bg.png',
width: 320.w,
height: 112.h,
fit: BoxFit.cover,
),
Padding(
padding: EdgeInsets.fromLTRB(20.w, 8.h, 20.w, 20.h),
child: Column(
children: [
Text(
'请在比赛结束前处理',
key: const ValueKey('manual-winner-dialog-title'),
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w700,
color: const Color(0xFF20242B),
),
),
SizedBox(height: 16.h),
_TeamChoice(
team: widget.matchup.teamA,
color: const Color(0xFFFF6B75),
selected: _selectedTeamId == widget.matchup.teamA.id,
onTap: () => setState(
() => _selectedTeamId = widget.matchup.teamA.id,
),
),
SizedBox(height: 10.h),
_TeamChoice(
team: widget.matchup.teamB,
color: const Color(0xFF20BFA9),
selected: _selectedTeamId == widget.matchup.teamB.id,
onTap: () => setState(
() => _selectedTeamId = widget.matchup.teamB.id,
),
),
SizedBox(height: 20.h),
Row(
children: [
Expanded(
child: AppButton(
label: '取消',
variant: AppButtonVariant.secondary,
onPressed: () => Navigator.of(context).pop(),
),
),
SizedBox(width: 14.w),
Expanded(
child: AppButton(
label: '确定',
onPressed: _selectedTeamId == null
? null
: () => Navigator.of(
context,
).pop(_selectedTeamId),
),
),
],
),
],
),
),
],
),
),
),
);
}
}
class _TeamChoice extends StatelessWidget {
const _TeamChoice({
required this.team,
required this.color,
required this.selected,
required this.onTap,
});
final CompetitionTeam team;
final Color color;
final bool selected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Semantics(
selected: selected,
button: true,
label: '选择${team.name}直接获胜',
child: Material(
color: color.withValues(alpha: selected ? 0.16 : 0.08),
borderRadius: BorderRadius.circular(12.r),
child: InkWell(
key: ValueKey('winner-choice-${team.id}'),
onTap: onTap,
borderRadius: BorderRadius.circular(12.r),
child: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 12.h),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: selected ? color : color.withValues(alpha: 0.35),
width: selected ? 2 : 1,
),
),
child: Row(
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 160),
width: 22.r,
height: 22.r,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: selected ? color : Colors.grey),
color: selected ? color : Colors.transparent,
),
child: selected
? Icon(Icons.check, size: 15.r, color: Colors.white)
: null,
),
SizedBox(width: 12.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
team.name,
style: TextStyle(
fontSize: 17.sp,
fontWeight: FontWeight.w600,
color: const Color(0xFF20242B),
),
),
SizedBox(height: 4.h),
Text(
team.playerNames,
style: TextStyle(
fontSize: 14.sp,
color: const Color(0xFF606874),
),
),
],
),
),
SizedBox(width: 8.w),
Text(
'直接获胜',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w600,
color: color,
),
),
],
),
),
),
),
);
}
}
@@ -7,6 +7,7 @@ import 'package:recording_tool/core/cache/app_storage.dart';
import 'package:recording_tool/core/cache/storage_keys.dart'; import 'package:recording_tool/core/cache/storage_keys.dart';
import 'package:recording_tool/features/auth/pages/page_auth.dart'; import 'package:recording_tool/features/auth/pages/page_auth.dart';
import 'package:recording_tool/features/auth/view_model_auth/view_model_auth.dart'; import 'package:recording_tool/features/auth/view_model_auth/view_model_auth.dart';
import 'package:recording_tool/features/competition_teams/pages/page_competition_team_list.dart';
import 'package:recording_tool/features/events/model/model_event_info.dart'; import 'package:recording_tool/features/events/model/model_event_info.dart';
import 'package:recording_tool/features/events/pages/page_event_info.dart'; import 'package:recording_tool/features/events/pages/page_event_info.dart';
import 'package:recording_tool/features/events/view_model/view_model_event_info.dart'; import 'package:recording_tool/features/events/view_model/view_model_event_info.dart';
@@ -112,7 +113,12 @@ class _AuthPageWidgetState extends ConsumerState<ScanQrCodePage> {
height: 80.h, height: 80.h,
child: AppButton( child: AppButton(
label: '参赛队伍', label: '参赛队伍',
onPressed: () async {}, onPressed: () async {
await AppNavigator.push(
const CompetitionTeamListPage(),
context: context,
);
},
variant: AppButtonVariant.secondary, variant: AppButtonVariant.secondary,
), ),
); );