Compare commits
56 Commits
edd2f57800
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f8f3f477e | ||
|
|
ea6049ffad | ||
|
|
6c1eee8999 | ||
|
|
57968f7935 | ||
|
|
00ff77a49a | ||
|
|
f4d0243de0 | ||
|
|
4c8d789a86 | ||
|
|
6aaed1ba15 | ||
|
|
8eab769b74 | ||
|
|
62808b2701 | ||
|
|
fbe67b7dd9 | ||
|
|
c7cce038aa | ||
|
|
3131f611da | ||
|
|
4c8b6db2ba | ||
|
|
123f0c4958 | ||
| 66690c3795 | |||
|
|
1c843b1dcc | ||
|
|
65419ae197 | ||
|
|
98293d34c2 | ||
|
|
4400081d0a | ||
|
|
087242dcea | ||
|
|
ed40d6633d | ||
|
|
064f8894be | ||
|
|
1530c332c9 | ||
|
|
b81dcf999f | ||
|
|
351f280927 | ||
|
|
9addc73193 | ||
|
|
62a15061ef | ||
|
|
9c65267769 | ||
|
|
36b2d7c151 | ||
| ab0c259231 | |||
| c3702fee8d | |||
|
|
5f4b90e491 | ||
|
|
37ab8411c3 | ||
|
|
3d54016a00 | ||
|
|
b5f2c7aa93 | ||
|
|
31a12467ba | ||
|
|
114613ae0d | ||
|
|
1fb3cfaa53 | ||
|
|
5cf59af63e | ||
|
|
4be851ab86 | ||
|
|
89948f582a | ||
|
|
c078333129 | ||
|
|
f20380815b | ||
|
|
de5e16dc15 | ||
|
|
ce02fd3b50 | ||
|
|
781d8dfcdf | ||
|
|
399f880054 | ||
|
|
00ec7d262f | ||
|
|
7d06f83d38 | ||
|
|
c3533e6338 | ||
|
|
09d4346608 | ||
|
|
cf3abfc12b | ||
|
|
994701e2ff | ||
|
|
7e6b0df97f | ||
|
|
7c33bd99d9 |
BIN
assets/images/123.jpg
Normal file
BIN
assets/images/123.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
BIN
assets/images/456.jpg
Normal file
BIN
assets/images/456.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
assets/images/bathroom.webp
Normal file
BIN
assets/images/bathroom.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 266 KiB |
BIN
assets/images/fallFactor.webp
Normal file
BIN
assets/images/fallFactor.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/images/fallRescue1.webp
Normal file
BIN
assets/images/fallRescue1.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/images/fallRescue2.webp
Normal file
BIN
assets/images/fallRescue2.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -1,12 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:topic/HomePage.dart';
|
||||||
|
|
||||||
import 'HistoricalRecord.dart';
|
import 'HistoricalRecord.dart';
|
||||||
import 'KnowledgePage.dart';
|
import 'KnowledgePage.dart';
|
||||||
import 'MessagePage.dart';
|
import 'MessagePage.dart';
|
||||||
import 'PersonalInfo.dart';
|
import 'PersonalInfo.dart';
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
|
||||||
class BottomNavBar extends StatelessWidget {
|
class BottomNavBar extends StatefulWidget {
|
||||||
const BottomNavBar({
|
const BottomNavBar({
|
||||||
super.key,
|
super.key,
|
||||||
required this.email,
|
required this.email,
|
||||||
@@ -16,64 +19,86 @@ class BottomNavBar extends StatelessWidget {
|
|||||||
final String email;
|
final String email;
|
||||||
final int initTabIndex;
|
final int initTabIndex;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BottomNavBar> createState() => _BottomNavBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BottomNavBarState extends State<BottomNavBar> {
|
||||||
List<PersistentTabConfig> _tabs(BuildContext context) => [
|
List<PersistentTabConfig> _tabs(BuildContext context) => [
|
||||||
PersistentTabConfig.noScreen(
|
PersistentTabConfig.noScreen(
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
icon: Icon(Icons.home),
|
icon: Icon(Icons.home),
|
||||||
title: "首頁",
|
title: S.of(context).home,
|
||||||
activeForegroundColor: Colors.orange,
|
activeForegroundColor: Colors.orange,
|
||||||
inactiveForegroundColor: Colors.grey,
|
inactiveForegroundColor: Colors.grey,
|
||||||
),
|
),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
Navigator.pop(context); // Now you can use context here
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage(email: widget.email)),
|
||||||
|
).then((value) => setState(() {}));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
screen: HistoricalRecord(
|
screen: HistoricalRecord(
|
||||||
email: email,
|
email: widget.email,
|
||||||
),
|
),
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
icon: Icon(Icons.history_edu),
|
icon: Icon(Icons.history_edu),
|
||||||
title: "跌倒紀錄",
|
title: S.of(context).fall_record,
|
||||||
activeForegroundColor: Colors.orange,
|
activeForegroundColor: Colors.orange,
|
||||||
inactiveForegroundColor: Colors.grey,
|
inactiveForegroundColor: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
screen: KnowledgePage(
|
screen: KnowledgePage(
|
||||||
email: email,
|
email: widget.email,
|
||||||
),
|
),
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
icon: Icon(Icons.lightbulb_outline),
|
icon: Icon(Icons.lightbulb_outline),
|
||||||
title: "知識補充",
|
title: S.of(context).additional_information,
|
||||||
activeForegroundColor: Colors.orange,
|
activeForegroundColor: Colors.orange,
|
||||||
inactiveForegroundColor: Colors.grey,
|
inactiveForegroundColor: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
screen: MessagePage(
|
screen: MessagePage(
|
||||||
email: email,
|
email: widget.email,
|
||||||
),
|
),
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
icon: Icon(Icons.monitor_outlined),
|
icon: Icon(Icons.monitor_outlined),
|
||||||
title: "切換畫面",
|
title: S.of(context).switch_camera,
|
||||||
activeForegroundColor: Colors.orange,
|
activeForegroundColor: Colors.orange,
|
||||||
inactiveForegroundColor: Colors.grey,
|
inactiveForegroundColor: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PersistentTabConfig(
|
PersistentTabConfig(
|
||||||
screen: PersonalInfo(
|
screen: PersonalInfo(
|
||||||
email: email,
|
email: widget.email,
|
||||||
),
|
),
|
||||||
item: ItemConfig(
|
item: ItemConfig(
|
||||||
icon: Icon(Icons.person_sharp),
|
icon: Icon(Icons.person_sharp),
|
||||||
title: "個人資料",
|
title: S.of(context).personal_information,
|
||||||
activeForegroundColor: Colors.orange,
|
activeForegroundColor: Colors.orange,
|
||||||
inactiveForegroundColor: Colors.grey,
|
inactiveForegroundColor: Colors.grey,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_setLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setLanguage() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String preLanguage = prefs.getString("language") ?? "";
|
||||||
|
Locale locale = preLanguage == "" ? Localizations.localeOf(context) : Locale(preLanguage) ;
|
||||||
|
S.load(locale);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PersistentTabView(
|
return PersistentTabView(
|
||||||
@@ -81,7 +106,7 @@ class BottomNavBar extends StatelessWidget {
|
|||||||
navBarBuilder: (navBarConfig) => Style1BottomNavBar(
|
navBarBuilder: (navBarConfig) => Style1BottomNavBar(
|
||||||
navBarConfig: navBarConfig,
|
navBarConfig: navBarConfig,
|
||||||
),
|
),
|
||||||
controller: PersistentTabController(initialIndex: initTabIndex),
|
controller: PersistentTabController(initialIndex: widget.initTabIndex),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
217
lib/ForgetPasswordPage.dart
Normal file
217
lib/ForgetPasswordPage.dart
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:mailer/mailer.dart';
|
||||||
|
import 'package:mailer/smtp_server.dart';
|
||||||
|
import 'package:validators/validators.dart' as validator;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(MaterialApp(
|
||||||
|
home: ForgetPasswordPage(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
class ForgetPasswordPage extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_ForgetPasswordPageState createState() => _ForgetPasswordPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ForgetPasswordPageState extends State<ForgetPasswordPage> {
|
||||||
|
late FocusNode _emailFocusNode;
|
||||||
|
final TextEditingController _emailController = TextEditingController();
|
||||||
|
|
||||||
|
bool _isButtonEnabled = true;
|
||||||
|
String _generatedCode = ''; // 用來存儲生成的臨時密碼
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchData();
|
||||||
|
_emailFocusNode = FocusNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fetchData() async {
|
||||||
|
final conn = await MySQLConnection.createConnection(
|
||||||
|
host: 'comprehensive-guardian.systems',
|
||||||
|
port: 33061,
|
||||||
|
userName: 'root',
|
||||||
|
password: 'Topic@2024',
|
||||||
|
databaseName: 'care',
|
||||||
|
);
|
||||||
|
await conn.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _generateVerificationCode() {
|
||||||
|
final random = Random();
|
||||||
|
const availableChars = '0123456789';
|
||||||
|
return List.generate(
|
||||||
|
6, (index) => availableChars[random.nextInt(availableChars.length)])
|
||||||
|
.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _sendEmail(String recipientEmail, String code) async {
|
||||||
|
final smtpServer = SmtpServer(
|
||||||
|
'smtp.mail.me.com',
|
||||||
|
port: 587,
|
||||||
|
username: 'ltesr125124015@icloud.com',
|
||||||
|
password: 'vwtp-bruz-xiav-rjee',
|
||||||
|
ssl: false,
|
||||||
|
allowInsecure: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final message = Message()
|
||||||
|
..from = Address('user_manager@comprehensive-guardian.systems', '全方位照護守護者')
|
||||||
|
..recipients.add(recipientEmail)
|
||||||
|
..subject = '您的臨時密碼'
|
||||||
|
..text = '您的臨時密碼是: $code';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await send(message, smtpServer);
|
||||||
|
print('臨時密碼發送成功');
|
||||||
|
} catch (e) {
|
||||||
|
print('臨時密碼發送失敗: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sendVerificationCode() async {// 發送並更新資料庫中的密碼
|
||||||
|
final email = _emailController.text;
|
||||||
|
if (email.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('請輸入電子信箱地址')),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else if (!validator.isEmail(email)) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('請輸入正確的電子信箱')),
|
||||||
|
);
|
||||||
|
_emailFocusNode.requestFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//_generatedCode = _generateVerificationCode();
|
||||||
|
//_sendEmail(email, _generatedCode);
|
||||||
|
|
||||||
|
final conn = await MySQLConnection.createConnection(
|
||||||
|
host: 'comprehensive-guardian.systems',
|
||||||
|
port: 33061,
|
||||||
|
userName: 'root',
|
||||||
|
password: 'Topic@2024',
|
||||||
|
databaseName: 'care',
|
||||||
|
);
|
||||||
|
await conn.connect();
|
||||||
|
try {
|
||||||
|
var result = await conn.execute(
|
||||||
|
'SELECT * FROM HomeLogin WHERE homeEmail = :email',
|
||||||
|
{'email': email},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.rows.isEmpty) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('失敗')),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
_generatedCode = _generateVerificationCode();
|
||||||
|
await conn.execute(
|
||||||
|
'UPDATE HomeLogin SET homePassword = :tempPassword WHERE homeEmail = :email',
|
||||||
|
{'tempPassword': _generatedCode, 'email': email},
|
||||||
|
);
|
||||||
|
|
||||||
|
// 發送臨時密碼到用戶電子信箱
|
||||||
|
_sendEmail(email, _generatedCode);
|
||||||
|
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('臨時密碼已發送到您的電子信箱')),
|
||||||
|
);
|
||||||
|
|
||||||
|
_emailController.clear();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('資料庫錯誤: $e');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('註冊失敗:系統錯誤')),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await conn.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_emailFocusNode.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: Center(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 40),
|
||||||
|
height: 100,
|
||||||
|
child: Icon(
|
||||||
|
Icons.account_circle,
|
||||||
|
size: 100,
|
||||||
|
color: Color(0xFF4FC3F7),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'全方位照護守護者',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextField(
|
||||||
|
controller: _emailController,
|
||||||
|
focusNode: _emailFocusNode,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
prefixIcon: Icon(Icons.email_outlined),
|
||||||
|
labelText: '電子信箱',
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _sendVerificationCode,
|
||||||
|
child: Text('送出'),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF4FC3F7),
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 50, vertical: 15),
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text('取消'),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
|
||||||
import 'package:topic/BottomNavBar.dart';
|
|
||||||
import 'package:topic/main.dart';
|
|
||||||
import 'package:topic/HomePage.dart';
|
|
||||||
import 'package:topic/PersonalInfo.dart';
|
|
||||||
import 'package:topic/KnowledgePage.dart';
|
|
||||||
import 'package:topic/MessagePage.dart';
|
|
||||||
import 'package:mysql_client/mysql_client.dart';
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
|
import 'package:video_player/video_player.dart';
|
||||||
|
import 'package:better_player_plus/better_player_plus.dart';
|
||||||
|
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
|
||||||
class HistoricalRecord extends StatefulWidget {
|
class HistoricalRecord extends StatefulWidget {
|
||||||
final String email; // 接收來自上個頁面的 email
|
final String email; // 接收來自上個頁面的 email
|
||||||
@@ -18,6 +15,7 @@ class HistoricalRecord extends StatefulWidget {
|
|||||||
|
|
||||||
class _HistoricalRecordState extends State<HistoricalRecord> {
|
class _HistoricalRecordState extends State<HistoricalRecord> {
|
||||||
List<Map<String, dynamic>> _results = [];
|
List<Map<String, dynamic>> _results = [];
|
||||||
|
int _totalRecords = 0; // 儲存總比數
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -29,36 +27,59 @@ class _HistoricalRecordState extends State<HistoricalRecord> {
|
|||||||
print('connect');
|
print('connect');
|
||||||
|
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
//host: '10.0.2.2',
|
|
||||||
//127.0.0.1 10.0.2.2
|
|
||||||
port: 33061,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: 'Topic@2024',
|
password: 'Topic@2024',
|
||||||
//password: '0000',
|
databaseName: 'care',
|
||||||
databaseName: 'care', //testdb
|
|
||||||
//databaseName: 'testdb',
|
|
||||||
);
|
);
|
||||||
await conn.connect();
|
await conn.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var result = await conn.execute('SELECT HomeElderFall.*, HomeElder.heName FROM HomeElderFall JOIN HomeElder ON HomeElderFall.heId = HomeElder.heId;');
|
var userNameResult = await conn.execute(
|
||||||
print('Result: ${result.length} rows found.');
|
'SELECT homeUserName FROM HomeLogin WHERE homeEmail = :email',
|
||||||
|
{'email': widget.email},
|
||||||
|
);
|
||||||
|
if (userNameResult.rows.isNotEmpty) {
|
||||||
|
String homeUserName = userNameResult.rows.first.colByName("homeUserName").toString();
|
||||||
|
print('homeUserName: $homeUserName');
|
||||||
|
|
||||||
if (result.rows.isEmpty) {
|
// 先計算該長者的跌倒紀錄總筆數
|
||||||
print('No data found in users table.');
|
var countResult = await conn.execute(
|
||||||
|
'SELECT COUNT(*) AS total FROM HomeElderFall WHERE homeUserName = :homeUserName',
|
||||||
|
{'homeUserName': homeUserName},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (countResult.rows.isNotEmpty) {
|
||||||
|
_totalRecords = int.parse(countResult.rows.first.colByName("total").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查詢對應 homeUserName 的跌倒資料,並按時間降序排列
|
||||||
|
var fallResult = await conn.execute(
|
||||||
|
'SELECT HomeElderFall.* FROM HomeElderFall WHERE homeUserName = :homeUserName ORDER BY hfTime DESC',
|
||||||
|
{'homeUserName': homeUserName},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fallResult.rows.isEmpty) {
|
||||||
|
print('No data found in users table.');
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_results = fallResult.rows
|
||||||
|
.map((row) =>
|
||||||
|
{
|
||||||
|
'跌倒編號': row.colAt(0),
|
||||||
|
'跌倒時間': row.colAt(1),
|
||||||
|
'跌倒原因': row.colAt(2),
|
||||||
|
'跌倒地點': row.colAt(6),
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_results = result.rows
|
_results = [];
|
||||||
.map((row) => {
|
|
||||||
'長者ID': row.colAt(0),//去裝資料庫的行數
|
|
||||||
'姓名': row.colAt(8),
|
|
||||||
'跌倒時間': row.colAt(1),
|
|
||||||
'跌倒原因': row.colAt(2),
|
|
||||||
'跌倒地點': row.colAt(7),
|
|
||||||
})
|
|
||||||
.toList();
|
|
||||||
});
|
});
|
||||||
|
print('No data found.');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error: $e');
|
print('Error: $e');
|
||||||
@@ -79,31 +100,175 @@ class _HistoricalRecordState extends State<HistoricalRecord> {
|
|||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'跌倒紀錄',
|
S.of(context).fall_record,
|
||||||
style: TextStyle(fontSize: 24, height: 5),
|
style: TextStyle(fontSize: 24, height: 5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: _results.isEmpty
|
||||||
padding: EdgeInsets.symmetric(vertical: 5), // 调整列表视图的 padding
|
? Center(
|
||||||
|
child: Text(
|
||||||
|
S.of(context).fall_record_empty, // 當沒有資料時顯示的訊息
|
||||||
|
style: TextStyle(fontSize: 20, color: Colors.grey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 5), // 調整列表視圖的 padding
|
||||||
itemCount: _results.length,
|
itemCount: _results.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return ListTile(
|
int displayIndex = _totalRecords - index; // 計算倒序編號
|
||||||
title: Text(_results[index]['姓名']),
|
return Padding(
|
||||||
subtitle: Text(
|
padding: const EdgeInsets.symmetric(
|
||||||
'長者ID: ${_results[index]['長者ID']}\n, '
|
vertical: 4.0, horizontal: 16.0),
|
||||||
'跌倒時間: ${_results[index]['跌倒時間']}\n, '
|
child:Card(
|
||||||
'跌倒原因: ${_results[index]['跌倒原因']}\n, '
|
elevation: 4, // 卡片陰影效果
|
||||||
'跌倒地點: ${_results[index]['跌倒地點']}\n',
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0), // 圓角卡片
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
left: BorderSide(color: Color(0xFFFFCC99), width: 5.0), // 左邊框
|
||||||
|
top: BorderSide(color: Color(0xFFFFCC99), width: 5.0), // 上邊框
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10.0), // 圓角
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
S.of(context).fall_id(displayIndex), // 使用倒序顯示編號
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
S.of(context).fall_detail(
|
||||||
|
_results[index]['跌倒時間'],
|
||||||
|
_results[index]['跌倒原因'],
|
||||||
|
_results[index]['跌倒地點'],
|
||||||
|
),
|
||||||
|
style: TextStyle(fontSize: 16, color: Colors.black), // 統一字體樣式
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
// 點擊時導向詳細資料頁面,並傳遞資料
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) =>
|
||||||
|
FallDetailPage(fallDetail: _results[index]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 60),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增詳細資料頁面
|
||||||
|
class FallDetailPage extends StatefulWidget {
|
||||||
|
final Map<String, dynamic> fallDetail;
|
||||||
|
|
||||||
|
FallDetailPage({required this.fallDetail});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FallDetailPage> createState() => _FallDetailPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FallDetailPageState extends State<FallDetailPage> {
|
||||||
|
late BetterPlayerController _betterPlayerController;
|
||||||
|
String fhvideoId = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
BetterPlayerConfiguration betterPlayerConfiguration =
|
||||||
|
const BetterPlayerConfiguration(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
);
|
||||||
|
_betterPlayerController = BetterPlayerController(betterPlayerConfiguration);
|
||||||
|
|
||||||
|
_fetchData(); // 連資料庫、取得影片連結
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_betterPlayerController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _fetchData() async {
|
||||||
|
final conn = await MySQLConnection.createConnection(
|
||||||
|
host: 'comprehensive-guardian.systems',
|
||||||
|
port: 33061,
|
||||||
|
userName: 'root',
|
||||||
|
password: 'Topic@2024',
|
||||||
|
databaseName: 'care',
|
||||||
|
);
|
||||||
|
await conn.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
var userNameResult = await conn.execute(// 使用傳遞過來的 hfId來找hfvideoId
|
||||||
|
'SELECT hfvideoId FROM HomeFallVideo WHERE hfallId = :fallId',
|
||||||
|
{'fallId': widget.fallDetail['跌倒編號']},
|
||||||
|
);
|
||||||
|
if (userNameResult.rows.isNotEmpty) {
|
||||||
|
fhvideoId = userNameResult.rows.first.colByName(
|
||||||
|
"hfvideoId").toString();
|
||||||
|
|
||||||
|
BetterPlayerDataSource dataSource = BetterPlayerDataSource(
|
||||||
|
BetterPlayerDataSourceType.network,
|
||||||
|
"https://uploader.comprehensive-guardian.systems/stream/${fhvideoId}/playlist.m3u8",
|
||||||
|
videoFormat: BetterPlayerVideoFormat.hls,
|
||||||
|
);
|
||||||
|
_betterPlayerController.setupDataSource(dataSource);
|
||||||
|
setState(() {}); // 重新繪製畫面
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
print('Error: $e');
|
||||||
|
} finally {
|
||||||
|
await conn.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(S.of(context).fall_detail_title),
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(S.of(context).fall_detail_time(widget.fallDetail['跌倒時間']), style: TextStyle(fontSize: 18)),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Text(S.of(context).fall_detail_reason(widget.fallDetail['跌倒原因']), style: TextStyle(fontSize: 18)),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Text(S.of(context).fall_detail_location(widget.fallDetail['跌倒地點']), style: TextStyle(fontSize: 18)),
|
||||||
|
if (fhvideoId != '') AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: BetterPlayer(controller: _betterPlayerController),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
// import 'dart:html';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:topic/TryPage.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
import 'BottomNavBar.dart';
|
import 'BottomNavBar.dart';
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
final String email;
|
final String email;
|
||||||
@@ -46,7 +45,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'全方位照護守護者',
|
S.of(context).app_name,
|
||||||
style: TextStyle(fontSize: 24, height: 5),
|
style: TextStyle(fontSize: 24, height: 5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -61,7 +60,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
height: 240,
|
height: 240,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
// TODO: 如果用http需要分別設定ios, android權限
|
// TODO: 如果用http需要分別設定ios, android權限
|
||||||
// TODO: 替換video_player to flutter_webView
|
|
||||||
child: WebViewWidget(
|
child: WebViewWidget(
|
||||||
controller: WebViewController()
|
controller: WebViewController()
|
||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
@@ -81,18 +79,18 @@ class _HomePageState extends State<HomePage> {
|
|||||||
},
|
},
|
||||||
onNavigationRequest: (NavigationRequest request) {
|
onNavigationRequest: (NavigationRequest request) {
|
||||||
if (request.url.startsWith(
|
if (request.url.startsWith(
|
||||||
'http://203.64.84.154:8088/video_feed')) {
|
'https://streamer.comprehensive-guardian.systems/video_feed')) {
|
||||||
return NavigationDecision.prevent;
|
return NavigationDecision.prevent;
|
||||||
}
|
}
|
||||||
return NavigationDecision.navigate;
|
return NavigationDecision.navigate;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
..loadRequest(Uri.parse('http://203.64.84.154:8088')),
|
..loadRequest(Uri.parse('https://streamer.comprehensive-guardian.systems/0')),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'即時畫面',
|
S.of(context).realtime_video,
|
||||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -105,10 +103,10 @@ class _HomePageState extends State<HomePage> {
|
|||||||
physics: NeverScrollableScrollPhysics(),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
children: [
|
children: [
|
||||||
_buildGridItem(Icons.history_edu, '跌倒紀錄', context),
|
_buildGridItem(Icons.history_edu, S.of(context).fall_record, context),
|
||||||
_buildGridItem(Icons.lightbulb_outline, '知識補充', context),
|
_buildGridItem(Icons.lightbulb_outline, S.of(context).additional_information, context),
|
||||||
_buildGridItem(Icons.monitor_outlined, '切換畫面', context),
|
_buildGridItem(Icons.monitor_outlined, S.of(context).switch_camera, context),
|
||||||
_buildGridItem(Icons.person_sharp, '個人資料', context),
|
_buildGridItem(Icons.person_sharp, S.of(context).personal_information, context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -128,7 +126,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (label == '跌倒紀錄') {
|
if (label == S.of(context).fall_record) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@@ -137,7 +135,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
initTabIndex: 1,
|
initTabIndex: 1,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
} else if (label == '知識補充') {
|
} else if (label == S.of(context).additional_information) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@@ -146,7 +144,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
initTabIndex: 2,
|
initTabIndex: 2,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
} else if (label == '切換畫面') {
|
} else if (label == S.of(context).switch_camera) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute
|
MaterialPageRoute
|
||||||
@@ -155,7 +153,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
initTabIndex: 3,
|
initTabIndex: 3,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
} else if (label == '個人資料') {
|
} else if (label == S.of(context).personal_information) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:topic/HomePage.dart';
|
import 'package:topic/HomePage.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import 'generated/l10n.dart';
|
||||||
/*void main() {
|
/*void main() {
|
||||||
runApp(MaterialApp(
|
runApp(MaterialApp(
|
||||||
home: KnowledgePage(),
|
home: KnowledgePage(),
|
||||||
@@ -9,6 +12,7 @@ import 'package:topic/HomePage.dart';
|
|||||||
class KnowledgePage extends StatelessWidget {
|
class KnowledgePage extends StatelessWidget {
|
||||||
final String email; // 接收來自上個頁面的 email
|
final String email; // 接收來自上個頁面的 email
|
||||||
KnowledgePage({required this.email});
|
KnowledgePage({required this.email});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -25,7 +29,7 @@ class KnowledgePage extends StatelessWidget {
|
|||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'知識補充',
|
S.of(context).additional_information,
|
||||||
style: TextStyle(fontSize: 24, height: 5),
|
style: TextStyle(fontSize: 24, height: 5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -35,14 +39,37 @@ class KnowledgePage extends StatelessWidget {
|
|||||||
padding: EdgeInsets.symmetric(vertical: 5), // 调整列表视图的 padding
|
padding: EdgeInsets.symmetric(vertical: 5), // 调整列表视图的 padding
|
||||||
itemCount: 3,
|
itemCount: 3,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
|
// 根據索引設置不同的圖片和文字
|
||||||
|
String imagePath;
|
||||||
|
String title;
|
||||||
|
String description;
|
||||||
|
Widget detailPage; // 新增一個變數用於跳轉到不同的詳細頁面
|
||||||
|
String? url; // 新增一個變數來存放網址
|
||||||
|
if (idx == 0) {
|
||||||
|
imagePath = 'assets/images/fallFactor.webp';
|
||||||
|
title = '老人跌倒常見的危險因子';
|
||||||
|
//description = '了解跌倒的各種潛在原因...';
|
||||||
|
detailPage = FallCauseDetailPage();
|
||||||
|
} else if (idx == 1) {
|
||||||
|
imagePath = 'assets/images/bathroom.webp';
|
||||||
|
title = '如何在日常生活上預防跌倒?';
|
||||||
|
//description = '哪些人容易跌倒?預防措施有哪些?';
|
||||||
|
//url = 'https://www.hpa.gov.tw/Pages/Detail.aspx?nodeid=807&pid=4327';
|
||||||
|
detailPage = FrequentFallersDetailPage();
|
||||||
|
} else {
|
||||||
|
imagePath = 'assets/images/fallRescue1.webp';
|
||||||
|
title = '跌倒了怎麼辦?';
|
||||||
|
//description = '發生跌倒後的應急處理方法...';
|
||||||
|
detailPage = FallSolutionDetailPage();
|
||||||
|
}
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => HomePage(
|
builder: (context) => detailPage, // 根據選擇的文章跳轉到不同的詳細報導
|
||||||
email: email,
|
),
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Card(
|
child: Card(
|
||||||
@@ -50,28 +77,32 @@ class KnowledgePage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(15.0), // 圓角設定
|
||||||
|
child:
|
||||||
Image.asset(
|
Image.asset(
|
||||||
'lib/images/456.jpg',
|
imagePath,
|
||||||
height: 180,
|
height: 180,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'文章標題 $idx',
|
title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18, fontWeight: FontWeight.bold),
|
fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
/*Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
padding: EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'這是文章的簡短描述...',
|
description,
|
||||||
style: TextStyle(fontSize: 16),
|
style: TextStyle(fontSize: 16),
|
||||||
),
|
),
|
||||||
),
|
),*/
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -79,6 +110,242 @@ class KnowledgePage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(height: 70),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 詳細頁面之一:跌倒原因 //https://www.hpa.gov.tw/Pages/Detail.aspx?nodeid=807&pid=4327
|
||||||
|
class FallCauseDetailPage extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('跌倒原因'),
|
||||||
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'老人跌倒常見的危險因子',
|
||||||
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'發布單位:衛生福利部國民健康署-慢性疾病防治組',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Image.asset('assets/images/fallFactor.webp',
|
||||||
|
height: 180,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'社會人口學因子',
|
||||||
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
// 條列式清單
|
||||||
|
buildBulletPoint('年齡:老人跌倒的風險隨年齡增加而上升。'),
|
||||||
|
buildBulletPoint('性別:年老女性跌倒的風險約是男性的兩倍。'),
|
||||||
|
buildBulletPoint('獨居:獨居老人跌倒的風險較高。'),
|
||||||
|
buildBulletPoint('其他:如跌倒史,缺乏運動,日常生活活動功能(ADL)或/及工具性日常生活活動功能(IADL)失能。'),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'身心功能、疾病與用藥',
|
||||||
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
buildBulletPoint('移動能力:步態與平衡是老人跌倒的重要危險因子。'),
|
||||||
|
buildBulletPoint('疾病:心臟病、中風、高血壓等疾病為老人跌倒的重要危險因子。'),
|
||||||
|
buildBulletPoint('藥物:使用多種藥物可能增加跌倒風險。'),
|
||||||
|
buildBulletPoint('生理失調:例如姿勢性低血壓會增加跌倒風險。'),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'環境因子',
|
||||||
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
buildBulletPoint('戶外環境:寒冷天氣、地面不平等會增加跌倒風險。'),
|
||||||
|
buildBulletPoint('居家環境:照明不足、地板太滑等都是潛在危險。'),
|
||||||
|
SizedBox(height: 60),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法來構建條列式清單
|
||||||
|
Widget buildBulletPoint(String text) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('• ', style: TextStyle(fontSize: 16)),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 詳細頁面之二:常跌倒的人
|
||||||
|
class FrequentFallersDetailPage extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('如何在日常生活上預防跌倒?'),
|
||||||
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'如何在日常生活上預防跌倒?',
|
||||||
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'發布單位:衛生福利部國民健康署-慢性疾病防治組',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Image.asset('assets/images/bathroom.webp',
|
||||||
|
height: 180,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'對於年長者而言,每週規律運動是需要的。運動不但可以增加肌力、柔軟度,也可提升身體平衡性,另外也請時常留意居住環境及人身安全,列舉相關注意要點如下:',
|
||||||
|
style: TextStyle(fontSize: 18,),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
buildBulletPoint('1.光線要明亮'),
|
||||||
|
buildBulletPoint('2.電線靠牆收'),
|
||||||
|
buildBulletPoint('3.地板保持乾燥'),
|
||||||
|
buildBulletPoint('4.移除平日活動路線上的雜物'),
|
||||||
|
buildBulletPoint('5.浴室加裝扶手'),
|
||||||
|
buildBulletPoint('6.確保樓梯扶手穩固'),
|
||||||
|
buildBulletPoint('7.選擇合適的鞋子及輔具'),
|
||||||
|
buildBulletPoint('8.下床、起身要緩慢'),
|
||||||
|
SizedBox(height:60),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法來構建條列式清單
|
||||||
|
Widget buildBulletPoint(String text) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('• ', style: TextStyle(fontSize: 16)),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 詳細頁面之三:跌倒了怎麼辦 //https://www.dentist2home.com/post/%E7%A4%BE%E5%8D%80%E8%A3%A1%E6%AF%8F3%E4%BD%8D%E9%95%B7%E8%BC%A9%EF%BC%8C1%E4%BD%8D%E6%9C%89%E8%B7%8C%E5%80%92%E7%9A%84%E7%B6%93%E9%A9%97%EF%BC%8C%E8%A6%81%E5%A6%82%E4%BD%95%E9%A0%90%E9%98%B2%EF%BC%9F%E7%9C%9F%E7%9A%84%E8%B7%8C%E5%80%92%E4%BA%86%E8%A6%81%E8%99%95%E7%90%86%E5%91%A2%EF%BC%9F
|
||||||
|
class FallSolutionDetailPage extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('跌倒了怎麼辦?'),
|
||||||
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'跌倒了怎麼辦?',
|
||||||
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'發布單位:牙驛通',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'自己跌倒怎麼辦?',
|
||||||
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
// 條列式清單
|
||||||
|
buildBulletPoint('1. 不小心跌倒後應保持冷靜,不要亂動,同時檢查傷勢和高聲呼救。'),
|
||||||
|
buildBulletPoint('2. 若受傷部位腫起來或有劇烈疼痛時,可能已發生骨折,應靜候救援'),
|
||||||
|
buildBulletPoint('3. 附近無人可提供幫助時,不要直接站起,應以在地上滑動的方式,到最近的電話求救。'),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Image.asset('assets/images/fallRescue1.webp',
|
||||||
|
height: 180,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'別人跌倒怎麼辦?',
|
||||||
|
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
// 條列式清單
|
||||||
|
buildBulletPoint('1. 當發現長者跌倒時,不要慌張,也不要急著將他拉起來,可能會造成長者傷勢加重。'),
|
||||||
|
buildBulletPoint('2. 檢查跌倒長者意識狀況,以及受傷或出血等狀況。'),
|
||||||
|
buildBulletPoint('3. 若發現長者有意識不清或大量出血情形,應盡快叫救護車並送醫急救。'),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
Image.asset('assets/images/fallRescue2.webp',
|
||||||
|
height: 180,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
SizedBox(height: 60),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 方法來構建條列式清單
|
||||||
|
Widget buildBulletPoint(String text) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text('• ', style: TextStyle(fontSize: 16)),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:mysql_client/mysql_client.dart';
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
|
||||||
|
|
||||||
import 'BottomNavBar.dart';
|
|
||||||
class MessagePage extends StatefulWidget {
|
class MessagePage extends StatefulWidget {
|
||||||
final String email; // 接收來自上個頁面的 email
|
final String email; // 接收來自上個頁面的 email
|
||||||
|
|
||||||
@@ -14,110 +17,130 @@ class MessagePage extends StatefulWidget {
|
|||||||
|
|
||||||
class _MessagePageState extends State<MessagePage> {
|
class _MessagePageState extends State<MessagePage> {
|
||||||
late WebViewController _webViewController;
|
late WebViewController _webViewController;
|
||||||
|
late int _cameraId = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_webViewController = WebViewController();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopScope(
|
return Scaffold(
|
||||||
child: Scaffold(
|
body: Center(
|
||||||
body: Center(
|
child: Column(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
Container(
|
||||||
Container(
|
height: 100,
|
||||||
height: 100,
|
// APPBar height
|
||||||
// APPBar height
|
color: Color(0xFFF5E3C3),
|
||||||
color: Color(0xFFF5E3C3),
|
width: double.infinity,
|
||||||
width: double.infinity,
|
padding: EdgeInsets.all(10.0),
|
||||||
padding: EdgeInsets.all(10.0),
|
child: Stack(children: [
|
||||||
child: Stack(children: [
|
Container(
|
||||||
Container(
|
padding: EdgeInsets.only(
|
||||||
padding: EdgeInsets.only(
|
left: MediaQuery.of(context).size.width,
|
||||||
left: MediaQuery.of(context).size.width,
|
top: MediaQuery.of(context).size.height / 2,
|
||||||
top: MediaQuery.of(context).size.height / 2,
|
|
||||||
),
|
|
||||||
child: Icon(Icons.settings, size: 48, color: Colors.orange),
|
|
||||||
),
|
),
|
||||||
Center(
|
child: Icon(Icons.settings, size: 48, color: Colors.orange),
|
||||||
child: Text(
|
),
|
||||||
'全方位照護守護者',
|
Center(
|
||||||
style: TextStyle(fontSize: 24, height: 5),
|
child: Text(
|
||||||
),
|
S.of(context).app_name,
|
||||||
|
style: TextStyle(fontSize: 24, height: 5),
|
||||||
),
|
),
|
||||||
])),
|
),
|
||||||
Container(
|
])),
|
||||||
color: Color(0xFFFFF0E0),
|
Container(
|
||||||
margin: EdgeInsets.only(top: 5),
|
color: Color(0xFFFFF0E0),
|
||||||
padding: EdgeInsets.all(16),
|
margin: EdgeInsets.only(top: 5),
|
||||||
child: Column(
|
padding: EdgeInsets.all(16),
|
||||||
children: [
|
child: Column(
|
||||||
Container(
|
children: [
|
||||||
height: 240,
|
Container(
|
||||||
width: double.infinity,
|
height: 240,
|
||||||
// TODO: 如果用http需要分別設定ios, android權限
|
width: double.infinity,
|
||||||
// TODO: 替換video_player to flutter_webView
|
child: Stack(
|
||||||
child: WebViewWidget(
|
children: [
|
||||||
controller: WebViewController()
|
WebViewWidget(
|
||||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
controller: _webViewController
|
||||||
..setNavigationDelegate(
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
NavigationDelegate(
|
..setNavigationDelegate(
|
||||||
onProgress: (int progress) {
|
NavigationDelegate(
|
||||||
// Update loading bar.
|
onProgress: (int progress) {
|
||||||
},
|
// Update loading bar.
|
||||||
onPageStarted: (String url) {},
|
},
|
||||||
onPageFinished: (String url) {},
|
onPageStarted: (String url) {},
|
||||||
onHttpError: (HttpResponseError error) {
|
onPageFinished: (String url) {},
|
||||||
print("httpError");
|
onHttpError: (HttpResponseError error) {
|
||||||
},
|
print("httpError");
|
||||||
onWebResourceError: (WebResourceError error) {
|
},
|
||||||
print("httpResourceError");
|
onWebResourceError: (WebResourceError error) {
|
||||||
print(error.description);
|
print("httpResourceError");
|
||||||
},
|
print(error.description);
|
||||||
onNavigationRequest: (NavigationRequest request) {
|
},
|
||||||
if (request.url.startsWith(
|
onNavigationRequest: (NavigationRequest request) {
|
||||||
'http://203.64.84.154:8088/video_feed')) {
|
if (request.url.startsWith('https://streamer.comprehensive-guardian.systems/video_feed')) {
|
||||||
return NavigationDecision.prevent;
|
return NavigationDecision.prevent;
|
||||||
}
|
}
|
||||||
return NavigationDecision.navigate;
|
return NavigationDecision.navigate;
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..loadRequest(Uri.parse('https://streamer.comprehensive-guardian.systems/0')),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
// Adjust padding as needed
|
||||||
|
child: SizedBox(
|
||||||
|
width: 45.0, // Set custom width
|
||||||
|
height: 45.0, // Set custom height
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
// Handle zoom-in action
|
||||||
|
pushWithoutNavBar(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => WebViewPage(_cameraId),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
child: Icon(Icons.zoom_in),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
..loadRequest(Uri.parse('http://203.64.84.154:8088')),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
'即時畫面',
|
Text(
|
||||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
S.of(context).realtime_video,
|
||||||
),
|
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: GridView.count(
|
Expanded(
|
||||||
crossAxisCount: 2,
|
child: GridView.count(
|
||||||
shrinkWrap: true,
|
crossAxisCount: 2,
|
||||||
physics: NeverScrollableScrollPhysics(),
|
shrinkWrap: true,
|
||||||
padding: EdgeInsets.all(16),
|
physics: NeverScrollableScrollPhysics(),
|
||||||
children: [
|
padding: EdgeInsets.all(16),
|
||||||
_buildGridItem(Icons.monitor_outlined, '畫面1', context),
|
children: [
|
||||||
_buildGridItem(Icons.monitor_outlined, '畫面2', context),
|
_buildGridItem(Icons.monitor_outlined, '畫面1', 0, context),
|
||||||
/*_buildGridItem(Icons.monitor_outlined, '切換畫面', context),
|
_buildGridItem(Icons.monitor_outlined, '畫面2', 1, context),
|
||||||
_buildGridItem(Icons.person_sharp, '個人資料', context),*/
|
],
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
canPop: false,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGridItem(IconData icon, String label, BuildContext context) {
|
Widget _buildGridItem(IconData icon, String label, int camera_id, BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@@ -125,43 +148,8 @@ class _MessagePageState extends State<MessagePage> {
|
|||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
/*if (label == '跌倒紀錄') {
|
_cameraId = camera_id;
|
||||||
Navigator.push(
|
_webViewController.loadRequest(Uri.parse('https://streamer.comprehensive-guardian.systems/$camera_id'));
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => BottomNavBar(
|
|
||||||
email: widget.email,
|
|
||||||
initTabIndex: 1,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
} else if (label == '知識補充') {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => BottomNavBar(
|
|
||||||
email: widget.email,
|
|
||||||
initTabIndex: 2,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
} else if (label == '切換畫面') {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute
|
|
||||||
(builder: (context) => BottomNavBar(
|
|
||||||
email: widget.email,
|
|
||||||
initTabIndex: 3,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
} else if (label == '個人資料') {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => BottomNavBar(
|
|
||||||
email: widget.email,
|
|
||||||
initTabIndex: 4,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}*/
|
|
||||||
},
|
},
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -177,3 +165,92 @@ class _MessagePageState extends State<MessagePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WebViewPage extends StatefulWidget {
|
||||||
|
final int _cameraId;
|
||||||
|
|
||||||
|
WebViewPage(this._cameraId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_WebViewPageState createState() => _WebViewPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebViewPageState extends State<WebViewPage> {
|
||||||
|
late WebViewController _webViewController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
SystemChrome.setPreferredOrientations([
|
||||||
|
DeviceOrientation.landscapeLeft,
|
||||||
|
DeviceOrientation.landscapeRight,
|
||||||
|
]);
|
||||||
|
_webViewController = WebViewController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
SystemChrome.setPreferredOrientations([
|
||||||
|
DeviceOrientation.portraitUp,
|
||||||
|
DeviceOrientation.portraitDown,
|
||||||
|
]);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
WebViewWidget(
|
||||||
|
controller: _webViewController
|
||||||
|
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||||
|
..setNavigationDelegate(
|
||||||
|
NavigationDelegate(
|
||||||
|
onProgress: (int progress) {
|
||||||
|
// Update loading bar.
|
||||||
|
},
|
||||||
|
onPageStarted: (String url) {},
|
||||||
|
onPageFinished: (String url) {},
|
||||||
|
onHttpError: (HttpResponseError error) {
|
||||||
|
print("httpError");
|
||||||
|
},
|
||||||
|
onWebResourceError: (WebResourceError error) {
|
||||||
|
print("httpResourceError");
|
||||||
|
print(error.description);
|
||||||
|
},
|
||||||
|
onNavigationRequest: (NavigationRequest request) {
|
||||||
|
if (request.url.startsWith('https://streamer.comprehensive-guardian.systems/video_feed')) {
|
||||||
|
return NavigationDecision.prevent;
|
||||||
|
}
|
||||||
|
return NavigationDecision.navigate;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..loadRequest(Uri.parse('https://streamer.comprehensive-guardian.systems/${widget._cameraId}')),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomRight,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 45.0,
|
||||||
|
height: 45.0,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
|
onPressed: () {
|
||||||
|
SystemChrome.setPreferredOrientations([
|
||||||
|
DeviceOrientation.portraitUp,
|
||||||
|
DeviceOrientation.portraitDown,
|
||||||
|
]);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Icon(Icons.arrow_back_ios),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,26 +1,32 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:mysql_client/mysql_client.dart';
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:topic/HomePage.dart';
|
||||||
import 'package:topic/NoSwipeBackRoute.dart';
|
import 'package:topic/NoSwipeBackRoute.dart';
|
||||||
import 'package:topic/main.dart';
|
import 'package:topic/main.dart';
|
||||||
/*void main() {
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
runApp(MaterialApp(
|
|
||||||
home: PersonalInfo(),
|
import 'generated/l10n.dart';
|
||||||
));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
class PersonalInfo extends StatefulWidget {
|
class PersonalInfo extends StatefulWidget {
|
||||||
final String email; // 接收來自上個頁面的 email
|
final String email; // 接收來自上個頁面的 email
|
||||||
PersonalInfo({required this.email});
|
PersonalInfo({required this.email});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_PersonalInfoState createState() => _PersonalInfoState();
|
_PersonalInfoState createState() => _PersonalInfoState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PersonalInfoState extends State<PersonalInfo> {
|
class _PersonalInfoState extends State<PersonalInfo> {
|
||||||
String _name = '', _phone = '', _gender = '', _address='', _email = '', _password = '';
|
String _username = '', _realname = '', _phone = '', _gender = '', _address = '', _email = '', _password = '';
|
||||||
|
|
||||||
bool _isEditing = false; //是否為編輯狀態
|
bool _isEditing = false; //是否為編輯狀態
|
||||||
final TextEditingController _nameController = TextEditingController();
|
bool _passwordNotVisible = true;
|
||||||
|
|
||||||
|
final TextEditingController _realnameController = TextEditingController();
|
||||||
final TextEditingController _phoneController = TextEditingController();
|
final TextEditingController _phoneController = TextEditingController();
|
||||||
final TextEditingController _genderController = TextEditingController();
|
final TextEditingController _genderController = TextEditingController();
|
||||||
final TextEditingController _addressController = TextEditingController();
|
final TextEditingController _addressController = TextEditingController();
|
||||||
@@ -29,19 +35,15 @@ class _PersonalInfoState extends State<PersonalInfo> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
//初始化
|
|
||||||
super.initState();
|
super.initState();
|
||||||
print('狀態:$_isEditing');
|
|
||||||
_fetchData();
|
_fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchData() async {
|
void _fetchData() async {
|
||||||
//MYSQL
|
print('傳遞過來的 email: ${widget.email}');
|
||||||
print('傳遞過來的 email: ${widget.email}'); // 打印 email 來確認它是否正確傳遞
|
|
||||||
|
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
//127.0.0.1 10.0.2.2
|
|
||||||
port: 33061,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: 'Topic@2024',
|
password: 'Topic@2024',
|
||||||
@@ -51,21 +53,20 @@ class _PersonalInfoState extends State<PersonalInfo> {
|
|||||||
await conn.connect();
|
await conn.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
print('ok');
|
var result = await conn.execute(
|
||||||
print('狀態:$_isEditing');
|
'SELECT * FROM HomeLogin WHERE homeemail = :email',
|
||||||
var result = await conn.execute('SELECT * FROM HomeLogin WHERE homeemail = :email',
|
{'email': widget.email},
|
||||||
{'email': widget.email}, // 傳入參數 email
|
|
||||||
);
|
);
|
||||||
if (result.rows.isNotEmpty) {
|
if (result.rows.isNotEmpty) {
|
||||||
//有資料
|
|
||||||
var row = result.rows.first;
|
var row = result.rows.first;
|
||||||
setState(() {
|
setState(() {
|
||||||
_name = row.colAt(0)??'';//如果沒有資料就是空直
|
_username = row.colAt(0) ?? '';
|
||||||
_phone = row.colAt(1)??'';
|
_realname = row.colAt(1) ?? '';
|
||||||
_gender = row.colAt(2)??'';
|
_phone = row.colAt(2) ?? '';
|
||||||
_address = row.colAt(3)??'';
|
_gender = row.colAt(3) ?? '';
|
||||||
_email = row.colAt(4)??'';
|
_address = row.colAt(4) ?? '';
|
||||||
_password = row.colAt(5)??'';
|
_email = row.colAt(5) ?? '';
|
||||||
|
_password = row.colAt(6) ?? '';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -78,8 +79,7 @@ class _PersonalInfoState extends State<PersonalInfo> {
|
|||||||
void _toggleEdit() {
|
void _toggleEdit() {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (!_isEditing) {
|
if (!_isEditing) {
|
||||||
// 當進入編輯模式時,將當前的資料賦予控制器
|
_realnameController.text = _realname;
|
||||||
_nameController.text = _name;
|
|
||||||
_phoneController.text = _phone;
|
_phoneController.text = _phone;
|
||||||
_genderController.text = _gender;
|
_genderController.text = _gender;
|
||||||
_addressController.text = _address;
|
_addressController.text = _address;
|
||||||
@@ -87,13 +87,12 @@ class _PersonalInfoState extends State<PersonalInfo> {
|
|||||||
_passwordController.text = _password;
|
_passwordController.text = _password;
|
||||||
}
|
}
|
||||||
_isEditing = !_isEditing;
|
_isEditing = !_isEditing;
|
||||||
print('編輯狀態:$_isEditing');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveChanges() async {
|
void _saveChanges() async {
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
port: 33061,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: 'Topic@2024',
|
password: 'Topic@2024',
|
||||||
@@ -104,185 +103,182 @@ class _PersonalInfoState extends State<PersonalInfo> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
'UPDATE HomeLogin SET homeName = :homeName, homePhone = :homePhone, homeGender = :homeGender, homeAddress = :homeAddress, homeEmail = :new_email, homePassword = :homePassword WHERE homeEmail = :old_email',
|
'UPDATE HomeLogin SET homeRealName = :homeRealName , homePhone = :homePhone, homeGender = :homeGender, homeAddress = :homeAddress, homeEmail = :new_email, homePassword = :homePassword WHERE homeEmail = :old_email',
|
||||||
{
|
{
|
||||||
'homeName':_nameController.text,
|
'homeRealName': _realnameController.text,
|
||||||
'homePhone': _phoneController.text,
|
'homePhone': _phoneController.text,
|
||||||
'homeGender': _genderController.text,
|
'homeGender': _genderController.text,
|
||||||
'homeAddress': _addressController.text,
|
'homeAddress': _addressController.text,
|
||||||
'new_email': _emailController.text,
|
'new_email': _emailController.text,
|
||||||
'old_email': widget.email, // 用戶的舊 email 作為查找條件
|
'old_email': widget.email,
|
||||||
'homePassword': _passwordController.text,
|
'homePassword': _passwordController.text,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
_name = _nameController.text;
|
_realname = _realnameController.text;
|
||||||
_phone = _phoneController.text;
|
_phone = _phoneController.text;
|
||||||
_gender = _genderController.text;
|
_gender = _genderController.text;
|
||||||
_address = _addressController.text;
|
_address = _addressController.text;
|
||||||
_email = _emailController.text;
|
_email = _emailController.text;
|
||||||
_password = _passwordController.text;
|
_password = _passwordController.text;
|
||||||
|
_isEditing = false;
|
||||||
// 清空控制器內容
|
print('有道這裡1');
|
||||||
_nameController.clear();
|
|
||||||
_phoneController.clear();
|
|
||||||
_genderController.clear();
|
|
||||||
_addressController.clear();
|
|
||||||
_emailController.clear();
|
|
||||||
_passwordController.clear();
|
|
||||||
|
|
||||||
_isEditing = false; // 退出編輯
|
|
||||||
print('儲存狀態:$_isEditing');
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error: $e');
|
print('Error: $e');
|
||||||
} finally {
|
} finally {
|
||||||
await conn.close();
|
await conn.close();
|
||||||
await conn.close();
|
|
||||||
print('finally儲存更新狀態:$_isEditing');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loginOut() async {
|
void _loginOut() async {
|
||||||
print("loyout");
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
// remove all data in share preference(user data which save to identify user or others)
|
|
||||||
prefs.clear();
|
prefs.clear();
|
||||||
// navbar router setting
|
|
||||||
// replace screen with LoginPage and without navbar
|
|
||||||
pushReplacementWithoutNavBar(
|
pushReplacementWithoutNavBar(
|
||||||
context,
|
context,
|
||||||
NoSwipeBackRoute(
|
NoSwipeBackRoute(
|
||||||
builder: (context) => LoginPage(),
|
builder: (context) => LoginPage(),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//頁面
|
void _setLanguage(Locale locale) async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
prefs.setString("language", locale.languageCode);
|
||||||
|
}
|
||||||
|
|
||||||
//頁面
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
// appBar: AppBar(
|
|
||||||
// title: Text('個人資料'),
|
|
||||||
// backgroundColor: Color(0xFFF5E3C3),
|
|
||||||
// ),
|
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 100,
|
height: 100,
|
||||||
color: Color(0xFFF5E3C3),//背景底色
|
color: Color(0xFFF5E3C3),
|
||||||
width: double.infinity,
|
padding: EdgeInsets.only(top: 50, left: 20, right: 20),
|
||||||
padding: EdgeInsets.all(10.0),
|
child: Row(
|
||||||
child: Center(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Text(
|
children: [
|
||||||
'個人資料',
|
Text(
|
||||||
style: TextStyle(fontSize: 24, height: 5),
|
S.of(context).welcome_message(_username),
|
||||||
),
|
style: TextStyle(fontSize: 28, color: Colors.black),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.symmetric(vertical: 5), // 调整列表视图的 padding
|
padding: EdgeInsets.symmetric(vertical: 5),
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('姓名',style: TextStyle(fontSize: 20),),
|
leading: Icon(Icons.manage_accounts_outlined),
|
||||||
subtitle: Text(_name),
|
title: Text(S.of(context).basic_information_setting, style: TextStyle(fontSize: 18)),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => BasicInfoPage(
|
||||||
|
realname: _realname,
|
||||||
|
phone: _phone,
|
||||||
|
gender: _gender,
|
||||||
|
address: _address,
|
||||||
|
email: _email,
|
||||||
|
isEditing: _isEditing,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('手機'),
|
leading: Icon(Icons.lock_open_outlined),
|
||||||
subtitle: _isEditing
|
title: Text(S.of(context).account_setting, style: TextStyle(fontSize: 18)),
|
||||||
? TextField(
|
onTap: () {
|
||||||
controller: _phoneController,
|
Navigator.push(
|
||||||
onChanged: (value) {
|
context,
|
||||||
setState(() {
|
MaterialPageRoute(
|
||||||
_phone = value;
|
builder: (context) => AccountPage(
|
||||||
});
|
email: _email,
|
||||||
},
|
password: _password,
|
||||||
)
|
isEditing: _isEditing,
|
||||||
: Text(_phone),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
|
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text('性別'),
|
leading: Icon(Icons.language_outlined),
|
||||||
subtitle: _isEditing
|
title: Text(S.of(context).language_setting, style: TextStyle(fontSize: 18)),
|
||||||
? TextField(
|
onTap: () {
|
||||||
controller: _genderController,
|
if(S.current.login_button == 'Login'){
|
||||||
onChanged: (value) {
|
S.load(Locale('zh_Hant'));
|
||||||
setState(() {
|
_setLanguage(Locale('zh_Hant'));
|
||||||
_gender = value;
|
}else{
|
||||||
});
|
S.load(Locale('en', 'US'));
|
||||||
},
|
_setLanguage(Locale('en', 'US'));
|
||||||
)
|
|
||||||
: Text(_gender),
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
ListTile(
|
|
||||||
title: Text('地址'),
|
|
||||||
subtitle: _isEditing
|
|
||||||
? TextField(
|
|
||||||
controller: _addressController,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_address = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Text(_address),
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
ListTile(
|
|
||||||
title: Text('電子信箱'),
|
|
||||||
subtitle: _isEditing
|
|
||||||
? TextField(
|
|
||||||
controller: _emailController,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_email = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Text(_email),
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
ListTile(
|
|
||||||
title: Text('密碼'),
|
|
||||||
subtitle: _isEditing
|
|
||||||
? TextField(
|
|
||||||
controller: _passwordController,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_password = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Text(_password),
|
|
||||||
),
|
|
||||||
Divider(),
|
|
||||||
SizedBox(height: 20,width: 60,),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (_isEditing) {
|
|
||||||
_saveChanges(); // 儲存變更
|
|
||||||
} else {
|
|
||||||
_toggleEdit(); // 進入編輯模式
|
|
||||||
}
|
}
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
// Android-specific code
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(S.of(context).language_setting_alert_title),
|
||||||
|
content: Text(S.of(context).language_alert_android),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||||
|
},
|
||||||
|
child: Text(S.of(context).confirm),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// iOS-specific code
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(S.of(context).language_setting_alert_title),
|
||||||
|
content: Text(S.of(context).language_alert),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Text(S.of(context).cancel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
// SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||||
|
pushReplacementWithoutNavBar(context, MaterialPageRoute(builder:(context) => HomePage(
|
||||||
|
email: widget.email)),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(S.of(context).confirm),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text(_isEditing ? '儲存變更' : '修改資料'),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFFF5E3C3), // 無背景颜色
|
|
||||||
textStyle: TextStyle(fontSize: 18),
|
|
||||||
shadowColor: Colors.transparent, // 去除陰影
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 10,width: 60,),
|
Divider(),
|
||||||
ElevatedButton(
|
ListTile(
|
||||||
onPressed: () {
|
leading: Icon(Icons.local_phone_rounded),
|
||||||
_loginOut();
|
title: Text(S.of(context).call_phone, style: TextStyle(fontSize: 18)),
|
||||||
|
onTap: () {
|
||||||
|
_showDialerDialog(context); // 顯示對話框,確認是否撥打電話
|
||||||
},
|
},
|
||||||
child: Text('登出'),
|
),
|
||||||
|
Divider(),
|
||||||
|
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _loginOut,
|
||||||
|
child: Text(S.of(context).logout_button),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
backgroundColor: Color(0xFFF5E3C3),
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
textStyle: TextStyle(fontSize: 18),
|
textStyle: TextStyle(fontSize: 18),
|
||||||
@@ -297,3 +293,368 @@ class _PersonalInfoState extends State<PersonalInfo> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BasicInfoPage 類別
|
||||||
|
class BasicInfoPage extends StatefulWidget {
|
||||||
|
String realname;
|
||||||
|
String phone;
|
||||||
|
String gender;
|
||||||
|
String address;
|
||||||
|
String email;
|
||||||
|
bool isEditing;
|
||||||
|
|
||||||
|
BasicInfoPage({
|
||||||
|
required this.realname,
|
||||||
|
required this.phone,
|
||||||
|
required this.gender,
|
||||||
|
required this.address,
|
||||||
|
required this.email,
|
||||||
|
required this.isEditing,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BasicInfoPageState createState() => _BasicInfoPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BasicInfoPageState extends State<BasicInfoPage> {
|
||||||
|
late TextEditingController _realnameController;
|
||||||
|
late TextEditingController _phoneController;
|
||||||
|
late TextEditingController _genderController;
|
||||||
|
late TextEditingController _addressController;
|
||||||
|
|
||||||
|
bool _isEditing = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_realnameController = TextEditingController(text: widget.realname);
|
||||||
|
_phoneController = TextEditingController(text: widget.phone);
|
||||||
|
_genderController = TextEditingController(text: widget.gender);
|
||||||
|
_addressController = TextEditingController(text: widget.address);
|
||||||
|
_isEditing = widget.isEditing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleEdit() {
|
||||||
|
setState(() {
|
||||||
|
_isEditing = !_isEditing;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveChanges() async {
|
||||||
|
final conn = await MySQLConnection.createConnection(
|
||||||
|
host: 'comprehensive-guardian.systems',
|
||||||
|
port: 33061,
|
||||||
|
userName: 'root',
|
||||||
|
password: 'Topic@2024',
|
||||||
|
databaseName: 'care',
|
||||||
|
);
|
||||||
|
|
||||||
|
await conn.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await conn.execute(
|
||||||
|
'UPDATE HomeLogin SET homeRealName = :homeRealName, homePhone = :homePhone, homeGender = :homeGender, homeAddress = :homeAddress WHERE homeEmail = :old_email',
|
||||||
|
{
|
||||||
|
'homeRealName': _realnameController.text,
|
||||||
|
'homePhone': _phoneController.text,
|
||||||
|
'homeGender': _genderController.text,
|
||||||
|
'homeAddress': _addressController.text,
|
||||||
|
'old_email': widget.email,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
widget.realname = _realnameController.text;
|
||||||
|
widget.phone = _phoneController.text;
|
||||||
|
widget.gender = _genderController.text;
|
||||||
|
widget.address = _addressController.text;
|
||||||
|
_isEditing = false;
|
||||||
|
});
|
||||||
|
print('successsssssssssssssss');
|
||||||
|
} catch (e) {
|
||||||
|
print('Error: $e');
|
||||||
|
} finally {
|
||||||
|
await conn.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 100,
|
||||||
|
color: Color(0xFFF5E3C3),
|
||||||
|
width: double.infinity,
|
||||||
|
padding: EdgeInsets.only(left: 0.0, top: 50.0), // 調整左右和底部的間距
|
||||||
|
child:
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero, // 移除 ListTile 內的預設 padding
|
||||||
|
leading: IconButton(
|
||||||
|
icon: Icon(Icons.arrow_back_outlined, size: 28),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),title: Text(S.of(context).basic_information_setting, style: TextStyle(fontSize: 28)),
|
||||||
|
onTap: () {
|
||||||
|
},
|
||||||
|
),),
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(S.of(context).name),
|
||||||
|
subtitle: _isEditing
|
||||||
|
? TextField(
|
||||||
|
controller: _realnameController,
|
||||||
|
)
|
||||||
|
: Text(_realnameController.text),//Controller.text才會跟著修正更新
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
title: Text(S.of(context).phone),
|
||||||
|
subtitle: _isEditing
|
||||||
|
? TextField(
|
||||||
|
controller: _phoneController,
|
||||||
|
)
|
||||||
|
: Text(_phoneController.text),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
title: Text(S.of(context).gender),
|
||||||
|
subtitle: _isEditing
|
||||||
|
? TextField(
|
||||||
|
controller: _genderController,
|
||||||
|
)
|
||||||
|
: Text(_genderController.text),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
title: Text(S.of(context).address),
|
||||||
|
subtitle: _isEditing
|
||||||
|
? TextField(
|
||||||
|
controller: _addressController,
|
||||||
|
)
|
||||||
|
: Text(_addressController.text),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_isEditing) {
|
||||||
|
_saveChanges();
|
||||||
|
} else {
|
||||||
|
_toggleEdit();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(_isEditing ? S.of(context).save : S.of(context).edit),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BasicInfoPage 類別
|
||||||
|
class AccountPage extends StatefulWidget {
|
||||||
|
String email;
|
||||||
|
String password;
|
||||||
|
bool isEditing;
|
||||||
|
|
||||||
|
AccountPage({
|
||||||
|
required this.email,
|
||||||
|
required this.password,
|
||||||
|
required this.isEditing,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AccountPageState createState() => _AccountPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AccountPageState extends State<AccountPage> {
|
||||||
|
late TextEditingController _emailController;
|
||||||
|
late TextEditingController _passwordController;
|
||||||
|
|
||||||
|
bool _isEditing = false;
|
||||||
|
bool _passwordNotVisible = true; // 需要加上這行,來處理密碼可見性切換
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_emailController = TextEditingController(text: widget.email);
|
||||||
|
_passwordController = TextEditingController(text: widget.password);
|
||||||
|
_isEditing = widget.isEditing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleEdit() {
|
||||||
|
setState(() {
|
||||||
|
_isEditing = !_isEditing;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveChanges() async {
|
||||||
|
final conn = await MySQLConnection.createConnection(
|
||||||
|
host: 'comprehensive-guardian.systems',
|
||||||
|
port: 33061,
|
||||||
|
userName: 'root',
|
||||||
|
password: 'Topic@2024',
|
||||||
|
databaseName: 'care',
|
||||||
|
);
|
||||||
|
|
||||||
|
await conn.connect();
|
||||||
|
print('connectttttttttttttttttttttttttt');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await conn.execute(
|
||||||
|
'UPDATE HomeLogin SET homeEmail = :homeEmail,homePassword= :homePassword WHERE homeEmail = :old_email',
|
||||||
|
{
|
||||||
|
'homeEmail': _emailController.text,
|
||||||
|
'homePassword': _passwordController.text,
|
||||||
|
'old_email': widget.email,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
widget.email = _emailController.text;
|
||||||
|
widget.password = _passwordController.text;
|
||||||
|
_isEditing = false;
|
||||||
|
});
|
||||||
|
print('資料更新成功');
|
||||||
|
} catch (e) {
|
||||||
|
print('Error: $e');
|
||||||
|
} finally {
|
||||||
|
await conn.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 100,
|
||||||
|
color: Color(0xFFF5E3C3),
|
||||||
|
width: double.infinity,
|
||||||
|
padding: EdgeInsets.only(left: 0.0, top: 50.0), // 調整左右和底部的間距
|
||||||
|
child:
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero, // 移除 ListTile 內的預設 padding
|
||||||
|
leading: IconButton(
|
||||||
|
icon: Icon(Icons.arrow_back_outlined, size: 28),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
title: Text(S.of(context).account_setting, style: TextStyle(fontSize: 28)),
|
||||||
|
onTap: () {
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: Text(S.of(context).email),
|
||||||
|
subtitle: _isEditing
|
||||||
|
? TextField(
|
||||||
|
controller: _emailController,
|
||||||
|
)
|
||||||
|
: Text(_emailController.text),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
ListTile(
|
||||||
|
title: Text(S.of(context).password_label),
|
||||||
|
subtitle: _isEditing
|
||||||
|
? TextField(
|
||||||
|
controller: _passwordController,
|
||||||
|
obscureText: _passwordNotVisible,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(_passwordNotVisible ? Icons.visibility : Icons.visibility_off),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_passwordNotVisible = !_passwordNotVisible;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(_passwordController.text),
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_isEditing) {
|
||||||
|
_saveChanges();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_toggleEdit();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(_isEditing ? S.of(context).save : S.of(context).edit),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFFF5E3C3),
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
shadowColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _launchDialer(BuildContext context, String phoneNumber) async {
|
||||||
|
final Uri launchUri = Uri(
|
||||||
|
scheme: 'tel',
|
||||||
|
path: phoneNumber,
|
||||||
|
);
|
||||||
|
if (await canLaunch(launchUri.toString())) {
|
||||||
|
await launch(launchUri.toString());
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('無法撥打電話至 $phoneNumber')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 顯示確認撥打電話的對話框
|
||||||
|
void _showDialerDialog(BuildContext context) {
|
||||||
|
String phoneNumber = "123456789";
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text('撥打電話'),
|
||||||
|
content: Text('你確定要撥打電話至 $phoneNumber 嗎?'),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text('關閉'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
_launchDialer(context, phoneNumber);//跳至撥打電話
|
||||||
|
},
|
||||||
|
child: Text('撥打電話'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import 'package:mailer/mailer.dart';
|
|||||||
import 'package:mailer/smtp_server.dart';
|
import 'package:mailer/smtp_server.dart';
|
||||||
import 'package:validators/validators.dart' as validator;
|
import 'package:validators/validators.dart' as validator;
|
||||||
|
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(MaterialApp(
|
runApp(MaterialApp(
|
||||||
home: RegisterPage(),
|
home: RegisterPage(),
|
||||||
@@ -18,6 +20,7 @@ class RegisterPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _RegisterPageState extends State<RegisterPage> {
|
class _RegisterPageState extends State<RegisterPage> {
|
||||||
|
late FocusNode _nameFocusNode, _emailFocusNode, _passwordFocusNode, _codeFocusNode;
|
||||||
final TextEditingController _nameController = TextEditingController();
|
final TextEditingController _nameController = TextEditingController();
|
||||||
final TextEditingController _emailController = TextEditingController();
|
final TextEditingController _emailController = TextEditingController();
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
@@ -33,11 +36,15 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_fetchData();
|
_fetchData();
|
||||||
|
_nameFocusNode = FocusNode();
|
||||||
|
_emailFocusNode = FocusNode();
|
||||||
|
_passwordFocusNode = FocusNode();
|
||||||
|
_codeFocusNode = FocusNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchData() async {
|
void _fetchData() async {
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
port: 33061,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: 'Topic@2024',
|
password: 'Topic@2024',
|
||||||
@@ -73,16 +80,16 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
|
|
||||||
Future<void> _sendEmail(String recipientEmail, String code) async {
|
Future<void> _sendEmail(String recipientEmail, String code) async {
|
||||||
final smtpServer = SmtpServer(
|
final smtpServer = SmtpServer(
|
||||||
'smtp.gmail.com',
|
'smtp.mail.me.com',
|
||||||
port: 587,
|
port: 587,
|
||||||
username: 'kueikuei8011@gmail.com',
|
username: 'ltesr125124015@icloud.com',
|
||||||
password: 'yqns onwf tydq obzl',
|
password: 'vwtp-bruz-xiav-rjee',
|
||||||
ssl: false,
|
ssl: false,
|
||||||
allowInsecure: false,
|
allowInsecure: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
final message = Message()
|
final message = Message()
|
||||||
..from = Address('kueikuei8011@gmail.com', 'Kuei')
|
..from = Address('user_manager@comprehensive-guardian.systems', '全方位照護守護者')
|
||||||
..recipients.add(recipientEmail)
|
..recipients.add(recipientEmail)
|
||||||
..subject = '您的驗證碼'
|
..subject = '您的驗證碼'
|
||||||
..text = '您的驗證碼是: $code';
|
..text = '您的驗證碼是: $code';
|
||||||
@@ -90,6 +97,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
try {
|
try {
|
||||||
await send(message, smtpServer);
|
await send(message, smtpServer);
|
||||||
print('驗證碼發送成功');
|
print('驗證碼發送成功');
|
||||||
|
print('生成的驗證碼是: $code');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('驗證碼發送失敗: $e');
|
print('驗證碼發送失敗: $e');
|
||||||
}
|
}
|
||||||
@@ -99,13 +107,14 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
final email = _emailController.text;
|
final email = _emailController.text;
|
||||||
if (email.isEmpty) {
|
if (email.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('請輸入電子信箱地址')),
|
SnackBar(content: Text(S.of(context).register_error_email_empty)),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (!validator.isEmail(email)) {
|
} else if (!validator.isEmail(email)) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('請輸入正確的電子信箱')),
|
SnackBar(content: Text(S.of(context).register_error_email_invalid)),
|
||||||
);
|
);
|
||||||
|
_emailFocusNode.requestFocus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,39 +123,83 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
_startTimer();
|
_startTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isDataCorrect() {
|
||||||
|
String name = _nameController.text;
|
||||||
|
String email = _emailController.text;
|
||||||
|
String password = _passwordController.text;
|
||||||
|
String code = _codeController.text;
|
||||||
|
|
||||||
|
if (name == '') {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(S.of(context).register_error_username_empty)),
|
||||||
|
);
|
||||||
|
_nameFocusNode.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (email == '') {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(S.of(context).register_error_email_empty)),
|
||||||
|
);
|
||||||
|
_emailFocusNode.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password == '') {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(S.of(context).register_error_password_empty)),
|
||||||
|
);
|
||||||
|
_passwordFocusNode.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_generatedCode == ''){
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(S.of(context).register_error_verificationCode_not_send)),
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == '' || code != _generatedCode) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(S.of(context).register_error_verificationCode_not_match)),
|
||||||
|
);
|
||||||
|
_codeFocusNode.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void registerBtn() async {
|
void registerBtn() async {
|
||||||
// TODO: 驗證碼未實作驗證
|
if (!_isDataCorrect()) return;
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '10.0.2.2',
|
host: 'comprehensive-guardian.systems',
|
||||||
port: 3306,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: '0000',
|
password: 'Topic@2024',
|
||||||
databaseName: 'testdb',
|
databaseName: 'care',
|
||||||
);
|
);
|
||||||
await conn.connect();
|
await conn.connect();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String name = _nameController.text;
|
String name = _nameController.text;
|
||||||
String email = _emailController.text;
|
String email = _emailController.text;
|
||||||
String password = _passwordController.text;
|
String password = _passwordController.text;
|
||||||
|
|
||||||
var result = await conn.execute(
|
var result = await conn.execute(
|
||||||
'SELECT * FROM users WHERE email = :email AND password = :password',
|
'SELECT * FROM HomeLogin WHERE homeEmail = :email OR homeUserName = :name',
|
||||||
{'email': email, 'password': password},
|
{'email': email, 'name': name},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.rows.isNotEmpty) {
|
if (result.rows.isNotEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('登入失敗:帳號或密碼錯誤')),
|
SnackBar(content: Text(S.of(context).register_error_usernameOrEmail_exist)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await conn.execute(
|
await conn.execute(
|
||||||
'INSERT INTO users (name, email, password) VALUES (:name, :email, :password)',
|
'INSERT INTO HomeLogin (homeUserName, homeEmail, homePassword) VALUES (:name, :email, :password)',
|
||||||
{'name': name, 'email': email, 'password': password},
|
{'name': name, 'email': email, 'password': password},
|
||||||
);
|
);
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('註冊成功!')),
|
SnackBar(content: Text(S.of(context).register_success)),
|
||||||
);
|
);
|
||||||
|
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
@@ -163,7 +216,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('資料庫錯誤: $e');
|
print('資料庫錯誤: $e');
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('登入失敗:系統錯誤')),
|
SnackBar(content: Text(S.of(context).register_error_server_error)),
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
await conn.close();
|
await conn.close();
|
||||||
@@ -172,8 +225,13 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_timer?.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
_timer?.cancel();
|
||||||
|
|
||||||
|
_nameFocusNode.dispose();
|
||||||
|
_emailFocusNode.dispose();
|
||||||
|
_passwordFocusNode.dispose();
|
||||||
|
_codeFocusNode.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -197,7 +255,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'全方位照護守護者',
|
S.of(context).app_name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 32,
|
fontSize: 32,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
@@ -207,15 +265,17 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _nameController,
|
controller: _nameController,
|
||||||
|
focusNode: _nameFocusNode,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
prefixIcon: Icon(Icons.person_outlined),
|
prefixIcon: Icon(Icons.person_outlined),
|
||||||
labelText: '用戶姓名',
|
labelText: '使用者名稱',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _emailController,
|
controller: _emailController,
|
||||||
|
focusNode: _emailFocusNode,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
prefixIcon: Icon(Icons.email_outlined),
|
prefixIcon: Icon(Icons.email_outlined),
|
||||||
@@ -226,6 +286,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
|
focusNode: _passwordFocusNode,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
prefixIcon: Icon(Icons.lock_outlined),
|
prefixIcon: Icon(Icons.lock_outlined),
|
||||||
@@ -235,7 +296,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
: Icons.visibility_off),
|
: Icons.visibility_off),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(
|
setState(
|
||||||
() {
|
() {
|
||||||
_passwordNotVisible = !_passwordNotVisible;
|
_passwordNotVisible = !_passwordNotVisible;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -251,6 +312,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _codeController,
|
controller: _codeController,
|
||||||
|
focusNode: _codeFocusNode,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: '驗證碼',
|
labelText: '驗證碼',
|
||||||
hintText: '填寫驗證碼',
|
hintText: '填寫驗證碼',
|
||||||
@@ -315,11 +377,33 @@ class VerifyPage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Verify Email'),
|
title: Text(S.of(context).register_success),
|
||||||
),
|
),
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Text('Email: $email'),
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text('Email: $email'),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
// 返回登入頁面
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Text(S.of(context).register_turn_back),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF4FC3F7),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 15),
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class _MySqlDataState extends State<MySqlData> {
|
|||||||
print('connect');
|
print('connect');
|
||||||
|
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
port: 33061,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: 'Topic@2024',
|
password: 'Topic@2024',
|
||||||
|
|||||||
67
lib/generated/intl/messages_all.dart
Normal file
67
lib/generated/intl/messages_all.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that looks up messages for specific locales by
|
||||||
|
// delegating to the appropriate library.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:implementation_imports, file_names, unnecessary_new
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
|
||||||
|
// ignore_for_file:argument_type_not_assignable, invalid_assignment
|
||||||
|
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:comment_references
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
import 'package:intl/src/intl_helpers.dart';
|
||||||
|
|
||||||
|
import 'messages_en.dart' as messages_en;
|
||||||
|
import 'messages_zh_Hant.dart' as messages_zh_hant;
|
||||||
|
|
||||||
|
typedef Future<dynamic> LibraryLoader();
|
||||||
|
Map<String, LibraryLoader> _deferredLibraries = {
|
||||||
|
'en': () => new SynchronousFuture(null),
|
||||||
|
'zh_Hant': () => new SynchronousFuture(null),
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageLookupByLibrary? _findExact(String localeName) {
|
||||||
|
switch (localeName) {
|
||||||
|
case 'en':
|
||||||
|
return messages_en.messages;
|
||||||
|
case 'zh_Hant':
|
||||||
|
return messages_zh_hant.messages;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// User programs should call this before using [localeName] for messages.
|
||||||
|
Future<bool> initializeMessages(String localeName) {
|
||||||
|
var availableLocale = Intl.verifiedLocale(
|
||||||
|
localeName, (locale) => _deferredLibraries[locale] != null,
|
||||||
|
onFailure: (_) => null);
|
||||||
|
if (availableLocale == null) {
|
||||||
|
return new SynchronousFuture(false);
|
||||||
|
}
|
||||||
|
var lib = _deferredLibraries[availableLocale];
|
||||||
|
lib == null ? new SynchronousFuture(false) : lib();
|
||||||
|
initializeInternalMessageLookup(() => new CompositeMessageLookup());
|
||||||
|
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
|
||||||
|
return new SynchronousFuture(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _messagesExistFor(String locale) {
|
||||||
|
try {
|
||||||
|
return _findExact(locale) != null;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
|
||||||
|
var actualLocale =
|
||||||
|
Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
|
||||||
|
if (actualLocale == null) return null;
|
||||||
|
return _findExact(actualLocale);
|
||||||
|
}
|
||||||
109
lib/generated/intl/messages_en.dart
Normal file
109
lib/generated/intl/messages_en.dart
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that provides messages for a en locale. All the
|
||||||
|
// messages from the main program should be duplicated here with the same
|
||||||
|
// function name.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||||
|
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||||
|
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
|
||||||
|
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
|
||||||
|
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
|
||||||
|
final messages = new MessageLookup();
|
||||||
|
|
||||||
|
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||||
|
|
||||||
|
class MessageLookup extends MessageLookupByLibrary {
|
||||||
|
String get localeName => 'en';
|
||||||
|
|
||||||
|
static String m0(time, reason, location) =>
|
||||||
|
"Time: ${time}\nReason: ${reason},\nLocation: ${location}\n";
|
||||||
|
|
||||||
|
static String m1(location) => "Location: ${location}";
|
||||||
|
|
||||||
|
static String m2(reason) => "Reason: ${reason}";
|
||||||
|
|
||||||
|
static String m3(time) => "Time: ${time}";
|
||||||
|
|
||||||
|
static String m4(displayIndex) => "ID: ${displayIndex}";
|
||||||
|
|
||||||
|
static String m5(username) => "Welcome, ${username}!";
|
||||||
|
|
||||||
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
|
"account_setting": MessageLookupByLibrary.simpleMessage("Account"),
|
||||||
|
"additional_information":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Knowledge"),
|
||||||
|
"address": MessageLookupByLibrary.simpleMessage("Address"),
|
||||||
|
"app_name":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Comprehensive Care Guardian"),
|
||||||
|
"basic_information_setting":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Basic Information"),
|
||||||
|
"call_phone":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Call Emergency Phone"),
|
||||||
|
"cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
||||||
|
"confirm": MessageLookupByLibrary.simpleMessage("Confirm"),
|
||||||
|
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
|
||||||
|
"email": MessageLookupByLibrary.simpleMessage("Email"),
|
||||||
|
"email_label": MessageLookupByLibrary.simpleMessage("Email/Username"),
|
||||||
|
"fall_detail": m0,
|
||||||
|
"fall_detail_location": m1,
|
||||||
|
"fall_detail_reason": m2,
|
||||||
|
"fall_detail_time": m3,
|
||||||
|
"fall_detail_title":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Fall Detail"),
|
||||||
|
"fall_id": m4,
|
||||||
|
"fall_record": MessageLookupByLibrary.simpleMessage("Fall Record"),
|
||||||
|
"fall_record_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("No fall record"),
|
||||||
|
"forgot_password":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Forgot password"),
|
||||||
|
"gender": MessageLookupByLibrary.simpleMessage("Gender"),
|
||||||
|
"home": MessageLookupByLibrary.simpleMessage("Home"),
|
||||||
|
"language_alert": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Return to the home page to complete the language setting(Confirm to redirect), otherwise some languages will not be updated in real time!"),
|
||||||
|
"language_alert_android": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Reopen the APP to complete the language setting, otherwise some languages will not be updated in real time!"),
|
||||||
|
"language_setting": MessageLookupByLibrary.simpleMessage("Language"),
|
||||||
|
"language_setting_alert_title":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Language Setting"),
|
||||||
|
"login_button": MessageLookupByLibrary.simpleMessage("Login"),
|
||||||
|
"logout_button": MessageLookupByLibrary.simpleMessage("Logout"),
|
||||||
|
"name": MessageLookupByLibrary.simpleMessage("Name"),
|
||||||
|
"password_label": MessageLookupByLibrary.simpleMessage("Password"),
|
||||||
|
"personal_information": MessageLookupByLibrary.simpleMessage("Profile"),
|
||||||
|
"phone": MessageLookupByLibrary.simpleMessage("Phone Number"),
|
||||||
|
"realtime_video": MessageLookupByLibrary.simpleMessage("Live View"),
|
||||||
|
"register_button": MessageLookupByLibrary.simpleMessage("Register"),
|
||||||
|
"register_error_email_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Please enter your email"),
|
||||||
|
"register_error_email_invalid":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Please enter a valid email"),
|
||||||
|
"register_error_password_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Please enter your password"),
|
||||||
|
"register_error_server_error": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Register Failed: Server error, please try again later"),
|
||||||
|
"register_error_usernameOrEmail_exist":
|
||||||
|
MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Username or email already exists, please try again"),
|
||||||
|
"register_error_username_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Please enter your username"),
|
||||||
|
"register_error_verificationCode_not_match":
|
||||||
|
MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Verification code does not match"),
|
||||||
|
"register_error_verificationCode_not_send":
|
||||||
|
MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Please send the verification code first"),
|
||||||
|
"register_success":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Register Success"),
|
||||||
|
"register_turn_back":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Return to Login Page"),
|
||||||
|
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
||||||
|
"switch_camera": MessageLookupByLibrary.simpleMessage("Switch View"),
|
||||||
|
"welcome_message": m5
|
||||||
|
};
|
||||||
|
}
|
||||||
98
lib/generated/intl/messages_zh_Hant.dart
Normal file
98
lib/generated/intl/messages_zh_Hant.dart
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that provides messages for a zh_Hant locale. All the
|
||||||
|
// messages from the main program should be duplicated here with the same
|
||||||
|
// function name.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||||
|
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||||
|
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
|
||||||
|
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
|
||||||
|
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
|
||||||
|
final messages = new MessageLookup();
|
||||||
|
|
||||||
|
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||||
|
|
||||||
|
class MessageLookup extends MessageLookupByLibrary {
|
||||||
|
String get localeName => 'zh_Hant';
|
||||||
|
|
||||||
|
static String m0(time, reason, location) =>
|
||||||
|
"跌倒時間: ${time}\n跌倒原因: ${reason},\n跌倒地點: ${location}\n";
|
||||||
|
|
||||||
|
static String m1(location) => "跌倒地點: ${location}";
|
||||||
|
|
||||||
|
static String m2(reason) => "跌倒原因: ${reason}";
|
||||||
|
|
||||||
|
static String m3(time) => "跌倒時間: ${time}";
|
||||||
|
|
||||||
|
static String m4(displayIndex) => "編號: ${displayIndex}";
|
||||||
|
|
||||||
|
static String m5(username) => "${username} 您好!";
|
||||||
|
|
||||||
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
|
"account_setting": MessageLookupByLibrary.simpleMessage("帳號設定"),
|
||||||
|
"additional_information": MessageLookupByLibrary.simpleMessage("知識補充"),
|
||||||
|
"address": MessageLookupByLibrary.simpleMessage("地址"),
|
||||||
|
"app_name": MessageLookupByLibrary.simpleMessage("全方位照護守護者"),
|
||||||
|
"basic_information_setting":
|
||||||
|
MessageLookupByLibrary.simpleMessage("基本資料"),
|
||||||
|
"call_phone": MessageLookupByLibrary.simpleMessage("撥打電話"),
|
||||||
|
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
|
||||||
|
"confirm": MessageLookupByLibrary.simpleMessage("確認"),
|
||||||
|
"edit": MessageLookupByLibrary.simpleMessage("修改資料"),
|
||||||
|
"email": MessageLookupByLibrary.simpleMessage("電子信箱"),
|
||||||
|
"email_label": MessageLookupByLibrary.simpleMessage("電子信箱/帳號"),
|
||||||
|
"fall_detail": m0,
|
||||||
|
"fall_detail_location": m1,
|
||||||
|
"fall_detail_reason": m2,
|
||||||
|
"fall_detail_time": m3,
|
||||||
|
"fall_detail_title": MessageLookupByLibrary.simpleMessage("詳細資料"),
|
||||||
|
"fall_id": m4,
|
||||||
|
"fall_record": MessageLookupByLibrary.simpleMessage("跌倒紀錄"),
|
||||||
|
"fall_record_empty": MessageLookupByLibrary.simpleMessage("尚無跌倒紀錄"),
|
||||||
|
"forgot_password": MessageLookupByLibrary.simpleMessage("忘記密碼"),
|
||||||
|
"gender": MessageLookupByLibrary.simpleMessage("性別"),
|
||||||
|
"home": MessageLookupByLibrary.simpleMessage("首頁"),
|
||||||
|
"language_alert": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"跳轉回首頁以完成語言設定,否則會有部分語言無法即時更新!"),
|
||||||
|
"language_alert_android": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"重新開啟APP以完成語言設定,否則會有部分語言無法即時更新!"),
|
||||||
|
"language_setting": MessageLookupByLibrary.simpleMessage("語言設定"),
|
||||||
|
"language_setting_alert_title":
|
||||||
|
MessageLookupByLibrary.simpleMessage("語言設定"),
|
||||||
|
"login_button": MessageLookupByLibrary.simpleMessage("登入"),
|
||||||
|
"logout_button": MessageLookupByLibrary.simpleMessage("登出"),
|
||||||
|
"name": MessageLookupByLibrary.simpleMessage("姓名"),
|
||||||
|
"password_label": MessageLookupByLibrary.simpleMessage("密碼"),
|
||||||
|
"personal_information": MessageLookupByLibrary.simpleMessage("個人資料"),
|
||||||
|
"phone": MessageLookupByLibrary.simpleMessage("電話"),
|
||||||
|
"realtime_video": MessageLookupByLibrary.simpleMessage("即時畫面"),
|
||||||
|
"register_button": MessageLookupByLibrary.simpleMessage("立即註冊"),
|
||||||
|
"register_error_email_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("電子信箱不可為空"),
|
||||||
|
"register_error_email_invalid":
|
||||||
|
MessageLookupByLibrary.simpleMessage("請輸入有效的電子信箱地址"),
|
||||||
|
"register_error_password_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("密碼不可為空"),
|
||||||
|
"register_error_server_error":
|
||||||
|
MessageLookupByLibrary.simpleMessage("註冊失敗:系統錯誤"),
|
||||||
|
"register_error_usernameOrEmail_exist":
|
||||||
|
MessageLookupByLibrary.simpleMessage("註冊失敗:電子信箱或帳號名稱已被使用,請嘗試更換或登入"),
|
||||||
|
"register_error_username_empty":
|
||||||
|
MessageLookupByLibrary.simpleMessage("用戶姓名不可為空"),
|
||||||
|
"register_error_verificationCode_not_match":
|
||||||
|
MessageLookupByLibrary.simpleMessage("驗證碼錯誤"),
|
||||||
|
"register_error_verificationCode_not_send":
|
||||||
|
MessageLookupByLibrary.simpleMessage("請先發送驗證碼"),
|
||||||
|
"register_success": MessageLookupByLibrary.simpleMessage("註冊成功"),
|
||||||
|
"register_turn_back": MessageLookupByLibrary.simpleMessage("返回登入頁面"),
|
||||||
|
"save": MessageLookupByLibrary.simpleMessage("儲存變更"),
|
||||||
|
"switch_camera": MessageLookupByLibrary.simpleMessage("切換畫面"),
|
||||||
|
"welcome_message": m5
|
||||||
|
};
|
||||||
|
}
|
||||||
549
lib/generated/l10n.dart
Normal file
549
lib/generated/l10n.dart
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'intl/messages_all.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: Flutter Intl IDE plugin
|
||||||
|
// Made by Localizely
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
|
||||||
|
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
|
||||||
|
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
|
||||||
|
|
||||||
|
class S {
|
||||||
|
S();
|
||||||
|
|
||||||
|
static S? _current;
|
||||||
|
|
||||||
|
static S get current {
|
||||||
|
assert(_current != null,
|
||||||
|
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.');
|
||||||
|
return _current!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||||
|
|
||||||
|
static Future<S> load(Locale locale) {
|
||||||
|
final name = (locale.countryCode?.isEmpty ?? false)
|
||||||
|
? locale.languageCode
|
||||||
|
: locale.toString();
|
||||||
|
final localeName = Intl.canonicalizedLocale(name);
|
||||||
|
return initializeMessages(localeName).then((_) {
|
||||||
|
Intl.defaultLocale = localeName;
|
||||||
|
final instance = S();
|
||||||
|
S._current = instance;
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static S of(BuildContext context) {
|
||||||
|
final instance = S.maybeOf(context);
|
||||||
|
assert(instance != null,
|
||||||
|
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
|
||||||
|
return instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static S? maybeOf(BuildContext context) {
|
||||||
|
return Localizations.of<S>(context, S);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Comprehensive Care Guardian`
|
||||||
|
String get app_name {
|
||||||
|
return Intl.message(
|
||||||
|
'Comprehensive Care Guardian',
|
||||||
|
name: 'app_name',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Email/Username`
|
||||||
|
String get email_label {
|
||||||
|
return Intl.message(
|
||||||
|
'Email/Username',
|
||||||
|
name: 'email_label',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Password`
|
||||||
|
String get password_label {
|
||||||
|
return Intl.message(
|
||||||
|
'Password',
|
||||||
|
name: 'password_label',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Login`
|
||||||
|
String get login_button {
|
||||||
|
return Intl.message(
|
||||||
|
'Login',
|
||||||
|
name: 'login_button',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Logout`
|
||||||
|
String get logout_button {
|
||||||
|
return Intl.message(
|
||||||
|
'Logout',
|
||||||
|
name: 'logout_button',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Register`
|
||||||
|
String get register_button {
|
||||||
|
return Intl.message(
|
||||||
|
'Register',
|
||||||
|
name: 'register_button',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Forgot password`
|
||||||
|
String get forgot_password {
|
||||||
|
return Intl.message(
|
||||||
|
'Forgot password',
|
||||||
|
name: 'forgot_password',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please enter your email`
|
||||||
|
String get register_error_email_empty {
|
||||||
|
return Intl.message(
|
||||||
|
'Please enter your email',
|
||||||
|
name: 'register_error_email_empty',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please enter a valid email`
|
||||||
|
String get register_error_email_invalid {
|
||||||
|
return Intl.message(
|
||||||
|
'Please enter a valid email',
|
||||||
|
name: 'register_error_email_invalid',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please enter your username`
|
||||||
|
String get register_error_username_empty {
|
||||||
|
return Intl.message(
|
||||||
|
'Please enter your username',
|
||||||
|
name: 'register_error_username_empty',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please enter your password`
|
||||||
|
String get register_error_password_empty {
|
||||||
|
return Intl.message(
|
||||||
|
'Please enter your password',
|
||||||
|
name: 'register_error_password_empty',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please send the verification code first`
|
||||||
|
String get register_error_verificationCode_not_send {
|
||||||
|
return Intl.message(
|
||||||
|
'Please send the verification code first',
|
||||||
|
name: 'register_error_verificationCode_not_send',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Verification code does not match`
|
||||||
|
String get register_error_verificationCode_not_match {
|
||||||
|
return Intl.message(
|
||||||
|
'Verification code does not match',
|
||||||
|
name: 'register_error_verificationCode_not_match',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Username or email already exists, please try again`
|
||||||
|
String get register_error_usernameOrEmail_exist {
|
||||||
|
return Intl.message(
|
||||||
|
'Username or email already exists, please try again',
|
||||||
|
name: 'register_error_usernameOrEmail_exist',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Register Failed: Server error, please try again later`
|
||||||
|
String get register_error_server_error {
|
||||||
|
return Intl.message(
|
||||||
|
'Register Failed: Server error, please try again later',
|
||||||
|
name: 'register_error_server_error',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Register Success`
|
||||||
|
String get register_success {
|
||||||
|
return Intl.message(
|
||||||
|
'Register Success',
|
||||||
|
name: 'register_success',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Return to Login Page`
|
||||||
|
String get register_turn_back {
|
||||||
|
return Intl.message(
|
||||||
|
'Return to Login Page',
|
||||||
|
name: 'register_turn_back',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Live View`
|
||||||
|
String get realtime_video {
|
||||||
|
return Intl.message(
|
||||||
|
'Live View',
|
||||||
|
name: 'realtime_video',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Fall Record`
|
||||||
|
String get fall_record {
|
||||||
|
return Intl.message(
|
||||||
|
'Fall Record',
|
||||||
|
name: 'fall_record',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Switch View`
|
||||||
|
String get switch_camera {
|
||||||
|
return Intl.message(
|
||||||
|
'Switch View',
|
||||||
|
name: 'switch_camera',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Knowledge`
|
||||||
|
String get additional_information {
|
||||||
|
return Intl.message(
|
||||||
|
'Knowledge',
|
||||||
|
name: 'additional_information',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Profile`
|
||||||
|
String get personal_information {
|
||||||
|
return Intl.message(
|
||||||
|
'Profile',
|
||||||
|
name: 'personal_information',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Home`
|
||||||
|
String get home {
|
||||||
|
return Intl.message(
|
||||||
|
'Home',
|
||||||
|
name: 'home',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Welcome, {username}!`
|
||||||
|
String welcome_message(Object username) {
|
||||||
|
return Intl.message(
|
||||||
|
'Welcome, $username!',
|
||||||
|
name: 'welcome_message',
|
||||||
|
desc: '',
|
||||||
|
args: [username],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Basic Information`
|
||||||
|
String get basic_information_setting {
|
||||||
|
return Intl.message(
|
||||||
|
'Basic Information',
|
||||||
|
name: 'basic_information_setting',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Account`
|
||||||
|
String get account_setting {
|
||||||
|
return Intl.message(
|
||||||
|
'Account',
|
||||||
|
name: 'account_setting',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Language`
|
||||||
|
String get language_setting {
|
||||||
|
return Intl.message(
|
||||||
|
'Language',
|
||||||
|
name: 'language_setting',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Call Emergency Phone`
|
||||||
|
String get call_phone {
|
||||||
|
return Intl.message(
|
||||||
|
'Call Emergency Phone',
|
||||||
|
name: 'call_phone',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Name`
|
||||||
|
String get name {
|
||||||
|
return Intl.message(
|
||||||
|
'Name',
|
||||||
|
name: 'name',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Phone Number`
|
||||||
|
String get phone {
|
||||||
|
return Intl.message(
|
||||||
|
'Phone Number',
|
||||||
|
name: 'phone',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Gender`
|
||||||
|
String get gender {
|
||||||
|
return Intl.message(
|
||||||
|
'Gender',
|
||||||
|
name: 'gender',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Address`
|
||||||
|
String get address {
|
||||||
|
return Intl.message(
|
||||||
|
'Address',
|
||||||
|
name: 'address',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Edit`
|
||||||
|
String get edit {
|
||||||
|
return Intl.message(
|
||||||
|
'Edit',
|
||||||
|
name: 'edit',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Save`
|
||||||
|
String get save {
|
||||||
|
return Intl.message(
|
||||||
|
'Save',
|
||||||
|
name: 'save',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Email`
|
||||||
|
String get email {
|
||||||
|
return Intl.message(
|
||||||
|
'Email',
|
||||||
|
name: 'email',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `ID: {displayIndex}`
|
||||||
|
String fall_id(Object displayIndex) {
|
||||||
|
return Intl.message(
|
||||||
|
'ID: $displayIndex',
|
||||||
|
name: 'fall_id',
|
||||||
|
desc: '',
|
||||||
|
args: [displayIndex],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Time: {time}\nReason: {reason},\nLocation: {location}\n`
|
||||||
|
String fall_detail(Object time, Object reason, Object location) {
|
||||||
|
return Intl.message(
|
||||||
|
'Time: $time\nReason: $reason,\nLocation: $location\n',
|
||||||
|
name: 'fall_detail',
|
||||||
|
desc: '',
|
||||||
|
args: [time, reason, location],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Fall Detail`
|
||||||
|
String get fall_detail_title {
|
||||||
|
return Intl.message(
|
||||||
|
'Fall Detail',
|
||||||
|
name: 'fall_detail_title',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Reason: {reason}`
|
||||||
|
String fall_detail_reason(Object reason) {
|
||||||
|
return Intl.message(
|
||||||
|
'Reason: $reason',
|
||||||
|
name: 'fall_detail_reason',
|
||||||
|
desc: '',
|
||||||
|
args: [reason],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Location: {location}`
|
||||||
|
String fall_detail_location(Object location) {
|
||||||
|
return Intl.message(
|
||||||
|
'Location: $location',
|
||||||
|
name: 'fall_detail_location',
|
||||||
|
desc: '',
|
||||||
|
args: [location],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Time: {time}`
|
||||||
|
String fall_detail_time(Object time) {
|
||||||
|
return Intl.message(
|
||||||
|
'Time: $time',
|
||||||
|
name: 'fall_detail_time',
|
||||||
|
desc: '',
|
||||||
|
args: [time],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `No fall record`
|
||||||
|
String get fall_record_empty {
|
||||||
|
return Intl.message(
|
||||||
|
'No fall record',
|
||||||
|
name: 'fall_record_empty',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Language Setting`
|
||||||
|
String get language_setting_alert_title {
|
||||||
|
return Intl.message(
|
||||||
|
'Language Setting',
|
||||||
|
name: 'language_setting_alert_title',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Return to the home page to complete the language setting(Confirm to redirect), otherwise some languages will not be updated in real time!`
|
||||||
|
String get language_alert {
|
||||||
|
return Intl.message(
|
||||||
|
'Return to the home page to complete the language setting(Confirm to redirect), otherwise some languages will not be updated in real time!',
|
||||||
|
name: 'language_alert',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Reopen the APP to complete the language setting, otherwise some languages will not be updated in real time!`
|
||||||
|
String get language_alert_android {
|
||||||
|
return Intl.message(
|
||||||
|
'Reopen the APP to complete the language setting, otherwise some languages will not be updated in real time!',
|
||||||
|
name: 'language_alert_android',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Cancel`
|
||||||
|
String get cancel {
|
||||||
|
return Intl.message(
|
||||||
|
'Cancel',
|
||||||
|
name: 'cancel',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Confirm`
|
||||||
|
String get confirm {
|
||||||
|
return Intl.message(
|
||||||
|
'Confirm',
|
||||||
|
name: 'confirm',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
const AppLocalizationDelegate();
|
||||||
|
|
||||||
|
List<Locale> get supportedLocales {
|
||||||
|
return const <Locale>[
|
||||||
|
Locale.fromSubtags(languageCode: 'en'),
|
||||||
|
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isSupported(Locale locale) => _isSupported(locale);
|
||||||
|
@override
|
||||||
|
Future<S> load(Locale locale) => S.load(locale);
|
||||||
|
@override
|
||||||
|
bool shouldReload(AppLocalizationDelegate old) => false;
|
||||||
|
|
||||||
|
bool _isSupported(Locale locale) {
|
||||||
|
for (var supportedLocale in supportedLocales) {
|
||||||
|
if (supportedLocale.languageCode == locale.languageCode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
lib/l10n/intl_en.arb
Normal file
57
lib/l10n/intl_en.arb
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"app_name": "Comprehensive Care Guardian",
|
||||||
|
"email_label": "Email/Username",
|
||||||
|
"password_label": "Password",
|
||||||
|
"login_button": "Login",
|
||||||
|
"logout_button": "Logout",
|
||||||
|
"register_button": "Register",
|
||||||
|
"forgot_password": "Forgot password",
|
||||||
|
|
||||||
|
"register_error_email_empty": "Please enter your email",
|
||||||
|
"register_error_email_invalid": "Please enter a valid email",
|
||||||
|
"register_error_username_empty": "Please enter your username",
|
||||||
|
"register_error_password_empty": "Please enter your password",
|
||||||
|
"register_error_verificationCode_not_send": "Please send the verification code first",
|
||||||
|
"register_error_verificationCode_not_match": "Verification code does not match",
|
||||||
|
"register_error_usernameOrEmail_exist": "Username or email already exists, please try again",
|
||||||
|
"register_error_server_error": "Register Failed: Server error, please try again later",
|
||||||
|
"register_success": "Register Success",
|
||||||
|
"register_turn_back": "Return to Login Page",
|
||||||
|
|
||||||
|
"realtime_video": "Live View",
|
||||||
|
"fall_record": "Fall Record",
|
||||||
|
"switch_camera": "Switch View",
|
||||||
|
"additional_information": "Knowledge",
|
||||||
|
"personal_information": "Profile",
|
||||||
|
|
||||||
|
"home": "Home",
|
||||||
|
|
||||||
|
"welcome_message": "Welcome, {username}!",
|
||||||
|
"basic_information_setting": "Basic Information",
|
||||||
|
"account_setting": "Account",
|
||||||
|
"language_setting": "Language",
|
||||||
|
"call_phone": "Call Emergency Phone",
|
||||||
|
|
||||||
|
"name": "Name",
|
||||||
|
"phone" : "Phone Number",
|
||||||
|
"gender": "Gender",
|
||||||
|
"address": "Address",
|
||||||
|
"edit": "Edit",
|
||||||
|
"save": "Save",
|
||||||
|
|
||||||
|
"email": "Email",
|
||||||
|
|
||||||
|
"fall_id": "ID: {displayIndex}",
|
||||||
|
"fall_detail": "Time: {time}\nReason: {reason},\nLocation: {location}\n",
|
||||||
|
"fall_detail_title": "Fall Detail",
|
||||||
|
"fall_detail_reason": "Reason: {reason}",
|
||||||
|
"fall_detail_location": "Location: {location}",
|
||||||
|
"fall_detail_time": "Time: {time}",
|
||||||
|
"fall_record_empty": "No fall record",
|
||||||
|
|
||||||
|
"language_setting_alert_title": "Language Setting",
|
||||||
|
"language_alert": "Return to the home page to complete the language setting(Confirm to redirect), otherwise some languages will not be updated in real time!",
|
||||||
|
"language_alert_android": "Reopen the APP to complete the language setting, otherwise some languages will not be updated in real time!",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"confirm": "Confirm"
|
||||||
|
}
|
||||||
58
lib/l10n/intl_zh_Hant.arb
Normal file
58
lib/l10n/intl_zh_Hant.arb
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"app_name": "全方位照護守護者",
|
||||||
|
"email_label": "電子信箱/帳號",
|
||||||
|
"password_label": "密碼",
|
||||||
|
"login_button": "登入",
|
||||||
|
"logout_button": "登出",
|
||||||
|
"register_button": "立即註冊",
|
||||||
|
"forgot_password": "忘記密碼",
|
||||||
|
|
||||||
|
"register_error_email_empty": "電子信箱不可為空",
|
||||||
|
"register_error_email_invalid": "請輸入有效的電子信箱地址",
|
||||||
|
"register_error_username_empty": "用戶姓名不可為空",
|
||||||
|
"register_error_password_empty": "密碼不可為空",
|
||||||
|
"register_error_verificationCode_not_send": "請先發送驗證碼",
|
||||||
|
"register_error_verificationCode_not_match": "驗證碼錯誤",
|
||||||
|
"register_error_usernameOrEmail_exist": "註冊失敗:電子信箱或帳號名稱已被使用,請嘗試更換或登入",
|
||||||
|
"register_error_server_error": "註冊失敗:系統錯誤",
|
||||||
|
"register_success": "註冊成功",
|
||||||
|
"register_turn_back": "返回登入頁面",
|
||||||
|
|
||||||
|
"realtime_video": "即時畫面",
|
||||||
|
"fall_record": "跌倒紀錄",
|
||||||
|
"switch_camera": "切換畫面",
|
||||||
|
"additional_information": "知識補充",
|
||||||
|
"personal_information": "個人資料",
|
||||||
|
|
||||||
|
"home": "首頁",
|
||||||
|
|
||||||
|
"welcome_message": "{username} 您好!",
|
||||||
|
"basic_information_setting": "基本資料",
|
||||||
|
"account_setting": "帳號設定",
|
||||||
|
"language_setting": "語言設定",
|
||||||
|
"call_phone": "撥打電話",
|
||||||
|
|
||||||
|
"name": "姓名",
|
||||||
|
"phone": "電話",
|
||||||
|
"gender": "性別",
|
||||||
|
"address": "地址",
|
||||||
|
"edit": "修改資料",
|
||||||
|
"save": "儲存變更",
|
||||||
|
|
||||||
|
"email": "電子信箱",
|
||||||
|
|
||||||
|
"fall_id": "編號: {displayIndex}",
|
||||||
|
"fall_detail": "跌倒時間: {time}\n跌倒原因: {reason},\n跌倒地點: {location}\n",
|
||||||
|
"fall_detail_title": "詳細資料",
|
||||||
|
"fall_detail_reason": "跌倒原因: {reason}",
|
||||||
|
"fall_detail_location": "跌倒地點: {location}",
|
||||||
|
"fall_detail_time": "跌倒時間: {time}",
|
||||||
|
"fall_record_empty": "尚無跌倒紀錄",
|
||||||
|
|
||||||
|
"language_setting_alert_title": "語言設定",
|
||||||
|
"language_alert": "跳轉回首頁以完成語言設定,否則會有部分語言無法即時更新!",
|
||||||
|
"language_alert_android": "重新開啟APP以完成語言設定,否則會有部分語言無法即時更新!",
|
||||||
|
"cancel": "取消",
|
||||||
|
"confirm": "確認"
|
||||||
|
|
||||||
|
}
|
||||||
322
lib/main.dart
322
lib/main.dart
@@ -1,17 +1,36 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:topic/HomePage.dart';
|
import 'package:topic/HomePage.dart';
|
||||||
import 'package:mysql_client/mysql_client.dart';
|
import 'package:mysql_client/mysql_client.dart';
|
||||||
import 'package:topic/NoSwipeBackRoute.dart';
|
import 'package:topic/NoSwipeBackRoute.dart';
|
||||||
import 'package:topic/RegisterPage.dart';
|
import 'package:topic/RegisterPage.dart';
|
||||||
|
import 'package:topic/ForgetPasswordPage.dart';
|
||||||
|
import 'package:validators/validators.dart' as validator;
|
||||||
|
|
||||||
|
import 'generated/l10n.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(MaterialApp(
|
runApp(
|
||||||
home: LoginPage(),
|
MaterialApp(
|
||||||
));
|
localizationsDelegates: [
|
||||||
|
S.delegate,
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
], //add
|
||||||
|
supportedLocales: S.delegate.supportedLocales, // add
|
||||||
|
home: LoginPage(),
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {//ful會改變
|
class LoginPage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_LoginPageState createState() => _LoginPageState();
|
_LoginPageState createState() => _LoginPageState();
|
||||||
}
|
}
|
||||||
@@ -22,16 +41,18 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
bool _passwordNotVisible = true;
|
bool _passwordNotVisible = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {//初始化
|
void initState() {
|
||||||
|
//初始化
|
||||||
|
_setLanguage(); //設定語言
|
||||||
super.initState();
|
super.initState();
|
||||||
_fetchData();//連資料庫
|
_fetchData(); //連資料庫
|
||||||
_CheckPreLoginInfo();//確定有先前有無登入
|
_CheckPreLoginInfo(); //確定有先前有無登入
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchData() async {
|
void _fetchData() async {
|
||||||
//MYSQL
|
//MYSQL
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
// host:'10.0.2.2',
|
// host:'10.0.2.2',
|
||||||
//127.0.0.1 10.0.2.2
|
//127.0.0.1 10.0.2.2
|
||||||
port: 33061,
|
port: 33061,
|
||||||
@@ -44,12 +65,12 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
await conn.connect();
|
await conn.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _CheckPreLoginInfo() async{
|
void _CheckPreLoginInfo() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
//get user data from share preference else set empty to check not login before
|
//get user data from share preference else set empty to check not login before
|
||||||
String loginUserEmail = prefs.getString("email") ?? "";
|
String loginUserEmail = prefs.getString("email") ?? "";
|
||||||
//replace screen to HomePage if there are previous data in share preference
|
//replace screen to HomePage if there are previous data in share preference
|
||||||
if(loginUserEmail != ""){
|
if (loginUserEmail != "") {
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
NoSwipeBackRoute(
|
NoSwipeBackRoute(
|
||||||
@@ -61,145 +82,27 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setLoginInfo(String email) async{
|
void _setLanguage() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String preLanguage = prefs.getString("language") ?? "";
|
||||||
|
Locale locale = preLanguage == "" ? Localizations.localeOf(context) : Locale(preLanguage);
|
||||||
|
S.load(locale);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setLoginInfo(String email) async {
|
||||||
//save user email into share preference to let app can auto login next open
|
//save user email into share preference to let app can auto login next open
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
prefs.setString("email", email);
|
prefs.setString("email", email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
debugPaintSizeEnabled=false;
|
|
||||||
//by use PopScope and disable can Pop avoid android user pop back by back button
|
|
||||||
return PopScope(
|
|
||||||
canPop: false,
|
|
||||||
child: Scaffold(
|
|
||||||
//appBar: AppBar(
|
|
||||||
//title: Text('Demo'),
|
|
||||||
//backgroundColor: Color(0xFF81D4FA),
|
|
||||||
//),
|
|
||||||
body: SafeArea(
|
|
||||||
child: Center(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: EdgeInsets.only(bottom: 40), // 添加間距
|
|
||||||
height: 100, // 設置logo高度
|
|
||||||
child: Icon(
|
|
||||||
Icons.account_circle,
|
|
||||||
size: 100, // 設置圖標大小
|
|
||||||
color: Color(0xFF4FC3F7), // 設置圖標颜色
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'全方位照護守護者',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 32,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),//全方位照護守護者
|
|
||||||
SizedBox(height: 20),
|
|
||||||
TextField(
|
|
||||||
controller: _emailController, // 绑定電子信箱输入框的控制器
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
prefixIcon: Icon(Icons.email_outlined),//https://www.fluttericon.cn/v
|
|
||||||
labelText: '電子信箱',
|
|
||||||
),
|
|
||||||
),//電子信箱
|
|
||||||
SizedBox(height: 20),
|
|
||||||
TextField(
|
|
||||||
controller: _ageController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
prefixIcon: Icon(Icons.lock_outlined),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(_passwordNotVisible
|
|
||||||
? Icons.visibility
|
|
||||||
: Icons.visibility_off),
|
|
||||||
onPressed: () {
|
|
||||||
setState(
|
|
||||||
() {
|
|
||||||
_passwordNotVisible = !_passwordNotVisible;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
labelText: '密碼',
|
|
||||||
),
|
|
||||||
obscureText: _passwordNotVisible,
|
|
||||||
),//密碼
|
|
||||||
SizedBox(height: 20),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: loginBtn,/*() {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (context) => HomePage()),
|
|
||||||
);
|
|
||||||
},*/
|
|
||||||
child: Text(' 登入'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Color(0xFF4FC3F7),
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 50, vertical: 15),
|
|
||||||
textStyle: TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
),//登入
|
|
||||||
SizedBox(height: 10),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (context) => RegisterPage()),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text('立即註冊'),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
backgroundColor: Colors.transparent, // 無背景颜色
|
|
||||||
textStyle: TextStyle(fontSize: 18),
|
|
||||||
shadowColor: Colors.transparent, // 去除陰影
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(builder: (context) => HomePage(
|
|
||||||
email: _emailController.text,
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text('忘記密碼'),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
backgroundColor: Colors.transparent, // 無背景颜色
|
|
||||||
textStyle: TextStyle(fontSize: 18),
|
|
||||||
shadowColor: Colors.transparent, // 去除陰影
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
void loginBtn() async {
|
void loginBtn() async {
|
||||||
final conn = await MySQLConnection.createConnection(
|
final conn = await MySQLConnection.createConnection(
|
||||||
host: '203.64.84.154',
|
host: 'comprehensive-guardian.systems',
|
||||||
// host:'10.0.2.2',
|
|
||||||
//127.0.0.1 10.0.2.2
|
|
||||||
port: 33061,
|
port: 33061,
|
||||||
userName: 'root',
|
userName: 'root',
|
||||||
password: 'Topic@2024',
|
password: 'Topic@2024',
|
||||||
// password: '0000',
|
databaseName: 'care',
|
||||||
databaseName: 'care', //testdb
|
|
||||||
// databaseName: 'testdb',
|
|
||||||
);
|
);
|
||||||
await conn.connect();
|
await conn.connect();
|
||||||
|
|
||||||
@@ -212,10 +115,20 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
print('输入的密碼: $password');
|
print('输入的密碼: $password');
|
||||||
|
|
||||||
// 查詢數據庫,检查是否有匹配的帳號
|
// 查詢數據庫,检查是否有匹配的帳號
|
||||||
var result = await conn.execute(
|
IResultSet result;
|
||||||
'SELECT * FROM HomeLogin WHERE homeEmail = :email AND homePassword = :password',
|
if (validator.isEmail(email)) {
|
||||||
{'email': email, 'password': password},
|
result = await conn.execute(
|
||||||
);
|
'SELECT * FROM HomeLogin WHERE homeEmail = :email AND homePassword = :password',
|
||||||
|
{'email': email, 'password': password},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = await conn.execute(
|
||||||
|
'SELECT * FROM HomeLogin WHERE homeUserName = :email AND homePassword = :password',
|
||||||
|
{'email': email, 'password': password},
|
||||||
|
);
|
||||||
|
email = result.rows.first.colByName("homeEmail").toString();
|
||||||
|
}
|
||||||
|
|
||||||
print('查詢結果行數: ${result.rows.length}');
|
print('查詢結果行數: ${result.rows.length}');
|
||||||
|
|
||||||
@@ -247,4 +160,127 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
await conn.close();
|
await conn.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
debugPaintSizeEnabled = false;
|
||||||
|
//by use PopScope and disable can Pop avoid android user pop back by back button
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: Scaffold(
|
||||||
|
body: SafeArea(
|
||||||
|
child: Center(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 40), // 添加間距
|
||||||
|
height: 100, // 設置logo高度
|
||||||
|
child: Icon(
|
||||||
|
Icons.account_circle,
|
||||||
|
size: 100, // 設置圖標大小
|
||||||
|
color: Color(0xFF4FC3F7), // 設置圖標颜色
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
S.of(context).app_name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
), //全方位照護守護者
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextField(
|
||||||
|
controller: _emailController, // 绑定電子信箱输入框的控制器
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
prefixIcon: Icon(Icons.email_outlined),
|
||||||
|
//https://www.fluttericon.cn/v
|
||||||
|
labelText: S.of(context).email_label,
|
||||||
|
),
|
||||||
|
), //電子信箱
|
||||||
|
SizedBox(height: 20),
|
||||||
|
TextField(
|
||||||
|
controller: _ageController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
prefixIcon: Icon(Icons.lock_outlined),
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
icon: Icon(_passwordNotVisible
|
||||||
|
? Icons.visibility
|
||||||
|
: Icons.visibility_off),
|
||||||
|
onPressed: () {
|
||||||
|
setState(
|
||||||
|
() {
|
||||||
|
_passwordNotVisible = !_passwordNotVisible;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
labelText: S.of(context).password_label,
|
||||||
|
),
|
||||||
|
obscureText: _passwordNotVisible,
|
||||||
|
), //密碼
|
||||||
|
SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: loginBtn,
|
||||||
|
/*() {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (context) => HomePage()),
|
||||||
|
);
|
||||||
|
},*/
|
||||||
|
child: Text(S.of(context).login_button),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF4FC3F7),
|
||||||
|
padding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 50, vertical: 15),
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
|
), //登入
|
||||||
|
SizedBox(height: 10),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => RegisterPage()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(S.of(context).register_button),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
// TODO: In physical devices, the background color of the button is transparent
|
||||||
|
backgroundColor: Colors.transparent, // 無背景颜色
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
shadowColor: Colors.transparent, // 去除陰影
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => ForgetPasswordPage()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(S.of(context).forgot_password),
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
backgroundColor: Colors.transparent, // 無背景颜色
|
||||||
|
textStyle: TextStyle(fontSize: 18),
|
||||||
|
shadowColor: Colors.transparent, // 去除陰影
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
15
pubspec.yaml
15
pubspec.yaml
@@ -30,8 +30,10 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
mysql_client: ^0.0.27
|
mysql_client: ^0.0.27
|
||||||
|
url_launcher: ^6.0.10
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
@@ -43,6 +45,7 @@ dependencies:
|
|||||||
persistent_bottom_nav_bar_v2: ^5.3.0
|
persistent_bottom_nav_bar_v2: ^5.3.0
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
validators: ^3.0.0
|
validators: ^3.0.0
|
||||||
|
better_player_plus: ^1.0.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@@ -60,7 +63,13 @@ dev_dependencies:
|
|||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
# The following section is specific to Flutter packages.
|
||||||
flutter:
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/images/456.jpg
|
||||||
|
- assets/images/123.jpg
|
||||||
|
- assets/images/bathroom.webp
|
||||||
|
- assets/images/fallFactor.webp
|
||||||
|
- assets/images/fallRescue1.webp
|
||||||
|
- assets/images/fallRescue2.webp
|
||||||
# The following line ensures that the Material Icons font is
|
# The following line ensures that the Material Icons font is
|
||||||
# included with your application, so that you can use the icons in
|
# included with your application, so that you can use the icons in
|
||||||
# the material Icons class.
|
# the material Icons class.
|
||||||
@@ -96,3 +105,5 @@ flutter:
|
|||||||
#
|
#
|
||||||
# For details regarding fonts from package dependencies,
|
# For details regarding fonts from package dependencies,
|
||||||
# see https://flutter.dev/custom-fonts/#from-packages
|
# see https://flutter.dev/custom-fonts/#from-packages
|
||||||
|
flutter_intl:
|
||||||
|
enabled: true
|
||||||
|
|||||||
Reference in New Issue
Block a user