release 4.8.9
This commit is contained in:
17257
all_combined.txt
17257
all_combined.txt
File diff suppressed because it is too large
Load Diff
BIN
assets/images/QR_certificate.png
Normal file
BIN
assets/images/QR_certificate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
BIN
assets/images/QR_gifuroge_stage1.png
Normal file
BIN
assets/images/QR_gifuroge_stage1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 518 B |
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 60;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -398,11 +398,11 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 486;
|
||||
CURRENT_PROJECT_VERSION = 488;
|
||||
DEVELOPMENT_TEAM = UMNEWT25JR;
|
||||
ENABLE_BITCODE = NO;
|
||||
FLUTTER_BUILD_NAME = 4.8.6;
|
||||
FLUTTER_BUILD_NUMBER = 486;
|
||||
FLUTTER_BUILD_NAME = 4.8.8;
|
||||
FLUTTER_BUILD_NUMBER = 488;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
||||
@ -411,7 +411,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 4.8.6;
|
||||
MARKETING_VERSION = 4.8.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -539,11 +539,11 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 486;
|
||||
CURRENT_PROJECT_VERSION = 488;
|
||||
DEVELOPMENT_TEAM = UMNEWT25JR;
|
||||
ENABLE_BITCODE = NO;
|
||||
FLUTTER_BUILD_NAME = 4.8.6;
|
||||
FLUTTER_BUILD_NUMBER = 486;
|
||||
FLUTTER_BUILD_NAME = 4.8.8;
|
||||
FLUTTER_BUILD_NUMBER = 488;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
||||
@ -552,7 +552,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 4.8.6;
|
||||
MARKETING_VERSION = 4.8.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -571,11 +571,11 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 486;
|
||||
CURRENT_PROJECT_VERSION = 488;
|
||||
DEVELOPMENT_TEAM = UMNEWT25JR;
|
||||
ENABLE_BITCODE = NO;
|
||||
FLUTTER_BUILD_NAME = 4.8.6;
|
||||
FLUTTER_BUILD_NUMBER = 486;
|
||||
FLUTTER_BUILD_NAME = 4.8.8;
|
||||
FLUTTER_BUILD_NUMBER = 488;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "岐阜ナビ";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.healthcare-fitness";
|
||||
@ -584,7 +584,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 4.8.6;
|
||||
MARKETING_VERSION = 4.8.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.dvox.gifunavi;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
@ -34,13 +34,28 @@ class NewCategory {
|
||||
id: json['id'] ?? 0,
|
||||
categoryName: json['category_name'] ?? 'Unknown Category',
|
||||
categoryNumber: json['category_number'] ?? 0,
|
||||
duration: Duration(seconds: json['duration'] ?? 0),
|
||||
duration: parseDuration(json['duration']),
|
||||
numOfMember: json['num_of_member'] ?? 1,
|
||||
family: json['family'] ?? false,
|
||||
female: json['female'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
static Duration parseDuration(String s) {
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int micros;
|
||||
List<String> parts = s.split(':');
|
||||
if (parts.length > 2) {
|
||||
hours = int.parse(parts[parts.length - 3]);
|
||||
}
|
||||
if (parts.length > 1) {
|
||||
minutes = int.parse(parts[parts.length - 2]);
|
||||
}
|
||||
micros = (double.parse(parts[parts.length - 1]) * 1000000).round();
|
||||
return Duration(hours: hours, minutes: minutes, microseconds: micros);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
|
||||
@ -10,6 +10,7 @@ class Entry {
|
||||
final Event event;
|
||||
final NewCategory category;
|
||||
final DateTime? date;
|
||||
final int zekkenNumber; // 新しく追加
|
||||
final String owner;
|
||||
|
||||
Entry({
|
||||
@ -18,6 +19,7 @@ class Entry {
|
||||
required this.event,
|
||||
required this.category,
|
||||
required this.date,
|
||||
required this.zekkenNumber,
|
||||
required this.owner,
|
||||
});
|
||||
|
||||
@ -30,6 +32,7 @@ class Entry {
|
||||
date: json['date'] != null
|
||||
? DateTime.tryParse(json['date'])
|
||||
: null,
|
||||
zekkenNumber: json['zekken_number'], // 新しく追加
|
||||
owner: json['owner'] is Map ? json['owner']['name'] ?? '' : json['owner'] ?? '',
|
||||
);
|
||||
}
|
||||
@ -41,6 +44,7 @@ class Entry {
|
||||
'event': event.toJson(),
|
||||
'category': category.toJson(),
|
||||
'date': date?.toIso8601String(),
|
||||
'zekken_number': zekkenNumber, // 新しく追加
|
||||
'owner': owner,
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import 'user.dart';
|
||||
|
||||
class Team {
|
||||
final int id;
|
||||
final String zekkenNumber;
|
||||
// final String zekkenNumber;
|
||||
final String teamName;
|
||||
final NewCategory category;
|
||||
final User owner;
|
||||
@ -14,7 +14,7 @@ class Team {
|
||||
|
||||
Team({
|
||||
required this.id,
|
||||
required this.zekkenNumber,
|
||||
// required this.zekkenNumber,
|
||||
required this.teamName,
|
||||
required this.category,
|
||||
required this.owner,
|
||||
@ -23,7 +23,7 @@ class Team {
|
||||
factory Team.fromJson(Map<String, dynamic> json) {
|
||||
return Team(
|
||||
id: json['id'] ?? 0,
|
||||
zekkenNumber: json['zekken_number'] ?? 'Unknown',
|
||||
//zekkenNumber: json['zekken_number'] ?? 'Unknown',
|
||||
teamName: json['team_name'] ?? 'Unknown Team',
|
||||
category: json['category'] != null
|
||||
? NewCategory.fromJson(json['category'])
|
||||
@ -37,7 +37,7 @@ class Team {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'zekken_number': zekkenNumber,
|
||||
//'zekken_number': zekkenNumber,
|
||||
'team_name': teamName,
|
||||
'category': category.toJson(),
|
||||
'owner': owner.toJson(),
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert'; // この行を追加または確認
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:rogapp/model/destination.dart';
|
||||
import 'package:rogapp/pages/destination/destination_controller.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
@ -493,7 +496,10 @@ class CameraPage extends StatelessWidget {
|
||||
if (buyPointPhoto == true) {
|
||||
// buyPointPhotoがtrueの場合は、BuyPointCameraウィジェットを返します。
|
||||
//print("--- buy point camera ${destination.toString()}");
|
||||
return BuyPointCamera(destination: destination);
|
||||
//return BuyPointCamera(destination: destination);
|
||||
|
||||
return SwitchableBuyPointCamera(destination: destination);
|
||||
|
||||
//}else if(destination.use_qr_code){
|
||||
// return QRCodeScannerPage(destination: destination);
|
||||
} else if (destinationController.isInRog.value) {
|
||||
@ -617,24 +623,103 @@ class StartRogaining extends StatelessWidget {
|
||||
// 完了ボタンをタップすると、購入ポイントの処理が行われます。
|
||||
// 購入なしボタンをタップすると、購入ポイントがキャンセルされます。
|
||||
//
|
||||
class SwitchableBuyPointCamera extends StatefulWidget {
|
||||
final Destination destination;
|
||||
|
||||
const SwitchableBuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SwitchableBuyPointCameraState createState() => _SwitchableBuyPointCameraState();
|
||||
}
|
||||
|
||||
class _SwitchableBuyPointCameraState extends State<SwitchableBuyPointCamera> {
|
||||
bool isQRMode = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final screenHeight = MediaQuery.of(context).size.height;
|
||||
final qrViewWidth = screenWidth * 2 / 3;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text("${widget.destination.sub_loc_id} : ${widget.destination.name}"),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
if (isQRMode)
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: screenHeight * 0.1),
|
||||
Center(
|
||||
child: SizedBox(
|
||||
width: qrViewWidth,
|
||||
height: qrViewWidth,
|
||||
child: BuyPointCamera_QR(destination: widget.destination),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Text(
|
||||
"岐阜ロゲQRコードにかざしてください。",
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
else
|
||||
Positioned.fill(
|
||||
child: BuyPointCamera(destination: widget.destination),
|
||||
),
|
||||
Positioned(
|
||||
right: 16,
|
||||
bottom: 16,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(isQRMode ? "カメラへ" : "QRへ"),
|
||||
Switch(
|
||||
value: isQRMode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
isQRMode = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BuyPointCamera extends StatelessWidget {
|
||||
BuyPointCamera({Key? key, required this.destination}) : super(key: key);
|
||||
|
||||
Destination destination;
|
||||
|
||||
DestinationController destinationController =
|
||||
Get.find<DestinationController>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
"${destination.sub_loc_id} : ${destination.name}",
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
@ -642,8 +727,12 @@ class BuyPointCamera extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
child: Obx(
|
||||
() => Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
() =>
|
||||
Container(
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width,
|
||||
height: 370,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
@ -665,7 +754,8 @@ class BuyPointCamera extends StatelessWidget {
|
||||
spacing: 16.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
Obx(() => ElevatedButton(
|
||||
Obx(() =>
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
destinationController.openCamera(context, destination);
|
||||
},
|
||||
@ -691,7 +781,8 @@ class BuyPointCamera extends StatelessWidget {
|
||||
destinationController.isPhotoShoot.value = false;
|
||||
},
|
||||
child: const Text("買い物なし")),
|
||||
Obx(() => destinationController.photos.isNotEmpty
|
||||
Obx(() =>
|
||||
destinationController.photos.isNotEmpty
|
||||
? ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red),
|
||||
@ -716,7 +807,6 @@ class BuyPointCamera extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -743,28 +833,9 @@ class _BuyPointCamera_QRState extends State<BuyPointCamera_QR> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text("${widget.destination.sub_loc_id} : ${widget.destination.name}"),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: QRView(
|
||||
return QRView(
|
||||
key: qrKey,
|
||||
onQRViewCreated: _onQRViewCreated,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: Text('QRコードをスキャンしてください'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -773,11 +844,39 @@ class _BuyPointCamera_QRState extends State<BuyPointCamera_QR> {
|
||||
controller.scannedDataStream.listen((scanData) {
|
||||
if (!isQRScanned && scanData.code != null && scanData.code!.startsWith('https://rogaining.sumasen.net/api/activate_buy_point/')) {
|
||||
isQRScanned = true;
|
||||
_activateBuyPoint(scanData.code!);
|
||||
_processBuyPoint();
|
||||
//_activateBuyPoint(scanData.code!);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> getImageFilePathFromAssets(String assetPath) async {
|
||||
final byteData = await rootBundle.load(assetPath);
|
||||
final buffer = byteData.buffer;
|
||||
Directory tempDir = await getTemporaryDirectory();
|
||||
String tempPath = tempDir.path;
|
||||
var filePath = '$tempPath/temp_qr_receipt.png';
|
||||
return (await File(filePath).writeAsBytes(
|
||||
buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes)
|
||||
)).path;
|
||||
}
|
||||
|
||||
|
||||
void _processBuyPoint() async {
|
||||
// アセットの画像をテンポラリファイルにコピー
|
||||
String predefinedImagePath = await getImageFilePathFromAssets('assets/images/QR_certificate.png');
|
||||
|
||||
try {
|
||||
await destinationController.makeBuyPoint(widget.destination, predefinedImagePath);
|
||||
Get.snackbar('成功', 'お買い物ポイントが有効化されました');
|
||||
Navigator.of(context).pop();
|
||||
} catch (e) {
|
||||
Get.snackbar('エラー', 'お買い物ポイントの有効化に失敗しました');
|
||||
} finally {
|
||||
isQRScanned = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _activateBuyPoint(String qrCode) async {
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import 'package:rogapp/model/gps_data.dart';
|
||||
import 'package:rogapp/pages/camera/camera_page.dart';
|
||||
import 'package:rogapp/pages/camera/custom_camera_view.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/pages/team/team_controller.dart';
|
||||
import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/services/DatabaseService.dart';
|
||||
import 'package:rogapp/services/destination_service.dart';
|
||||
@ -44,6 +45,7 @@ import 'package:rogapp/pages/permission/permission.dart';
|
||||
class DestinationController extends GetxController {
|
||||
late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。
|
||||
|
||||
//late TeamController teamController = TeamController();
|
||||
//Timer? _GPStimer; // GPSタイマーを保持する変数です。
|
||||
|
||||
var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。
|
||||
@ -161,11 +163,11 @@ class DestinationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//==== Akira .. GPS信号シミュレーション用 ======= ここまで
|
||||
*/
|
||||
|
||||
|
||||
// ルートをクリアする関数です。
|
||||
void clearRoute() {
|
||||
indexController.routePoints.clear();
|
||||
@ -1156,9 +1158,13 @@ class DestinationController extends GetxController {
|
||||
//await _saveImageFromPath(imageurl);
|
||||
await _saveImageToGallery(imageurl);
|
||||
|
||||
|
||||
|
||||
if (indexController.currentUser.isNotEmpty) {
|
||||
double cpNum = destination.cp!;
|
||||
|
||||
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||
|
||||
int userId = indexController.currentUser[0]["user"]["id"];
|
||||
//print("--- Pressed -----");
|
||||
String team = indexController.currentUser[0]["user"]['team_name'];
|
||||
@ -1222,6 +1228,11 @@ class DestinationController extends GetxController {
|
||||
if (indexController.currentUser.isNotEmpty) {
|
||||
double cpNum = destination.cp!;
|
||||
|
||||
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||
//Team team0 = teamController.teams[0];
|
||||
//print("team={team0}");
|
||||
|
||||
|
||||
int userId = indexController.currentUser[0]["user"]["id"];
|
||||
//print("--- Pressed -----");
|
||||
String team = indexController.currentUser[0]["user"]['team_name'];
|
||||
@ -1238,7 +1249,7 @@ class DestinationController extends GetxController {
|
||||
// print("------ checkin event $eventCode ------");
|
||||
ExternalService()
|
||||
.makeCheckpoint(
|
||||
userId,
|
||||
userId, // teamIdを使用
|
||||
token,
|
||||
formattedDate,
|
||||
team,
|
||||
@ -1716,6 +1727,8 @@ class DestinationController extends GetxController {
|
||||
//print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val');
|
||||
Map<String, dynamic> res = {};
|
||||
if (val == "wifi" || val == "mobile") {
|
||||
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||
|
||||
String token = indexController.currentUser[0]["token"];
|
||||
DatabaseHelper db = DatabaseHelper.instance;
|
||||
db.allRogianing().then((value) {
|
||||
@ -1725,7 +1738,7 @@ class DestinationController extends GetxController {
|
||||
} else if (e.rog_action_type == 1) {
|
||||
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
|
||||
res = await ExternalService().makeCheckpoint(
|
||||
e.user_id!,
|
||||
e.user_id!, // teamId???
|
||||
token,
|
||||
getFormatedTime(datetime),
|
||||
e.team_name!,
|
||||
@ -1735,7 +1748,7 @@ class DestinationController extends GetxController {
|
||||
} else if (e.rog_action_type == 2) {
|
||||
var datetime = DateTime.fromMicrosecondsSinceEpoch(e.checkintime!);
|
||||
res = await ExternalService().makeGoal(
|
||||
e.user_id!,
|
||||
e.user_id!, // // teamId???
|
||||
token,
|
||||
e.team_name!,
|
||||
e.image!,
|
||||
|
||||
@ -257,6 +257,7 @@ class DrawerPage extends StatelessWidget {
|
||||
},
|
||||
|
||||
),
|
||||
/*
|
||||
ListTile(
|
||||
leading: const Icon(Icons.developer_mode),
|
||||
title: const Text('open_settings'),
|
||||
@ -265,6 +266,8 @@ class DrawerPage extends StatelessWidget {
|
||||
Get.toNamed('/debug'); // デバッグ画面に遷移
|
||||
},
|
||||
),
|
||||
*/
|
||||
|
||||
// ListTile(
|
||||
// leading: const Icon(Icons.router),
|
||||
// title: Text("my_route".tr),
|
||||
|
||||
@ -8,6 +8,8 @@ import 'package:rogapp/model/team.dart';
|
||||
import 'package:rogapp/model/category.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
|
||||
import '../index/index_controller.dart';
|
||||
|
||||
class EntryController extends GetxController {
|
||||
late ApiService _apiService;
|
||||
|
||||
@ -155,26 +157,44 @@ class EntryController extends GetxController {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isLoading.value = true;
|
||||
// Get zekken number
|
||||
final updatedCategory = await _apiService.getZekkenNumber(selectedCategory.value!.id);
|
||||
final zekkenNumber = updatedCategory.categoryNumber.toString();
|
||||
|
||||
final newEntry = await _apiService.createEntry(
|
||||
selectedTeam.value!.id,
|
||||
selectedEvent.value!.id,
|
||||
selectedCategory.value!.id,
|
||||
selectedDate.value!,
|
||||
zekkenNumber,
|
||||
);
|
||||
entries.add(newEntry);
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
print('Error creating entry: $e');
|
||||
Get.snackbar('Error', 'Failed to create entry');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateEntryAndRefreshMap() async {
|
||||
await updateEntry();
|
||||
|
||||
// エントリーが正常に更新された後、マップをリフレッシュ
|
||||
final indexController = Get.find<IndexController>();
|
||||
final eventCode = currentEntry.value?.event.eventName ?? '';
|
||||
indexController.reloadMap(eventCode);
|
||||
}
|
||||
|
||||
Future<void> updateEntry() async {
|
||||
if (currentEntry.value == null) {
|
||||
Get.snackbar('Error', 'No entry selected for update');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isLoading.value = true;
|
||||
final updatedEntry = await _apiService.updateEntry(
|
||||
currentEntry.value!.id,
|
||||
currentEntry.value!.team.id,
|
||||
@ -190,6 +210,8 @@ class EntryController extends GetxController {
|
||||
} catch (e) {
|
||||
print('Error updating entry: $e');
|
||||
Get.snackbar('Error', 'Failed to update entry');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,12 +221,15 @@ class EntryController extends GetxController {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await _apiService.deleteEntry(currentEntry.value!.id);
|
||||
entries.removeWhere((entry) => entry.id == currentEntry.value!.id);
|
||||
Get.back();
|
||||
} catch (e) {
|
||||
print('Error deleting entry: $e');
|
||||
Get.snackbar('Error', 'Failed to delete entry');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,10 +2,63 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:rogapp/pages/entry/entry_controller.dart';
|
||||
import 'package:rogapp/routes/app_pages.dart';
|
||||
|
||||
class EntryListPage extends GetView<EntryController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('エントリー管理'),
|
||||
),
|
||||
body: Obx(() {
|
||||
if (controller.entries.isEmpty) {
|
||||
return Center(
|
||||
child: Text('表示するエントリーがありません。'),
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: controller.entries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final entry = controller.entries[index];
|
||||
return ListTile(
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('${_formatDate(entry.date)}: ${entry.event.eventName}'),
|
||||
),
|
||||
Text(entry.team.teamName, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('カテゴリー: ${entry.category.categoryName}'),
|
||||
),
|
||||
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
||||
],
|
||||
),
|
||||
onTap: () =>
|
||||
Get.toNamed(AppPages.ENTRY_DETAIL,
|
||||
arguments: {'mode': 'edit', 'entry': entry}),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime? date) {
|
||||
if (date == null) {
|
||||
return '日時未設定';
|
||||
}
|
||||
return DateFormat('yyyy-MM-dd').format(date);
|
||||
}
|
||||
}
|
||||
|
||||
class EntryListPage_old extends GetView<EntryController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -19,6 +72,10 @@ class EntryListPage extends GetView<EntryController> {
|
||||
],
|
||||
),
|
||||
body: Obx((){
|
||||
if (controller.isLoading.value) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// エントリーを日付昇順にソート
|
||||
final sortedEntries = controller.entries.toList()
|
||||
..sort((a, b) => (a.date ?? DateTime(0)).compareTo(b.date ?? DateTime(0)));
|
||||
|
||||
@ -1,17 +1,26 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rogapp/model/entry.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/pages/destination/destination_controller.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventEntriesController extends GetxController {
|
||||
final ApiService _apiService = Get.find<ApiService>();
|
||||
final IndexController _indexController = Get.find<IndexController>();
|
||||
late final DestinationController _destinationController;
|
||||
|
||||
final entries = <Entry>[].obs;
|
||||
final filteredEntries = <Entry>[].obs;
|
||||
final showTodayEntries = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// DestinationControllerが登録されていない場合に備えて、lazyPutを使用
|
||||
Get.lazyPut<DestinationController>(() => DestinationController(), fenix: true);
|
||||
_destinationController = Get.find<DestinationController>();
|
||||
|
||||
fetchEntries();
|
||||
}
|
||||
|
||||
@ -19,14 +28,51 @@ class EventEntriesController extends GetxController {
|
||||
try {
|
||||
final fetchedEntries = await _apiService.getEntries();
|
||||
entries.assignAll(fetchedEntries);
|
||||
filterEntries();
|
||||
} catch (e) {
|
||||
print('Error fetching entries: $e');
|
||||
// エラー処理を追加
|
||||
}
|
||||
}
|
||||
|
||||
void filterEntries() {
|
||||
if (showTodayEntries.value) {
|
||||
filterEntriesForToday();
|
||||
} else {
|
||||
filteredEntries.assignAll(entries);
|
||||
}
|
||||
}
|
||||
|
||||
void filterEntriesForToday() {
|
||||
final now = DateTime.now();
|
||||
filteredEntries.assignAll(entries.where((entry) =>
|
||||
entry.date?.year == now.year &&
|
||||
entry.date?.month == now.month &&
|
||||
entry.date?.day == now.day
|
||||
));
|
||||
}
|
||||
|
||||
void toggleShowTodayEntries() {
|
||||
showTodayEntries.toggle();
|
||||
filterEntries();
|
||||
}
|
||||
|
||||
void refreshMap() {
|
||||
final tk = _indexController.currentUser[0]["token"];
|
||||
if (tk != null) {
|
||||
|
||||
_destinationController.fixMapBound(tk);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> joinEvent(Entry entry) async {
|
||||
try {
|
||||
final now = DateTime.now();
|
||||
bool isToday = entry.date?.year == now.year &&
|
||||
entry.date?.month == now.month &&
|
||||
entry.date?.day == now.day;
|
||||
|
||||
_indexController.setReferenceMode(!isToday);
|
||||
_indexController.setSelectedEventName(entry.event.eventName);
|
||||
|
||||
final userid = _indexController.currentUser[0]["user"]["id"];
|
||||
|
||||
@ -35,14 +81,24 @@ class EventEntriesController extends GetxController {
|
||||
_indexController.currentUser[0]["user"]["event_code"] = entry.event.eventName;
|
||||
_indexController.currentUser[0]["user"]["team_name"] = entry.team.teamName;
|
||||
_indexController.currentUser[0]["user"]["group"] = entry.team.category.categoryName;
|
||||
_indexController.currentUser[0]["user"]["zekken_number"] = entry.team.zekkenNumber;
|
||||
_indexController.currentUser[0]["user"]["zekken_number"] = entry.zekkenNumber;
|
||||
|
||||
Get.back(); // エントリー一覧ページを閉じる
|
||||
//_indexController.isLoading.value = true;
|
||||
_indexController.reloadMap(entry.event.eventName); // マップをリロード
|
||||
} catch (e) {
|
||||
print('Error joining event: $e');
|
||||
// エラー処理を追加
|
||||
_indexController.reloadMap(entry.event.eventName);
|
||||
|
||||
refreshMap();
|
||||
|
||||
if (isToday) {
|
||||
Get.snackbar('成功', 'イベントに参加しました。',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white);
|
||||
} else {
|
||||
Get.snackbar('参照モード', '過去または未来のイベントを参照しています。ロゲの開始やチェックインはできません。',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.orange,
|
||||
colorText: Colors.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:rogapp/pages/entry/event_entries_controller.dart';
|
||||
|
||||
class EventEntriesPage extends GetView<EventEntriesController> {
|
||||
class EventEntriesPage_old extends GetView<EventEntriesController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -21,3 +22,81 @@ class EventEntriesPage extends GetView<EventEntriesController> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EventEntriesPage extends GetView<EventEntriesController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Obx(() => Text(controller.showTodayEntries.value ? 'イベント参加' : 'イベント参照')),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Obx(() => Text(
|
||||
controller.showTodayEntries.value ? '本日のエントリー' : 'すべてのエントリー',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
)),
|
||||
Obx(() => Switch(
|
||||
value: !controller.showTodayEntries.value,
|
||||
onChanged: (value) {
|
||||
controller.toggleShowTodayEntries();
|
||||
},
|
||||
activeColor: Colors.blue,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
if (controller.filteredEntries.isEmpty) {
|
||||
return Center(
|
||||
child: Text('表示するエントリーがありません。'),
|
||||
);
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: controller.filteredEntries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final entry = controller.filteredEntries[index];
|
||||
return ListTile(
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('${_formatDate(entry.date)}: ${entry.event.eventName}'),
|
||||
),
|
||||
Text(entry.team.teamName, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text('カテゴリー: ${entry.category.categoryName}'),
|
||||
),
|
||||
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
||||
],
|
||||
),
|
||||
onTap: () async {
|
||||
await controller.joinEvent(entry);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime? date) {
|
||||
if (date == null) {
|
||||
return '日時未設定';
|
||||
}
|
||||
return DateFormat('yyyy-MM-dd').format(date);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -82,6 +82,12 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
String areaDropdownValue = "-1";
|
||||
String cateogory = "-all-";
|
||||
|
||||
final selectedEventName = 'add_location'.tr.obs;
|
||||
|
||||
void setSelectedEventName(String eventName) {
|
||||
selectedEventName.value = eventName;
|
||||
}
|
||||
|
||||
ConnectivityResult connectionStatus = ConnectivityResult.none;
|
||||
var connectionStatusName = "".obs;
|
||||
final Connectivity _connectivity = Connectivity();
|
||||
@ -89,6 +95,9 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
final Rx<DateTime> lastUserUpdateTime = DateTime.now().obs;
|
||||
|
||||
RxInt teamId = RxInt(-1); // チームIDを保存するための変数
|
||||
|
||||
//late TeamController teamController = TeamController();
|
||||
/*
|
||||
void updateUserInfo(Map<String, dynamic> newUserInfo) {
|
||||
currentUser.clear();
|
||||
@ -97,6 +106,20 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
}
|
||||
*/
|
||||
|
||||
final isReferenceMode = false.obs;
|
||||
|
||||
void setReferenceMode(bool value) {
|
||||
isReferenceMode.value = value;
|
||||
}
|
||||
|
||||
bool canStartRoge() {
|
||||
return !isReferenceMode.value;
|
||||
}
|
||||
|
||||
bool canCheckin() {
|
||||
return !isReferenceMode.value;
|
||||
}
|
||||
|
||||
void toggleMode() {
|
||||
if (mode.value == 0) {
|
||||
mode += 1;
|
||||
@ -214,6 +237,8 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
print('IndexController onInit called'); // デバッグ用の出力を追加
|
||||
|
||||
//teamController = Get.find<TeamController>();
|
||||
|
||||
}
|
||||
|
||||
Future<void> initializeApiService() async {
|
||||
@ -462,16 +487,27 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
|
||||
//
|
||||
void register(String email, String password, BuildContext context) {
|
||||
AuthService.register(email, password).then((value) {
|
||||
void register(String email, String password, String password2, BuildContext context) {
|
||||
AuthService.register(email, password,password2).then((value) {
|
||||
if (value.isNotEmpty) {
|
||||
debugPrint("ユーザー登録成功:${email}, ${password}");
|
||||
logManager.addOperationLog("User tried to register new account : ${email} , ${password} .");
|
||||
|
||||
currentUser.clear();
|
||||
currentUser.add(value);
|
||||
//currentUser.add(value);
|
||||
isLoading.value = false;
|
||||
Navigator.pop(context);
|
||||
|
||||
// ユーザー登録成功メッセージを表示
|
||||
Get.snackbar(
|
||||
'success'.tr,
|
||||
'user_registration_successful'.tr,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white,
|
||||
snackPosition: SnackPosition.TOP,
|
||||
duration: const Duration(seconds: 3),
|
||||
);
|
||||
|
||||
//Navigator.pop(context);
|
||||
Get.toNamed(AppPages.INDEX);
|
||||
} else {
|
||||
debugPrint("ユーザー登録失敗:${email}, ${password}");
|
||||
@ -518,7 +554,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
}
|
||||
*/
|
||||
|
||||
void changeUser(Map<String, dynamic> value, {bool replace = true}) {
|
||||
void changeUser(Map<String, dynamic> value, {bool replace = true}) async{
|
||||
currentUser.clear();
|
||||
currentUser.add(value);
|
||||
if (replace) {
|
||||
@ -529,12 +565,29 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
if (currentUser.isNotEmpty) {
|
||||
rogMode.value = 0;
|
||||
restoreGame();
|
||||
|
||||
// チームデータを取得
|
||||
await fetchTeamData();
|
||||
} else {
|
||||
rogMode.value = 1;
|
||||
}
|
||||
Get.toNamed(AppPages.INDEX);
|
||||
}
|
||||
|
||||
Future<void> fetchTeamData() async {
|
||||
try {
|
||||
Get.put(TeamController());
|
||||
// \"TeamController\" not found. You need to call \"Get.put(TeamController())\" or \"Get.lazyPut(()=>TeamController())\"
|
||||
final teamController = Get.find<TeamController>();
|
||||
await teamController.fetchTeams();
|
||||
if (teamController.teams.isNotEmpty) {
|
||||
teamId.value = teamController.teams.first.id;
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching team data: $e");
|
||||
}
|
||||
}
|
||||
|
||||
loadUserDetailsForToken(String token) async {
|
||||
AuthService.userForToken(token).then((value) {
|
||||
print("----token val-- $value ------");
|
||||
|
||||
@ -78,7 +78,8 @@ class _IndexPageState extends State<IndexPage> {
|
||||
//
|
||||
drawer: DrawerPage(),
|
||||
appBar: AppBar(
|
||||
title: Text("add_location".tr),
|
||||
title: Obx(() => Text(indexController.selectedEventName.value)),
|
||||
//title: Text("add_location".tr),
|
||||
actions: [
|
||||
// IconButton(
|
||||
// onPressed: () {
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
|
||||
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
||||
// エラーメッセージをローカライズすることを検討してください。
|
||||
@ -16,9 +17,11 @@ class LoginPage extends StatefulWidget {
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
//class LoginPage extends StatelessWidget {
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
final ApiService apiService = Get.find<ApiService>();
|
||||
|
||||
TextEditingController emailController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
bool _obscureText = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -31,6 +34,62 @@ class _LoginPageState extends State<LoginPage> {
|
||||
});
|
||||
}
|
||||
|
||||
void _showResetPasswordDialog() {
|
||||
TextEditingController resetEmailController = TextEditingController();
|
||||
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('パスワードのリセット'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('パスワードをリセットするメールアドレスを入力してください。'),
|
||||
SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: resetEmailController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'メールアドレス',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('キャンセル'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text('リセット'),
|
||||
onPressed: () async {
|
||||
if (resetEmailController.text.isNotEmpty) {
|
||||
bool success = await apiService.resetPassword(resetEmailController.text);
|
||||
Get.back();
|
||||
if (success) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('パスワードリセット'),
|
||||
content: Text('パスワードリセットメールを送信しました。メールのリンクからパスワードを設定してください。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('OK'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Get.snackbar('エラー', 'パスワードリセットに失敗しました。もう一度お試しください。',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -43,10 +102,11 @@ class _LoginPageState extends State<LoginPage> {
|
||||
backgroundColor: Colors.white,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
body: indexController.currentUser.isEmpty
|
||||
body: GestureDetector(
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: indexController.currentUser.isEmpty
|
||||
? SizedBox(
|
||||
width: double.infinity,
|
||||
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
@ -72,10 +132,15 @@ class _LoginPageState extends State<LoginPage> {
|
||||
children: [
|
||||
makeInput(
|
||||
label: "email".tr, controller: emailController),
|
||||
makeInput(
|
||||
makePasswordInput(
|
||||
label: "password".tr,
|
||||
controller: passwordController,
|
||||
obsureText: true),
|
||||
obscureText: _obscureText,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_obscureText = !_obscureText;
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -122,8 +187,6 @@ class _LoginPageState extends State<LoginPage> {
|
||||
SnackPosition.TOP,
|
||||
duration: const Duration(
|
||||
seconds: 3),
|
||||
// backgroundColor: Colors.yellow,
|
||||
//icon:Image(image:AssetImage("assets/images/dora.png"))
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -167,27 +230,6 @@ class _LoginPageState extends State<LoginPage> {
|
||||
color: Colors.white70),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2.0,
|
||||
),
|
||||
MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 36,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
color: Colors.grey,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: Text(
|
||||
"cancel".tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: Colors.white70),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
@ -198,16 +240,11 @@ class _LoginPageState extends State<LoginPage> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
TextButton(
|
||||
onPressed: _showResetPasswordDialog,
|
||||
child: Text(
|
||||
"rogaining_user_need_tosign_up".tr,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontSize: 10.0
|
||||
),
|
||||
),
|
||||
"forgot_password".tr,
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -237,10 +274,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
child: Text(
|
||||
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
||||
style: TextStyle(
|
||||
//overflow: TextOverflow.ellipsis,
|
||||
fontSize:
|
||||
10.0, // Consider adjusting the font size if the text is too small.
|
||||
// Removed overflow: TextOverflow.ellipsis to allow text wrapping.
|
||||
fontSize: 10.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -251,18 +285,60 @@ class _LoginPageState extends State<LoginPage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
)
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
indexController.currentUser.clear();
|
||||
indexController.logout();
|
||||
Get.offAllNamed(AppPages.LOGIN);
|
||||
},
|
||||
child: const Text("Already Logged in, Click to logout"),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget makePasswordInput({
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
required bool obscureText,
|
||||
required VoidCallback onToggleVisibility,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: onToggleVisibility,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30.0)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeInput(
|
||||
{label, required TextEditingController controller, obsureText = false}) {
|
||||
return Column(
|
||||
|
||||
381
lib/pages/login/login_page.dart_backup
Normal file
381
lib/pages/login/login_page.dart_backup
Normal file
@ -0,0 +1,381 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
|
||||
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
||||
// エラーメッセージをローカライズすることを検討してください。
|
||||
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
|
||||
//
|
||||
class LoginPage extends StatefulWidget {
|
||||
@override
|
||||
_LoginPageState createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
//class LoginPage extends StatelessWidget {
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
final ApiService apiService = Get.find<ApiService>();
|
||||
|
||||
TextEditingController emailController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
bool _obscureText = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showHelperDialog(
|
||||
'参加するにはユーザー登録が必要です。サインアップからユーザー登録してください。',
|
||||
'login_page'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _showResetPasswordDialog() {
|
||||
TextEditingController resetEmailController = TextEditingController();
|
||||
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('パスワードのリセット'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('パスワードをリセットするメールアドレスを入力してください。'),
|
||||
SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: resetEmailController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'メールアドレス',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('キャンセル'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text('リセット'),
|
||||
onPressed: () async {
|
||||
if (resetEmailController.text.isNotEmpty) {
|
||||
bool success = await apiService.resetPassword(resetEmailController.text);
|
||||
Get.back();
|
||||
if (success) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('パスワードリセット'),
|
||||
content: Text('パスワードリセットメールを送信しました。メールのリンクからパスワードを設定してください。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('OK'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Get.snackbar('エラー', 'パスワードリセットに失敗しました。もう一度お試しください。',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.white,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
body: indexController.currentUser.isEmpty
|
||||
? SizedBox(
|
||||
width: double.infinity,
|
||||
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height / 6,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
'assets/images/login_image.jpg'))),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Column(
|
||||
children: [
|
||||
makeInput(
|
||||
label: "email".tr, controller: emailController),
|
||||
makePasswordInput(
|
||||
label: "password".tr,
|
||||
controller: passwordController,
|
||||
obscureText: _obscureText,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_obscureText = !_obscureText;
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 3, left: 3),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
),
|
||||
child: Obx(
|
||||
(() => indexController.isLoading.value == true
|
||||
? MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 60,
|
||||
onPressed: () {},
|
||||
color: Colors.grey[400],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: const CircularProgressIndicator(),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 40,
|
||||
onPressed: () async {
|
||||
if (emailController.text.isEmpty ||
|
||||
passwordController
|
||||
.text.isEmpty) {
|
||||
Get.snackbar(
|
||||
"no_values".tr,
|
||||
"email_and_password_required"
|
||||
.tr,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
icon: const Icon(
|
||||
Icons
|
||||
.assistant_photo_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.blue),
|
||||
snackPosition:
|
||||
SnackPosition.TOP,
|
||||
duration: const Duration(
|
||||
seconds: 3),
|
||||
// backgroundColor: Colors.yellow,
|
||||
//icon:Image(image:AssetImage("assets/images/dora.png"))
|
||||
);
|
||||
return;
|
||||
}
|
||||
indexController.isLoading.value =
|
||||
true;
|
||||
indexController.login(
|
||||
emailController.text,
|
||||
passwordController.text,
|
||||
context);
|
||||
},
|
||||
color: Colors.indigoAccent[400],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: Text(
|
||||
"login".tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: Colors.white70),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5.0,
|
||||
),
|
||||
MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 36,
|
||||
onPressed: () {
|
||||
Get.toNamed(AppPages.REGISTER);
|
||||
},
|
||||
color: Colors.redAccent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: Text(
|
||||
"sign_up".tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: Colors.white70),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
)),
|
||||
),
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 3,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _showResetPasswordDialog,
|
||||
child: Text(
|
||||
"forgot_password".tr,
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"app_developed_by_gifu_dx".tr,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontSize: 10.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
||||
style: TextStyle(
|
||||
//overflow: TextOverflow.ellipsis,
|
||||
fontSize:
|
||||
10.0, // Consider adjusting the font size if the text is too small.
|
||||
// Removed overflow: TextOverflow.ellipsis to allow text wrapping.
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
)
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
indexController.logout();
|
||||
Get.offAllNamed(AppPages.LOGIN);
|
||||
},
|
||||
child: const Text("Already Logged in, Click to logout"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget makePasswordInput({
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
required bool obscureText,
|
||||
required VoidCallback onToggleVisibility,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: onToggleVisibility,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30.0)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeInput(
|
||||
{label, required TextEditingController controller, obsureText = false}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
TextField(
|
||||
controller: controller,
|
||||
obscureText: obsureText,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: (Colors.grey[400])!,
|
||||
),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: (Colors.grey[400])!),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30.0,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -207,7 +207,8 @@ class LoginPopupPage extends StatelessWidget {
|
||||
)
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
indexController.currentUser.clear();
|
||||
indexController.logout();
|
||||
Get.offAllNamed(AppPages.LOGIN);
|
||||
},
|
||||
child: const Text("Already Logged in, Click to logout"),
|
||||
),
|
||||
|
||||
@ -16,6 +16,9 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
final TextEditingController passwordController = TextEditingController();
|
||||
final TextEditingController confirmPasswordController = TextEditingController();
|
||||
|
||||
bool _obscurePassword = true;
|
||||
bool _obscureConfirmPassword = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -60,8 +63,28 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
makeInput(label: "email".tr, controller: emailController),
|
||||
makeInput(label: "password".tr, controller: passwordController, obsureText: true),
|
||||
makeInput(label: "confirm_password".tr, controller: confirmPasswordController, obsureText: true),
|
||||
//makeInput(label: "password".tr, controller: passwordController, obsureText: true),
|
||||
//makeInput(label: "confirm_password".tr, controller: confirmPasswordController, obsureText: true),
|
||||
makePasswordInput(
|
||||
label: "password".tr,
|
||||
controller: passwordController,
|
||||
obscureText: _obscurePassword,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
makePasswordInput(
|
||||
label: "confirm_password".tr,
|
||||
controller: confirmPasswordController,
|
||||
obscureText: _obscureConfirmPassword,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_obscureConfirmPassword = !_obscureConfirmPassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ElevatedButton(
|
||||
onPressed: _handleRegister,
|
||||
@ -91,6 +114,38 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget makePasswordInput({
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
required bool obscureText,
|
||||
required VoidCallback onToggleVisibility,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87)),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[400]!)),
|
||||
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey[400]!)),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: onToggleVisibility,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _handleRegister() {
|
||||
if (passwordController.text != confirmPasswordController.text) {
|
||||
_showErrorSnackbar("no_match".tr, "password_does_not_match".tr);
|
||||
@ -106,6 +161,7 @@ class _RegisterPageState extends State<RegisterPage> {
|
||||
indexController.register(
|
||||
emailController.text,
|
||||
passwordController.text,
|
||||
confirmPasswordController.text,
|
||||
context
|
||||
);
|
||||
// 登録が成功したと仮定し、ログインページに遷移
|
||||
|
||||
@ -146,11 +146,6 @@ class _TeamDetailPageState extends State<TeamDetailPage> {
|
||||
onChanged: (value) => controller.updateCategory(value),
|
||||
);
|
||||
}),
|
||||
if (mode.value == 'edit')
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text('ゼッケン番号: ${controller.selectedTeam.value?.zekkenNumber ?? ""}'),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Text('所有者: ${controller.currentUser.value?.email ?? "未設定"}'),
|
||||
|
||||
@ -35,7 +35,7 @@ class TeamListPage extends GetWidget<TeamController> {
|
||||
final team = controller.teams[index];
|
||||
return ListTile(
|
||||
title: Text(team.teamName),
|
||||
subtitle: Text('${team.category.categoryName} - ${team.zekkenNumber}'),
|
||||
subtitle: Text('${team.category.categoryName} '),
|
||||
onTap: () async {
|
||||
await Get.toNamed(
|
||||
AppPages.TEAM_DETAIL,
|
||||
|
||||
@ -114,9 +114,14 @@ class ApiService extends GetxService{
|
||||
|
||||
List<NewCategory> categories = [];
|
||||
for (var categoryJson in categoriesJson) {
|
||||
try {
|
||||
//print('\nCategory Data:');
|
||||
//_printDataComparison(categoryJson, NewCategory);
|
||||
categories.add(NewCategory.fromJson(categoryJson));
|
||||
}catch(e){
|
||||
print('Error parsing category: $e');
|
||||
print('Problematic JSON: $categoryJson');
|
||||
}
|
||||
}
|
||||
|
||||
return categories;
|
||||
@ -131,6 +136,26 @@ class ApiService extends GetxService{
|
||||
}
|
||||
}
|
||||
|
||||
Future<NewCategory> getZekkenNumber(int categoryId) async {
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/categories-viewset/$categoryId/get_zekken_number/'),
|
||||
headers: {'Authorization': 'Token $token',"Content-Type": "application/json; charset=UTF-8"},
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final decodedResponse = utf8.decode(response.bodyBytes);
|
||||
print('User Response body: $decodedResponse');
|
||||
final categoriesJson = json.decode(decodedResponse);
|
||||
|
||||
return NewCategory.fromJson(categoriesJson);
|
||||
} else {
|
||||
throw Exception('Failed to increment category number');
|
||||
}
|
||||
} catch (e) {
|
||||
throw Exception('Error incrementing category number: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<User> getCurrentUser() async {
|
||||
init();
|
||||
getToken();
|
||||
@ -457,7 +482,7 @@ class ApiService extends GetxService{
|
||||
}
|
||||
}
|
||||
|
||||
Future<Entry> createEntry(int teamId, int eventId, int categoryId, DateTime date) async {
|
||||
Future<Entry> createEntry(int teamId, int eventId, int categoryId, DateTime date,String zekkenNumber) async {
|
||||
init();
|
||||
getToken();
|
||||
|
||||
@ -477,6 +502,7 @@ class ApiService extends GetxService{
|
||||
'event': eventId,
|
||||
'category': categoryId,
|
||||
'date': formattedDate,
|
||||
'zekken_number':zekkenNumber,
|
||||
}),
|
||||
);
|
||||
|
||||
@ -508,7 +534,7 @@ class ApiService extends GetxService{
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
body: json.encode({
|
||||
'zekken_number': entry.team.zekkenNumber,
|
||||
'zekken_number': entry.zekkenNumber,
|
||||
'event_code': entry.event.eventName,
|
||||
'group': entry.team.category.categoryName,
|
||||
'team_name': entry.team.teamName,
|
||||
@ -608,4 +634,29 @@ class ApiService extends GetxService{
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> resetPassword(String email) async {
|
||||
init();
|
||||
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse('$baseUrl/password-reset/'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
body: json.encode({
|
||||
'email': email,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
print('Password reset failed with status code: ${response.statusCode}');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error in resetPassword: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,6 +132,7 @@ class AuthService {
|
||||
return cats;
|
||||
}
|
||||
|
||||
|
||||
// ユーザー登録
|
||||
//
|
||||
/*
|
||||
@ -151,7 +152,7 @@ class AuthService {
|
||||
*/
|
||||
|
||||
static Future<Map<String, dynamic>> register(
|
||||
String email, String password) async {
|
||||
String email, String password, String password2) async {
|
||||
Map<String, dynamic> cats = {};
|
||||
String serverUrl = ConstValues.currentServer();
|
||||
String url = '$serverUrl/api/register/';
|
||||
@ -161,10 +162,10 @@ class AuthService {
|
||||
headers: <String, String>{
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
body: jsonEncode(<String, String>{'email': email, 'password': password}),
|
||||
body: jsonEncode(<String, String>{'email': email, 'password': password, 'password2': password2}),
|
||||
);
|
||||
//print(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
if (response.statusCode == 201) {
|
||||
cats = json.decode(utf8.decode(response.bodyBytes));
|
||||
}
|
||||
return cats;
|
||||
|
||||
@ -4,8 +4,10 @@ import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:rogapp/model/rog.dart';
|
||||
import 'package:rogapp/model/team.dart';
|
||||
import 'package:rogapp/pages/destination/destination_controller.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/pages/team/team_controller.dart';
|
||||
import 'package:rogapp/utils/database_gps.dart';
|
||||
import 'package:rogapp/utils/database_helper.dart';
|
||||
import 'dart:convert';
|
||||
@ -32,11 +34,19 @@ class ExternalService {
|
||||
|
||||
Future<Map<String, dynamic>> startRogaining() async {
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
//final TeamController teamController = Get.find<TeamController>();
|
||||
|
||||
debugPrint("== startRogaining ==");
|
||||
|
||||
Map<String, dynamic> res = {};
|
||||
|
||||
//final teamController = TeamController();
|
||||
|
||||
//Team team0 = teamController.teams[0];
|
||||
//print("team={team0}");
|
||||
|
||||
//int teamId = indexController.currentUser[0]["user"]["team"]["id"];
|
||||
|
||||
int userId = indexController.currentUser[0]["user"]["id"];
|
||||
//print("--- Pressed -----");
|
||||
String team = indexController.currentUser[0]["user"]['team_name'];
|
||||
@ -60,8 +70,9 @@ class ExternalService {
|
||||
} else {
|
||||
debugPrint("== startRogaining processing==");
|
||||
|
||||
String url = 'https://rogaining.sumasen.net/gifuroge/start_from_rogapp';
|
||||
//print('++++++++$url');
|
||||
String serverUrl = ConstValues.currentServer();
|
||||
String url = '$serverUrl/gifuroge/start_from_rogapp';
|
||||
print('++++++++$url');
|
||||
final http.Response response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: <String, String>{
|
||||
@ -82,7 +93,7 @@ class ExternalService {
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> makeCheckpoint(
|
||||
int userId,
|
||||
int userId, // 中身はteamId
|
||||
String token,
|
||||
String checkinTime,
|
||||
String teamname,
|
||||
@ -93,9 +104,17 @@ class ExternalService {
|
||||
// print("~~~~ cp is $cp ~~~~");
|
||||
//print("--cpcp-- ${cp}");
|
||||
Map<String, dynamic> res = {};
|
||||
String url = 'https://rogaining.sumasen.net/gifuroge/checkin_from_rogapp';
|
||||
String serverUrl = ConstValues.currentServer();
|
||||
String url = '$serverUrl/gifuroge/checkin_from_rogapp';
|
||||
//print('++++++++$url');
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
//final TeamController teamController = Get.find<TeamController>();
|
||||
|
||||
|
||||
// Team team0 = indexController.teamController.teams[0];
|
||||
// print("team={team0}");
|
||||
|
||||
//int teamId = indexController.teamController.teams[0];
|
||||
|
||||
if (imageurl != null) {
|
||||
if (indexController.connectionStatusName.value != "wifi" &&
|
||||
@ -160,7 +179,7 @@ class ExternalService {
|
||||
'cp_number': cp.toString(),
|
||||
'event_code': eventcode,
|
||||
'image': res["checkinimage"].toString().replaceAll(
|
||||
'http://localhost:8100', 'http://rogaining.sumasen.net')
|
||||
'http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net')
|
||||
}),
|
||||
);
|
||||
var vv = jsonEncode(<String, String>{
|
||||
@ -168,7 +187,7 @@ class ExternalService {
|
||||
'cp_number': cp.toString(),
|
||||
'event_code': eventcode,
|
||||
'image': res["checkinimage"].toString().replaceAll(
|
||||
'http://localhost:8100', 'http://rogaining.sumasen.net')
|
||||
'http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net')
|
||||
});
|
||||
//print("~~~~ api 2 values $vv ~~~~");
|
||||
//print("--json-- $vv");
|
||||
@ -253,6 +272,9 @@ class ExternalService {
|
||||
final DestinationController destinationController =
|
||||
Get.find<DestinationController>();
|
||||
|
||||
// チームIDを取得
|
||||
int teamId = indexController.currentUser[0]["user"]["team"]["id"];
|
||||
|
||||
debugPrint("== goal Rogaining ==");
|
||||
|
||||
//if(indexController.connectionStatusName != "wifi" && indexController.connectionStatusName != "mobile"){
|
||||
@ -261,7 +283,7 @@ class ExternalService {
|
||||
id: 1,
|
||||
team_name: teamname,
|
||||
event_code: eventcode,
|
||||
user_id: userId,
|
||||
user_id: teamId, //userId,
|
||||
cp_number: -1,
|
||||
checkintime: DateTime.now().toUtc().microsecondsSinceEpoch,
|
||||
image: image,
|
||||
@ -283,7 +305,7 @@ class ExternalService {
|
||||
},
|
||||
// 'id', 'user', 'goalimage', 'goaltime', 'team_name', 'event_code','cp_number'
|
||||
body: jsonEncode(<String, String>{
|
||||
'user': userId.toString(),
|
||||
'user': teamId.toString(), //userId.toString(),
|
||||
'team_name': teamname,
|
||||
'event_code': eventcode,
|
||||
'goaltime': goalTime,
|
||||
@ -292,7 +314,8 @@ class ExternalService {
|
||||
}),
|
||||
);
|
||||
|
||||
String url = 'https://rogaining.sumasen.net/gifuroge/goal_from_rogapp';
|
||||
//String serverUrl = ConstValues.currentServer();
|
||||
String url = '$serverUrl/gifuroge/goal_from_rogapp';
|
||||
//print('++++++++$url');
|
||||
if (response.statusCode == 201) {
|
||||
Map<String, dynamic> res = json.decode(utf8.decode(response.bodyBytes));
|
||||
@ -308,7 +331,7 @@ class ExternalService {
|
||||
'event_code': eventcode,
|
||||
'goal_time': goalTime,
|
||||
'image': res["goalimage"].toString().replaceAll(
|
||||
'http://localhost:8100', 'http://rogaining.sumasen.net')
|
||||
'http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net')
|
||||
}),
|
||||
);
|
||||
String rec = jsonEncode(<String, String>{
|
||||
@ -317,7 +340,7 @@ class ExternalService {
|
||||
'goal_time': goalTime,
|
||||
'image': res["goalimage"]
|
||||
.toString()
|
||||
.replaceAll('http://localhost:8100', 'http://rogaining.sumasen.net')
|
||||
.replaceAll('http://localhost:8100', serverUrl) //'http://rogaining.sumasen.net')
|
||||
});
|
||||
//print("-- json -- $rec");
|
||||
//print('----- response2 is $response2 --------');
|
||||
@ -343,8 +366,8 @@ class ExternalService {
|
||||
indexController.connectionStatusName.value != "mobile") {
|
||||
return Future.value(false);
|
||||
} else {
|
||||
String url =
|
||||
'https://rogaining.sumasen.net/gifuroge/remove_checkin_from_rogapp';
|
||||
String serverUrl = ConstValues.currentServer();
|
||||
String url = '$serverUrl/gifuroge/remove_checkin_from_rogapp';
|
||||
//print('++++++++$url');
|
||||
final http.Response response = await http.post(
|
||||
Uri.parse(url),
|
||||
@ -413,8 +436,8 @@ class ExternalService {
|
||||
|
||||
//print("calling push gps step 2 ${payload}");
|
||||
|
||||
String urlS =
|
||||
'https://rogaining.sumasen.net/gifuroge/get_waypoint_datas_from_rogapp';
|
||||
String serverUrl = ConstValues.currentServer();
|
||||
String urlS = '$serverUrl/gifuroge/get_waypoint_datas_from_rogapp';
|
||||
//print('++++++++$url');
|
||||
var url = Uri.parse(urlS); // Replace with your server URL
|
||||
var response = await http.post(
|
||||
@ -440,8 +463,8 @@ class ExternalService {
|
||||
static Future<Map<String, dynamic>> usersEventCode(
|
||||
String teamcode, String password) async {
|
||||
Map<String, dynamic> res = {};
|
||||
String url =
|
||||
"https://rogaining.sumasen.net/gifuroge/check_event_code?zekken_number=$teamcode&password=$password";
|
||||
String serverUrl = ConstValues.currentServer();
|
||||
String url = "$serverUrl/gifuroge/check_event_code?zekken_number=$teamcode&password=$password";
|
||||
//print('++++++++$url');
|
||||
final http.Response response =
|
||||
await http.get(Uri.parse(url), headers: <String, String>{
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
|
||||
class ConstValues{
|
||||
static const container_svr = "http://container.intranet.sumasen.net:8100";
|
||||
static const server_uri = "https://rogaining.intranet.sumasen.net";
|
||||
//static const container_svr = "http://container.sumasen.net:8100";
|
||||
//static const server_uri = "https://rogaining.sumasen.net";
|
||||
//static const container_svr = "http://container.intranet.sumasen.net:8100";
|
||||
//static const server_uri = "https://rogaining.intranet.sumasen.net";
|
||||
static const container_svr = "http://container.sumasen.net:8100";
|
||||
static const server_uri = "https://rogaining.sumasen.net";
|
||||
static const dev_server = "http://localhost:8100";
|
||||
static const dev_ip_server = "http://192.168.8.100:8100";
|
||||
static const dev_home_ip_server = "http://172.20.10.9:8100";
|
||||
|
||||
@ -213,6 +213,9 @@ class StringValues extends Translations{
|
||||
'reset_explain': 'All data has been reset. You should tap start rogaining to start game.',
|
||||
'no_match': 'No match!',
|
||||
'password_does_not_match':'The passwords you entered were not match.',
|
||||
'forgot_password':'Forgot password',
|
||||
'user_registration_successful':'Sent activation mail to you. Pls click activation link on the email.',
|
||||
|
||||
},
|
||||
'ja_JP': {
|
||||
'drawer_title':'ロゲイニング参加者はログイン するとチェックポイントが参照 できます',
|
||||
@ -428,6 +431,8 @@ class StringValues extends Translations{
|
||||
'reset_explain': 'すべてリセットされました。ロゲ開始から再開して下さい。',
|
||||
'no_match': '不一致',
|
||||
'password_does_not_match':'入力したパスワードが一致しません',
|
||||
'forgot_password':'パスワードを忘れた場合',
|
||||
'user_registration_successful':'ユーザー認証のメールをお届けしました。メール上のリンクをクリックして正式登録してください。',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -216,6 +216,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
|
||||
);
|
||||
|
||||
saveGameState();
|
||||
//int teamId = indexController.teamId.value; // teamIdを使用
|
||||
await ExternalService().startRogaining();
|
||||
Get.back();
|
||||
Get.back();// Close the dialog and potentially navigate away
|
||||
|
||||
381
login_page.dart
Normal file
381
login_page.dart
Normal file
@ -0,0 +1,381 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:rogapp/pages/index/index_controller.dart';
|
||||
import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/widgets/helper_dialog.dart';
|
||||
import 'package:rogapp/services/api_service.dart';
|
||||
|
||||
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
|
||||
// エラーメッセージをローカライズすることを検討してください。
|
||||
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
|
||||
//
|
||||
class LoginPage extends StatefulWidget {
|
||||
@override
|
||||
_LoginPageState createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
//class LoginPage extends StatelessWidget {
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
final ApiService apiService = Get.find<ApiService>();
|
||||
|
||||
TextEditingController emailController = TextEditingController();
|
||||
TextEditingController passwordController = TextEditingController();
|
||||
bool _obscureText = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showHelperDialog(
|
||||
'参加するにはユーザー登録が必要です。サインアップからユーザー登録してください。',
|
||||
'login_page'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _showResetPasswordDialog() {
|
||||
TextEditingController resetEmailController = TextEditingController();
|
||||
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('パスワードのリセット'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('パスワードをリセットするメールアドレスを入力してください。'),
|
||||
SizedBox(height: 10),
|
||||
TextField(
|
||||
controller: resetEmailController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'メールアドレス',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('キャンセル'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text('リセット'),
|
||||
onPressed: () async {
|
||||
if (resetEmailController.text.isNotEmpty) {
|
||||
bool success = await apiService.resetPassword(resetEmailController.text);
|
||||
Get.back();
|
||||
if (success) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('パスワードリセット'),
|
||||
content: Text('パスワードリセットメールを送信しました。メールのリンクからパスワードを設定してください。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('OK'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Get.snackbar('エラー', 'パスワードリセットに失敗しました。もう一度お試しください。',
|
||||
snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//LoginPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
backgroundColor: Colors.white,
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.white,
|
||||
automaticallyImplyLeading: false,
|
||||
),
|
||||
body: indexController.currentUser.isEmpty
|
||||
? SizedBox(
|
||||
width: double.infinity,
|
||||
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height / 6,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
'assets/images/login_image.jpg'))),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Column(
|
||||
children: [
|
||||
makeInput(
|
||||
label: "email".tr, controller: emailController),
|
||||
makePasswordInput(
|
||||
label: "password".tr,
|
||||
controller: passwordController,
|
||||
obscureText: _obscureText,
|
||||
onToggleVisibility: () {
|
||||
setState(() {
|
||||
_obscureText = !_obscureText;
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 3, left: 3),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
),
|
||||
child: Obx(
|
||||
(() => indexController.isLoading.value == true
|
||||
? MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 60,
|
||||
onPressed: () {},
|
||||
color: Colors.grey[400],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: const CircularProgressIndicator(),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 40,
|
||||
onPressed: () async {
|
||||
if (emailController.text.isEmpty ||
|
||||
passwordController
|
||||
.text.isEmpty) {
|
||||
Get.snackbar(
|
||||
"no_values".tr,
|
||||
"email_and_password_required"
|
||||
.tr,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
icon: const Icon(
|
||||
Icons
|
||||
.assistant_photo_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.blue),
|
||||
snackPosition:
|
||||
SnackPosition.TOP,
|
||||
duration: const Duration(
|
||||
seconds: 3),
|
||||
// backgroundColor: Colors.yellow,
|
||||
//icon:Image(image:AssetImage("assets/images/dora.png"))
|
||||
);
|
||||
return;
|
||||
}
|
||||
indexController.isLoading.value =
|
||||
true;
|
||||
indexController.login(
|
||||
emailController.text,
|
||||
passwordController.text,
|
||||
context);
|
||||
},
|
||||
color: Colors.indigoAccent[400],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: Text(
|
||||
"login".tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: Colors.white70),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5.0,
|
||||
),
|
||||
MaterialButton(
|
||||
minWidth: double.infinity,
|
||||
height: 36,
|
||||
onPressed: () {
|
||||
Get.toNamed(AppPages.REGISTER);
|
||||
},
|
||||
color: Colors.redAccent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: Text(
|
||||
"sign_up".tr,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: Colors.white70),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
)),
|
||||
),
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 3,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _showResetPasswordDialog,
|
||||
child: Text(
|
||||
"forgot_password".tr,
|
||||
style: TextStyle(color: Colors.blue),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"app_developed_by_gifu_dx".tr,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontSize: 10.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
|
||||
style: TextStyle(
|
||||
//overflow: TextOverflow.ellipsis,
|
||||
fontSize:
|
||||
10.0, // Consider adjusting the font size if the text is too small.
|
||||
// Removed overflow: TextOverflow.ellipsis to allow text wrapping.
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
)
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
indexController.logout();
|
||||
Get.offAllNamed(AppPages.LOGIN);
|
||||
},
|
||||
child: const Text("Already Logged in, Click to logout"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget makePasswordInput({
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
required bool obscureText,
|
||||
required VoidCallback onToggleVisibility,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
TextField(
|
||||
controller: controller,
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey[400]!),
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
obscureText ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.grey,
|
||||
),
|
||||
onPressed: onToggleVisibility,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30.0)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget makeInput(
|
||||
{label, required TextEditingController controller, obsureText = false}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 15, fontWeight: FontWeight.w400, color: Colors.black87),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
TextField(
|
||||
controller: controller,
|
||||
obscureText: obsureText,
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: (Colors.grey[400])!,
|
||||
),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: (Colors.grey[400])!),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30.0,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 4.8.5+485
|
||||
version: 4.8.6+486
|
||||
|
||||
environment:
|
||||
sdk: ">=3.2.0 <4.0.0"
|
||||
|
||||
Reference in New Issue
Block a user