From e13aeefa1c788893fe65f93851526239f03ffec5 Mon Sep 17 00:00:00 2001 From: efrilm Date: Wed, 13 Aug 2025 00:15:53 +0700 Subject: [PATCH] feat: language page --- lib/common/theme/theme.dart | 5 +- .../pages/language/language_page.dart | 116 ++++++++++++++++++ .../pages/language/widgets/language_tile.dart | 73 +++++++++++ .../pages/profile/widgets/app_setting.dart | 6 +- lib/presentation/router/app_router.dart | 3 + lib/presentation/router/app_router.gr.dart | 98 +++++++++------ 6 files changed, 256 insertions(+), 45 deletions(-) create mode 100644 lib/presentation/pages/language/language_page.dart create mode 100644 lib/presentation/pages/language/widgets/language_tile.dart diff --git a/lib/common/theme/theme.dart b/lib/common/theme/theme.dart index f4cda5d..6d1e1eb 100644 --- a/lib/common/theme/theme.dart +++ b/lib/common/theme/theme.dart @@ -58,8 +58,9 @@ class ThemeApp { appBarTheme: AppBarTheme( backgroundColor: AppColor.primary, elevation: 1, - titleTextStyle: AppStyle.lg.copyWith( - fontWeight: FontWeight.w600, + titleTextStyle: AppStyle.xl.copyWith( + fontWeight: FontWeight.w700, + letterSpacing: -0.5, color: AppColor.white, ), ), diff --git a/lib/presentation/pages/language/language_page.dart b/lib/presentation/pages/language/language_page.dart new file mode 100644 index 0000000..fe72a3e --- /dev/null +++ b/lib/presentation/pages/language/language_page.dart @@ -0,0 +1,116 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; + +import '../../../common/theme/theme.dart'; +import '../../components/button/button.dart'; +import 'widgets/language_tile.dart'; + +@RoutePage() +class LanguagePage extends StatefulWidget { + const LanguagePage({super.key}); + + @override + State createState() => _LanguagePageState(); +} + +class _LanguagePageState extends State { + String selectedLanguage = 'en'; // Default language + + final List> languages = [ + {'code': 'en', 'name': 'English', 'nativeName': 'English', 'flag': '🇺🇸'}, + { + 'code': 'id', + 'name': 'Indonesian', + 'nativeName': 'Bahasa Indonesia', + 'flag': '🇮🇩', + }, + {'code': 'es', 'name': 'Spanish', 'nativeName': 'Español', 'flag': '🇪🇸'}, + {'code': 'fr', 'name': 'French', 'nativeName': 'Français', 'flag': '🇫🇷'}, + {'code': 'de', 'name': 'German', 'nativeName': 'Deutsch', 'flag': '🇩🇪'}, + {'code': 'ja', 'name': 'Japanese', 'nativeName': '日本語', 'flag': '🇯🇵'}, + {'code': 'ko', 'name': 'Korean', 'nativeName': '한국어', 'flag': '🇰🇷'}, + {'code': 'zh', 'name': 'Chinese', 'nativeName': '中文', 'flag': '🇨🇳'}, + {'code': 'ar', 'name': 'Arabic', 'nativeName': 'العربية', 'flag': '🇸🇦'}, + { + 'code': 'pt', + 'name': 'Portuguese', + 'nativeName': 'Português', + 'flag': '🇵🇹', + }, + ]; + + void _selectLanguage(String languageCode) { + setState(() { + selectedLanguage = languageCode; + }); + } + + void _confirmSelection() { + // Here you would typically save the selected language to SharedPreferences + // or pass it back to the parent widget + Navigator.pop(context, selectedLanguage); + + // Show confirmation snackbar + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Language changed to ${_getLanguageName(selectedLanguage)}', + ), + backgroundColor: Colors.green, + duration: const Duration(seconds: 2), + ), + ); + } + + String _getLanguageName(String code) { + return languages.firstWhere((lang) => lang['code'] == code)['name']; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColor.background, + appBar: AppBar( + title: const Text('Select Language'), + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () => context.router.back(), + ), + ), + body: Column( + children: [ + // Language list + Expanded( + child: ListView.builder( + padding: const EdgeInsets.all(16), + itemCount: languages.length, + itemBuilder: (context, index) { + final language = languages[index]; + final isSelected = selectedLanguage == language['code']; + + return LanguageTile( + isSelected: isSelected, + language: language, + onTap: () => _selectLanguage(language['code']), + ); + }, + ), + ), + + // Confirm button + Container( + padding: const EdgeInsets.all(16), + width: double.infinity, + decoration: BoxDecoration(color: AppColor.white), + child: AppElevatedButton( + text: 'Confirm', + isLoading: false, + onPressed: _confirmSelection, + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/pages/language/widgets/language_tile.dart b/lib/presentation/pages/language/widgets/language_tile.dart new file mode 100644 index 0000000..f52dee0 --- /dev/null +++ b/lib/presentation/pages/language/widgets/language_tile.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; + +import '../../../../common/theme/theme.dart'; + +class LanguageTile extends StatelessWidget { + final bool isSelected; + final Map language; + final Function() onTap; + const LanguageTile({ + super.key, + required this.isSelected, + required this.language, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isSelected ? AppColor.primary : AppColor.borderLight, + width: isSelected ? 2 : 1, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + leading: Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: Colors.grey[100], + borderRadius: BorderRadius.circular(24), + ), + child: Center( + child: Text(language['flag'], style: const TextStyle(fontSize: 24)), + ), + ), + title: Text( + language['name'], + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + + color: isSelected ? AppColor.primary : AppColor.textPrimary, + ), + ), + subtitle: Text( + language['nativeName'], + style: AppStyle.md.copyWith( + color: isSelected ? AppColor.primary : AppColor.textPrimary, + ), + ), + trailing: isSelected + ? Icon(Icons.check_circle, color: AppColor.primary, size: 24) + : Icon( + Icons.radio_button_unchecked, + color: AppColor.textLight, + size: 24, + ), + onTap: onTap, + ), + ); + } +} diff --git a/lib/presentation/pages/profile/widgets/app_setting.dart b/lib/presentation/pages/profile/widgets/app_setting.dart index 8f34b27..7e65e96 100644 --- a/lib/presentation/pages/profile/widgets/app_setting.dart +++ b/lib/presentation/pages/profile/widgets/app_setting.dart @@ -1,6 +1,8 @@ +import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import '../../../../common/theme/theme.dart'; +import '../../../router/app_router.gr.dart'; import 'divider.dart'; import 'profile_tile.dart'; @@ -75,9 +77,7 @@ class _ProfileAppSettingState extends State { icon: Icons.language_outlined, title: 'Language', subtitle: 'English (US)', - onTap: () { - // Show language selector - }, + onTap: () => context.router.push(LanguageRoute()), ), ProfileDivider(), diff --git a/lib/presentation/router/app_router.dart b/lib/presentation/router/app_router.dart index 57f4892..d0091de 100644 --- a/lib/presentation/router/app_router.dart +++ b/lib/presentation/router/app_router.dart @@ -21,5 +21,8 @@ class AppRouter extends RootStackRouter { AutoRoute(page: ProfileRoute.page), ], ), + + // Language + AutoRoute(page: LanguageRoute.page), ]; } diff --git a/lib/presentation/router/app_router.gr.dart b/lib/presentation/router/app_router.gr.dart index 2972cc6..bd838f8 100644 --- a/lib/presentation/router/app_router.gr.dart +++ b/lib/presentation/router/app_router.gr.dart @@ -10,30 +10,32 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'package:apskel_owner_flutter/presentation/pages/auth/login/login_page.dart' - as _i2; + as _i3; import 'package:apskel_owner_flutter/presentation/pages/home/home_page.dart' as _i1; +import 'package:apskel_owner_flutter/presentation/pages/language/language_page.dart' + as _i2; import 'package:apskel_owner_flutter/presentation/pages/main/main_page.dart' - as _i3; -import 'package:apskel_owner_flutter/presentation/pages/profile/profile_page.dart' as _i4; -import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/profile/profile_page.dart' as _i5; -import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/report/report_page.dart' as _i6; -import 'package:apskel_owner_flutter/presentation/pages/transaction/transaction_page.dart' +import 'package:apskel_owner_flutter/presentation/pages/splash/splash_page.dart' as _i7; -import 'package:auto_route/auto_route.dart' as _i8; +import 'package:apskel_owner_flutter/presentation/pages/transaction/transaction_page.dart' + as _i8; +import 'package:auto_route/auto_route.dart' as _i9; /// generated route for /// [_i1.HomePage] -class HomeRoute extends _i8.PageRouteInfo { - const HomeRoute({List<_i8.PageRouteInfo>? children}) +class HomeRoute extends _i9.PageRouteInfo { + const HomeRoute({List<_i9.PageRouteInfo>? children}) : super(HomeRoute.name, initialChildren: children); static const String name = 'HomeRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { return const _i1.HomePage(); @@ -42,97 +44,113 @@ class HomeRoute extends _i8.PageRouteInfo { } /// generated route for -/// [_i2.LoginPage] -class LoginRoute extends _i8.PageRouteInfo { - const LoginRoute({List<_i8.PageRouteInfo>? children}) +/// [_i2.LanguagePage] +class LanguageRoute extends _i9.PageRouteInfo { + const LanguageRoute({List<_i9.PageRouteInfo>? children}) + : super(LanguageRoute.name, initialChildren: children); + + static const String name = 'LanguageRoute'; + + static _i9.PageInfo page = _i9.PageInfo( + name, + builder: (data) { + return const _i2.LanguagePage(); + }, + ); +} + +/// generated route for +/// [_i3.LoginPage] +class LoginRoute extends _i9.PageRouteInfo { + const LoginRoute({List<_i9.PageRouteInfo>? children}) : super(LoginRoute.name, initialChildren: children); static const String name = 'LoginRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { - return const _i2.LoginPage(); + return const _i3.LoginPage(); }, ); } /// generated route for -/// [_i3.MainPage] -class MainRoute extends _i8.PageRouteInfo { - const MainRoute({List<_i8.PageRouteInfo>? children}) +/// [_i4.MainPage] +class MainRoute extends _i9.PageRouteInfo { + const MainRoute({List<_i9.PageRouteInfo>? children}) : super(MainRoute.name, initialChildren: children); static const String name = 'MainRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { - return const _i3.MainPage(); + return const _i4.MainPage(); }, ); } /// generated route for -/// [_i4.ProfilePage] -class ProfileRoute extends _i8.PageRouteInfo { - const ProfileRoute({List<_i8.PageRouteInfo>? children}) +/// [_i5.ProfilePage] +class ProfileRoute extends _i9.PageRouteInfo { + const ProfileRoute({List<_i9.PageRouteInfo>? children}) : super(ProfileRoute.name, initialChildren: children); static const String name = 'ProfileRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { - return const _i4.ProfilePage(); + return const _i5.ProfilePage(); }, ); } /// generated route for -/// [_i5.ReportPage] -class ReportRoute extends _i8.PageRouteInfo { - const ReportRoute({List<_i8.PageRouteInfo>? children}) +/// [_i6.ReportPage] +class ReportRoute extends _i9.PageRouteInfo { + const ReportRoute({List<_i9.PageRouteInfo>? children}) : super(ReportRoute.name, initialChildren: children); static const String name = 'ReportRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { - return const _i5.ReportPage(); + return const _i6.ReportPage(); }, ); } /// generated route for -/// [_i6.SplashPage] -class SplashRoute extends _i8.PageRouteInfo { - const SplashRoute({List<_i8.PageRouteInfo>? children}) +/// [_i7.SplashPage] +class SplashRoute extends _i9.PageRouteInfo { + const SplashRoute({List<_i9.PageRouteInfo>? children}) : super(SplashRoute.name, initialChildren: children); static const String name = 'SplashRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { - return const _i6.SplashPage(); + return const _i7.SplashPage(); }, ); } /// generated route for -/// [_i7.TransactionPage] -class TransactionRoute extends _i8.PageRouteInfo { - const TransactionRoute({List<_i8.PageRouteInfo>? children}) +/// [_i8.TransactionPage] +class TransactionRoute extends _i9.PageRouteInfo { + const TransactionRoute({List<_i9.PageRouteInfo>? children}) : super(TransactionRoute.name, initialChildren: children); static const String name = 'TransactionRoute'; - static _i8.PageInfo page = _i8.PageInfo( + static _i9.PageInfo page = _i9.PageInfo( name, builder: (data) { - return const _i7.TransactionPage(); + return const _i8.TransactionPage(); }, ); }