diff --git a/assets/icons/ic-report-product.png b/assets/icons/ic-report-product.png new file mode 100644 index 0000000..d5c008f Binary files /dev/null and b/assets/icons/ic-report-product.png differ diff --git a/assets/icons/ic-report-profit-loss.png b/assets/icons/ic-report-profit-loss.png new file mode 100644 index 0000000..a24d838 Binary files /dev/null and b/assets/icons/ic-report-profit-loss.png differ diff --git a/assets/icons/ic-report-purchase.png b/assets/icons/ic-report-purchase.png new file mode 100644 index 0000000..3d97aa5 Binary files /dev/null and b/assets/icons/ic-report-purchase.png differ diff --git a/assets/icons/ic-report-sales.png b/assets/icons/ic-report-sales.png new file mode 100644 index 0000000..11daa81 Binary files /dev/null and b/assets/icons/ic-report-sales.png differ diff --git a/lib/common/extension/date_extension.dart b/lib/common/extension/date_extension.dart index 0c552fd..65e60a1 100644 --- a/lib/common/extension/date_extension.dart +++ b/lib/common/extension/date_extension.dart @@ -30,4 +30,9 @@ extension DateTimeIndonesia on DateTime { String get toHourMinute { return DateFormat('HH:mm', 'id_ID').format(this); } + + /// Format jam + detik: 14:30:05 + String get toHourMinuteSecond { + return DateFormat('HH:mm:ss', 'id_ID').format(this); + } } diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 4c8628d..642318a 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -128,9 +128,9 @@ extension GetItInjectableX on _i174.GetIt { final gh = _i526.GetItHelper(this, environment, environmentFilter); final firebaseDi = _$FirebaseDi(); final sharedPreferencesDi = _$SharedPreferencesDi(); + final dioDi = _$DioDi(); final autoRouteDi = _$AutoRouteDi(); final connectivityDi = _$ConnectivityDi(); - final dioDi = _$DioDi(); final packageInfoDi = _$PackageInfoDi(); await gh.factoryAsync<_i982.FirebaseApp>( () => firebaseDi.firebaseApp, @@ -140,15 +140,15 @@ extension GetItInjectableX on _i174.GetIt { () => sharedPreferencesDi.prefs, preResolve: true, ); + gh.lazySingleton<_i361.Dio>(() => dioDi.dio); gh.lazySingleton<_i258.AppRouter>(() => autoRouteDi.appRouter); gh.lazySingleton<_i895.Connectivity>(() => connectivityDi.connectivity); - gh.lazySingleton<_i361.Dio>(() => dioDi.dio); await gh.lazySingletonAsync<_i655.PackageInfo>( () => packageInfoDi.packageInfo, preResolve: true, ); - gh.lazySingleton<_i179.FcmService>(() => _i179.FcmService()); gh.lazySingleton<_i902.DeviceInfoService>(() => _i902.DeviceInfoService()); + gh.lazySingleton<_i179.FcmService>(() => _i179.FcmService()); gh.lazySingleton<_i543.NetworkClient>( () => _i543.NetworkClient(gh<_i895.Connectivity>()), ); @@ -163,29 +163,29 @@ extension GetItInjectableX on _i174.GetIt { () => _i115.ApiClient(gh<_i361.Dio>(), gh<_i6.Env>()), ); gh.factory<_i6.Env>(() => _i6.ProdEnv(), registerFor: {_prod}); - gh.factory<_i866.AnalyticRemoteDataProvider>( - () => _i866.AnalyticRemoteDataProvider(gh<_i115.ApiClient>()), - ); - gh.factory<_i17.AuthRemoteDataProvider>( - () => _i17.AuthRemoteDataProvider(gh<_i115.ApiClient>()), + gh.factory<_i130.OrderRemoteDataProvider>( + () => _i130.OrderRemoteDataProvider(gh<_i115.ApiClient>()), ); gh.factory<_i333.CategoryRemoteDataProvider>( () => _i333.CategoryRemoteDataProvider(gh<_i115.ApiClient>()), ); - gh.factory<_i1006.CustomerRemoteDataProvider>( - () => _i1006.CustomerRemoteDataProvider(gh<_i115.ApiClient>()), + gh.factory<_i17.AuthRemoteDataProvider>( + () => _i17.AuthRemoteDataProvider(gh<_i115.ApiClient>()), ); - gh.factory<_i130.OrderRemoteDataProvider>( - () => _i130.OrderRemoteDataProvider(gh<_i115.ApiClient>()), - ); - gh.factory<_i27.OutletRemoteDataProvider>( - () => _i27.OutletRemoteDataProvider(gh<_i115.ApiClient>()), + gh.factory<_i785.UserRemoteDataProvider>( + () => _i785.UserRemoteDataProvider(gh<_i115.ApiClient>()), ); gh.factory<_i823.ProductRemoteDataProvider>( () => _i823.ProductRemoteDataProvider(gh<_i115.ApiClient>()), ); - gh.factory<_i785.UserRemoteDataProvider>( - () => _i785.UserRemoteDataProvider(gh<_i115.ApiClient>()), + gh.factory<_i27.OutletRemoteDataProvider>( + () => _i27.OutletRemoteDataProvider(gh<_i115.ApiClient>()), + ); + gh.factory<_i866.AnalyticRemoteDataProvider>( + () => _i866.AnalyticRemoteDataProvider(gh<_i115.ApiClient>()), + ); + gh.factory<_i1006.CustomerRemoteDataProvider>( + () => _i1006.CustomerRemoteDataProvider(gh<_i115.ApiClient>()), ); gh.factory<_i48.ICustomerRepository>( () => _i550.CustomerRepository(gh<_i1006.CustomerRemoteDataProvider>()), @@ -232,20 +232,17 @@ extension GetItInjectableX on _i174.GetIt { gh.factory<_i183.CategoryLoaderBloc>( () => _i183.CategoryLoaderBloc(gh<_i1020.ICategoryRepository>()), ); - gh.factory<_i889.SalesLoaderBloc>( - () => _i889.SalesLoaderBloc(gh<_i477.IAnalyticRepository>()), - ); gh.factory<_i473.HomeBloc>( () => _i473.HomeBloc(gh<_i477.IAnalyticRepository>()), ); + gh.factory<_i889.SalesLoaderBloc>( + () => _i889.SalesLoaderBloc(gh<_i477.IAnalyticRepository>()), + ); gh.factory<_i337.CurrentOutletLoaderBloc>( () => _i337.CurrentOutletLoaderBloc(gh<_i197.IOutletRepository>()), ); - gh.factory<_i1038.CategoryAnalyticLoaderBloc>( - () => _i1038.CategoryAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), - ); - gh.factory<_i516.DashboardAnalyticLoaderBloc>( - () => _i516.DashboardAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), + gh.factory<_i221.ProductAnalyticLoaderBloc>( + () => _i221.ProductAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), ); gh.factory<_i785.InventoryAnalyticLoaderBloc>( () => _i785.InventoryAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), @@ -255,12 +252,15 @@ extension GetItInjectableX on _i174.GetIt { gh<_i477.IAnalyticRepository>(), ), ); - gh.factory<_i221.ProductAnalyticLoaderBloc>( - () => _i221.ProductAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), + gh.factory<_i1038.CategoryAnalyticLoaderBloc>( + () => _i1038.CategoryAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), ); gh.factory<_i11.ProfitLossLoaderBloc>( () => _i11.ProfitLossLoaderBloc(gh<_i477.IAnalyticRepository>()), ); + gh.factory<_i516.DashboardAnalyticLoaderBloc>( + () => _i516.DashboardAnalyticLoaderBloc(gh<_i477.IAnalyticRepository>()), + ); gh.factory<_i945.AuthBloc>( () => _i945.AuthBloc(gh<_i49.IAuthRepository>()), ); @@ -270,12 +270,12 @@ extension GetItInjectableX on _i174.GetIt { gh.factory<_i1058.OrderLoaderBloc>( () => _i1058.OrderLoaderBloc(gh<_i219.IOrderRepository>()), ); - gh.factory<_i1030.ChangePasswordFormBloc>( - () => _i1030.ChangePasswordFormBloc(gh<_i635.IUserRepository>()), - ); gh.factory<_i147.UserEditFormBloc>( () => _i147.UserEditFormBloc(gh<_i635.IUserRepository>()), ); + gh.factory<_i1030.ChangePasswordFormBloc>( + () => _i1030.ChangePasswordFormBloc(gh<_i635.IUserRepository>()), + ); gh.factory<_i775.LoginFormBloc>( () => _i775.LoginFormBloc( gh<_i49.IAuthRepository>(), @@ -283,14 +283,14 @@ extension GetItInjectableX on _i174.GetIt { gh<_i179.FcmService>(), ), ); - gh.factory<_i346.InventoryReportBloc>( - () => _i346.InventoryReportBloc( + gh.factory<_i605.TransactionReportBloc>( + () => _i605.TransactionReportBloc( gh<_i477.IAnalyticRepository>(), gh<_i197.IOutletRepository>(), ), ); - gh.factory<_i605.TransactionReportBloc>( - () => _i605.TransactionReportBloc( + gh.factory<_i346.InventoryReportBloc>( + () => _i346.InventoryReportBloc( gh<_i477.IAnalyticRepository>(), gh<_i197.IOutletRepository>(), ), @@ -303,10 +303,10 @@ class _$FirebaseDi extends _i73.FirebaseDi {} class _$SharedPreferencesDi extends _i402.SharedPreferencesDi {} +class _$DioDi extends _i103.DioDi {} + class _$AutoRouteDi extends _i311.AutoRouteDi {} class _$ConnectivityDi extends _i586.ConnectivityDi {} -class _$DioDi extends _i103.DioDi {} - class _$PackageInfoDi extends _i227.PackageInfoDi {} diff --git a/lib/presentation/components/assets/assets.gen.dart b/lib/presentation/components/assets/assets.gen.dart index d2a149c..84d22e0 100644 --- a/lib/presentation/components/assets/assets.gen.dart +++ b/lib/presentation/components/assets/assets.gen.dart @@ -11,6 +11,34 @@ import 'package:flutter/widgets.dart'; +class $AssetsIconsGen { + const $AssetsIconsGen(); + + /// File path: assets/icons/ic-report-product.png + AssetGenImage get icReportProduct => + const AssetGenImage('assets/icons/ic-report-product.png'); + + /// File path: assets/icons/ic-report-profit-loss.png + AssetGenImage get icReportProfitLoss => + const AssetGenImage('assets/icons/ic-report-profit-loss.png'); + + /// File path: assets/icons/ic-report-purchase.png + AssetGenImage get icReportPurchase => + const AssetGenImage('assets/icons/ic-report-purchase.png'); + + /// File path: assets/icons/ic-report-sales.png + AssetGenImage get icReportSales => + const AssetGenImage('assets/icons/ic-report-sales.png'); + + /// List of all assets + List get values => [ + icReportProduct, + icReportProfitLoss, + icReportPurchase, + icReportSales, + ]; +} + class $AssetsImagesGen { const $AssetsImagesGen(); @@ -28,6 +56,7 @@ class $AssetsImagesGen { class Assets { const Assets._(); + static const $AssetsIconsGen icons = $AssetsIconsGen(); static const $AssetsImagesGen images = $AssetsImagesGen(); } diff --git a/lib/presentation/components/widgets/particle_card.dart b/lib/presentation/components/widgets/particle_card.dart new file mode 100644 index 0000000..fd810f5 --- /dev/null +++ b/lib/presentation/components/widgets/particle_card.dart @@ -0,0 +1,166 @@ +import 'package:flutter/material.dart'; + +import '../../../common/painter/wave_painter.dart'; +import '../../../common/theme/theme.dart'; + +class ParticleCard extends StatelessWidget { + /// Content yang ditampilkan di atas particle background + final Widget child; + + /// Gradient background card. Default pakai primaryGradient + final List? gradientColors; + + /// Arah gradient. Default topLeft → bottomRight + final AlignmentGeometry gradientBegin; + final AlignmentGeometry gradientEnd; + + /// Border radius card. Default 16 + final double borderRadius; + + /// Padding konten. Default 16 semua sisi + final EdgeInsetsGeometry? padding; + + /// Height card. Null = wrap content + final double? height; + + /// Opacity particle & wave. Default 1.0 + final double decorationOpacity; + + const ParticleCard({ + super.key, + required this.child, + this.gradientColors, + this.gradientBegin = Alignment.topLeft, + this.gradientEnd = Alignment.bottomRight, + this.borderRadius = 16, + this.padding, + this.height, + this.decorationOpacity = 1.0, + }); + + @override + Widget build(BuildContext context) { + final colors = gradientColors ?? AppColor.primaryGradient; + + return ClipRRect( + borderRadius: BorderRadius.circular(borderRadius), + child: Container( + height: height, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(borderRadius), + gradient: LinearGradient( + colors: colors, + begin: gradientBegin, + end: gradientEnd, + ), + boxShadow: [ + BoxShadow( + color: colors.first.withOpacity(0.35), + blurRadius: 16, + offset: const Offset(0, 6), + ), + ], + ), + child: Stack( + children: [ + // --- Decorative background --- + Opacity( + opacity: decorationOpacity, + child: Stack( + children: [ + // Circles kanan atas + Positioned( + top: -24, + right: -24, + child: _circle(120, AppColor.white, 0.10), + ), + Positioned( + top: 28, + right: 16, + child: _circle(60, AppColor.white, 0.06), + ), + Positioned( + top: 70, + right: -10, + child: _circle(36, AppColor.white, 0.08), + ), + + // Circles kiri bawah + Positioned( + bottom: -20, + left: -20, + child: _circle(90, AppColor.white, 0.06), + ), + Positioned( + bottom: 20, + left: 40, + child: _circle(40, AppColor.white, 0.04), + ), + + // Sparkle icons + ..._sparkles(context), + + // Wave pattern + Positioned.fill( + child: CustomPaint( + painter: WavePainter( + animation: 0.0, + color: AppColor.white.withOpacity(0.08), + ), + ), + ), + + // Radial gradient overlay + Container( + decoration: BoxDecoration( + gradient: RadialGradient( + center: const Alignment(0.7, -0.5), + radius: 1.4, + colors: [ + Colors.white.withOpacity(0.06), + Colors.transparent, + ], + ), + ), + ), + ], + ), + ), + + // --- Content --- + Padding( + padding: padding ?? const EdgeInsets.all(16), + child: child, + ), + ], + ), + ), + ); + } + + Widget _circle(double size, Color color, double opacity) { + return Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color.withOpacity(opacity), + ), + ); + } + + List _sparkles(BuildContext context) { + final width = MediaQuery.of(context).size.width; + return List.generate(5, (i) { + return Positioned( + left: (i * 70.0) % (width - 20), + top: 10 + (i * 18.0), + child: Icon( + Icons.auto_awesome, + size: 8 + (i % 3) * 3.0, + color: AppColor.white.withOpacity(0.18), + ), + ); + }); + } +} diff --git a/lib/presentation/pages/home/home_page.dart b/lib/presentation/pages/home/home_page.dart index 62625bc..ea60d5c 100644 --- a/lib/presentation/pages/home/home_page.dart +++ b/lib/presentation/pages/home/home_page.dart @@ -11,6 +11,7 @@ import '../../components/button/button.dart'; import '../../components/spacer/spacer.dart'; import 'widgets/feature.dart'; import 'widgets/header.dart'; +import 'widgets/promo_banner.dart'; import 'widgets/stats.dart'; import 'widgets/top_product.dart'; @@ -170,6 +171,7 @@ class _HomePageState extends State with TickerProviderStateMixin { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + const HomePromoBanner(), HomeFeature(), HomeStats(overview: state.dashboard.overview), HomeTopProduct( diff --git a/lib/presentation/pages/home/widgets/feature.dart b/lib/presentation/pages/home/widgets/feature.dart index 36b2fe6..693cef5 100644 --- a/lib/presentation/pages/home/widgets/feature.dart +++ b/lib/presentation/pages/home/widgets/feature.dart @@ -4,6 +4,7 @@ import 'package:line_icons/line_icons.dart'; import '../../../../common/extension/extension.dart'; import '../../../../common/theme/theme.dart'; +import '../../../components/assets/assets.gen.dart'; import '../../../router/app_router.gr.dart'; import 'feature_tile.dart'; @@ -38,26 +39,22 @@ class HomeFeature extends StatelessWidget { children: [ HomeFeatureTile( title: context.lang.sales, - color: const Color(0xFF4CAF50), - icon: LineIcons.receipt, + iconPath: Assets.icons.icReportSales.path, onTap: () => context.router.push(SalesRoute()), ), HomeFeatureTile( title: context.lang.purchase, - color: const Color(0xFF2196F3), - icon: LineIcons.shoppingCart, + iconPath: Assets.icons.icReportPurchase.path, onTap: () => context.router.push(PurchaseRoute()), ), HomeFeatureTile( title: context.lang.profit_loss, - color: const Color(0xFF8BC34A), - icon: LineIcons.moneyCheck, + iconPath: Assets.icons.icReportProfitLoss.path, onTap: () => context.router.push(FinanceRoute()), ), HomeFeatureTile( title: context.lang.product, - color: const Color(0xFFFF9800), - icon: LineIcons.box, + iconPath: Assets.icons.icReportProduct.path, onTap: () => context.router.push(ProductAnalyticRoute()), ), ], diff --git a/lib/presentation/pages/home/widgets/feature_tile.dart b/lib/presentation/pages/home/widgets/feature_tile.dart index ba6e95a..da66151 100644 --- a/lib/presentation/pages/home/widgets/feature_tile.dart +++ b/lib/presentation/pages/home/widgets/feature_tile.dart @@ -5,14 +5,12 @@ import '../../../components/spacer/spacer.dart'; class HomeFeatureTile extends StatelessWidget { final String title; - final IconData icon; - final Color color; + final String iconPath; final Function() onTap; const HomeFeatureTile({ super.key, required this.title, - required this.icon, - required this.color, + required this.iconPath, required this.onTap, }); @@ -32,14 +30,13 @@ class HomeFeatureTile extends StatelessWidget { height: 56, decoration: BoxDecoration( gradient: LinearGradient( - colors: [color.withOpacity(0.1), color.withOpacity(0.05)], + colors: [AppColor.primary.withOpacity(0.1), AppColor.primary.withOpacity(0.05)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), - border: Border.all(color: color.withOpacity(0.2), width: 1), ), - child: Icon(icon, color: color, size: 28), + child: Image.asset(iconPath), ), const SpaceHeight(12), Text( diff --git a/lib/presentation/pages/home/widgets/omset_balance.dart b/lib/presentation/pages/home/widgets/omset_balance.dart index 425f91b..915a15b 100644 --- a/lib/presentation/pages/home/widgets/omset_balance.dart +++ b/lib/presentation/pages/home/widgets/omset_balance.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:line_icons/line_icon.dart'; import 'package:line_icons/line_icons.dart'; @@ -7,11 +9,35 @@ import '../../../../common/extension/extension.dart'; import '../../../../domain/user/user.dart'; import '../../../components/spacer/spacer.dart'; -class HomeOmsetBalance extends StatelessWidget { +class HomeOmsetBalance extends StatefulWidget { final int totalOmset; final User user; const HomeOmsetBalance({super.key, required this.totalOmset, required this.user}); + @override + State createState() => _HomeOmsetBalanceState(); +} + +class _HomeOmsetBalanceState extends State { + late DateTime _now; + late Timer _timer; + bool _isBalanceVisible = true; + + @override + void initState() { + super.initState(); + _now = DateTime.now(); + _timer = Timer.periodic(const Duration(seconds: 1), (_) { + setState(() => _now = DateTime.now()); + }); + } + + @override + void dispose() { + _timer.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Container( @@ -36,34 +62,41 @@ class HomeOmsetBalance extends StatelessWidget { Widget _bottom(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: () { - // context.router.push(BillingHistoryRoute()); - }, + onTap: () {}, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( - color: AppColor.white.withOpacity(0.2), - borderRadius: BorderRadius.vertical( + color: AppColor.white, + border: Border(top: BorderSide(color: AppColor.border)), + borderRadius: const BorderRadius.vertical( bottom: Radius.circular(16), ), ), child: Row( children: [ - LineIcon(LineIcons.calendar, color: AppColor.white, size: 14), + LineIcon(LineIcons.calendar, color: AppColor.black, size: 14), SpaceWidth(6), Expanded( - child: IgnorePointer( - child: Text( - DateTime.now().toDate, - style: AppStyle.sm.copyWith( - color: AppColor.white, - fontWeight: FontWeight.w600, - letterSpacing: 0.3, - ), + child: Text( + _now.toDate, + style: AppStyle.md.copyWith( + color: AppColor.black, + fontWeight: FontWeight.w600, + letterSpacing: 0.3, ), ), ), + LineIcon(LineIcons.clock, color: AppColor.textSecondary, size: 14), + SpaceWidth(4), + Text( + _now.toHourMinuteSecond, + style: AppStyle.md.copyWith( + color: AppColor.textSecondary, + fontWeight: FontWeight.w500, + fontFeatures: [const FontFeature.tabularFigures()], + ), + ), ], ), ), @@ -73,7 +106,7 @@ class HomeOmsetBalance extends StatelessWidget { Container _middle(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), - decoration: BoxDecoration(color: AppColor.white.withOpacity(0.3)), + decoration: BoxDecoration(color: AppColor.white), child: Row( children: [ Expanded( @@ -85,25 +118,65 @@ class HomeOmsetBalance extends StatelessWidget { Text( context.lang.sales_today, style: AppStyle.sm.copyWith( - color: AppColor.white, + color: AppColor.black, fontWeight: FontWeight.w400, letterSpacing: 0.3, ), ), SpaceHeight(2), - Text( - totalOmset.currencyFormatRp, - style: AppStyle.xl.copyWith( - color: AppColor.white, - fontWeight: FontWeight.w900, - letterSpacing: 0.3, - ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: (child, animation) { + return FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: const Offset(0, 0.3), + end: Offset.zero, + ).animate(CurvedAnimation( + parent: animation, + curve: Curves.easeOutCubic, + )), + child: child, + ), + ); + }, + child: _isBalanceVisible + ? Text( + widget.totalOmset.currencyFormatRp, + key: const ValueKey('visible'), + style: AppStyle.xl.copyWith( + color: AppColor.black, + fontWeight: FontWeight.w900, + letterSpacing: 0.3, + ), + ) + : Text( + 'Rp ••••••••', + key: const ValueKey('hidden'), + style: AppStyle.xl.copyWith( + color: AppColor.black, + fontWeight: FontWeight.w900, + letterSpacing: 2, + ), + ), ), ], ), ), ), - LineIcon(LineIcons.eye, color: AppColor.white, size: 16), + GestureDetector( + onTap: () => setState(() => _isBalanceVisible = !_isBalanceVisible), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: LineIcon( + _isBalanceVisible ? LineIcons.eye : LineIcons.eyeSlash, + key: ValueKey(_isBalanceVisible), + color: AppColor.primary, + size: 16, + ), + ), + ), ], ), ); @@ -115,8 +188,8 @@ class HomeOmsetBalance extends StatelessWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( - color: AppColor.white.withOpacity(0.2), - borderRadius: BorderRadius.vertical( + gradient: LinearGradient(colors: AppColor.primaryGradient), + borderRadius: const BorderRadius.vertical( top: Radius.circular(16), ), ), @@ -128,24 +201,13 @@ class HomeOmsetBalance extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Enaklo Bakso Bakmi 343', + 'Semua Outlet', style: AppStyle.sm.copyWith( color: AppColor.white, fontWeight: FontWeight.w600, letterSpacing: 0.3, ), ), - SpaceHeight(2), - Text( - 'Jl. Tawes No.53, Rawamangun, Kec. Pulo Gadung', - style: AppStyle.sm.copyWith( - color: AppColor.white, - fontWeight: FontWeight.w600, - letterSpacing: 0.3, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), ], ), ), diff --git a/lib/presentation/pages/home/widgets/promo_banner.dart b/lib/presentation/pages/home/widgets/promo_banner.dart new file mode 100644 index 0000000..a2b77f8 --- /dev/null +++ b/lib/presentation/pages/home/widgets/promo_banner.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +import '../../../../common/theme/theme.dart'; +import '../../../components/spacer/spacer.dart'; +import '../../../components/widgets/particle_card.dart'; + +class HomePromoBanner extends StatelessWidget { + const HomePromoBanner({super.key}); + + static const _outlets = [ + 'ENAKLO RAWAMANGUNG', + 'ENAKLO WOKU PEDAS\nKELAPA GADING', + ]; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB( + AppValue.padding, + 24, + AppValue.padding, + 0, + ), + child: Row( + children: [ + for (int i = 0; i < _outlets.length; i++) ...[ + Expanded( + child: ParticleCard( + height: 100, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 12, + ), + decorationOpacity: 0.8, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric( + horizontal: 7, + vertical: 2, + ), + decoration: BoxDecoration( + color: AppColor.white.withOpacity(0.2), + borderRadius: BorderRadius.circular(20), + ), + child: Text( + 'OUTLET', + style: AppStyle.xs.copyWith( + color: AppColor.white, + fontWeight: FontWeight.w700, + letterSpacing: 1.0, + fontSize: 10, + ), + ), + ), + const SpaceHeight(6), + Text( + _outlets[i], + style: AppStyle.sm.copyWith( + color: AppColor.white, + fontWeight: FontWeight.w800, + height: 1.25, + ), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + ), + if (i < _outlets.length - 1) const SpaceWidth(12), + ], + ], + ), + ); + } +} diff --git a/lib/presentation/pages/main/widgets/bottom_navbar.dart b/lib/presentation/pages/main/widgets/bottom_navbar.dart index 3876d5c..58e6761 100644 --- a/lib/presentation/pages/main/widgets/bottom_navbar.dart +++ b/lib/presentation/pages/main/widgets/bottom_navbar.dart @@ -1,5 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; +import 'package:hugeicons/hugeicons.dart'; import 'package:line_icons/line_icon.dart'; import 'package:line_icons/line_icons.dart'; @@ -27,22 +28,22 @@ class _MainBottomNavbarState extends State { type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( - icon: LineIcon(LineIcons.home), + icon: HugeIcon(icon: HugeIcons.strokeRoundedHome01), label: context.lang.home, tooltip: context.lang.home, ), BottomNavigationBarItem( - icon: LineIcon(LineIcons.moneyBill), + icon: HugeIcon(icon: HugeIcons.strokeRoundedBorderFull), label: context.lang.order, tooltip: context.lang.order, ), BottomNavigationBarItem( - icon: LineIcon(LineIcons.barChart), + icon: HugeIcon(icon: HugeIcons.strokeRoundedChart03), label: context.lang.report, tooltip: context.lang.report, ), BottomNavigationBarItem( - icon: LineIcon(LineIcons.user), + icon: HugeIcon(icon: HugeIcons.strokeRoundedUser), label: context.lang.profile, tooltip: context.lang.profile, ), diff --git a/pubspec.lock b/pubspec.lock index d84b1fb..b34f9c9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -717,6 +717,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + hugeicons: + dependency: "direct main" + description: + name: hugeicons + sha256: "03845d6fc8fed4a9df27d3710c94499f6f624e2cd343e858be2578a4ec69ad0a" + url: "https://pub.dev" + source: hosted + version: "1.1.6" image: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a114888..064d1cc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: firebase_core: ^3.13.1 firebase_messaging: ^15.2.5 flutter_local_notifications: ^18.0.1 + hugeicons: ^1.1.6 dev_dependencies: flutter_test: @@ -70,7 +71,7 @@ flutter: uses-material-design: true assets: - assets/images/ - # - assets/icons/ + - assets/icons/ # - assets/json/ fonts: