diff --git a/lib/login_page.dart b/lib/login_page.dart index e5f891d..d37db57 100644 --- a/lib/login_page.dart +++ b/lib/login_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'home_page.dart'; // 要跳轉的頁面 +import 'new_resident_step.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @@ -64,6 +65,15 @@ class _LoginPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ + const Text( + '社區通', + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: Colors.indigo, + ), + ), + const SizedBox(height: 16), CircleAvatar( radius: 64, backgroundImage: AssetImage('assets/images/login.png'), @@ -113,15 +123,43 @@ class _LoginPageState extends State { ), ), const SizedBox(height: 16), - TextButton( - onPressed: () {}, - child: const Text( - '忘記密碼?', - style: TextStyle( - color: Colors.indigo, - decoration: TextDecoration.underline, + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + OutlinedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const NewResidentStepPage(), + ), + ); + }, + style: OutlinedButton.styleFrom( + foregroundColor: Colors.indigo, + side: const BorderSide(color: Colors.indigo), + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + child: const Text('新住戶'), ), - ), + OutlinedButton( + onPressed: () { + // TODO: 忘記密碼事件 + }, + style: OutlinedButton.styleFrom( + foregroundColor: Colors.indigo, + side: const BorderSide(color: Colors.indigo), + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 12, + ), + ), + child: const Text('忘記密碼'), + ), + ], ), ], ), diff --git a/lib/new_resident_step.dart b/lib/new_resident_step.dart new file mode 100644 index 0000000..cf49037 --- /dev/null +++ b/lib/new_resident_step.dart @@ -0,0 +1,424 @@ +import 'package:flutter/material.dart'; + +class NewResidentStepPage extends StatefulWidget { + const NewResidentStepPage({super.key}); + + @override + State createState() => _NewResidentStepPageState(); +} + +class _NewResidentStepPageState extends State { + int currentStep = 1; + final TextEditingController _emailController = TextEditingController(); + final List _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( + 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: () { + // 跳轉到掃描開通畫面 + Navigator.pushNamed(context, '/scan_activate'); + }, + 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), + ), + ), + ), + ], + ), + ), + ], + ); + } + + @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(); + default: + return const Text('未知步驟'); + } + } +}