feat(app): seed restaurants, geocode addresses, refresh sharing
This commit is contained in:
@@ -223,7 +223,7 @@ class AddRestaurantForm extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'* 위도/경도를 입력하지 않으면 서울시청 기준으로 저장됩니다',
|
||||
'주소가 정확하지 않을 경우 위도/경도를 현재 위치로 입력합니다.',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodySmall?.copyWith(color: Colors.grey),
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:lunchpick/core/constants/app_colors.dart';
|
||||
import 'package:lunchpick/core/constants/app_typography.dart';
|
||||
import 'package:lunchpick/domain/entities/restaurant.dart';
|
||||
import 'package:lunchpick/presentation/providers/restaurant_provider.dart';
|
||||
import 'package:lunchpick/presentation/providers/di_providers.dart';
|
||||
import 'package:lunchpick/presentation/providers/location_provider.dart';
|
||||
|
||||
import 'add_restaurant_form.dart';
|
||||
|
||||
/// 기존 맛집 정보를 편집하는 다이얼로그
|
||||
class EditRestaurantDialog extends ConsumerStatefulWidget {
|
||||
final Restaurant restaurant;
|
||||
|
||||
const EditRestaurantDialog({super.key, required this.restaurant});
|
||||
|
||||
@override
|
||||
ConsumerState<EditRestaurantDialog> createState() =>
|
||||
_EditRestaurantDialogState();
|
||||
}
|
||||
|
||||
class _EditRestaurantDialogState extends ConsumerState<EditRestaurantDialog> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
late final TextEditingController _nameController;
|
||||
late final TextEditingController _categoryController;
|
||||
late final TextEditingController _subCategoryController;
|
||||
late final TextEditingController _descriptionController;
|
||||
late final TextEditingController _phoneController;
|
||||
late final TextEditingController _roadAddressController;
|
||||
late final TextEditingController _jibunAddressController;
|
||||
late final TextEditingController _latitudeController;
|
||||
late final TextEditingController _longitudeController;
|
||||
|
||||
bool _isSaving = false;
|
||||
late final String _originalRoadAddress;
|
||||
late final String _originalJibunAddress;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final restaurant = widget.restaurant;
|
||||
_nameController = TextEditingController(text: restaurant.name);
|
||||
_categoryController = TextEditingController(text: restaurant.category);
|
||||
_subCategoryController = TextEditingController(
|
||||
text: restaurant.subCategory,
|
||||
);
|
||||
_descriptionController = TextEditingController(
|
||||
text: restaurant.description ?? '',
|
||||
);
|
||||
_phoneController = TextEditingController(
|
||||
text: restaurant.phoneNumber ?? '',
|
||||
);
|
||||
_roadAddressController = TextEditingController(
|
||||
text: restaurant.roadAddress,
|
||||
);
|
||||
_jibunAddressController = TextEditingController(
|
||||
text: restaurant.jibunAddress,
|
||||
);
|
||||
_latitudeController = TextEditingController(
|
||||
text: restaurant.latitude.toString(),
|
||||
);
|
||||
_longitudeController = TextEditingController(
|
||||
text: restaurant.longitude.toString(),
|
||||
);
|
||||
_originalRoadAddress = restaurant.roadAddress;
|
||||
_originalJibunAddress = restaurant.jibunAddress;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_categoryController.dispose();
|
||||
_subCategoryController.dispose();
|
||||
_descriptionController.dispose();
|
||||
_phoneController.dispose();
|
||||
_roadAddressController.dispose();
|
||||
_jibunAddressController.dispose();
|
||||
_latitudeController.dispose();
|
||||
_longitudeController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onFieldChanged(String _) {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _save() async {
|
||||
if (_formKey.currentState?.validate() != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isSaving = true);
|
||||
|
||||
final addressChanged =
|
||||
_roadAddressController.text.trim() != _originalRoadAddress ||
|
||||
_jibunAddressController.text.trim() != _originalJibunAddress;
|
||||
final coords = await _resolveCoordinates(
|
||||
latitudeText: _latitudeController.text.trim(),
|
||||
longitudeText: _longitudeController.text.trim(),
|
||||
roadAddress: _roadAddressController.text.trim(),
|
||||
jibunAddress: _jibunAddressController.text.trim(),
|
||||
forceRecalculate: addressChanged,
|
||||
);
|
||||
|
||||
_latitudeController.text = coords.latitude.toString();
|
||||
_longitudeController.text = coords.longitude.toString();
|
||||
|
||||
final updatedRestaurant = widget.restaurant.copyWith(
|
||||
name: _nameController.text.trim(),
|
||||
category: _categoryController.text.trim(),
|
||||
subCategory: _subCategoryController.text.trim().isEmpty
|
||||
? _categoryController.text.trim()
|
||||
: _subCategoryController.text.trim(),
|
||||
description: _descriptionController.text.trim().isEmpty
|
||||
? null
|
||||
: _descriptionController.text.trim(),
|
||||
phoneNumber: _phoneController.text.trim().isEmpty
|
||||
? null
|
||||
: _phoneController.text.trim(),
|
||||
roadAddress: _roadAddressController.text.trim(),
|
||||
jibunAddress: _jibunAddressController.text.trim().isEmpty
|
||||
? _roadAddressController.text.trim()
|
||||
: _jibunAddressController.text.trim(),
|
||||
latitude: coords.latitude,
|
||||
longitude: coords.longitude,
|
||||
updatedAt: DateTime.now(),
|
||||
needsAddressVerification: coords.usedCurrentLocation,
|
||||
);
|
||||
|
||||
try {
|
||||
await ref
|
||||
.read(restaurantNotifierProvider.notifier)
|
||||
.updateRestaurant(updatedRestaurant);
|
||||
if (!mounted) return;
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
Navigator.of(context).pop(true);
|
||||
messenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Row(
|
||||
children: const [
|
||||
Icon(Icons.check_circle, color: Colors.white, size: 20),
|
||||
SizedBox(width: 8),
|
||||
Text('맛집 정보가 업데이트되었습니다'),
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
setState(() => _isSaving = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('수정에 실패했습니다: $e'),
|
||||
backgroundColor: AppColors.lightError,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Dialog(
|
||||
backgroundColor: isDark ? AppColors.darkSurface : AppColors.lightSurface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 420),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
'맛집 정보 수정',
|
||||
style: AppTypography.heading1(isDark),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
AddRestaurantForm(
|
||||
formKey: _formKey,
|
||||
nameController: _nameController,
|
||||
categoryController: _categoryController,
|
||||
subCategoryController: _subCategoryController,
|
||||
descriptionController: _descriptionController,
|
||||
phoneController: _phoneController,
|
||||
roadAddressController: _roadAddressController,
|
||||
jibunAddressController: _jibunAddressController,
|
||||
latitudeController: _latitudeController,
|
||||
longitudeController: _longitudeController,
|
||||
onFieldChanged: _onFieldChanged,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: _isSaving
|
||||
? null
|
||||
: () => Navigator.of(context).pop(),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: _isSaving ? null : _save,
|
||||
child: _isSaving
|
||||
? const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const Text('저장'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<({double latitude, double longitude, bool usedCurrentLocation})>
|
||||
_resolveCoordinates({
|
||||
required String latitudeText,
|
||||
required String longitudeText,
|
||||
required String roadAddress,
|
||||
required String jibunAddress,
|
||||
bool forceRecalculate = false,
|
||||
}) async {
|
||||
if (!forceRecalculate) {
|
||||
final parsedLat = double.tryParse(latitudeText);
|
||||
final parsedLon = double.tryParse(longitudeText);
|
||||
if (parsedLat != null && parsedLon != null) {
|
||||
return (
|
||||
latitude: parsedLat,
|
||||
longitude: parsedLon,
|
||||
usedCurrentLocation: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final geocodingService = ref.read(geocodingServiceProvider);
|
||||
final address = roadAddress.isNotEmpty ? roadAddress : jibunAddress;
|
||||
if (address.isNotEmpty) {
|
||||
final result = await geocodingService.geocode(address);
|
||||
if (result != null) {
|
||||
return (
|
||||
latitude: result.latitude,
|
||||
longitude: result.longitude,
|
||||
usedCurrentLocation: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final position = await ref.read(currentLocationProvider.future);
|
||||
if (position != null) {
|
||||
return (
|
||||
latitude: position.latitude,
|
||||
longitude: position.longitude,
|
||||
usedCurrentLocation: true,
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
final fallback = geocodingService.defaultCoordinates();
|
||||
return (
|
||||
latitude: fallback.latitude,
|
||||
longitude: fallback.longitude,
|
||||
usedCurrentLocation: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import 'package:lunchpick/core/constants/app_typography.dart';
|
||||
import 'package:lunchpick/domain/entities/restaurant.dart';
|
||||
import 'package:lunchpick/presentation/providers/restaurant_provider.dart';
|
||||
import 'package:lunchpick/presentation/providers/visit_provider.dart';
|
||||
import 'edit_restaurant_dialog.dart';
|
||||
|
||||
class RestaurantCard extends ConsumerWidget {
|
||||
final Restaurant restaurant;
|
||||
final double? distanceKm;
|
||||
|
||||
const RestaurantCard({super.key, required this.restaurant});
|
||||
const RestaurantCard({super.key, required this.restaurant, this.distanceKm});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -49,41 +51,94 @@ class RestaurantCard extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
restaurant.name,
|
||||
style: AppTypography.heading2(isDark),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
restaurant.category,
|
||||
style: AppTypography.body2(isDark),
|
||||
),
|
||||
if (restaurant.subCategory !=
|
||||
restaurant.category) ...[
|
||||
Text(' • ', style: AppTypography.body2(isDark)),
|
||||
Text(
|
||||
restaurant.subCategory,
|
||||
style: AppTypography.body2(isDark),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
restaurant.name,
|
||||
style: AppTypography.heading2(isDark),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
restaurant.category,
|
||||
style: AppTypography.body2(isDark),
|
||||
),
|
||||
if (restaurant.subCategory !=
|
||||
restaurant.category) ...[
|
||||
Text(
|
||||
' • ',
|
||||
style: AppTypography.body2(isDark),
|
||||
),
|
||||
Text(
|
||||
restaurant.subCategory,
|
||||
style: AppTypography.body2(isDark),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// 더보기 버튼
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.more_vert,
|
||||
color: isDark
|
||||
? AppColors.darkTextSecondary
|
||||
: AppColors.lightTextSecondary,
|
||||
),
|
||||
onPressed: () => _showOptions(context, ref, isDark),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
_BadgesRow(
|
||||
distanceKm: distanceKm,
|
||||
needsAddressVerification:
|
||||
restaurant.needsAddressVerification,
|
||||
isDark: isDark,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 더보기 버튼
|
||||
PopupMenuButton<_RestaurantMenuAction>(
|
||||
icon: Icon(
|
||||
Icons.more_vert,
|
||||
color: isDark
|
||||
? AppColors.darkTextSecondary
|
||||
: AppColors.lightTextSecondary,
|
||||
),
|
||||
offset: const Offset(0, 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
onSelected: (action) =>
|
||||
_handleMenuAction(action, context, ref),
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: _RestaurantMenuAction.edit,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.edit, color: AppColors.lightPrimary),
|
||||
SizedBox(width: 8),
|
||||
Text('수정'),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuItem(
|
||||
value: _RestaurantMenuAction.delete,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.delete, color: AppColors.lightError),
|
||||
SizedBox(width: 8),
|
||||
Text('삭제'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -240,75 +295,172 @@ class RestaurantCard extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _showOptions(BuildContext context, WidgetRef ref, bool isDark) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: isDark ? AppColors.darkSurface : AppColors.lightSurface,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (context) {
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
margin: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark
|
||||
? AppColors.darkDivider
|
||||
: AppColors.lightDivider,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
void _handleMenuAction(
|
||||
_RestaurantMenuAction action,
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
) async {
|
||||
switch (action) {
|
||||
case _RestaurantMenuAction.edit:
|
||||
await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => EditRestaurantDialog(restaurant: restaurant),
|
||||
);
|
||||
break;
|
||||
case _RestaurantMenuAction.delete:
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('맛집 삭제'),
|
||||
content: Text('${restaurant.name}을(를) 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text(
|
||||
'삭제',
|
||||
style: TextStyle(color: AppColors.lightError),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.edit, color: AppColors.lightPrimary),
|
||||
title: const Text('수정'),
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
// TODO: 수정 기능 구현
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete, color: AppColors.lightError),
|
||||
title: const Text('삭제'),
|
||||
onTap: () async {
|
||||
Navigator.pop(context);
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('맛집 삭제'),
|
||||
content: Text('${restaurant.name}을(를) 삭제하시겠습니까?'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('취소'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text(
|
||||
'삭제',
|
||||
style: TextStyle(color: AppColors.lightError),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed == true) {
|
||||
await ref
|
||||
.read(restaurantNotifierProvider.notifier)
|
||||
.deleteRestaurant(restaurant.id);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
if (confirmed == true) {
|
||||
await ref
|
||||
.read(restaurantNotifierProvider.notifier)
|
||||
.deleteRestaurant(restaurant.id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _DistanceBadge extends StatelessWidget {
|
||||
final double distanceKm;
|
||||
final bool isDark;
|
||||
|
||||
const _DistanceBadge({required this.distanceKm, required this.isDark});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final text = _formatDistance(distanceKm);
|
||||
final isFar = distanceKm * 1000 >= 2000;
|
||||
final Color bgColor;
|
||||
final Color textColor;
|
||||
|
||||
if (isFar) {
|
||||
bgColor = isDark
|
||||
? AppColors.darkError.withOpacity(0.15)
|
||||
: AppColors.lightError.withOpacity(0.15);
|
||||
textColor = AppColors.lightError;
|
||||
} else {
|
||||
bgColor = isDark
|
||||
? AppColors.darkPrimary.withOpacity(0.12)
|
||||
: AppColors.lightPrimary.withOpacity(0.12);
|
||||
textColor = AppColors.lightPrimary;
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.place, size: 16, color: textColor),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
text,
|
||||
style: AppTypography.caption(
|
||||
isDark,
|
||||
).copyWith(color: textColor, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDistance(double distanceKm) {
|
||||
final meters = distanceKm * 1000;
|
||||
if (meters >= 2000) {
|
||||
return '2.0km 이상';
|
||||
}
|
||||
if (meters >= 1000) {
|
||||
return '${distanceKm.toStringAsFixed(1)}km';
|
||||
}
|
||||
return '${meters.round()}m';
|
||||
}
|
||||
}
|
||||
|
||||
enum _RestaurantMenuAction { edit, delete }
|
||||
|
||||
class _BadgesRow extends StatelessWidget {
|
||||
final double? distanceKm;
|
||||
final bool needsAddressVerification;
|
||||
final bool isDark;
|
||||
|
||||
const _BadgesRow({
|
||||
required this.distanceKm,
|
||||
required this.needsAddressVerification,
|
||||
required this.isDark,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final badges = <Widget>[];
|
||||
if (needsAddressVerification) {
|
||||
badges.add(_AddressVerificationChip(isDark: isDark));
|
||||
}
|
||||
if (distanceKm != null) {
|
||||
badges.add(_DistanceBadge(distanceKm: distanceKm!, isDark: isDark));
|
||||
}
|
||||
|
||||
if (badges.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
return Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
alignment: WrapAlignment.end,
|
||||
children: badges,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AddressVerificationChip extends StatelessWidget {
|
||||
final bool isDark;
|
||||
|
||||
const _AddressVerificationChip({required this.isDark});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bgColor = isDark
|
||||
? AppColors.darkError.withOpacity(0.12)
|
||||
: AppColors.lightError.withOpacity(0.12);
|
||||
final textColor = isDark ? AppColors.darkError : AppColors.lightError;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.error_outline, size: 16, color: textColor),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'주소확인',
|
||||
style: TextStyle(color: textColor, fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user