From c226e31ba4864748330a69a8b5896441ae552a85 Mon Sep 17 00:00:00 2001 From: TBooran Date: Mon, 16 Mar 2026 16:37:54 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=94=E3=83=B3=E3=82=92=E3=82=BF=E3=83=83?= =?UTF-8?q?=E3=83=97=E3=81=97=E3=81=9F=E3=82=89=E3=83=A1=E3=83=A2=E3=82=92?= =?UTF-8?q?=E7=B7=A8=E9=9B=86=E3=81=99=E3=82=8B=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=81=AB=E9=81=B7=E7=A7=BB=E3=81=99=E3=82=8B=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/features/map/data/pin_repository.dart | 4 + lib/features/map/presentation/map_screen.dart | 5 +- .../map/presentation/memo_edit_screen.dart | 89 +++++++++++++++++++ lib/features/map/providers/pin_provider.dart | 25 ++++++ lib/router/app_router.dart | 8 +- 5 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 lib/features/map/presentation/memo_edit_screen.dart diff --git a/lib/features/map/data/pin_repository.dart b/lib/features/map/data/pin_repository.dart index 6cdc2df..4832fc1 100644 --- a/lib/features/map/data/pin_repository.dart +++ b/lib/features/map/data/pin_repository.dart @@ -61,6 +61,7 @@ class PinData { final LatLng position; final DateTime createdAt; final bool isLocal; + final String? memo; PinData({ required this.id, @@ -68,6 +69,7 @@ class PinData { required this.position, required this.createdAt, this.isLocal = false, + this.memo, }); factory PinData.local(LatLng position) { @@ -90,6 +92,7 @@ class PinData { ), createdAt: DateTime.parse(json['createdAt'] as String), isLocal: json['isLocal'] as bool? ?? false, + memo: json['memo'] as String?, ); } @@ -101,6 +104,7 @@ class PinData { 'longitude': position.longitude, 'createdAt': createdAt.toUtc().toIso8601String(), 'isLocal': isLocal, + 'memo': memo, }; } } diff --git a/lib/features/map/presentation/map_screen.dart b/lib/features/map/presentation/map_screen.dart index f606fbd..937f2c0 100644 --- a/lib/features/map/presentation/map_screen.dart +++ b/lib/features/map/presentation/map_screen.dart @@ -39,7 +39,10 @@ class _MapScreenState extends ConsumerState { width: 60, height: 60, alignment: Alignment.topCenter, - child: const Icon(Icons.location_pin, size: 60, color: Colors.red), + child: GestureDetector( + onTap: () => context.push('/memo/${pin.id}'), + child: const Icon(Icons.location_pin, size: 60, color: Colors.red), + ), ); }).toList(); } diff --git a/lib/features/map/presentation/memo_edit_screen.dart b/lib/features/map/presentation/memo_edit_screen.dart new file mode 100644 index 0000000..89e2eb4 --- /dev/null +++ b/lib/features/map/presentation/memo_edit_screen.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:memomap/features/map/providers/pin_provider.dart'; + +class MemoEditScreen extends ConsumerStatefulWidget { + const MemoEditScreen({super.key, required this.pinId}); + + final String pinId; + + @override + ConsumerState createState() => _MemoEditScreenState(); +} + +class _MemoEditScreenState extends ConsumerState { + late final TextEditingController _controller; + String? _initialMemo; + + @override + void initState() { + super.initState(); + final pins = ref.read(pinsProvider).valueOrNull ?? []; + final pin = pins.where((p) => p.id == widget.pinId).firstOrNull; + _initialMemo = pin?.memo; + _controller = TextEditingController(text: _initialMemo ?? ''); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void _saveMemo() { + final text = _controller.text.trim(); + final memo = text.isEmpty ? null : text; + ref.read(pinsProvider.notifier).updatePinMemo(widget.pinId, memo); + context.pop(); + } + + void _deleteMemo() { + ref.read(pinsProvider.notifier).updatePinMemo(widget.pinId, null); + context.pop(); + } + + @override + Widget build(BuildContext context) { + final hasMemo = _initialMemo != null && _initialMemo!.isNotEmpty; + + return Scaffold( + appBar: AppBar( + title: Text(hasMemo ? 'メモを編集' : 'メモを追加'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + TextField( + controller: _controller, + maxLines: null, + decoration: const InputDecoration( + hintText: 'ここにメモを入力', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (hasMemo) + TextButton( + style: TextButton.styleFrom( + foregroundColor: Colors.red, + ), + onPressed: _deleteMemo, + child: const Text('削除'), + ), + ElevatedButton( + onPressed: _saveMemo, + child: const Text('保存'), + ), + ], + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/features/map/providers/pin_provider.dart b/lib/features/map/providers/pin_provider.dart index f7edd3c..5b0154b 100644 --- a/lib/features/map/providers/pin_provider.dart +++ b/lib/features/map/providers/pin_provider.dart @@ -126,6 +126,31 @@ class PinsNotifier extends AsyncNotifier> { } } + Future updatePinMemo(String id, String? memo) async { + final pins = state.value ?? []; + final index = pins.indexWhere((p) => p.id == id); + if (index == -1) return; + + final updatedPin = PinData( + id: pins[index].id, + userId: pins[index].userId, + position: pins[index].position, + createdAt: pins[index].createdAt, + isLocal: pins[index].isLocal, + memo: memo, + ); + + final newPins = List.from(pins); + newPins[index] = updatedPin; + + state = AsyncValue.data(newPins); + + // Update local storage + final storage = ref.read(localPinStorageProvider); + await storage.setLocalPins(newPins.where((p) => p.isLocal).toList()); + await storage.setCachedPins(newPins.where((p) => !p.isLocal).toList()); + } + Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => build()); diff --git a/lib/router/app_router.dart b/lib/router/app_router.dart index 6b95be3..a99e180 100644 --- a/lib/router/app_router.dart +++ b/lib/router/app_router.dart @@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:memomap/features/auth/presentation/login_screen.dart'; import 'package:memomap/features/auth/providers/auth_provider.dart'; import 'package:memomap/features/map/presentation/map_screen.dart'; +import 'package:memomap/features/map/presentation/memo_edit_screen.dart'; import 'package:memomap/features/profile/presentation/profile_screen.dart'; final routerProvider = Provider((ref) { @@ -43,8 +44,11 @@ final routerProvider = Provider((ref) { }, ), GoRoute( - path: '/auth-callback', - builder: (context, state) => const AuthCallbackScreen(), + path: '/memo/:pinId', + builder: (context, state) { + final pinId = state.pathParameters['pinId']!; + return MemoEditScreen(pinId: pinId); + }, ), ], errorBuilder: (context, state) => Scaffold(