From d3120c1bf299ed3a3629adc6b72770b18034dc05 Mon Sep 17 00:00:00 2001 From: efrilm Date: Wed, 27 Aug 2025 20:12:38 +0700 Subject: [PATCH] feat: profile page --- .../main/pages/profile/profile_page.dart | 538 +++++++++++++++++- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 64 +++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 8 files changed, 613 insertions(+), 1 deletion(-) diff --git a/lib/presentation/pages/main/pages/profile/profile_page.dart b/lib/presentation/pages/main/pages/profile/profile_page.dart index 99429f7..3f64861 100644 --- a/lib/presentation/pages/main/pages/profile/profile_page.dart +++ b/lib/presentation/pages/main/pages/profile/profile_page.dart @@ -1,5 +1,8 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../../../../../common/theme/theme.dart'; @RoutePage() class ProfilePage extends StatelessWidget { @@ -7,6 +10,539 @@ class ProfilePage extends StatelessWidget { @override Widget build(BuildContext context) { - return Center(child: Text('Profile Page')); + return Scaffold( + backgroundColor: AppColor.background, + appBar: AppBar(title: Text('Akun')), + body: SingleChildScrollView( + child: Column( + children: [ + // Profile Header + Container( + width: double.infinity, + color: AppColor.white, + padding: const EdgeInsets.all(20), + child: Column( + children: [ + // Profile Avatar & Info + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: AppColor.primaryGradient, + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(16), + ), + child: Stack( + children: [ + // Background Pattern + Positioned( + top: -20, + right: -20, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.white.withOpacity(0.1), + ), + ), + ), + Positioned( + top: 30, + right: 20, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.white.withOpacity(0.08), + ), + ), + ), + Positioned( + bottom: -10, + left: -10, + child: Container( + width: 60, + height: 60, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.white.withOpacity(0.06), + ), + ), + ), + // Decorative Lines + Positioned( + top: 10, + left: -5, + child: Transform.rotate( + angle: 0.5, + child: Container( + width: 30, + height: 2, + decoration: BoxDecoration( + color: AppColor.white.withOpacity(0.15), + borderRadius: BorderRadius.circular(1), + ), + ), + ), + ), + Positioned( + bottom: 15, + right: 10, + child: Transform.rotate( + angle: -0.5, + child: Container( + width: 25, + height: 2, + decoration: BoxDecoration( + color: AppColor.white.withOpacity(0.15), + borderRadius: BorderRadius.circular(1), + ), + ), + ), + ), + // Main Content + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + // Avatar + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: AppColor.white, + shape: BoxShape.circle, + boxShadow: [ + BoxShadow( + color: AppColor.black.withOpacity(0.1), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Icon( + Icons.person, + size: 30, + color: AppColor.primary, + ), + ), + const SizedBox(width: 16), + // User Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'EFRIL', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.bold, + color: AppColor.white, + letterSpacing: 0.5, + ), + ), + const SizedBox(height: 4), + Text( + '+6283873987851', + style: AppStyle.sm.copyWith( + color: AppColor.white.withOpacity(0.9), + ), + ), + ], + ), + ), + // Arrow Icon + Icon( + Icons.arrow_forward_ios, + color: AppColor.white, + size: 14, + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 20), + // Share the Sip Card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColor.backgroundLight, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColor.borderLight), + ), + child: Row( + children: [ + // Share Icon + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: AppColor.success.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + Icons.share, + color: AppColor.success, + size: 20, + ), + ), + const SizedBox(width: 12), + // Share Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Share the Sip', + style: AppStyle.md.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + Text( + 'Bagikan kode referral, dapatkan hadiah', + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + ), + ), + ], + ), + ), + Icon( + Icons.arrow_forward_ios, + color: AppColor.textSecondary, + size: 14, + ), + ], + ), + ), + ], + ), + ), + + const SizedBox(height: 8), + + // Menu Items + Container( + color: AppColor.white, + child: Column( + children: [ + _buildMenuItem( + icon: Icons.location_on_outlined, + title: 'Alamat Tersimpan', + onTap: () {}, + ), + _buildMenuItem( + icon: Icons.payment_outlined, + title: 'Pembayaran', + onTap: () {}, + ), + _buildMenuItem( + icon: Icons.help_outline, + title: 'Pusat Bantuan', + onTap: () {}, + ), + _buildMenuItem( + icon: Icons.settings_outlined, + title: 'Pengaturan', + onTap: () {}, + showDivider: false, + ), + ], + ), + ), + + const SizedBox(height: 8), + + // Legal & Privacy Section + Container( + color: AppColor.white, + child: Column( + children: [ + _buildMenuItem( + icon: Icons.description_outlined, + title: 'Syarat dan Ketentuan', + onTap: () {}, + ), + _buildMenuItem( + icon: Icons.privacy_tip_outlined, + title: 'Kebijakan Privasi', + onTap: () {}, + showDivider: false, + ), + ], + ), + ), + + const SizedBox(height: 8), + + // Social Media Section + Container( + color: AppColor.white, + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Media Sosial', + style: AppStyle.md.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildSocialButton( + icon: Icons.camera_alt, + color: Colors.purple, + onTap: () => _launchSocialMedia('instagram'), + ), + _buildSocialButton( + icon: Icons.facebook, + color: Colors.blue, + onTap: () => _launchSocialMedia('facebook'), + ), + _buildSocialButton( + icon: Icons.play_arrow, + color: Colors.red, + onTap: () => _launchSocialMedia('youtube'), + ), + _buildSocialButton( + icon: Icons.close, + color: Colors.black, + onTap: () => _launchSocialMedia('twitter'), + ), + ], + ), + ], + ), + ), + + const SizedBox(height: 8), + + // Customer Service Section + Container( + color: AppColor.white, + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Butuh Bantuan?', + style: AppStyle.lg.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.textPrimary, + ), + ), + const SizedBox(height: 8), + Text( + 'Customer Service kami siap untuk membantu', + style: AppStyle.sm.copyWith(color: AppColor.textSecondary), + ), + const SizedBox(height: 16), + // WhatsApp Customer Service + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColor.backgroundLight, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColor.borderLight), + ), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: Colors.green, + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + Icons.chat, + color: AppColor.white, + size: 20, + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Fore Customer Service (chat only)', + style: AppStyle.sm.copyWith( + color: AppColor.textSecondary, + ), + ), + Text( + '0812-1111-8456', + style: AppStyle.md.copyWith( + fontWeight: FontWeight.w600, + color: AppColor.success, + ), + ), + ], + ), + ), + Icon( + Icons.arrow_forward_ios, + color: AppColor.textSecondary, + size: 14, + ), + ], + ), + ), + ], + ), + ), + + const SizedBox(height: 20), + + // Footer Section + Container( + color: AppColor.white, + padding: const EdgeInsets.all(20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Version 4.6.1', + style: AppStyle.sm.copyWith(color: AppColor.textSecondary), + ), + GestureDetector( + onTap: () => _showLogoutDialog(context), + child: Text( + 'Logout', + style: AppStyle.sm.copyWith( + color: AppColor.primary, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + + const SizedBox(height: 100), // Bottom spacing + ], + ), + ), + ); + } + + Widget _buildMenuItem({ + required IconData icon, + required String title, + required VoidCallback onTap, + bool showDivider = true, + }) { + return Column( + children: [ + ListTile( + leading: Icon(icon, color: AppColor.textSecondary, size: 24), + title: Text( + title, + style: AppStyle.md.copyWith(color: AppColor.textPrimary), + ), + trailing: Icon( + Icons.arrow_forward_ios, + color: AppColor.textSecondary, + size: 16, + ), + onTap: onTap, + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 4, + ), + ), + if (showDivider) + Divider(height: 1, color: AppColor.borderLight, indent: 60), + ], + ); + } + + Widget _buildSocialButton({ + required IconData icon, + required Color color, + required VoidCallback onTap, + }) { + return GestureDetector( + onTap: onTap, + child: Container( + width: 48, + height: 48, + decoration: BoxDecoration(color: color, shape: BoxShape.circle), + child: Icon(icon, color: AppColor.white, size: 24), + ), + ); + } + + void _launchSocialMedia(String platform) async { + String url = ''; + switch (platform) { + case 'instagram': + url = 'https://instagram.com/'; + break; + case 'facebook': + url = 'https://facebook.com/'; + break; + case 'youtube': + url = 'https://youtube.com/'; + break; + case 'twitter': + url = 'https://twitter.com/'; + break; + } + + if (await canLaunch(url)) { + await launch(url); + } + } + + void _showLogoutDialog(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + 'Logout', + style: AppStyle.lg.copyWith(fontWeight: FontWeight.w600), + ), + content: Text( + 'Apakah Anda yakin ingin keluar dari aplikasi?', + style: AppStyle.md, + ), + actions: [ + TextButton( + child: Text( + 'Batal', + style: AppStyle.md.copyWith(color: AppColor.textSecondary), + ), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: Text( + 'Logout', + style: AppStyle.md.copyWith( + color: AppColor.primary, + fontWeight: FontWeight.w600, + ), + ), + onPressed: () { + Navigator.of(context).pop(); + // Add logout logic here + // Example: context.router.pushAndClearStack('/login'); + }, + ), + ], + ); + }, + ); } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..f6f23bf 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..f16b4c3 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 7d12a67..a79a42f 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,9 +8,11 @@ import Foundation import connectivity_plus import path_provider_foundation import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index efe4f0c..62adc26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -933,6 +933,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "69ee86740f2847b9a4ba6cffa74ed12ce500bbe2b07f3dc1e643439da60637b7" + url: "https://pub.dev" + source: hosted + version: "6.3.18" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 + url: "https://pub.dev" + source: hosted + version: "6.3.4" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f + url: "https://pub.dev" + source: hosted + version: "3.2.3" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2881c9f..15e27c8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,7 @@ dependencies: json_annotation: ^4.9.0 shared_preferences: ^2.5.3 carousel_slider: ^5.1.1 + url_launcher: ^6.3.2 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8777c93..5777988 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index cc1361d..3103206 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST