학생 계정 관리 화면 레이아웃 재구성 (정사각형 2칸 + 타일형 학생 목록)
웹 넓은 화면에서 계정 추가/엑셀 일괄 등록을 정사각형 2칸으로 나란히 배치(왼쪽 계정 추가, 오른쪽 엑셀 등록)하고, 그 아래 학생 목록을 두 칸 합친 폭에 맞춘 타일 그리드로 변경. 각 타일은 아이콘/ 이름/상태/액션 버튼을 모두 중앙 정렬. 폰 화면은 기존처럼 세로 스택 유지. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -372,59 +372,30 @@ class _TeacherStudentManagementPageState
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
// 🏷️ 섹션 제목 (색 인디케이터 바 + 텍스트).
|
||||||
Widget build(BuildContext context) {
|
Widget _sectionTitle(String text, {Color color = Colors.orange}) {
|
||||||
return ListenableBuilder(
|
return Row(
|
||||||
listenable: _controller,
|
|
||||||
builder: (context, _) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.grey[100],
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text(
|
|
||||||
'⚙️ 학생 통합 관리 센터',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.orange,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.all(24.0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// 🏷️ 인디케이터 바 1 (추가 메뉴)
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 4,
|
width: 4,
|
||||||
height: 16,
|
height: 16,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.orange,
|
color: color,
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
const Text(
|
Text(
|
||||||
'신규 학생 계정 추가',
|
text,
|
||||||
style: TextStyle(
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
const SizedBox(height: 16),
|
}
|
||||||
|
|
||||||
// 📝 학생 추가 컨테이너 폼
|
// 📝 [정사각형 박스 1] 신규 학생 계정 추가 폼.
|
||||||
// 🖥️ 웹 넓은 화면에서 폼이 지나치게 길어지지 않도록 현재 폭의 2/3로 제한한다.
|
Widget _buildAddAccountBox() {
|
||||||
LayoutBuilder(
|
return Container(
|
||||||
builder: (context, constraints) {
|
|
||||||
return ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
maxWidth: constraints.maxWidth * 2 / 3,
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@@ -436,7 +407,9 @@ class _TeacherStudentManagementPageState
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
controller: _addIdController,
|
controller: _addIdController,
|
||||||
@@ -475,17 +448,14 @@ class _TeacherStudentManagementPageState
|
|||||||
DropdownMenuItem(value: 2, child: Text('2학년')),
|
DropdownMenuItem(value: 2, child: Text('2학년')),
|
||||||
DropdownMenuItem(value: 3, child: Text('3학년')),
|
DropdownMenuItem(value: 3, child: Text('3학년')),
|
||||||
],
|
],
|
||||||
onChanged: (value) =>
|
onChanged: (value) => setState(() => _selectedGrade = value),
|
||||||
setState(() => _selectedGrade = value),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 50,
|
height: 50,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: _controller.isWorking
|
onPressed: _controller.isWorking ? null : _addStudentAccount,
|
||||||
? null
|
|
||||||
: _addStudentAccount,
|
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: Colors.orange,
|
backgroundColor: Colors.orange,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
@@ -494,9 +464,7 @@ class _TeacherStudentManagementPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: _controller.isWorking
|
child: _controller.isWorking
|
||||||
? const CircularProgressIndicator(
|
? const CircularProgressIndicator(color: Colors.white)
|
||||||
color: Colors.white,
|
|
||||||
)
|
|
||||||
: const Text(
|
: const Text(
|
||||||
'학생 등록 완료',
|
'학생 등록 완료',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -510,34 +478,11 @@ class _TeacherStudentManagementPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
),
|
|
||||||
const SizedBox(height: 36),
|
|
||||||
|
|
||||||
// 🏷️ 인디케이터 바 (엑셀 일괄 등록)
|
// 📤 [정사각형 박스 2] 엑셀 일괄 등록 드롭존.
|
||||||
Row(
|
Widget _buildExcelBox() {
|
||||||
children: [
|
return DropTarget(
|
||||||
Container(
|
|
||||||
width: 4,
|
|
||||||
height: 16,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.orange,
|
|
||||||
borderRadius: BorderRadius.circular(2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Text(
|
|
||||||
'엑셀로 한번에 추가 (신입생 등)',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
DropTarget(
|
|
||||||
onDragEntered: (_) => setState(() => _isDragging = true),
|
onDragEntered: (_) => setState(() => _isDragging = true),
|
||||||
onDragExited: (_) => setState(() => _isDragging = false),
|
onDragExited: (_) => setState(() => _isDragging = false),
|
||||||
onDragDone: (details) async {
|
onDragDone: (details) async {
|
||||||
@@ -552,71 +497,227 @@ class _TeacherStudentManagementPageState
|
|||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 28),
|
height: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: _isDragging ? Colors.orange[50] : Colors.white,
|
color: _isDragging ? Colors.orange[50] : Colors.white,
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: _isDragging
|
color: _isDragging ? Colors.orange : Colors.grey.shade300,
|
||||||
? Colors.orange
|
|
||||||
: Colors.grey.shade300,
|
|
||||||
width: _isDragging ? 2 : 1,
|
width: _isDragging ? 2 : 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.upload_file_rounded,
|
Icons.upload_file_rounded,
|
||||||
size: 40,
|
size: 40,
|
||||||
color: _isDragging
|
color: _isDragging ? Colors.orange : Colors.grey[400],
|
||||||
? Colors.orange
|
|
||||||
: Colors.grey[400],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
_isDragging ? '여기에 놓으세요' : '엑셀 파일을 드래그하거나 눌러서 선택',
|
_isDragging ? '여기에 놓으세요' : '엑셀 파일을 드래그하거나 눌러서 선택',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: _isDragging
|
color: _isDragging ? Colors.orange[800] : Colors.black87,
|
||||||
? Colors.orange[800]
|
|
||||||
: Colors.black87,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'.xlsx · 1행은 머리글, A열=학번 B열=이름 C열=학년(선택)',
|
'.xlsx · 1행은 머리글, A열=학번 B열=이름 C열=학년(선택)',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 12, color: Colors.grey[500]),
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey[500],
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🧑🎓 [학생 목록 타일] 아이콘/이름/상태/액션을 모두 중앙 정렬한 정사각 타일.
|
||||||
|
Widget _buildStudentTile(Map<String, dynamic> student) {
|
||||||
|
final String studentId = student['id'].toString();
|
||||||
|
final String studentName = student['name'].toString();
|
||||||
|
final int? grade = student['grade'] as int?;
|
||||||
|
final bool needsReset = student['device'] == '초기화 필요';
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.04),
|
||||||
|
blurRadius: 12,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
CircleAvatar(
|
||||||
|
radius: 24,
|
||||||
|
backgroundColor: needsReset ? Colors.red[50] : Colors.blue[50],
|
||||||
|
child: Icon(
|
||||||
|
needsReset ? Icons.lock_reset : Icons.person,
|
||||||
|
color: needsReset ? Colors.red : Colors.blue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'$studentName ($studentId)',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
needsReset ? '초기화 대기중' : '정상 등록',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: needsReset ? Colors.red : Colors.grey,
|
||||||
|
fontWeight: needsReset ? FontWeight.bold : FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 2,
|
||||||
|
runSpacing: 2,
|
||||||
|
children: [
|
||||||
|
Builder(
|
||||||
|
builder: (chipContext) => ActionChip(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
label: Text(
|
||||||
|
grade != null ? '$grade학년' : '미배정',
|
||||||
|
style: const TextStyle(fontSize: 11),
|
||||||
|
),
|
||||||
|
onPressed: () => _showGradeMenu(
|
||||||
|
chipContext,
|
||||||
|
studentId,
|
||||||
|
studentName,
|
||||||
|
grade,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.lock_reset,
|
||||||
|
color: Colors.purple,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
tooltip: '기기 리셋',
|
||||||
|
onPressed: () => _confirmResetDevice(studentId, studentName),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete_forever_rounded,
|
||||||
|
color: Colors.redAccent,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
tooltip: '계정 영구 삭제',
|
||||||
|
onPressed: () => _confirmDeleteStudent(studentId, studentName),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListenableBuilder(
|
||||||
|
listenable: _controller,
|
||||||
|
builder: (context, _) {
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: Colors.grey[100],
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text(
|
||||||
|
'⚙️ 학생 통합 관리 센터',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.all(24.0),
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
// 🖥️ 웹(넓은 화면)은 계정 추가/엑셀 등록을 정사각형 2칸으로 나란히, 폰은 기존처럼 세로로 쌓는다.
|
||||||
|
final bool isWide = constraints.maxWidth >= 800;
|
||||||
|
final double squareSize = (constraints.maxWidth - 20) / 2;
|
||||||
|
final double listWidth = isWide
|
||||||
|
? squareSize * 2 + 20
|
||||||
|
: constraints.maxWidth;
|
||||||
|
|
||||||
|
final Widget addBoxColumn = Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_sectionTitle('신규 학생 계정 추가'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
SizedBox(
|
||||||
|
width: isWide ? squareSize : double.infinity,
|
||||||
|
height: isWide ? squareSize : null,
|
||||||
|
child: _buildAddAccountBox(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final Widget excelBoxColumn = Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_sectionTitle('엑셀로 한번에 추가 (신입생 등)'),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
SizedBox(
|
||||||
|
width: isWide ? squareSize : double.infinity,
|
||||||
|
height: isWide ? squareSize : 240,
|
||||||
|
child: _buildExcelBox(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
isWide
|
||||||
|
? Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
addBoxColumn,
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
excelBoxColumn,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
addBoxColumn,
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
excelBoxColumn,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 36),
|
const SizedBox(height: 36),
|
||||||
|
|
||||||
// 🏷️ 인디케이터 바 (전체 학생 목록)
|
// 🏷️ 인디케이터 바 (전체 학생 목록) — 위 두 칸을 합친 폭에 맞춘다.
|
||||||
|
SizedBox(
|
||||||
|
width: listWidth,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
_sectionTitle('전체 학생 목록', color: Colors.blue),
|
||||||
width: 4,
|
|
||||||
height: 16,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.blue,
|
|
||||||
borderRadius: BorderRadius.circular(2),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Text(
|
|
||||||
'전체 학생 목록',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.refresh_rounded),
|
icon: const Icon(Icons.refresh_rounded),
|
||||||
@@ -630,14 +731,18 @@ class _TeacherStudentManagementPageState
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
const Text(
|
const Text(
|
||||||
'학년 칩을 눌러 학년을 바꾸고, 기기 리셋은 학생이 폰을 바꿨을 때 사용하세요.',
|
'학년 칩을 눌러 학년을 바꾸고, 기기 리셋은 학생이 폰을 바꿨을 때 사용하세요.',
|
||||||
style: TextStyle(color: Colors.black54, fontSize: 12),
|
style: TextStyle(
|
||||||
|
color: Colors.black54,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
_controller.isLoadingStudents
|
_controller.isLoadingStudents
|
||||||
? const Padding(
|
? const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 40),
|
padding: EdgeInsets.symmetric(vertical: 40),
|
||||||
child: Center(child: CircularProgressIndicator()),
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: _controller.students.isEmpty
|
: _controller.students.isEmpty
|
||||||
? const Padding(
|
? const Padding(
|
||||||
@@ -649,98 +754,29 @@ class _TeacherStudentManagementPageState
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: ListView.builder(
|
: GridView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate:
|
||||||
|
SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: isWide ? 4 : 2,
|
||||||
|
crossAxisSpacing: 16,
|
||||||
|
mainAxisSpacing: 16,
|
||||||
|
childAspectRatio: isWide ? 1.5 : 0.75,
|
||||||
|
),
|
||||||
itemCount: _controller.students.length,
|
itemCount: _controller.students.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) =>
|
||||||
final student = _controller.students[index];
|
_buildStudentTile(
|
||||||
final String studentId = student['id'].toString();
|
_controller.students[index],
|
||||||
final String studentName = student['name'].toString();
|
|
||||||
final int? grade = student['grade'] as int?;
|
|
||||||
final bool needsReset = student['device'] == '초기화 필요';
|
|
||||||
|
|
||||||
return Card(
|
|
||||||
elevation: 1,
|
|
||||||
margin: const EdgeInsets.only(bottom: 10),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
leading: CircleAvatar(
|
|
||||||
backgroundColor: needsReset
|
|
||||||
? Colors.red[50]
|
|
||||||
: Colors.blue[50],
|
|
||||||
child: Icon(
|
|
||||||
needsReset ? Icons.lock_reset : Icons.person,
|
|
||||||
color: needsReset ? Colors.red : Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
'$studentName ($studentId)',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
needsReset ? '기기 초기화 승인 대기중' : '정상 등록 상태',
|
|
||||||
style: TextStyle(
|
|
||||||
color: needsReset ? Colors.red : Colors.grey,
|
|
||||||
fontWeight: needsReset
|
|
||||||
? FontWeight.bold
|
|
||||||
: FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Builder(
|
|
||||||
builder: (chipContext) => ActionChip(
|
|
||||||
label: Text(
|
|
||||||
grade != null ? '$grade학년' : '미배정',
|
|
||||||
),
|
|
||||||
onPressed: () => _showGradeMenu(
|
|
||||||
chipContext,
|
|
||||||
studentId,
|
|
||||||
studentName,
|
|
||||||
grade,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.lock_reset,
|
|
||||||
color: Colors.purple,
|
|
||||||
),
|
|
||||||
tooltip: '기기 리셋',
|
|
||||||
onPressed: () => _confirmResetDevice(
|
|
||||||
studentId,
|
|
||||||
studentName,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.delete_forever_rounded,
|
|
||||||
color: Colors.redAccent,
|
|
||||||
),
|
|
||||||
tooltip: '계정 영구 삭제',
|
|
||||||
onPressed: () => _confirmDeleteStudent(
|
|
||||||
studentId,
|
|
||||||
studentName,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user