refactor(flutter): 공공데이터 사진 위젯과 출처 모델을 공용으로 분리

장소 화면도 공공누리 사진과 출처 표기를 써야 해서, 행사 쪽에 있던 것을
shared 로 옮겼다. Type3 사진을 자르지 않는 규칙이 한 위젯(PublicImage)에만 있게 한다.
event_models 는 출처 모델을 다시 내보내 기존 import 는 그대로 동작한다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-04 16:44:55 +09:00
parent b6abc3d118
commit 8f711c1706
4 changed files with 128 additions and 69 deletions
@@ -0,0 +1,36 @@
/// 공공데이터 출처 표기 — 행사·장소가 함께 쓴다.
library;
/// 사진 공공누리 유형 — Type3 은 변경 금지(자르지 않고 그대로 보인다)
enum ImageLicense {
type1('Type1'),
type3('Type3');
const ImageLicense(this.code);
final String code;
static ImageLicense? parse(Object? value) {
for (final license in values) {
if (license.code == value) return license;
}
return null;
}
}
/// 정보 출처 표기 — 화면에 반드시 보인다
class Attribution {
const Attribution({required this.provider, this.asOf});
final String provider;
/// 원본 기준 시점 — 'YYYY-MM-DD' 또는 상가정보 기준년월 'YYYYMM'
final String? asOf;
factory Attribution.fromJson(Map<String, dynamic> json) {
return Attribution(
provider: json['provider'] as String,
asOf: json['asOf'] as String?,
);
}
}
@@ -0,0 +1,76 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:wandergo/core/theme/app_colors.dart';
import 'package:wandergo/core/theme/app_theme.dart';
import 'package:wandergo/shared/models/source_models.dart';
/// 사진이 없거나 못 불러올 때의 자리 표시 — 분류별 배경색·아이콘
typedef ImagePlaceholder = ({
Color background,
Color foreground,
IconData icon,
});
/// 공공데이터 사진 — 공공누리 Type3 은 자르지 않고(변경 금지) 통째로 보인다.
/// [fit] 을 주지 않으면 Type3 은 contain, 나머지는 cover.
class PublicImage extends StatelessWidget {
const PublicImage({
super.key,
required this.url,
required this.license,
required this.placeholder,
this.width,
this.height,
this.radius = kRadius,
this.fit,
this.iconSize = 28,
});
final String? url;
final ImageLicense? license;
final ImagePlaceholder placeholder;
final double? width;
final double? height;
final double radius;
final BoxFit? fit;
final double iconSize;
@override
Widget build(BuildContext context) {
final fallback = ColoredBox(
color: placeholder.background,
child: Center(
child: Icon(
placeholder.icon,
size: iconSize,
color: placeholder.foreground,
),
),
);
final imageUrl = url;
return ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: SizedBox(
width: width,
height: height,
child: imageUrl == null
? fallback
: ColoredBox(
color: AppColors.placeholder,
child: CachedNetworkImage(
imageUrl: imageUrl,
width: width,
height: height,
fit:
fit ??
(license == ImageLicense.type3
? BoxFit.contain
: BoxFit.cover),
errorWidget: (_, _, _) => fallback,
),
),
),
);
}
}