Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2dd593e93 | ||
|
|
5ffe524e20 |
|
After Width: | Height: | Size: 509 KiB |
|
After Width: | Height: | Size: 159 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 633 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -24,7 +24,7 @@ class AppConfig {
|
||||
static AppPackageInfo? packageInfo;
|
||||
|
||||
static const appName = 'SportsX裁判工作台';
|
||||
static const designSize = Size(375, 812);
|
||||
static const designSize = Size(640, 1024);
|
||||
|
||||
/// 主裁判计分 H5 链接
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ import 'package:recording_tool/features/competition_teams/widgets/widget_manual_
|
||||
import 'package:recording_tool/features/events/model/model_event_info.dart';
|
||||
import 'package:recording_tool/features/events/request_model/request_model_event.dart';
|
||||
import 'package:recording_tool/features/events/server/server_events.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_button.dart';
|
||||
import 'package:recording_tool/gen/assets.gen.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_bar.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_qr_scanner_dialog.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_toast.dart';
|
||||
import 'package:recording_tool/shared/widgets/app_webview.dart';
|
||||
@@ -236,52 +237,68 @@ class _EventTeamMatchPageState extends ConsumerState<EventTeamMatchPage> {
|
||||
final redTeam = _redTeam;
|
||||
final blueTeam = _blueTeam;
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(),
|
||||
backgroundColor: const Color(0xFFF6F7F9),
|
||||
appBar: myAppBar(
|
||||
context: context,
|
||||
titleWidget: Text(
|
||||
'选手检录',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
key: const ValueKey('event-team-manual-process'),
|
||||
onPressed: _manualProcess,
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
padding: EdgeInsets.symmetric(horizontal: 14.w),
|
||||
minimumSize: Size(72.w, kToolbarHeight),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
child: Text(
|
||||
'人工处理',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white.withValues(alpha: 0.8),
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 10.h),
|
||||
_MatchMetadata(detail: _detail),
|
||||
SizedBox(height: 12.h),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.fromLTRB(20.w, 12.h, 20.w, 20.h),
|
||||
padding: EdgeInsets.fromLTRB(8.w, 0, 8.w, 20.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_MatchMetadata(detail: _detail),
|
||||
SizedBox(height: 10.h),
|
||||
|
||||
SizedBox(height: 8.h),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
key: const ValueKey('event-team-manual-process'),
|
||||
onPressed: _manualProcess,
|
||||
child: Text(
|
||||
'人工处理',
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: const Color(0xFF078AF2),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
_TeamCard(
|
||||
teamLabel: '红队',
|
||||
sideLabel: '红方',
|
||||
team: redTeam,
|
||||
members: _redMembers,
|
||||
backgroundColor: const Color(0xFFFFEEF0),
|
||||
backgroundImage: 'assets/images/image_red_team.png',
|
||||
accentColor: const Color(0xFFE84B5B),
|
||||
verifiedUserIds: _verifiedUserIds,
|
||||
winner: _winnerSideIndex == 0,
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
SizedBox(height: 8.h),
|
||||
_TeamCard(
|
||||
teamLabel: '蓝队',
|
||||
sideLabel: '蓝方',
|
||||
team: blueTeam,
|
||||
members: _blueMembers,
|
||||
backgroundColor: const Color(0xFFEDF5FF),
|
||||
backgroundImage: 'assets/images/image_blue_team.png',
|
||||
accentColor: const Color(0xFF287FDD),
|
||||
verifiedUserIds: _verifiedUserIds,
|
||||
winner: _winnerSideIndex == 1,
|
||||
@@ -292,21 +309,20 @@ class _EventTeamMatchPageState extends ConsumerState<EventTeamMatchPage> {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(20.w, 12.h, 20.w, 20.h),
|
||||
padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 24.h),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: AppButton(
|
||||
_TeamActionButton(
|
||||
label: '继续扫码',
|
||||
variant: AppButtonVariant.outline,
|
||||
iconPath: Assets.images.imageScan.path,
|
||||
onPressed: _continueScan,
|
||||
),
|
||||
filled: true,
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: AppButton(label: '直接开赛', onPressed: _startDirectly),
|
||||
_TeamActionButton(
|
||||
label: '直接开赛',
|
||||
iconPath: 'assets/images/image_start.png',
|
||||
onPressed: _startDirectly,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -341,25 +357,30 @@ class _MatchMetadata extends StatelessWidget {
|
||||
final groupName = detail.groupName?.trim() ?? '';
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.all(18.r),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF7F9FC),
|
||||
borderRadius: BorderRadius.circular(14.r),
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.04),
|
||||
blurRadius: 12.r,
|
||||
offset: Offset(0, 4.h),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _MetadataText(label: '比赛项目', value: itemName),
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
_MetadataText(label: '场地', value: matchPlace),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 14.h),
|
||||
_MetadataText(label: '组别', value: groupName),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _MetadataText(label: '场地', value: matchPlace),
|
||||
),
|
||||
SizedBox(width: 6.w),
|
||||
Expanded(
|
||||
child: _MetadataText(label: '赛项', value: itemName),
|
||||
),
|
||||
SizedBox(width: 6.w),
|
||||
Expanded(
|
||||
child: _MetadataText(label: '组别', value: groupName),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -376,13 +397,14 @@ class _MetadataText extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
'$label:${value.isEmpty ? '暂无' : value}',
|
||||
maxLines: 2,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 17.sp,
|
||||
fontSize: 13.sp,
|
||||
height: 1.35,
|
||||
color: const Color(0xFF303640),
|
||||
color: const Color(0xFF858B95),
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -390,20 +412,20 @@ class _MetadataText extends StatelessWidget {
|
||||
|
||||
class _TeamCard extends StatelessWidget {
|
||||
const _TeamCard({
|
||||
required this.teamLabel,
|
||||
required this.sideLabel,
|
||||
required this.team,
|
||||
required this.members,
|
||||
required this.backgroundColor,
|
||||
required this.backgroundImage,
|
||||
required this.accentColor,
|
||||
required this.verifiedUserIds,
|
||||
required this.winner,
|
||||
this.emptyMessage = '暂无成员数据',
|
||||
});
|
||||
|
||||
final String teamLabel;
|
||||
final String sideLabel;
|
||||
final CompetitionTeam team;
|
||||
final List<EventTeamMember> members;
|
||||
final Color backgroundColor;
|
||||
final String backgroundImage;
|
||||
final Color accentColor;
|
||||
final Set<String> verifiedUserIds;
|
||||
final bool winner;
|
||||
@@ -414,30 +436,38 @@ class _TeamCard extends StatelessWidget {
|
||||
final visibleMembers = members
|
||||
.where((member) => member.userId.isNotEmpty || member.name.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
final leader = _leaderOf(visibleMembers);
|
||||
final players = visibleMembers
|
||||
.where((member) => !identical(member, leader))
|
||||
.toList(growable: false);
|
||||
final leaderVerified =
|
||||
leader != null && verifiedUserIds.contains(leader.userId);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.all(18.r),
|
||||
constraints: BoxConstraints(minHeight: 106.h),
|
||||
padding: EdgeInsets.fromLTRB(12.w, 16.h, 16.w, 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(14.r),
|
||||
border: Border.all(
|
||||
color: winner ? accentColor : accentColor.withValues(alpha: 0.24),
|
||||
width: winner ? 2 : 1,
|
||||
image: DecorationImage(
|
||||
image: AssetImage(backgroundImage),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'$teamLabel:${team.name}',
|
||||
'$sideLabel队伍名称:${team.name}',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 19.sp,
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: const Color(0xFF252B34),
|
||||
color: const Color(0xFF30343B),
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -456,78 +486,191 @@ class _TeamCard extends StatelessWidget {
|
||||
'胜方',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12.sp,
|
||||
fontSize: 11.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 14.h),
|
||||
SizedBox(height: 6.h),
|
||||
if (visibleMembers.isEmpty)
|
||||
Text(
|
||||
emptyMessage,
|
||||
style: TextStyle(fontSize: 15.sp, color: const Color(0xFF747D89)),
|
||||
style: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
color: const Color(0xFF747D89),
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
)
|
||||
else
|
||||
...visibleMembers.map(
|
||||
(member) => _MemberRow(
|
||||
member: member,
|
||||
verified: verifiedUserIds.contains(member.userId),
|
||||
accentColor: accentColor,
|
||||
else ...[
|
||||
if (leader != null)
|
||||
_TeamTextLine(
|
||||
label: '队长',
|
||||
text: _formatMember(leader),
|
||||
showOk: leaderVerified || leader.isLeader,
|
||||
),
|
||||
if (players.isNotEmpty)
|
||||
_TeamTextLine(
|
||||
label: '选手',
|
||||
text: players.map(_formatMember).join('、'),
|
||||
showOk: players.any(
|
||||
(member) => verifiedUserIds.contains(member.userId),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
EventTeamMember? _leaderOf(List<EventTeamMember> members) {
|
||||
for (final member in members) {
|
||||
if (member.isLeader) return member;
|
||||
}
|
||||
return members.isEmpty ? null : members.first;
|
||||
}
|
||||
|
||||
String _formatMember(EventTeamMember member) {
|
||||
final name = member.name.trim().isEmpty ? '暂无' : member.name.trim();
|
||||
// final id = member.userId.trim();
|
||||
// return id.isEmpty ? name : '$name($id)';
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
class _MemberRow extends StatelessWidget {
|
||||
const _MemberRow({
|
||||
required this.member,
|
||||
required this.verified,
|
||||
required this.accentColor,
|
||||
class _TeamTextLine extends StatelessWidget {
|
||||
const _TeamTextLine({
|
||||
required this.label,
|
||||
required this.text,
|
||||
required this.showOk,
|
||||
});
|
||||
|
||||
final EventTeamMember member;
|
||||
final bool verified;
|
||||
final Color accentColor;
|
||||
final String label;
|
||||
final String text;
|
||||
final bool showOk;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 5.h),
|
||||
padding: EdgeInsets.only(top: 4.h),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 6.r,
|
||||
height: 6.r,
|
||||
decoration: BoxDecoration(
|
||||
color: accentColor,
|
||||
shape: BoxShape.circle,
|
||||
Text(
|
||||
'$label:',
|
||||
style: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
height: 1.35,
|
||||
color: const Color(0xFF747B86),
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10.w),
|
||||
Expanded(
|
||||
Flexible(
|
||||
child: Text(
|
||||
'${member.isLeader ? '队长' : '选手'}:${member.name.isEmpty ? '暂无' : member.name}',
|
||||
style: TextStyle(fontSize: 16.sp, color: const Color(0xFF303640)),
|
||||
text.isEmpty ? '暂无' : text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
height: 1.35,
|
||||
color: const Color(0xFF4D545F),
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
child: verified
|
||||
? Icon(
|
||||
Icons.check_circle,
|
||||
key: ValueKey('verified-member-${member.userId}'),
|
||||
color: const Color(0xFF18A957),
|
||||
size: 24.r,
|
||||
)
|
||||
: SizedBox(width: 24.r, height: 24.r),
|
||||
),
|
||||
if (showOk)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 6.w),
|
||||
child: Image.asset(
|
||||
Assets.images.imageOk.path,
|
||||
width: 18.w,
|
||||
height: 14.h,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TeamActionButton extends StatelessWidget {
|
||||
const _TeamActionButton({
|
||||
required this.label,
|
||||
required this.iconPath,
|
||||
required this.onPressed,
|
||||
this.filled = false,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String iconPath;
|
||||
final VoidCallback onPressed;
|
||||
final bool filled;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final radius = BorderRadius.circular(28.r);
|
||||
final content = Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset(
|
||||
iconPath,
|
||||
width: filled ? 18.w : 18.w,
|
||||
height: filled ? 18.h : 18.h,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
SizedBox(width: 4.w),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: filled ? Colors.white : const Color(0xFF359FED),
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontFamily: 'PingFang SC',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 45.h,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: filled
|
||||
? const LinearGradient(
|
||||
colors: [Color(0xFF2F8DFF), Color(0xFF57D2EE)],
|
||||
)
|
||||
: null,
|
||||
color: filled ? null : Colors.white,
|
||||
borderRadius: radius,
|
||||
border: filled
|
||||
? null
|
||||
: Border.all(color: const Color(0xFF9FD0F5), width: 1.w),
|
||||
boxShadow: filled
|
||||
? [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF2F8DFF).withValues(alpha: 0.24),
|
||||
blurRadius: 10.r,
|
||||
offset: Offset(0, 5.h),
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onPressed,
|
||||
borderRadius: radius,
|
||||
child: Center(child: content),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:recording_tool/app/config/app_config.dart';
|
||||
@@ -67,32 +68,32 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
final state = ref.watch(eventInfoProvider);
|
||||
final profile = state.profile;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle.light.copyWith(
|
||||
statusBarColor: Colors.transparent,
|
||||
systemNavigationBarColor: const Color(0xFFF5F6F8),
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: const Color(0xFFF5F6F8),
|
||||
appBar: myAppBar(context: context),
|
||||
body: Stack(
|
||||
children: [
|
||||
_Header(onBack: () => AppNavigator.pop(context: context)),
|
||||
const _TopGradientHeader(),
|
||||
SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
children: [
|
||||
// _Header(onBack: () => AppNavigator.pop(context: context)),
|
||||
SizedBox(height: 10.h),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
||||
child: _ProfileSection(
|
||||
profile: profile,
|
||||
eventTitle: state.eventTitle,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.fromLTRB(62.w, 8.h, 62.w, 40.h),
|
||||
child: Column(
|
||||
children: [
|
||||
_ProfileSection(profile: profile),
|
||||
SizedBox(height: 20.h),
|
||||
Text(
|
||||
state.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: _ScheduleList(
|
||||
state: state,
|
||||
onRetry: () => ref
|
||||
@@ -104,7 +105,6 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -112,6 +112,31 @@ class _EventInfoPageState extends ConsumerState<EventInfoPage> {
|
||||
}
|
||||
}
|
||||
|
||||
class _TopGradientHeader extends StatelessWidget {
|
||||
const _TopGradientHeader();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 219.h,
|
||||
width: double.infinity,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(36.r),
|
||||
bottomRight: Radius.circular(36.r),
|
||||
),
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [Color(0xFF5EC9FF), Color(0xFF178BFF)],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget buildEventRegistrationDestination({
|
||||
required EventRegistrationItem item,
|
||||
required String playerId,
|
||||
@@ -135,16 +160,16 @@ class _Header extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 52.h,
|
||||
height: 44.h,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: IconButton(
|
||||
onPressed: onBack,
|
||||
icon: Icon(Icons.arrow_back_ios_new, size: 32.r),
|
||||
color: Colors.black,
|
||||
icon: Icon(Icons.chevron_left_rounded, size: 28.r),
|
||||
color: Colors.white,
|
||||
tooltip: '返回',
|
||||
padding: EdgeInsets.only(left: 16.w),
|
||||
constraints: BoxConstraints(minWidth: 56.w, minHeight: 52.h),
|
||||
padding: EdgeInsets.only(left: 10.w),
|
||||
constraints: BoxConstraints(minWidth: 44.w, minHeight: 44.h),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -152,52 +177,143 @@ class _Header extends StatelessWidget {
|
||||
}
|
||||
|
||||
class _ProfileSection extends StatelessWidget {
|
||||
const _ProfileSection({required this.profile});
|
||||
const _ProfileSection({required this.profile, required this.eventTitle});
|
||||
|
||||
final EventProfile profile;
|
||||
final String eventTitle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
final title = eventTitle.trim().isEmpty ? '赛事信息' : eventTitle.trim();
|
||||
return Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
padding: EdgeInsets.fromLTRB(12.w, 10.h, 12.w, 12.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6.r),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Positioned(
|
||||
// top: -28.h,
|
||||
// right: -14.w,
|
||||
// child: Transform.rotate(
|
||||
// angle: 0.68,
|
||||
// child: Container(
|
||||
// width: 110.w,
|
||||
// height: 138.h,
|
||||
// decoration: BoxDecoration(
|
||||
// gradient: LinearGradient(
|
||||
// begin: Alignment.topCenter,
|
||||
// end: Alignment.bottomCenter,
|
||||
// colors: [
|
||||
// const Color(0xFF53D7FF).withValues(alpha: 0.12),
|
||||
// const Color(0xFFFF85C8).withValues(alpha: 0.13),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
profile.avatarUrl.isEmpty
|
||||
? AppAvatar(size: 50.r)
|
||||
: AppAvatar(size: 50.r, imageUrl: profile.avatarUrl),
|
||||
|
||||
SizedBox(width: 28.w),
|
||||
? AppAvatar(
|
||||
size: 52.r,
|
||||
initials: profile.name.isEmpty ? 'S' : profile.name,
|
||||
)
|
||||
: AppAvatar(size: 52.r, imageUrl: profile.avatarUrl),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 30.h),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
profile.name,
|
||||
profile.name.isEmpty ? '参赛选手' : profile.name,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 22.sp,
|
||||
fontSize: 18.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF2F2F2F),
|
||||
color: const Color(0xFF33363B),
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 36.h),
|
||||
SizedBox(height: 5.h),
|
||||
Text(
|
||||
profile.phone,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 22.sp,
|
||||
fontSize: 10.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF2F2F2F),
|
||||
color: const Color(0xFF969CA6),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
Divider(
|
||||
height: 1.h,
|
||||
thickness: 1.h,
|
||||
color: const Color(0xFFE8EAED),
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
_EventTitleHighlight(title: title),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EventTitleHighlight extends StatelessWidget {
|
||||
const _EventTitleHighlight({required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: IntrinsicWidth(
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomLeft,
|
||||
children: [
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 2.h,
|
||||
child: Container(
|
||||
height: 8.h,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFB8F5E8),
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
height: 1.25,
|
||||
color: const Color(0xFF33363B),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -223,97 +339,68 @@ class _ScheduleList extends StatelessWidget {
|
||||
Text(
|
||||
'暂无赛事报名信息',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18.sp, color: const Color(0xFF2F2F2F)),
|
||||
style: TextStyle(fontSize: 16.sp, color: const Color(0xFF3A3D42)),
|
||||
),
|
||||
SizedBox(height: 18.h),
|
||||
SizedBox(height: 14.h),
|
||||
TextButton(onPressed: onRetry, child: const Text('重新加载')),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 24.h),
|
||||
itemCount: state.items.length,
|
||||
separatorBuilder: (_, _) => SizedBox(height: 8.h),
|
||||
itemBuilder: (context, index) {
|
||||
final item = state.items[index];
|
||||
return _ScheduleCard(item: item, onTap: () => onItemTap(item));
|
||||
return _ScheduleCard(
|
||||
item: item,
|
||||
scheduleIndex: index + 1,
|
||||
onTap: () => onItemTap(item),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ScheduleCard extends StatelessWidget {
|
||||
const _ScheduleCard({required this.item, required this.onTap});
|
||||
const _ScheduleCard({
|
||||
required this.item,
|
||||
required this.scheduleIndex,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final EventRegistrationItem item;
|
||||
final int scheduleIndex;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6.r),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(6.r),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(minHeight: 138.h),
|
||||
padding: EdgeInsets.fromLTRB(10.w, 14.h, 14.w, 0),
|
||||
constraints: BoxConstraints(minHeight: 86.h),
|
||||
padding: EdgeInsets.fromLTRB(12.w, 10.h, 14.w, 10.h),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: const Color(0xFF7A7A7A)),
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(6.r),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.groupName.isEmpty
|
||||
? item.itemName
|
||||
: '${item.itemName} (${item.groupName})',
|
||||
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.matchPlace,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 20.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF2F2F2F),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 26.h),
|
||||
Text(
|
||||
item.scheduleTime,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 20.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF2F2F2F),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: _ScheduleDetails(
|
||||
item: item,
|
||||
scheduleIndex: scheduleIndex,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
SizedBox(
|
||||
width: 148.w,
|
||||
height: 120.h,
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: _ScheduleBadge(item: item),
|
||||
),
|
||||
),
|
||||
_ScheduleStatus(item: item),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -322,72 +409,148 @@ class _ScheduleCard extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ScheduleBadge extends StatelessWidget {
|
||||
const _ScheduleBadge({required this.item});
|
||||
class _ScheduleDetails extends StatelessWidget {
|
||||
const _ScheduleDetails({required this.item, required this.scheduleIndex});
|
||||
|
||||
final EventRegistrationItem item;
|
||||
final int scheduleIndex;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final memberLine = _formatMemberLine(item);
|
||||
final opponentLine = _formatOpponentLine(item);
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
_formatTitle(item),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF30343A),
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 6.h),
|
||||
_MetaText(_formatVenue(item, scheduleIndex)),
|
||||
SizedBox(height: 3.h),
|
||||
_MetaText(_formatScheduleTime(item)),
|
||||
if (memberLine.isNotEmpty) ...[
|
||||
SizedBox(height: 3.h),
|
||||
_MetaText(memberLine),
|
||||
],
|
||||
if (opponentLine.isNotEmpty) ...[
|
||||
SizedBox(height: 3.h),
|
||||
_MetaText(opponentLine),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MetaText extends StatelessWidget {
|
||||
const _MetaText(this.text);
|
||||
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 10.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF6E747D),
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ScheduleStatus extends StatelessWidget {
|
||||
const _ScheduleStatus({required this.item});
|
||||
|
||||
final EventRegistrationItem item;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (item.statusLabel != null) {
|
||||
return CustomPaint(
|
||||
painter: _CutCornerBorderPainter(color: const Color(0xFF7A7A7A)),
|
||||
child: SizedBox(
|
||||
width: 148.w,
|
||||
height: 90.h,
|
||||
child: Center(
|
||||
child: Text(
|
||||
item.statusLabel!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 20.sp, color: const Color(0xFF2F2F2F)),
|
||||
),
|
||||
),
|
||||
final status = item.statusLabel;
|
||||
if (status != null) {
|
||||
return Text(
|
||||
status,
|
||||
style: TextStyle(
|
||||
fontSize: 10.sp,
|
||||
height: 1.2,
|
||||
color: const Color(0xFF1DBF73),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Text(
|
||||
item.matchPlace,
|
||||
_formatNumberBadge(item),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 34.sp,
|
||||
fontSize: 20.sp,
|
||||
height: 1,
|
||||
color: const Color(0xFF2F2F2F),
|
||||
color: const Color(0xFFF27D69),
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CutCornerBorderPainter extends CustomPainter {
|
||||
const _CutCornerBorderPainter({required this.color});
|
||||
String _formatTitle(EventRegistrationItem item) {
|
||||
if (item.groupName.isEmpty) return item.itemName;
|
||||
return '${item.itemName} (${item.groupName})';
|
||||
}
|
||||
|
||||
final Color color;
|
||||
String _formatVenue(EventRegistrationItem item, int scheduleIndex) {
|
||||
final place = item.matchPlace.trim();
|
||||
if (place.isEmpty) return '$scheduleIndex号场馆$scheduleIndex区';
|
||||
return '$place号场馆$scheduleIndex区';
|
||||
}
|
||||
|
||||
@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);
|
||||
String _formatScheduleTime(EventRegistrationItem item) {
|
||||
final start = DateTime.tryParse(item.matchStartTime);
|
||||
final end = DateTime.tryParse(item.matchEndTime);
|
||||
if (start == null && end == null) return item.scheduleTime;
|
||||
if (start != null && end != null) {
|
||||
return '${start.month}月${start.day}日 ${_formatClock(start)}-${_formatClock(end)}';
|
||||
}
|
||||
final value = start ?? end!;
|
||||
return '${value.month}月${value.day}日 ${_formatClock(value)}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _CutCornerBorderPainter oldDelegate) {
|
||||
return oldDelegate.color != color;
|
||||
}
|
||||
String _formatClock(DateTime value) {
|
||||
return '${value.hour}:${value.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
String _formatMemberLine(EventRegistrationItem item) {
|
||||
final names = item.teamMembers
|
||||
.map((member) => member.name.trim())
|
||||
.where((name) => name.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
return names.join('、');
|
||||
}
|
||||
|
||||
String _formatOpponentLine(EventRegistrationItem item) {
|
||||
final opponent = item.opponentName.trim();
|
||||
if (opponent.isEmpty) return '';
|
||||
return '对手:$opponent';
|
||||
}
|
||||
|
||||
String _formatNumberBadge(EventRegistrationItem item) {
|
||||
final place = item.matchPlace.trim();
|
||||
if (place.isEmpty) return '';
|
||||
final match = RegExp(r'\d+').firstMatch(place);
|
||||
if (match != null) return '${match.group(0)}号';
|
||||
return place;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ class $AssetsHtmlGen {
|
||||
class $AssetsImagesGen {
|
||||
const $AssetsImagesGen();
|
||||
|
||||
/// File path: assets/images/image_app_bar_bg.png
|
||||
AssetGenImage get imageAppBarBg =>
|
||||
const AssetGenImage('assets/images/image_app_bar_bg.png');
|
||||
|
||||
/// File path: assets/images/image_app_icon.png
|
||||
AssetGenImage get imageAppIcon =>
|
||||
const AssetGenImage('assets/images/image_app_icon.png');
|
||||
@@ -32,6 +36,10 @@ class $AssetsImagesGen {
|
||||
AssetGenImage get imageAppNameText =>
|
||||
const AssetGenImage('assets/images/image_app_name_text.png');
|
||||
|
||||
/// File path: assets/images/image_blue_team.png
|
||||
AssetGenImage get imageBlueTeam =>
|
||||
const AssetGenImage('assets/images/image_blue_team.png');
|
||||
|
||||
/// File path: assets/images/image_dialog_bg.png
|
||||
AssetGenImage get imageDialogBg =>
|
||||
const AssetGenImage('assets/images/image_dialog_bg.png');
|
||||
@@ -40,26 +48,48 @@ class $AssetsImagesGen {
|
||||
AssetGenImage get imageLogo =>
|
||||
const AssetGenImage('assets/images/image_logo.png');
|
||||
|
||||
/// File path: assets/images/image_ok.png
|
||||
AssetGenImage get imageOk =>
|
||||
const AssetGenImage('assets/images/image_ok.png');
|
||||
|
||||
/// File path: assets/images/image_page_bg.png
|
||||
AssetGenImage get imagePageBg =>
|
||||
const AssetGenImage('assets/images/image_page_bg.png');
|
||||
|
||||
/// File path: assets/images/image_red_team.png
|
||||
AssetGenImage get imageRedTeam =>
|
||||
const AssetGenImage('assets/images/image_red_team.png');
|
||||
|
||||
/// File path: assets/images/image_scan.png
|
||||
AssetGenImage get imageScan =>
|
||||
const AssetGenImage('assets/images/image_scan.png');
|
||||
|
||||
/// File path: assets/images/image_scan_qrcode.png
|
||||
AssetGenImage get imageScanQrcode =>
|
||||
const AssetGenImage('assets/images/image_scan_qrcode.png');
|
||||
|
||||
/// File path: assets/images/image_start.png
|
||||
AssetGenImage get imageStart =>
|
||||
const AssetGenImage('assets/images/image_start.png');
|
||||
|
||||
/// File path: assets/images/image_vs.png
|
||||
AssetGenImage get imageVs =>
|
||||
const AssetGenImage('assets/images/image_vs.png');
|
||||
|
||||
/// List of all assets
|
||||
List<AssetGenImage> get values => [
|
||||
imageAppBarBg,
|
||||
imageAppIcon,
|
||||
imageAppNameText,
|
||||
imageBlueTeam,
|
||||
imageDialogBg,
|
||||
imageLogo,
|
||||
imageOk,
|
||||
imagePageBg,
|
||||
imageRedTeam,
|
||||
imageScan,
|
||||
imageScanQrcode,
|
||||
imageStart,
|
||||
imageVs,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,31 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:recording_tool/app/router/app_navigator.dart';
|
||||
import 'package:recording_tool/gen/assets.gen.dart';
|
||||
|
||||
AppBar myAppBar({
|
||||
required BuildContext context,
|
||||
String title = '',
|
||||
Widget? titleWidget,
|
||||
}) => AppBar(
|
||||
leadingWidth: 56.w,
|
||||
/// 全局页面标题栏:背景图撑满 + 居中标题 + 返回按钮。
|
||||
/// 可直接赋给 [Scaffold.appBar]。
|
||||
class AppPageBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const AppPageBar({
|
||||
super.key,
|
||||
this.title = '',
|
||||
this.titleWidget,
|
||||
this.backgroundImage,
|
||||
this.onBack,
|
||||
this.actions,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final Widget? titleWidget;
|
||||
final String? backgroundImage;
|
||||
final VoidCallback? onBack;
|
||||
final List<Widget>? actions;
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final imagePath = backgroundImage ?? Assets.images.imageAppBarBg.path;
|
||||
|
||||
return AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
systemOverlayStyle: SystemUiOverlayStyle.light,
|
||||
flexibleSpace: SizedBox.expand(
|
||||
child: Image.asset(
|
||||
imagePath,
|
||||
fit: BoxFit.cover,
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.of(context).maybePop();
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.chevron_left_rounded,
|
||||
color: Colors.white,
|
||||
size: 28.sp,
|
||||
),
|
||||
onPressed: onBack ?? () => AppNavigator.pop(context: context),
|
||||
),
|
||||
title:
|
||||
titleWidget ??
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 20.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'PingFang SC',
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
);
|
||||
actions: actions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 兼容旧调用;内部委托 [AppPageBar]。
|
||||
PreferredSizeWidget myAppBar({
|
||||
required BuildContext context,
|
||||
String title = '',
|
||||
Widget? titleWidget,
|
||||
List<Widget>? actions,
|
||||
}) {
|
||||
return AppPageBar(title: title, titleWidget: titleWidget, actions: actions);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export 'app_avatar.dart';
|
||||
export 'app_bar.dart';
|
||||
export 'app_button.dart';
|
||||
export 'app_card.dart';
|
||||
export 'app_dialog.dart';
|
||||
|
||||