264 lines
9.1 KiB
Dart
264 lines
9.1 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
|||
|
|
import 'package:get/get.dart';
|
|||
|
|
import 'package:gifunavi/model/user.dart';
|
|||
|
|
import 'package:gifunavi/routes/app_pages.dart';
|
|||
|
|
import 'package:gifunavi/services/api_service.dart';
|
|||
|
|
import 'package:gifunavi/pages/index/index_controller.dart';
|
|||
|
|
import 'package:intl/intl.dart';
|
|||
|
|
|
|||
|
|
import 'package:gifunavi/widgets/custom_date_picker.dart'; // 追加: 日付フォーマット用
|
|||
|
|
|
|||
|
|
class UserDetailsEditPage extends StatefulWidget {
|
|||
|
|
const UserDetailsEditPage({super.key});
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
_UserDetailsEditPageState createState() => _UserDetailsEditPageState();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class _UserDetailsEditPageState extends State<UserDetailsEditPage> {
|
|||
|
|
final _formKey = GlobalKey<FormState>();
|
|||
|
|
final IndexController indexController = Get.find<IndexController>();
|
|||
|
|
late User _user;
|
|||
|
|
final TextEditingController _firstnameController = TextEditingController();
|
|||
|
|
final TextEditingController _lastnameController = TextEditingController();
|
|||
|
|
final TextEditingController _dateOfBirthController = TextEditingController();
|
|||
|
|
late bool _female;
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
void initState() {
|
|||
|
|
super.initState();
|
|||
|
|
_user = User.fromJson(indexController.currentUser[0]['user']);
|
|||
|
|
_firstnameController.text = _user.firstname;
|
|||
|
|
_lastnameController.text = _user.lastname;
|
|||
|
|
_dateOfBirthController.text = _user.dateOfBirth != null
|
|||
|
|
? '${_user.dateOfBirth!.year}/${_user.dateOfBirth!.month.toString().padLeft(2, '0')}/${_user.dateOfBirth!.day.toString().padLeft(2, '0')}'
|
|||
|
|
: '';
|
|||
|
|
_female = _user.female;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@override
|
|||
|
|
Widget build(BuildContext context) {
|
|||
|
|
return Scaffold(
|
|||
|
|
appBar: AppBar(
|
|||
|
|
title: const Text('個人情報の修正'),
|
|||
|
|
automaticallyImplyLeading: false,
|
|||
|
|
),
|
|||
|
|
body: Form(
|
|||
|
|
key: _formKey,
|
|||
|
|
child: ListView(
|
|||
|
|
padding: const EdgeInsets.all(16.0),
|
|||
|
|
children: [
|
|||
|
|
TextFormField(
|
|||
|
|
controller: _lastnameController,
|
|||
|
|
decoration: const InputDecoration(
|
|||
|
|
labelText: '姓',
|
|||
|
|
border: OutlineInputBorder(),
|
|||
|
|
),
|
|||
|
|
validator: (value) {
|
|||
|
|
if (value == null || value.isEmpty) {
|
|||
|
|
return '姓を入力してください';
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
TextFormField(
|
|||
|
|
controller: _firstnameController,
|
|||
|
|
decoration: const InputDecoration(
|
|||
|
|
labelText: '名',
|
|||
|
|
border: OutlineInputBorder(),
|
|||
|
|
),
|
|||
|
|
validator: (value) {
|
|||
|
|
if (value == null || value.isEmpty) {
|
|||
|
|
return '名を入力してください';
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
TextFormField(
|
|||
|
|
controller: _dateOfBirthController,
|
|||
|
|
decoration: InputDecoration(
|
|||
|
|
labelText: '生年月日 (YYYY/MM/DD)',
|
|||
|
|
border: const OutlineInputBorder(),
|
|||
|
|
hintText: 'YYYY/MM/DD',
|
|||
|
|
suffixIcon: IconButton(
|
|||
|
|
icon: const Icon(Icons.calendar_today),
|
|||
|
|
onPressed: () => _selectDate(context),
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
keyboardType: TextInputType.number, // <=datetime,
|
|||
|
|
onChanged: (value) {
|
|||
|
|
// スラッシュを除去
|
|||
|
|
value = value.replaceAll('/', '');
|
|||
|
|
|
|||
|
|
if (value.length <= 8) {
|
|||
|
|
String formattedValue = '';
|
|||
|
|
|
|||
|
|
// 年の処理(4桁)
|
|||
|
|
if (value.length >= 4) {
|
|||
|
|
formattedValue += '${value.substring(0, 4)}/';
|
|||
|
|
value = value.substring(4);
|
|||
|
|
} else {
|
|||
|
|
formattedValue += value;
|
|||
|
|
value = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 月の処理(2桁)
|
|||
|
|
if (value.length >= 2) {
|
|||
|
|
formattedValue += '${value.substring(0, 2)}/';
|
|||
|
|
value = value.substring(2);
|
|||
|
|
} else if (value.isNotEmpty) {
|
|||
|
|
formattedValue += value;
|
|||
|
|
value = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 残りの日付
|
|||
|
|
formattedValue += value;
|
|||
|
|
|
|||
|
|
// 末尾のスラッシュを削除
|
|||
|
|
if (formattedValue.endsWith('/')) {
|
|||
|
|
formattedValue = formattedValue.substring(0, formattedValue.length - 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_dateOfBirthController.value = TextEditingValue(
|
|||
|
|
text: formattedValue,
|
|||
|
|
selection: TextSelection.collapsed(offset: formattedValue.length),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
validator: (value) {
|
|||
|
|
if (value == null || value.isEmpty) {
|
|||
|
|
return '生年月日を入力してください';
|
|||
|
|
}
|
|||
|
|
if (!RegExp(r'^\d{4}/\d{2}/\d{2}$').hasMatch(value)) {
|
|||
|
|
return '正しい形式で入力してください (YYYY/MM/DD)';
|
|||
|
|
}
|
|||
|
|
final date = DateTime.tryParse(value.replaceAll('/', '-'));
|
|||
|
|
if (date == null) {
|
|||
|
|
return '有効な日付を入力してください';
|
|||
|
|
}
|
|||
|
|
if (date.isAfter(DateTime.now())) {
|
|||
|
|
return '未来の日付は入力できません';
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
SwitchListTile(
|
|||
|
|
title: const Text('性別'),
|
|||
|
|
subtitle: Text(_female ? '女性' : '男性'),
|
|||
|
|
value: _female,
|
|||
|
|
onChanged: (bool value) {
|
|||
|
|
setState(() {
|
|||
|
|
_female = value;
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
TextFormField(
|
|||
|
|
initialValue: _user.email,
|
|||
|
|
decoration: const InputDecoration(
|
|||
|
|
labelText: 'メールアドレス',
|
|||
|
|
border: OutlineInputBorder(),
|
|||
|
|
),
|
|||
|
|
enabled: false,
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 16),
|
|||
|
|
SwitchListTile(
|
|||
|
|
title: const Text('アクティブ状態'),
|
|||
|
|
value: _user.isActive,
|
|||
|
|
onChanged: null,
|
|||
|
|
),
|
|||
|
|
const SizedBox(height: 32),
|
|||
|
|
ElevatedButton(
|
|||
|
|
onPressed: _updateUserDetails,
|
|||
|
|
child: const Text('更新'),
|
|||
|
|
),
|
|||
|
|
],
|
|||
|
|
),
|
|||
|
|
),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 日付選択用の関数を追加
|
|||
|
|
Future<void> _selectDate(BuildContext context) async {
|
|||
|
|
final DateTime? picked = await showDialog<DateTime>(
|
|||
|
|
context: context,
|
|||
|
|
builder: (BuildContext context) {
|
|||
|
|
return CustomDatePicker(
|
|||
|
|
initialDate: _user.dateOfBirth ?? DateTime.now(),
|
|||
|
|
firstDate: DateTime(1900),
|
|||
|
|
lastDate: DateTime.now(),
|
|||
|
|
currentDateText: _dateOfBirthController.text,
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
if (picked != null) {
|
|||
|
|
setState(() {
|
|||
|
|
_dateOfBirthController.text = DateFormat('yyyy/MM/dd').format(picked);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void _updateUserDetails() async {
|
|||
|
|
if (_formKey.currentState!.validate()) {
|
|||
|
|
final dateOfBirth = DateTime.tryParse(_dateOfBirthController.text.replaceAll('/', '-'));
|
|||
|
|
if (dateOfBirth == null || dateOfBirth.isAfter(DateTime.now())) {
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
const SnackBar(content: Text('生年月日が無効です', style: TextStyle(color: Colors.red))),
|
|||
|
|
);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 13歳以上かどうかをチェック
|
|||
|
|
final now = DateTime.now();
|
|||
|
|
final age = now.year - dateOfBirth.year -
|
|||
|
|
(now.month > dateOfBirth.month ||
|
|||
|
|
(now.month == dateOfBirth.month && now.day >= dateOfBirth.day) ? 0 : 1);
|
|||
|
|
|
|||
|
|
if (age < 13) {
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
const SnackBar(content: Text('13歳未満の方は登録できません', style: TextStyle(color: Colors.red))),
|
|||
|
|
);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
User updatedUser = User(
|
|||
|
|
id: _user.id,
|
|||
|
|
email: _user.email,
|
|||
|
|
firstname: _firstnameController.text,
|
|||
|
|
lastname: _lastnameController.text,
|
|||
|
|
dateOfBirth: dateOfBirth,
|
|||
|
|
female: _female,
|
|||
|
|
isActive: _user.isActive,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
bool success = await ApiService.updateUserDetail(updatedUser, indexController.currentUser[0]['token']);
|
|||
|
|
if (success) {
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
const SnackBar(content: Text('個人情報が更新されました')),
|
|||
|
|
);
|
|||
|
|
indexController.updateCurrentUser(updatedUser);
|
|||
|
|
Get.offAllNamed(AppPages.INDEX);
|
|||
|
|
//Get.back();
|
|||
|
|
} else {
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
const SnackBar(content: Text('更新に失敗しました', style: TextStyle(color: Colors.red))),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|||
|
|
SnackBar(content: Text('エラーが発生しました: $e', style: const TextStyle(color: Colors.red))),
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|