하단 내비게이션 바 여백 · 인증 칩 · 위치 선택 지도 · 퀘스트 상세 값 정렬

This commit is contained in:
Sn-Kinos
2026-10-06 01:32:25 +09:00
parent d441170dd7
commit 807e446b8b
8 changed files with 263 additions and 96 deletions
+79 -15
View File
@@ -1,3 +1,5 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geocoding/geocoding.dart';
@@ -62,11 +64,20 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
Future<void> _drop(LatLng point) async {
final coords =
'${point.latitude.toStringAsFixed(5)}, ${point.longitude.toStringAsFixed(5)}';
setState(() => _selected = QuestPlace(
id: '', name: coords, lat: point.latitude, lng: point.longitude));
setState(
() => _selected = QuestPlace(
id: '',
name: coords,
lat: point.latitude,
lng: point.longitude,
),
);
var address = coords;
try {
final marks = await Geocoding().placemarkFromCoordinates(point.latitude, point.longitude);
final marks = await Geocoding().placemarkFromCoordinates(
point.latitude,
point.longitude,
);
if (marks.isNotEmpty) {
final line = placeAddress(marks.first);
if (line.isNotEmpty) address = line;
@@ -75,8 +86,14 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
// 지오코더가 없거나(에뮬레이터 · 테스트 — 플랫폼 구현 없음) 실패 — 좌표 표시로 둔다.
}
if (!mounted || _selected?.lat != point.latitude) return;
setState(() => _selected = QuestPlace(
id: '', name: address, lat: point.latitude, lng: point.longitude));
setState(
() => _selected = QuestPlace(
id: '',
name: address,
lat: point.latitude,
lng: point.longitude,
),
);
}
@override
@@ -129,6 +146,8 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'kr.co.fanuzu.app',
// Figma `371:356705` 은 어두운 지도다 — 같은 OSM 타일을 어둡게 칠한다(flutter_map 내장).
tileBuilder: darkModeTileBuilder,
),
MarkerLayer(
markers: [
@@ -138,14 +157,21 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
point: LatLng(p.lat!, p.lng!),
child: GestureDetector(
onTap: () => _choose(p),
child: const Icon(Icons.place, color: AppColors.cosmicAccent),
child: const Icon(
Icons.place,
color: AppColors.cosmicAccent,
),
),
),
if (pinned != null && pinned.lat != null && pinned.lng != null)
if (pinned != null &&
pinned.lat != null &&
pinned.lng != null)
Marker(
point: LatLng(pinned.lat!, pinned.lng!),
width: 38,
height: 44,
alignment: Alignment.topCenter,
child: const Icon(Icons.location_pin, size: 40, color: Colors.redAccent),
child: const CustomPaint(painter: _PinPainter()),
),
],
),
@@ -154,7 +180,7 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
alignment: Alignment.bottomRight,
child: Text(
'© OpenStreetMap',
style: TextStyle(fontSize: 10, color: Colors.black54),
style: TextStyle(fontSize: 10, color: Colors.white54),
),
),
],
@@ -181,7 +207,8 @@ class _PlacePickerScreenState extends State<PlacePickerScreen> {
// 디폴트 미선택 · 단수 선택.
: RadioGroup<String>(
groupValue: pinned?.id,
onChanged: (v) => _choose(widget.places.firstWhere((p) => p.id == v)),
onChanged: (v) =>
_choose(widget.places.firstWhere((p) => p.id == v)),
child: ListView.builder(
itemCount: _visible.length,
itemBuilder: (context, i) {
@@ -243,9 +270,46 @@ String placeAddress(Placemark m) {
}
final road = street.isNotEmpty
? street
: [t(m.thoroughfare), t(m.subThoroughfare)].where((s) => s.isNotEmpty).join(' ');
return [area, t(m.locality), t(m.subLocality), road]
.where((s) => s.isNotEmpty)
.toSet()
.join(' ');
: [
t(m.thoroughfare),
t(m.subThoroughfare),
].where((s) => s.isNotEmpty).join(' ');
return [
area,
t(m.locality),
t(m.subLocality),
road,
].where((s) => s.isNotEmpty).toSet().join(' ');
}
/// Figma `372:356854` 핀 — 38×44 분홍(#FF7DDD) 물방울 · 흰 테두리 · 가운데 흰 점 12.
class _PinPainter extends CustomPainter {
const _PinPainter();
@override
void paint(Canvas canvas, Size size) {
final r = size.width / 2;
final drop = ui.Path.combine(
PathOperation.union,
ui.Path()..addOval(Rect.fromCircle(center: Offset(r, r), radius: r)),
ui.Path()
..moveTo(r * 0.16, r * 1.5)
..lineTo(size.width - r * 0.16, r * 1.5)
..lineTo(r, size.height)
..close(),
);
canvas
..drawPath(drop, Paint()..color = AppColors.cosmicAccent)
..drawPath(
drop,
Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 1.5,
)
..drawCircle(Offset(r, r), 6, Paint()..color = Colors.white);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}