첫 커밋: 로컬 프로젝트 업로드

This commit is contained in:
2026-06-10 15:51:34 +09:00
commit 6a8dbeb2e9
1211 changed files with 312864 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import 'package:easy_localization/easy_localization.dart' hide tr;
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:userfront/i18n.dart';
import '../i18n/locale_storage.dart';
import '../i18n/locale_utils.dart';
class LanguageSelector extends StatelessWidget {
const LanguageSelector({super.key, this.compact = false});
final bool compact;
@override
Widget build(BuildContext context) {
final localization = EasyLocalization.of(context);
final resolvedCurrent = normalizeLocaleCode(
localization?.currentLocale?.languageCode,
);
final current = (resolvedCurrent == 'ko' || resolvedCurrent == 'en')
? resolvedCurrent
: 'en';
final items = [
DropdownMenuItem(value: 'ko', child: Text(tr('ui.common.language_ko'))),
DropdownMenuItem(
value: 'en',
child: Text(tr('ui.common.language_en', fallback: 'English')),
),
];
final iconSize = compact ? 16.0 : 18.0;
final dropdown = DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: current,
items: items,
isDense: true,
icon: Icon(Icons.arrow_drop_down, size: compact ? 18 : 20),
onChanged: (value) async {
if (value == null || value == current) {
return;
}
LocaleStorage.write(value);
if (localization != null) {
await localization.setLocale(Locale(value));
}
if (!context.mounted) return;
Uri uri;
try {
uri = GoRouterState.of(context).uri;
} catch (_) {
uri = Uri.base;
}
final target = buildLocalizedPath(value, uri);
context.go(target);
},
),
);
return Padding(
padding: EdgeInsets.only(top: compact ? 0 : 2),
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: compact ? 24 : 28),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.language, size: iconSize),
const SizedBox(width: 6),
dropdown,
],
),
),
);
}
}

View File

@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:userfront/i18n.dart';
import '../theme/theme_scope.dart';
class ThemeToggleButton extends StatelessWidget {
const ThemeToggleButton({super.key, this.compact = false});
final bool compact;
@override
Widget build(BuildContext context) {
Localizations.localeOf(context);
final controller = ThemeScope.of(context);
return ValueListenableBuilder<ThemeMode>(
valueListenable: controller,
builder: (context, mode, _) {
final isLight = mode == ThemeMode.light;
final icon = isLight
? Icons.light_mode_outlined
: Icons.dark_mode_outlined;
final label = isLight
? tr('ui.common.theme_light', fallback: 'Light')
: tr('ui.common.theme_dark', fallback: 'Dark');
final tooltip = tr('ui.common.theme_toggle', fallback: '테마 전환');
if (compact) {
return IconButton(
tooltip: tooltip,
onPressed: () => controller.toggle(),
icon: Icon(icon),
);
}
return OutlinedButton.icon(
onPressed: () => controller.toggle(),
icon: Icon(icon, size: 18),
label: Text(label),
);
},
);
}
}