1. 新增报告计算进度圆环、科目卡片、完成弹窗组件 2. 重构report_computation_page,替换旧的自定义组件为新封装组件 3. 新增student_info_page的主题资源和UI重构,适配Flutter屏幕适配 4. 新增临时调试入口用于开发测试
655 lines
23 KiB
Dart
655 lines
23 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:new_learn/core/api/report/api.dart';
|
||
import 'package:new_learn/core/api/report/model.dart';
|
||
import 'package:new_learn/core/logger/app_talker.dart';
|
||
import 'package:new_learn/core/providers/user_provider.dart';
|
||
import 'package:new_learn/core/utils/img_url.dart';
|
||
|
||
/// ============================================================
|
||
/// 【StudentInfoPage】诊断报告欢迎介绍页 —— 对标 Vue studentInfo.vue
|
||
///
|
||
/// Vue 端用 tPageImg / tPageCss / tPageColor 从 themes/default.json 抽主题;
|
||
/// Flutter 端未做主题抽离,所以直接硬编码 default.json 里这一页的资源路径、
|
||
/// 颜色值、渐变配置。集中在本文件顶部 _StudentTheme 常量,便于后续抽离。
|
||
///
|
||
/// 路由参数(GoRouteState.extra):
|
||
/// context.go('/studentInfo', extra: {'type': 'placementTest'});
|
||
/// - type: placementTest(入门测)| exitTest(出门测)
|
||
///
|
||
/// 完整链路:登录成功/全局拦截 → 此页 → H5壳子 → reportComputation(入门测)
|
||
/// 或 switchTab 首页(出门测)
|
||
/// ============================================================
|
||
|
||
/// 本页用到的所有主题资源(对标 Vue themes/default.json → pages/reportPackage/studentInfo)
|
||
class _StudentTheme {
|
||
// ---- 图片(default.json images)----
|
||
// Flutter 端用 network 加载,需拼 CDN 域名;这里存相对路径,
|
||
// 使用时调 ImgUrl.build(path)
|
||
static const String bg =
|
||
'/StaticResource/2026-06-10/bg-report1781093056538.png';
|
||
static const String bubble =
|
||
'/StaticResource/2026-06-12/report_kuang1781230963682.png';
|
||
static const String welcome =
|
||
'/StaticResource/2026-06-10/Group3331781094167604.png';
|
||
static const String analytics =
|
||
'/StaticResource/2026-06-10/analytics1781081543912.png';
|
||
static const String logo = '/StaticResource/2026-06-10/logo1781083733383.png';
|
||
static const String aiAnalysisTitle =
|
||
'/StaticResource/2026-06-10/ai-analysis-title1781087439752.png';
|
||
static const String analysisTitle =
|
||
'/StaticResource/2026-06-22/analysis-title1782110217839.png';
|
||
static const String levelA =
|
||
'/StaticResource/2026-06-23/levelA1782178351618.png';
|
||
static const String levelB =
|
||
'/StaticResource/2026-06-23/levelB1782178351618.png';
|
||
static const String levelC =
|
||
'/StaticResource/2026-06-23/levelC1782178351618.png';
|
||
static const String verify =
|
||
'/StaticResource/2026-06-10/verify1781081993368.png';
|
||
static const String starIcon =
|
||
'/StaticResource/2026-06-10/star1781089096898.png';
|
||
|
||
// ---- 颜色(default.json colors)----
|
||
static const Color pageBg = Color(0xFFE5F6FE); // background
|
||
static const Color highlight = Color(0xFF3D7DF5);
|
||
|
||
// ---- 渐变(default.json css 里的 linear-gradient/rgba 转 Flutter)----
|
||
// item-bg: linear-gradient(180deg, #F5FBFF → #E5F4FF)
|
||
static const LinearGradient itemBg = LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Color(0xFFF5FBFF), Color(0xFFE5F4FF)],
|
||
);
|
||
|
||
// item-border-color: linear-gradient(180deg, #FFFFFF → #C9E9FF(50%) → #BAE2FF)
|
||
static const LinearGradient itemBorder = LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Color(0xFFFFFFFF), Color(0xFFC9E9FF), Color(0xFFBAE2FF)],
|
||
);
|
||
|
||
// analysis-view-bg: linear-gradient(180deg, #DAF3FF → #D4ECFF(52%) → #E5FAFB)
|
||
static const LinearGradient analysisViewBg = LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Color(0xFFDAF3FF), Color(0xFFD4ECFF), Color(0xFFE5FAFB)],
|
||
);
|
||
|
||
// text-line: 底部渐变条(大字号等级的下划线)
|
||
// linear-gradient(90deg, rgba(61,125,245,0) → rgba(29,156,255,0.2) → rgba(138,236,253,0.5))
|
||
static const LinearGradient textLine = LinearGradient(
|
||
colors: [Color(0x003D7DF5), Color(0x331D9CFF), Color(0x808AECFD)],
|
||
);
|
||
|
||
// button-text-bg: linear-gradient(0deg, #FFFFFF → #E0F2FF)
|
||
static const LinearGradient buttonTextBg = LinearGradient(
|
||
begin: Alignment.bottomCenter,
|
||
end: Alignment.topCenter,
|
||
colors: [Color(0xFFFFFFFF), Color(0xFFE0F2FF)],
|
||
);
|
||
|
||
// start-button-bg 径向渐变 + #3d7df5(简化:Flutter 径向渐变不太好还原多层 radial,
|
||
// 这里用线性渐变近似,视觉上接近即可)
|
||
static const LinearGradient startButtonBg = LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [Color(0xFF5B9BFF), Color(0xFF3D7DF5)],
|
||
);
|
||
}
|
||
|
||
/// 图片 URL 拼接工具(对标 Vue $img('/StaticResource/xxx'),复用项目 ImgUrl.build)
|
||
String _img(String path) => ImgUrl.build(path);
|
||
|
||
class StudentInfoPage extends ConsumerStatefulWidget {
|
||
const StudentInfoPage({super.key});
|
||
|
||
@override
|
||
ConsumerState<StudentInfoPage> createState() => _StudentInfoPageState();
|
||
}
|
||
|
||
class _StudentInfoPageState extends ConsumerState<StudentInfoPage> {
|
||
UserReportType _report = const UserReportType();
|
||
bool _loading = true;
|
||
String _reportType = 'placementTest';
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
final extra =
|
||
GoRouterState.of(context).extra as Map<String, dynamic>? ?? {};
|
||
_reportType = (extra['type'] as String?) ?? 'placementTest';
|
||
_loadReport();
|
||
});
|
||
}
|
||
|
||
Future<void> _loadReport() async {
|
||
setState(() => _loading = true);
|
||
try {
|
||
final isExit = _reportType == 'exitTest';
|
||
final report = isExit
|
||
? await ReportApi.instance.getCurrentAfterReport()
|
||
: await ReportApi.instance.getCurrentReport();
|
||
if (mounted) {
|
||
setState(() {
|
||
_report = report;
|
||
_loading = false;
|
||
});
|
||
}
|
||
} catch (e) {
|
||
talker.error('加载报告数据失败', e);
|
||
if (mounted) setState(() => _loading = false);
|
||
}
|
||
}
|
||
|
||
Future<void> _onStart() async {
|
||
if (_report.subjectReports.isEmpty) {
|
||
if (_reportType == 'exitTest') {
|
||
await ref.read(currentUserProvider.notifier).markShowAfterReportFalse();
|
||
} else {
|
||
await ref.read(currentUserProvider.notifier).markShowReportFalse();
|
||
}
|
||
if (mounted) context.go('/');
|
||
return;
|
||
}
|
||
context.push('/reportWebView', extra: {'type': _reportType});
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final user = ref.watch(currentUserProvider).value;
|
||
final userName = _report.userName.isNotEmpty
|
||
? _report.userName
|
||
: (user?.userName ?? '');
|
||
|
||
return Scaffold(
|
||
backgroundColor: _StudentTheme.pageBg,
|
||
body: _loading
|
||
? const Center(child: CircularProgressIndicator())
|
||
: SizedBox.expand(
|
||
child: Stack(
|
||
children: [
|
||
// ===== 背景层(Vue bg-wrapper)铺满 =====
|
||
Positioned.fill(
|
||
child: Image.network(
|
||
_img(_StudentTheme.bg),
|
||
fit: BoxFit.cover,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
),
|
||
// ===== 内容层(滚动,底部留出按钮高度)=====
|
||
SafeArea(
|
||
child: SingleChildScrollView(
|
||
padding: const EdgeInsets.only(bottom: 140),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
_buildHeader(userName),
|
||
const SizedBox(height: 130),
|
||
// _buildAnalysisLevel(),
|
||
// const SizedBox(height: 15),
|
||
_buildSubjectCard(),
|
||
const SizedBox(height: 14),
|
||
_buildLogo(),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
// ===== 底部按钮(fixed,无边距)=====
|
||
Positioned(
|
||
left: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
child: SafeArea(top: false, child: _buildBottomButton()),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// ===== 顶部问候区 + welcome 插画 =====
|
||
// Vue: bubble-wrap(背景图气泡 + userName)+ welcome-wrap(欢迎插画)
|
||
Widget _buildHeader(String userName) {
|
||
return Padding(
|
||
padding: const EdgeInsets.only(left: 14, right: 14, top: 12),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// bubble 气泡(用 DecoratedBox 做背景图)
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||
decoration: BoxDecoration(
|
||
// borderRadius: BorderRadius.circular(12),
|
||
image: DecorationImage(
|
||
image: NetworkImage(_img(_StudentTheme.bubble)),
|
||
fit: BoxFit.fill,
|
||
),
|
||
),
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
const Text(
|
||
'Hi . ',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w900,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
Text(
|
||
userName,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
fontWeight: FontWeight.w900,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(height: 10),
|
||
// welcome 插画(占位,没图时跳过)
|
||
Image.network(
|
||
_img(_StudentTheme.welcome),
|
||
width: 165,
|
||
fit: BoxFit.fitWidth,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
// ===== 当前档次等级卡片(Vue analysis-view,被注释掉的)=====
|
||
Widget _buildAnalysisLevel() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||
child: Container(
|
||
width: double.infinity,
|
||
// analysis-view-bg 渐变外层
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(16),
|
||
gradient: _StudentTheme.analysisViewBg,
|
||
boxShadow: const [
|
||
BoxShadow(
|
||
color: Color(0x1A3D7DF5),
|
||
blurRadius: 15,
|
||
offset: Offset(0, 5),
|
||
),
|
||
],
|
||
),
|
||
padding: const EdgeInsets.fromLTRB(16, 18, 16, 16),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// analysis-title 图
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.start,
|
||
children: [
|
||
Image.network(
|
||
_img(_StudentTheme.analysisTitle),
|
||
height: 18,
|
||
fit: BoxFit.fitHeight,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
// 内层白色卡片:大字号 level + 文字
|
||
Container(
|
||
width: double.infinity,
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 18),
|
||
child: Stack(
|
||
clipBehavior: Clip.none,
|
||
children: [
|
||
Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 大字号等级 + text-line 下划线
|
||
Stack(
|
||
alignment: Alignment.bottomLeft,
|
||
children: [
|
||
Text(
|
||
_report.userLevel.isNotEmpty
|
||
? _report.userLevel
|
||
: '--',
|
||
style: const TextStyle(
|
||
fontSize: 30,
|
||
fontWeight: FontWeight.w600,
|
||
color: Color(0xFF1D2B47),
|
||
height: 1.0,
|
||
),
|
||
),
|
||
// 底部渐变条
|
||
Positioned(
|
||
left: 0,
|
||
right: 0,
|
||
bottom: -2,
|
||
child: Container(
|
||
height: 4,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(2),
|
||
gradient: _StudentTheme.textLine,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 12),
|
||
// ai-analysis-title 图("AI 智能诊断评定")
|
||
Image.network(
|
||
_img(_StudentTheme.aiAnalysisTitle),
|
||
height: 14,
|
||
fit: BoxFit.fitHeight,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
],
|
||
),
|
||
// 右上角 level${level} 图标
|
||
Positioned(
|
||
right: -18,
|
||
top: -44,
|
||
child: _LevelIcon(level: _report.userLevel),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// ===== 诊断科目分析卡片 =====
|
||
Widget _buildSubjectCard() {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||
child: Container(
|
||
width: double.infinity,
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(16),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// header: analytics 图标 + title + 数据已校准
|
||
Row(
|
||
children: [
|
||
Image.network(
|
||
_img(_StudentTheme.analytics),
|
||
width: 18,
|
||
height: 18,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
const SizedBox(width: 6),
|
||
const Text(
|
||
'诊断科目分析',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.w600,
|
||
color: Color(0xFF1D2B47),
|
||
),
|
||
),
|
||
const Spacer(),
|
||
Image.network(
|
||
_img(_StudentTheme.verify),
|
||
width: 16,
|
||
height: 16,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
const SizedBox(width: 3),
|
||
const Text(
|
||
'数据已校准',
|
||
style: TextStyle(fontSize: 11, color: Color(0xFF00CC96)),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: 16),
|
||
// subject list
|
||
_report.subjectReports.isEmpty
|
||
? const Center(
|
||
child: Text(
|
||
'暂无数据',
|
||
style: TextStyle(color: Color(0xFF687FAD)),
|
||
),
|
||
)
|
||
: Wrap(
|
||
spacing: 10,
|
||
runSpacing: 10,
|
||
children: _report.subjectReports
|
||
.map((s) => _SubjectCard(subject: s))
|
||
.toList(),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// ===== logo =====
|
||
Widget _buildLogo() {
|
||
return Center(
|
||
child: Image.network(
|
||
_img(_StudentTheme.logo),
|
||
width: 65, // 130rpx ≈ 65dp
|
||
fit: BoxFit.fitWidth,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
);
|
||
}
|
||
|
||
// ===== 底部按钮区(fixed 底部,无边距,圆角顶对齐屏幕)=====
|
||
Widget _buildBottomButton() {
|
||
return Container(
|
||
width: double.infinity,
|
||
decoration: const BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||
),
|
||
padding: const EdgeInsets.fromLTRB(16, 14, 16, 14),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
RichText(
|
||
text: TextSpan(
|
||
style: const TextStyle(
|
||
fontSize: 11,
|
||
height: 1.6,
|
||
color: Color(0xFF495A7A),
|
||
),
|
||
children: [
|
||
WidgetSpan(
|
||
alignment: PlaceholderAlignment.top,
|
||
child: Padding(
|
||
padding: const EdgeInsets.only(right: 6),
|
||
child: Image.network(
|
||
_img(_StudentTheme.aiAnalysisTitle),
|
||
width: 95,
|
||
fit: BoxFit.fitWidth,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
),
|
||
),
|
||
const TextSpan(text: '本报告基于'),
|
||
const TextSpan(
|
||
text: '15年高考真题精研',
|
||
style: TextStyle(color: Color(0xFF3D7DF5)),
|
||
),
|
||
const TextSpan(text: '、'),
|
||
const TextSpan(
|
||
text: '50万道名校模拟题库',
|
||
style: TextStyle(color: Color(0xFF3D7DF5)),
|
||
),
|
||
const TextSpan(text: '及'),
|
||
const TextSpan(
|
||
text: '3,682个',
|
||
style: TextStyle(color: Color(0xFF3D7DF5)),
|
||
),
|
||
const TextSpan(text: '底层知识点拓扑结构,通过爱学智算引擎进行深度解析。'),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(height: 10),
|
||
// start-button
|
||
SizedBox(
|
||
width: double.infinity,
|
||
height: 44,
|
||
child: ElevatedButton(
|
||
onPressed: _onStart,
|
||
style: ElevatedButton.styleFrom(
|
||
padding: EdgeInsets.zero,
|
||
foregroundColor: Colors.white,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(22),
|
||
),
|
||
elevation: 0,
|
||
backgroundColor: Colors.transparent,
|
||
),
|
||
child: Ink(
|
||
decoration: BoxDecoration(
|
||
gradient: _StudentTheme.startButtonBg,
|
||
borderRadius: BorderRadius.circular(22),
|
||
border: Border.all(color: const Color(0x4DFFFFFF), width: 2),
|
||
),
|
||
child: Container(
|
||
alignment: Alignment.center,
|
||
child: Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Image.network(
|
||
_img(_StudentTheme.starIcon),
|
||
width: 24,
|
||
height: 24,
|
||
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
|
||
),
|
||
const SizedBox(width: 6),
|
||
const Text(
|
||
'开启智能诊断报告',
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.w600,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 科目卡片 —— item-bg 渐变 + item-border-color 渐变边框
|
||
class _SubjectCard extends StatelessWidget {
|
||
const _SubjectCard({required this.subject});
|
||
final SubjectReportType subject;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Container(
|
||
width: 72, // 144rpx
|
||
height: 88, // 176rpx
|
||
// 渐变边框用双层 Container 实现
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(12),
|
||
gradient: _StudentTheme.itemBorder,
|
||
),
|
||
padding: const EdgeInsets.all(1), // 边框厚度
|
||
child: Container(
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(11),
|
||
gradient: _StudentTheme.itemBg,
|
||
),
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
ClipRRect(
|
||
borderRadius: BorderRadius.circular(8),
|
||
child: subject.icon.isNotEmpty
|
||
? Image.network(
|
||
subject.icon,
|
||
width: 40,
|
||
height: 40,
|
||
fit: BoxFit.cover,
|
||
errorBuilder: (_, __, ___) => const Icon(
|
||
Icons.menu_book,
|
||
size: 40,
|
||
color: Color(0xFF3D7DF5),
|
||
),
|
||
)
|
||
: const Icon(
|
||
Icons.menu_book,
|
||
size: 40,
|
||
color: Color(0xFF3D7DF5),
|
||
),
|
||
),
|
||
const SizedBox(height: 8),
|
||
Text(
|
||
subject.subjectName,
|
||
style: const TextStyle(
|
||
fontSize: 13,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFF1D2B47),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 右上角等级图标:levelA/B/C 图片,加载失败时用占位
|
||
class _LevelIcon extends StatelessWidget {
|
||
const _LevelIcon({required this.level});
|
||
final String level;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 大写化匹配 default.json 的 key
|
||
final key = level.toUpperCase();
|
||
final iconPath = switch (key) {
|
||
'A' => _StudentTheme.levelA,
|
||
'B' => _StudentTheme.levelB,
|
||
'C' => _StudentTheme.levelC,
|
||
_ => _StudentTheme.levelB, // 兜底
|
||
};
|
||
return Image.network(
|
||
_img(iconPath),
|
||
width: 110,
|
||
height: 110,
|
||
fit: BoxFit.contain,
|
||
errorBuilder: (_, __, ___) => Container(
|
||
width: 110,
|
||
height: 110,
|
||
alignment: Alignment.center,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(22),
|
||
gradient: _StudentTheme.startButtonBg,
|
||
),
|
||
child: Text(
|
||
key,
|
||
style: const TextStyle(
|
||
fontSize: 48,
|
||
fontWeight: FontWeight.w700,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|