502 lines
16 KiB
Dart
502 lines
16 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'login_page.dart';
|
||
|
||
class NewResidentStepPage extends StatefulWidget {
|
||
const NewResidentStepPage({super.key});
|
||
|
||
@override
|
||
State<NewResidentStepPage> createState() => _NewResidentStepPageState();
|
||
}
|
||
|
||
class _NewResidentStepPageState extends State<NewResidentStepPage> {
|
||
int currentStep = 1;
|
||
final TextEditingController _emailController = TextEditingController();
|
||
final List<TextEditingController> _codeControllers = List.generate(
|
||
6,
|
||
(_) => TextEditingController(),
|
||
);
|
||
|
||
String? statusMessage;
|
||
bool isSuccess = false;
|
||
|
||
void sendVerification() {
|
||
final email = _emailController.text.trim();
|
||
if (email.contains("@")) {
|
||
setState(() {
|
||
isSuccess = true;
|
||
statusMessage = '📧 驗證碼已寄出至 $email';
|
||
currentStep = 2;
|
||
});
|
||
} else {
|
||
setState(() {
|
||
isSuccess = false;
|
||
statusMessage = '❌ 請輸入有效的 Email';
|
||
});
|
||
}
|
||
}
|
||
|
||
void verifyCode() {
|
||
final code = _codeControllers.map((c) => c.text).join();
|
||
if (RegExp(r'^\d{6}$').hasMatch(code)) {
|
||
setState(() {
|
||
isSuccess = true;
|
||
statusMessage = '✅ 驗證成功!';
|
||
currentStep = 3;
|
||
});
|
||
} else {
|
||
setState(() {
|
||
isSuccess = false;
|
||
statusMessage = '❌ 請輸入正確的 6 位數驗證碼';
|
||
});
|
||
}
|
||
}
|
||
|
||
Widget buildStep1() {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
const Text(
|
||
'步驟 1 之 3',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||
),
|
||
const SizedBox(height: 10),
|
||
const Text(
|
||
'輸入電子郵件',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||
),
|
||
const SizedBox(height: 24),
|
||
TextField(
|
||
controller: _emailController,
|
||
keyboardType: TextInputType.emailAddress,
|
||
decoration: const InputDecoration(
|
||
labelText: '電子郵件',
|
||
border: OutlineInputBorder(),
|
||
hintText: 'example@mail.com',
|
||
),
|
||
),
|
||
const SizedBox(height: 20),
|
||
ElevatedButton(
|
||
onPressed: sendVerification,
|
||
style: ElevatedButton.styleFrom(backgroundColor: Colors.indigo),
|
||
child: const Text('發送驗證碼', style: TextStyle(color: Colors.white)),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget buildStep2() {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
const Text(
|
||
'步驟 2 之 3',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||
),
|
||
const SizedBox(height: 10),
|
||
const Text(
|
||
'請輸入驗證碼',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||
),
|
||
const SizedBox(height: 10),
|
||
const Text(
|
||
'我們已將 6 位數驗證碼寄至您的 Email',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(color: Colors.grey),
|
||
),
|
||
const SizedBox(height: 20),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: List.generate(6, (i) {
|
||
return Container(
|
||
width: 40,
|
||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||
child: TextField(
|
||
controller: _codeControllers[i],
|
||
maxLength: 1,
|
||
textAlign: TextAlign.center,
|
||
keyboardType: TextInputType.number,
|
||
decoration: const InputDecoration(counterText: ''),
|
||
onChanged: (val) {
|
||
if (val.length == 1 && i < 5) {
|
||
FocusScope.of(context).nextFocus();
|
||
}
|
||
},
|
||
),
|
||
);
|
||
}),
|
||
),
|
||
const SizedBox(height: 20),
|
||
ElevatedButton(
|
||
onPressed: verifyCode,
|
||
style: ElevatedButton.styleFrom(backgroundColor: Colors.indigo),
|
||
child: const Text('驗證並繼續', style: TextStyle(color: Colors.white)),
|
||
),
|
||
TextButton(
|
||
onPressed: () {
|
||
setState(() {
|
||
statusMessage = '📨 驗證碼已重新寄出,請再次查收 Email。';
|
||
});
|
||
},
|
||
child: const Text('重新發送驗證碼'),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
// 在 State 裡新增這些 controller 和變數:
|
||
final nameController = TextEditingController();
|
||
final birthdayController = TextEditingController();
|
||
final phoneController = TextEditingController();
|
||
final roomController = TextEditingController();
|
||
final plateController = TextEditingController();
|
||
final passwordController = TextEditingController();
|
||
final confirmPasswordController = TextEditingController();
|
||
String? selectedGender;
|
||
|
||
// 表單驗證 function
|
||
bool validateStep3Fields() {
|
||
return true;
|
||
/*
|
||
nameController.text.isNotEmpty &&
|
||
selectedGender != null &&
|
||
birthdayController.text.isNotEmpty &&
|
||
phoneController.text.isNotEmpty &&
|
||
roomController.text.isNotEmpty &&
|
||
plateController.text.isNotEmpty &&
|
||
passwordController.text.isNotEmpty &&
|
||
confirmPasswordController.text.isNotEmpty &&
|
||
passwordController.text == confirmPasswordController.text;
|
||
*/
|
||
}
|
||
|
||
// buildStep3 widget
|
||
Widget buildStep3() {
|
||
return Column(
|
||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||
children: [
|
||
const Text(
|
||
'步驟 3 之 3',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||
),
|
||
const SizedBox(height: 10),
|
||
const Text(
|
||
'填寫基本資料',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||
),
|
||
const SizedBox(height: 10),
|
||
const Text(
|
||
'請輸入您的基本資訊以完成註冊',
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(color: Colors.grey),
|
||
),
|
||
const SizedBox(height: 20),
|
||
|
||
TextField(
|
||
controller: nameController,
|
||
decoration: const InputDecoration(
|
||
labelText: '姓名',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
DropdownButtonFormField<String>(
|
||
value: selectedGender,
|
||
items: const [
|
||
DropdownMenuItem(value: '男', child: Text('男')),
|
||
DropdownMenuItem(value: '女', child: Text('女')),
|
||
DropdownMenuItem(value: '其他', child: Text('其他')),
|
||
],
|
||
onChanged: (value) {
|
||
setState(() {
|
||
selectedGender = value;
|
||
});
|
||
},
|
||
decoration: const InputDecoration(
|
||
labelText: '性別',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextField(
|
||
controller: birthdayController,
|
||
decoration: const InputDecoration(
|
||
labelText: '生日',
|
||
hintText: 'YYYY-MM-DD',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
keyboardType: TextInputType.datetime,
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextField(
|
||
controller: phoneController,
|
||
decoration: const InputDecoration(
|
||
labelText: '手機號碼',
|
||
hintText: '例如:0912345678',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
keyboardType: TextInputType.phone,
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextField(
|
||
controller: roomController,
|
||
decoration: const InputDecoration(
|
||
labelText: '房號 / 室別',
|
||
hintText: '例如:A棟 5F-2',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextField(
|
||
controller: plateController,
|
||
decoration: const InputDecoration(
|
||
labelText: '車牌號碼',
|
||
hintText: '例如:ABC-1234',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextField(
|
||
controller: passwordController,
|
||
obscureText: true,
|
||
decoration: const InputDecoration(
|
||
labelText: '密碼',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextField(
|
||
controller: confirmPasswordController,
|
||
obscureText: true,
|
||
decoration: const InputDecoration(
|
||
labelText: '確認密碼',
|
||
border: OutlineInputBorder(),
|
||
),
|
||
),
|
||
const SizedBox(height: 24),
|
||
ElevatedButton(
|
||
onPressed: () {
|
||
if (validateStep3Fields()) {
|
||
setState(() {
|
||
currentStep = 4;
|
||
statusMessage = null;
|
||
isSuccess = true;
|
||
});
|
||
ScaffoldMessenger.of(
|
||
context,
|
||
).showSnackBar(const SnackBar(content: Text('✅ 註冊完成!歡迎加入社區!')));
|
||
} else {
|
||
setState(() {
|
||
isSuccess = false;
|
||
statusMessage = '❌ 請完整填寫所有欄位,並確認密碼一致';
|
||
});
|
||
}
|
||
},
|
||
style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
|
||
child: const Text('完成註冊', style: TextStyle(color: Colors.white)),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget buildStep4() {
|
||
return Column(
|
||
children: [
|
||
// Body content
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min, // 讓 Column 的高度根據內容來調整
|
||
crossAxisAlignment: CrossAxisAlignment.center,
|
||
children: [
|
||
const Text(
|
||
'親愛的用戶您好',
|
||
style: TextStyle(
|
||
fontSize: 22,
|
||
fontWeight: FontWeight.w600,
|
||
color: Color(0xFF333333),
|
||
),
|
||
),
|
||
const SizedBox(height: 20),
|
||
const Text(
|
||
'您的新用戶申請已送至管理室,請您拿手機至管理室由管理員掃描開通。',
|
||
style: TextStyle(fontSize: 16, color: Color(0xFF666666)),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
const SizedBox(height: 40),
|
||
SizedBox(
|
||
width: double.infinity,
|
||
child: ElevatedButton(
|
||
onPressed: () {
|
||
setState(() {
|
||
currentStep = 5;
|
||
statusMessage = null;
|
||
isSuccess = true;
|
||
});
|
||
},
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: const Color(0xFF4CAF50),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(24),
|
||
),
|
||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||
),
|
||
child: const Text(
|
||
'掃描開通',
|
||
style: TextStyle(fontSize: 16, color: Colors.white),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget buildStep5() {
|
||
return Column(
|
||
children: [
|
||
// Body content
|
||
Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min, // 讓 Column 的高度根據內容來調整
|
||
crossAxisAlignment: CrossAxisAlignment.center,
|
||
children: [
|
||
const Text(
|
||
'掃描QR Code 來開通',
|
||
style: TextStyle(
|
||
fontSize: 22,
|
||
fontWeight: FontWeight.w600,
|
||
color: Color(0xFF333333),
|
||
),
|
||
),
|
||
const SizedBox(height: 20),
|
||
const Text(
|
||
'請掃描下面的 QR Code 開通此住戶。',
|
||
style: TextStyle(fontSize: 16, color: Color(0xFF666666)),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
const SizedBox(height: 20),
|
||
// 塞入QR CODE 圖片
|
||
Image.network(
|
||
'https://docs.lightburnsoftware.com/legacy/img/QRCode/ExampleCode.png',
|
||
width: 200,
|
||
height: 200,
|
||
),
|
||
const SizedBox(height: 40),
|
||
SizedBox(
|
||
width: double.infinity,
|
||
child: ElevatedButton(
|
||
onPressed: () {
|
||
Navigator.pushReplacement(
|
||
context,
|
||
MaterialPageRoute(
|
||
builder: (context) => const LoginPage(),
|
||
),
|
||
);
|
||
},
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: const Color(0xFF4CAF50),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(24),
|
||
),
|
||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||
),
|
||
child: const Text(
|
||
'掃描完畢,返回登入介面',
|
||
style: TextStyle(fontSize: 16, color: Colors.white),
|
||
),
|
||
),
|
||
),
|
||
const Text(
|
||
'如果無法掃描,請調整螢幕亮度。',
|
||
style: TextStyle(
|
||
fontSize: 14,
|
||
color: Color.fromARGB(255, 129, 129, 129),
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Scaffold(
|
||
backgroundColor: const Color(0xFFF7F8FA),
|
||
appBar: AppBar(
|
||
backgroundColor: const Color(0xFF9EAF9F),
|
||
foregroundColor: Colors.white,
|
||
title: const Text('新住戶註冊'),
|
||
leading:
|
||
currentStep > 1
|
||
? IconButton(
|
||
icon: const Icon(Icons.arrow_back),
|
||
onPressed: () {
|
||
setState(() {
|
||
currentStep--;
|
||
});
|
||
},
|
||
)
|
||
: null,
|
||
),
|
||
body: Padding(
|
||
padding: const EdgeInsets.all(20),
|
||
child: Container(
|
||
padding: const EdgeInsets.all(20),
|
||
decoration: BoxDecoration(
|
||
color: Colors.white,
|
||
borderRadius: BorderRadius.circular(12),
|
||
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 3)],
|
||
),
|
||
child: SingleChildScrollView(
|
||
child: Column(
|
||
children: [
|
||
buildStepContent(),
|
||
const SizedBox(height: 20),
|
||
if (statusMessage != null)
|
||
Text(
|
||
statusMessage!,
|
||
textAlign: TextAlign.center,
|
||
style: TextStyle(
|
||
fontSize: 16,
|
||
fontWeight: FontWeight.bold,
|
||
color: isSuccess ? Colors.green : Colors.red,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget buildStepContent() {
|
||
switch (currentStep) {
|
||
case 1:
|
||
return buildStep1();
|
||
case 2:
|
||
return buildStep2();
|
||
case 3:
|
||
return buildStep3();
|
||
case 4:
|
||
return buildStep4();
|
||
case 5:
|
||
return buildStep5();
|
||
default:
|
||
return const Text('未知步驟');
|
||
}
|
||
}
|
||
}
|