diff --git a/lib/ui/login_screen.dart b/lib/ui/login_screen.dart index 6e08425..3897930 100644 --- a/lib/ui/login_screen.dart +++ b/lib/ui/login_screen.dart @@ -3,9 +3,7 @@ import 'package:flutter/material.dart'; import '../config.dart' show schoolName; import '../function/login_controller.dart'; -import 'student_dashboard.dart'; -import 'teacher_dashboard.dart'; -import 'admin_dashboard.dart'; +import 'main_dashboard.dart'; import 'teacher_register_screen.dart'; // ------------------------------------------------------------- @@ -50,9 +48,10 @@ class _LoginScreenState extends State { Navigator.pushReplacement( context, MaterialPageRoute( - builder: (context) => StudentDashboard( - studentId: result.studentId!, - studentName: result.name!, + builder: (context) => MainDashboard( + userId: result.studentId!, + userName: result.name!, + role: 'student', isDeviceMatched: true, ), ), @@ -154,9 +153,9 @@ class _LoginScreenState extends State { if (success) { Navigator.pop(dialogContext); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(message)), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(message))); _navigateBasedOnRole( role, studentId, @@ -164,9 +163,9 @@ class _LoginScreenState extends State { isDeviceMatched, ); } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(message)), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text(message))); } }, child: const Text('변경하고 시작하기'), @@ -186,31 +185,17 @@ class _LoginScreenState extends State { String name, bool isDeviceMatched, ) { - if (role == 'teacher') { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => - TeacherDashboard(teacherId: studentId, teacherName: name), + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => MainDashboard( + userId: studentId, + userName: name, + role: role, + isDeviceMatched: isDeviceMatched, ), - ); - } else if (role == 'admin') { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => const AdminDashboard()), - ); - } else { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => StudentDashboard( - studentId: studentId, - studentName: name, - isDeviceMatched: isDeviceMatched, - ), - ), - ); - } + ), + ); } void _showErrorDialog(String message) { @@ -403,7 +388,7 @@ class _LoginScreenState extends State { onPressed: () => _showLegacyPasswordDialog( '선생님', '1234', - const TeacherDashboard(), + const MainDashboard(role: 'teacher'), ), ), TextButton.icon( @@ -419,7 +404,7 @@ class _LoginScreenState extends State { onPressed: () => _showLegacyPasswordDialog( '시스템 관리자', '4936', - const AdminDashboard(), + const MainDashboard(role: 'admin'), ), ), ], diff --git a/lib/ui/student_dashboard.dart b/lib/ui/main_dashboard.dart similarity index 69% rename from lib/ui/student_dashboard.dart rename to lib/ui/main_dashboard.dart index b2a44af..c69de2b 100644 --- a/lib/ui/student_dashboard.dart +++ b/lib/ui/main_dashboard.dart @@ -1,59 +1,68 @@ -// 🎓 학생 대시보드 (UI 전용). NFC 주머니 체크인 진입, 실시간 학교 상황 안내, -// 개발자 마스터 계정 전용 관리 메뉴(현황/DB제어/계정관리/삭제) 카드를 그린다. -// 계정 삭제 서버 통신은 lib/function/student_dashboard_controller.dart가 담당한다. +// 🏠 통합 대시보드 (UI 전용). 학생/교사/관리자가 전부 이 화면 하나를 공유하고, +// 권한(role)에 따라 배너 색상/문구와 보이는 타일만 달라진다. +// 레이아웃(그리드, 카드 디자인)을 한 곳에서 고치면 모든 역할에 동시에 적용된다. +// 계정 강제 삭제 서버 통신은 lib/function/student_dashboard_controller.dart가 담당한다. import 'package:flutter/material.dart'; import '../function/student_dashboard_controller.dart'; -import 'device_checkout_request_screen.dart'; -import 'nfc_poccket_checkin_screen.dart'; -import 'login_screen.dart'; -import 'teacher_attendance_page.dart'; import 'admin_dashboard.dart'; -import 'teacher_student_management_page.dart'; +import 'device_checkout_ledger_page.dart'; +import 'device_checkout_request_screen.dart'; +import 'login_screen.dart'; +import 'nfc_poccket_checkin_screen.dart'; import 'nfc_tag_writer_screen.dart'; +import 'teacher_attendance_page.dart'; +import 'teacher_student_management_page.dart'; -// ------------------------------------------------------------- -// 2. 학생 대시보드 (StudentDashboard) -// ------------------------------------------------------------- +class MainDashboard extends StatefulWidget { + final String? userId; + final String? userName; + final String role; // 'student' | 'teacher' | 'admin' + final bool isDeviceMatched; -class StudentDashboard extends StatefulWidget { - final String studentId; - final String studentName; - final bool isDeviceMatched; // 🆕 [변경] 기기 UUID가 매칭되었는지 확인하는 변수 추가 - - const StudentDashboard({ + const MainDashboard({ super.key, - required this.studentId, - required this.studentName, - required this.isDeviceMatched, // 🆕 [변경] 필수 매개변수로 등록 + this.userId, + this.userName, + required this.role, + this.isDeviceMatched = true, }); @override - State createState() => _StudentDashboardState(); + State createState() => _MainDashboardState(); } -class _StudentDashboardState extends State { +class _MainDashboardState extends State { final StudentDashboardController _controller = StudentDashboardController(); + // 🔑 [권한 판정] 2061은 role이 'student'로 저장돼 있지만 실질적으로 최고 권한을 가진다. + bool get _isDeveloper => widget.userId == '2061'; + bool get _isAdmin => widget.role == 'admin' || _isDeveloper; + bool get _isTeacherOrAbove => widget.role == 'teacher' || _isAdmin; + bool get _isStudent => widget.role == 'student'; + + String get _displayId => + widget.userId ?? (widget.role == 'admin' ? '관리자' : '간편인증'); + String get _displayName => + widget.userName ?? (widget.role == 'admin' ? '시스템 관리자' : '간편인증 선생'); + @override void dispose() { _controller.dispose(); super.dispose(); } - // 1️⃣ NFC 태그 카드 → 실제 NFC 주머니 체크인 화면으로 이동 void _openPocketCheckIn(BuildContext context) { Navigator.push( context, MaterialPageRoute( builder: (context) => NfcPocketCheckInScreen( - studentId: widget.studentId, - studentName: widget.studentName, + studentId: _displayId, + studentName: _displayName, ), ), ); } - // 2️⃣ [기존 동일] 마스터 계정 전용 회원 삭제 버튼 동작 Future _deleteUser(String userId) async { final (_, message) = await _controller.deleteUser(userId); if (!mounted) return; @@ -62,7 +71,6 @@ class _StudentDashboardState extends State { ).showSnackBar(SnackBar(content: Text(message))); } - // 3️⃣ [기존 동일] 학번/교직원 번호를 입력받는 모던 팝업창(Dialog) void _showDeleteUserDialog(BuildContext context) { final TextEditingController idController = TextEditingController(); @@ -152,27 +160,57 @@ class _StudentDashboardState extends State { ); } + // 🎨 [역할별 테마] 배너 색상/제목/문구만 역할에 따라 다르게. 레이아웃 자체는 공통. + ({String title, String subtitle, Color color, IconData icon}) _theme() { + if (_isDeveloper) { + return ( + title: '👑 MASTER CONTROL', + subtitle: '최고 관리 권한 활성화됨', + color: Colors.deepPurple[700]!, + icon: Icons.admin_panel_settings_rounded, + ); + } else if (widget.role == 'admin') { + return ( + title: '🛠️ ADMIN CONTROL', + subtitle: '시스템 관리자 권한 활성화됨', + color: Colors.deepPurple[700]!, + icon: Icons.admin_panel_settings_rounded, + ); + } else if (widget.role == 'teacher') { + return ( + title: '👨‍🏫 TEACHER PORTAL', + subtitle: '교직원 번호: $_displayId | 교사 권한 활성화됨', + color: Colors.green[700]!, + icon: Icons.admin_panel_settings_rounded, + ); + } + return ( + title: '🎓 STUDENT PORTAL', + subtitle: '학번: $_displayId | 인증 완료', + color: Colors.indigo[700]!, + icon: Icons.school_rounded, + ); + } + @override Widget build(BuildContext context) { return ListenableBuilder( listenable: _controller, builder: (context, _) { - final bool isDeveloper = widget.studentId == "2061"; + final theme = _theme(); return Scaffold( backgroundColor: Colors.grey[100], appBar: AppBar( title: Text( - isDeveloper ? '👑 MASTER CONTROL' : '🎓 STUDENT PORTAL', + theme.title, style: const TextStyle( fontWeight: FontWeight.bold, letterSpacing: 1.2, ), ), centerTitle: true, - backgroundColor: isDeveloper - ? Colors.deepPurple[700] - : Colors.indigo[700], + backgroundColor: theme.color, foregroundColor: Colors.white, elevation: 0, actions: [ @@ -196,9 +234,7 @@ class _StudentDashboardState extends State { vertical: 28, ), decoration: BoxDecoration( - color: isDeveloper - ? Colors.deepPurple[700] - : Colors.indigo[700], + color: theme.color, borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(32), bottomRight: Radius.circular(32), @@ -214,18 +250,8 @@ class _StudentDashboardState extends State { ), child: CircleAvatar( radius: 30, - backgroundColor: isDeveloper - ? Colors.deepPurple[50] - : Colors.indigo[50], - child: Icon( - isDeveloper - ? Icons.admin_panel_settings_rounded - : Icons.school_rounded, - size: 32, - color: isDeveloper - ? Colors.deepPurple - : Colors.indigo, - ), + backgroundColor: theme.color.withValues(alpha: 0.08), + child: Icon(theme.icon, size: 32, color: theme.color), ), ), const SizedBox(width: 18), @@ -233,7 +259,7 @@ class _StudentDashboardState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '${widget.studentName} 님', + '$_displayName 님', style: const TextStyle( fontSize: 22, fontWeight: FontWeight.bold, @@ -242,9 +268,7 @@ class _StudentDashboardState extends State { ), const SizedBox(height: 4), Text( - isDeveloper - ? '최고 관리 권한 활성화됨' - : '학번: ${widget.studentId} | 인증 완료', + theme.subtitle, style: TextStyle( color: Colors.white.withValues(alpha: 0.8), fontSize: 14, @@ -264,9 +288,7 @@ class _StudentDashboardState extends State { width: 4, height: 18, decoration: BoxDecoration( - color: isDeveloper - ? Colors.deepPurple - : Colors.indigo, + color: theme.color, borderRadius: BorderRadius.circular(2), ), ), @@ -302,8 +324,8 @@ class _StudentDashboardState extends State { mainAxisSpacing: 16, childAspectRatio: 0.95, children: [ - // 🆕 [변경 지점] 기기가 일치하면 정상 활성화, 일치하지 않으면 자물쇠Lock 처리 - if (isDeveloper || widget.studentId != "2061") + // 👨‍🎓 학생 전용 ----------------------------------- + if (_isStudent) _buildModernCard( icon: widget.isDeviceMatched ? Icons.contactless_rounded @@ -331,46 +353,47 @@ class _StudentDashboardState extends State { isActionButton: widget.isDeviceMatched, isLoading: _controller.isLoading, ), - - // 🆕 [추가 지점] 기기 일치 여부 상관없이 태블릿에서도 누구나 확인 가능한 학교 상황판 카드 - _buildModernCard( - icon: Icons.fastfood_rounded, - title: '실시간 학교 상황', - subtitle: '급식실 줄 & 매점 재고 확인', - color: Colors.orange[700]!, - onTap: () { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('🍔 실시간 학교 상황 페이지로 이동합니다.'), - ), - ); - }, - ), - - // 📱 패드는 NFC가 안 되니, 시간+목적 적어서 신청 → 선생님 승인받는 대체 절차 - _buildModernCard( - icon: Icons.tablet_mac_rounded, - title: '스마트기기 반출', - subtitle: '패드 사용 신청 (시간/목적)', - color: Colors.teal, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - DeviceCheckoutRequestScreen( - studentId: widget.studentId, - studentName: widget.studentName, + if (_isStudent) + _buildModernCard( + icon: Icons.fastfood_rounded, + title: '실시간 학교 상황', + subtitle: '급식실 줄 & 매점 재고 확인', + color: Colors.orange[700]!, + onTap: () { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + '🍔 실시간 학교 상황 페이지로 이동합니다.', ), + ), + ); + }, + ), + if (_isStudent) + _buildModernCard( + icon: Icons.tablet_mac_rounded, + title: '스마트기기 반출', + subtitle: '패드 사용 신청 (시간/목적)', + color: Colors.teal, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + DeviceCheckoutRequestScreen( + studentId: _displayId, + studentName: _displayName, + ), + ), ), ), - ), - if (isDeveloper) + // 👨‍🏫 교사 이상 ----------------------------------- + if (_isTeacherOrAbove) _buildModernCard( - icon: Icons.monitor_heart_rounded, - title: '실시간 현황', - subtitle: '교사용 수거 모니터링', - color: Colors.teal, + icon: Icons.assignment_turned_in_rounded, + title: '실시간 출석 확인', + subtitle: '학생 제출 로그 모니터링', + color: Colors.blue, onTap: () => Navigator.push( context, MaterialPageRoute( @@ -379,7 +402,40 @@ class _StudentDashboardState extends State { ), ), ), - if (isDeveloper) + if (_isTeacherOrAbove) + _buildModernCard( + icon: Icons.manage_accounts_rounded, + title: '학생 계정 관리', + subtitle: '계정 추가 및 강제 리셋', + color: Colors.orange, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const TeacherStudentManagementPage(), + ), + ), + ), + if (_isTeacherOrAbove) + _buildModernCard( + icon: Icons.tablet_mac_rounded, + title: '스마트기기 반출 대장', + subtitle: '패드 반출 신청 승인/거절', + color: Colors.teal, + onTap: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + DeviceCheckoutLedgerPage( + teacherId: widget.userId, + teacherName: widget.userName, + ), + ), + ), + ), + + // 🛠️ 관리자 전용 ----------------------------------- + if (_isAdmin) _buildModernCard( icon: Icons.terminal_rounded, title: '서버 DB 제어', @@ -393,21 +449,7 @@ class _StudentDashboardState extends State { ), ), ), - if (isDeveloper) - _buildModernCard( - icon: Icons.add_moderator_rounded, - title: '학생 계정 관리', - subtitle: 'UUID 리셋 및 승인', - color: Colors.purple, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const TeacherStudentManagementPage(), - ), - ), - ), - if (isDeveloper) + if (_isAdmin) _buildModernCard( icon: Icons.delete_sweep_rounded, title: '계정 강제 삭제', @@ -415,7 +457,7 @@ class _StudentDashboardState extends State { color: Colors.red[600]!, onTap: () => _showDeleteUserDialog(context), ), - if (isDeveloper) + if (_isAdmin) _buildModernCard( icon: Icons.edit_note_rounded, title: 'NFC 태그 쓰기', @@ -445,7 +487,7 @@ class _StudentDashboardState extends State { ); } - // 5️⃣ [카드 디자인 위젯] - 기존 형태 완전 보존 + // 💎 [카드 디자인 위젯] 학생/교사 대시보드에서 쓰던 것과 동일 — 이제 한 곳에만 존재. Widget _buildModernCard({ required IconData icon, required String title, @@ -489,12 +531,14 @@ class _StudentDashboardState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black87, + Expanded( + child: Text( + title, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), ), ), if (isLoading) diff --git a/lib/ui/teacher_dashboard.dart b/lib/ui/teacher_dashboard.dart deleted file mode 100644 index 9eef3ce..0000000 --- a/lib/ui/teacher_dashboard.dart +++ /dev/null @@ -1,295 +0,0 @@ -// 👨‍🏫 교사 대시보드. 실시간 출석 확인, 학생 계정 관리 화면으로 가는 메뉴만 담당한다. -import 'package:flutter/material.dart'; -import 'device_checkout_ledger_page.dart'; -import 'login_screen.dart'; -import 'teacher_attendance_page.dart'; -import 'teacher_student_management_page.dart'; - -// ========================================== -// 👨‍🏫 4. 선생님 대시보드 (기존 3초 타이머 완벽 유지) -// ========================================== -class TeacherDashboard extends StatefulWidget { - final String? teacherId; - final String? teacherName; - - const TeacherDashboard({super.key, this.teacherId, this.teacherName}); - - @override - State createState() => _TeacherDashboardState(); -} - -class _TeacherDashboardState extends State { - // 🧼 [수정] 사용하지 않던 _isLoading 변수를 삭제하여 경고를 완벽히 해결했습니다! - - @override - Widget build(BuildContext context) { - // 다른 화면에서 null이 넘어왔을 때를 대비한 안전망 방탄 코드 - final String displayName = widget.teacherName ?? "간편인증 선생"; - final String displayId = widget.teacherId ?? "간편인증"; - - return Scaffold( - backgroundColor: Colors.grey[100], - appBar: AppBar( - title: const Text( - '👨‍🏫 TEACHER PORTAL', - style: TextStyle(fontWeight: FontWeight.bold, letterSpacing: 1.2), - ), - centerTitle: true, - backgroundColor: Colors.green[700], // 교사 전용 그린 테마 컬러 - foregroundColor: Colors.white, - elevation: 0, - actions: [ - IconButton( - icon: const Icon(Icons.logout_rounded), - onPressed: () => Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => const LoginScreen()), - ), - ), - ], - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 💳 [상단 배너 가이드] StudentDashboard와 100% 일치하는 프로필 카드 레이아웃 - Container( - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 28), - decoration: BoxDecoration( - color: Colors.green[700], - borderRadius: const BorderRadius.only( - bottomLeft: Radius.circular(32), - bottomRight: Radius.circular(32), - ), - ), - child: Row( - children: [ - Container( - padding: const EdgeInsets.all(4), - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - child: CircleAvatar( - radius: 30, - backgroundColor: Colors.green[50], - child: Icon( - Icons.admin_panel_settings_rounded, - size: 32, - color: Colors.green[700], - ), - ), - ), - const SizedBox(width: 18), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '$displayName 님', - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - const SizedBox(height: 4), - Text( - '교직원 번호: $displayId | 교사 권한 활성화됨', - style: TextStyle( - color: Colors.white.withValues(alpha: 0.8), - fontSize: 14, - ), - ), - ], - ), - ], - ), - ), - const SizedBox(height: 32), - - // 🏷️ [세로 바 타이틀 인디케이터] 구조 일치화 - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Row( - children: [ - Container( - width: 4, - height: 18, - decoration: BoxDecoration( - color: Colors.green[700], - borderRadius: BorderRadius.circular(2), - ), - ), - const SizedBox(width: 10), - const Text( - '스마트 교사용 관리 메뉴', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), - ), - const SizedBox(height: 16), - - // 📊 [그리드 레이아웃 메뉴] 시후의 카드 컴포넌트 스타일 적용 - LayoutBuilder( - builder: (context, constraints) { - // 🖥️ 웹(넓은 화면)은 한 줄에 5개씩, 폰(좁은 화면)은 기존 2개 그대로. - final bool isWide = constraints.maxWidth >= 800; - return Center( - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: isWide ? 1300 : 700), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: GridView.count( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - crossAxisCount: isWide ? 5 : 2, - crossAxisSpacing: 16, - mainAxisSpacing: 16, - childAspectRatio: 0.95, - children: [ - _buildModernCard( - icon: Icons.assignment_turned_in_rounded, - title: '실시간 출석 확인', - subtitle: '학생 제출 로그 모니터링', - color: Colors.blue, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const TeacherAttendancePage(), - ), - ), - ), - _buildModernCard( - icon: Icons.manage_accounts_rounded, - title: '학생 계정 관리', - subtitle: '계정 추가 및 강제 리셋', - color: Colors.orange, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - const TeacherStudentManagementPage(), - ), - ), - ), - _buildModernCard( - icon: Icons.tablet_mac_rounded, - title: '스마트기기 반출 대장', - subtitle: '패드 반출 신청 승인/거절', - color: Colors.teal, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DeviceCheckoutLedgerPage( - teacherId: widget.teacherId, - teacherName: widget.teacherName, - ), - ), - ), - ), - ], - ), - ), - ), - ); - }, - ), - const SizedBox(height: 32), - ], - ), - ), - ); - } - - // 💎 [시후 대시보드 전용 카드 위젯 이식 완료] - Widget _buildModernCard({ - required IconData icon, - required String title, - required String subtitle, - required Color color, - required VoidCallback onTap, - bool isActionButton = false, - bool isLoading = false, - }) { - return InkWell( - onTap: isLoading ? null : onTap, - borderRadius: BorderRadius.circular(24), - child: Ink( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(24), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.04), - blurRadius: 16, - offset: const Offset(0, 4), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: color.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(16), - ), - child: Icon(icon, color: color, size: 28), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - title, - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ), - if (isLoading) - const SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator(strokeWidth: 2), - ) - else if (isActionButton) - Icon( - Icons.touch_app_rounded, - size: 16, - color: color.withValues(alpha: 0.5), - ), - ], - ), - const SizedBox(height: 4), - Text( - subtitle, - style: TextStyle( - fontSize: 12, - color: Colors.grey[500], - height: 1.2, - ), - ), - ], - ), - ], - ), - ), - ); - } -}