feat: home page

This commit is contained in:
efrilm 2025-08-12 20:44:27 +07:00
parent ae1293d28d
commit f0dac56802
15 changed files with 1447 additions and 8 deletions

View File

@ -8,7 +8,7 @@ plugins {
android { android {
namespace = "com.apskel.apskel_owner" namespace = "com.apskel.apskel_owner"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = "27.0.12077973"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11

View File

@ -12,7 +12,7 @@ class AppColor {
static const Color secondaryDark = Color(0xFF388E3C); static const Color secondaryDark = Color(0xFF388E3C);
// Background Colors // Background Colors
static const Color background = Color(0xFFF5F5F5); static const Color background = Color(0xFFF8F9FA);
static const Color backgroundLight = Color(0xFFFFFFFF); static const Color backgroundLight = Color(0xFFFFFFFF);
static const Color backgroundDark = Color(0xFF1A1A1A); static const Color backgroundDark = Color(0xFF1A1A1A);
static const Color surface = Color(0xFFFFFFFF); static const Color surface = Color(0xFFFFFFFF);

View File

@ -1,8 +1,7 @@
part of 'theme.dart'; part of 'theme.dart';
class AppValue { class AppValue {
static const double padding = 16.0; static const double padding = 20.0;
static const double margin = 16.0; static const double margin = 20.0;
static const double radius = 8.0; static const double radius = 16.0;
static const double elevation = 4.0;
} }

View File

@ -55,5 +55,13 @@ class ThemeApp {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),
appBarTheme: AppBarTheme(
backgroundColor: AppColor.white,
elevation: 1,
titleTextStyle: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.primary,
),
),
); );
} }

View File

@ -1,12 +1,224 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../common/theme/theme.dart';
import '../../components/spacer/spacer.dart';
import 'widgets/activity.dart';
import 'widgets/feature.dart';
import 'widgets/header.dart';
import 'widgets/performance.dart';
import 'widgets/stats.dart';
import 'widgets/task.dart';
@RoutePage() @RoutePage()
class HomePage extends StatelessWidget { class HomePage extends StatefulWidget {
const HomePage({super.key}); const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late ScrollController _scrollController;
late AnimationController _headerAnimationController;
late AnimationController _contentAnimationController;
late AnimationController _appBarAnimationController;
late Animation<double> _appBarOpacityAnimation;
late Animation<Offset> _appBarSlideAnimation;
bool _showAppBar = false;
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
_headerAnimationController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_contentAnimationController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
// AppBar Animation Controller
_appBarAnimationController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
// AppBar Animations
_appBarOpacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _appBarAnimationController,
curve: Curves.easeInOut,
),
);
_appBarSlideAnimation =
Tween<Offset>(begin: const Offset(0.0, -1.0), end: Offset.zero).animate(
CurvedAnimation(
parent: _appBarAnimationController,
curve: Curves.easeOutCubic,
),
);
_startAnimations();
}
void _startAnimations() {
_headerAnimationController.forward();
Future.delayed(const Duration(milliseconds: 200), () {
_contentAnimationController.forward();
});
}
void _scrollListener() {
const double threshold = 200.0;
if (_scrollController.offset > threshold && !_showAppBar) {
setState(() {
_showAppBar = true;
});
_appBarAnimationController.forward();
} else if (_scrollController.offset <= threshold && _showAppBar) {
_appBarAnimationController.reverse().then((_) {
if (mounted) {
setState(() {
_showAppBar = false;
});
}
});
}
}
@override
void dispose() {
_scrollController.removeListener(_scrollListener);
_scrollController.dispose();
_headerAnimationController.dispose();
_contentAnimationController.dispose();
_appBarAnimationController.dispose();
super.dispose();
}
PreferredSizeWidget? _buildAnimatedAppBar() {
if (!_showAppBar) return null;
return PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: SlideTransition(
position: _appBarSlideAnimation,
child: FadeTransition(
opacity: _appBarOpacityAnimation,
child: AppBar(
title: const Text(
'AppSkel POS Owner',
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 18,
letterSpacing: -0.5,
),
),
backgroundColor: AppColor.white,
foregroundColor: AppColor.textPrimary,
elevation: 0,
scrolledUnderElevation: 8,
shadowColor: AppColor.primary.withOpacity(0.1),
actions: [
Container(
margin: const EdgeInsets.only(right: 16),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primary.withOpacity(0.1),
AppColor.primaryLight.withOpacity(0.05),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColor.primary.withOpacity(0.1),
width: 1,
),
),
child: const Icon(
Icons.notifications_none_rounded,
color: AppColor.primary,
size: 20,
),
),
],
),
),
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center(child: Text('HomePage')); return Scaffold(
backgroundColor: AppColor.background,
appBar: _buildAnimatedAppBar(),
body: CustomScrollView(
controller: _scrollController,
physics: const BouncingScrollPhysics(),
slivers: [
// Enhanced Header
SliverToBoxAdapter(
child: AnimatedBuilder(
animation: _headerAnimationController,
builder: (context, child) {
return Transform.translate(
offset: Offset(
0,
50 * (1 - _headerAnimationController.value),
),
child: Opacity(
opacity: _headerAnimationController.value,
child: HomeHeader(),
),
);
},
),
),
// Main Content
SliverToBoxAdapter(
child: AnimatedBuilder(
animation: _contentAnimationController,
builder: (context, child) {
return Transform.translate(
offset: Offset(
0,
30 * (1 - _contentAnimationController.value),
),
child: Opacity(
opacity: _contentAnimationController.value,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
HomeFeature(),
HomeStats(),
HomeTask(),
HomeActivity(),
HomePerformance(),
const SpaceHeight(40),
],
),
),
);
},
),
),
],
),
);
} }
} }

View File

@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
import 'activity_tile.dart';
class HomeActivity extends StatelessWidget {
const HomeActivity({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 24,
horizontal: AppValue.padding,
).copyWith(bottom: 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Aktivitas Terkini',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
letterSpacing: -0.5,
),
),
TextButton.icon(
onPressed: () {},
icon: const Icon(Icons.arrow_forward_rounded, size: 16),
label: const Text('Lihat Semua'),
style: TextButton.styleFrom(
foregroundColor: AppColor.primary,
textStyle: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
],
),
const SpaceHeight(16),
Container(
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColor.border.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Column(
children: [
HomeActivityTile(
title: 'Transaksi Berhasil',
subtitle: 'Kasir-01 • Rp 125.000',
time: '2 menit lalu',
icon: Icons.check_circle_rounded,
color: AppColor.success,
isHighlighted: true,
),
const Divider(height: 1, color: AppColor.border),
HomeActivityTile(
title: 'Stok Menipis',
subtitle: 'Kopi Arabica • 5 unit tersisa',
time: '15 menit lalu',
icon: Icons.warning_amber_rounded,
color: AppColor.warning,
isHighlighted: false,
),
const Divider(height: 1, color: AppColor.border),
HomeActivityTile(
title: 'Login Kasir',
subtitle: 'Sari masuk shift pagi',
time: '1 Jam lalu',
icon: Icons.login_rounded,
color: AppColor.info,
isHighlighted: false,
),
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class HomeActivityTile extends StatelessWidget {
final String title;
final String subtitle;
final String time;
final IconData icon;
final Color color;
final bool isHighlighted;
const HomeActivityTile({
super.key,
required this.title,
required this.subtitle,
required this.time,
required this.icon,
required this.color,
required this.isHighlighted,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: isHighlighted ? color.withOpacity(0.02) : Colors.transparent,
borderRadius: isHighlighted ? BorderRadius.circular(16) : null,
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color.withOpacity(0.1), color.withOpacity(0.05)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withOpacity(0.2), width: 1),
),
child: Icon(icon, color: color, size: 20),
),
const SpaceWidth(16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
letterSpacing: -0.2,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SpaceHeight(4),
Text(
subtitle,
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
time,
style: AppStyle.xs.copyWith(
fontSize: 11,
color: AppColor.textLight,
fontWeight: FontWeight.w500,
),
),
if (isHighlighted) ...[
const SpaceHeight(4),
Container(
width: 6,
height: 6,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
],
],
),
],
),
);
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:line_icons/line_icons.dart';
import '../../../../common/theme/theme.dart';
import 'feature_tile.dart';
class HomeFeature extends StatelessWidget {
const HomeFeature({super.key});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(
vertical: 24,
horizontal: AppValue.padding,
).copyWith(bottom: 0),
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 10),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(AppValue.radius),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 10,
offset: const Offset(0, 5),
spreadRadius: 0,
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
HomeFeatureTile(
title: 'Penjualan',
color: const Color(0xFF4CAF50),
icon: LineIcons.receipt,
onTap: () {},
),
HomeFeatureTile(
title: 'Pembelian',
color: const Color(0xFF2196F3),
icon: LineIcons.shoppingCart,
onTap: () {},
),
HomeFeatureTile(
title: 'Biaya',
color: const Color(0xFF8BC34A),
icon: LineIcons.moneyCheck,
onTap: () {},
),
HomeFeatureTile(
title: 'Product',
color: const Color(0xFFFF9800),
icon: LineIcons.box,
onTap: () {},
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
HomeFeatureTile(
title: 'Laporan',
color: const Color(0xFFE91E63),
icon: LineIcons.pieChart,
onTap: () {},
),
HomeFeatureTile(
title: 'Kas & Bank',
color: const Color(0xFF9C27B0),
icon: LineIcons.university,
onTap: () {},
),
HomeFeatureTile(
title: 'Aset Tetap',
color: const Color(0xFF00BCD4),
icon: LineIcons.businessTime,
onTap: () {},
),
HomeFeatureTile(
title: 'Kontak',
color: const Color(0xFFFF5722),
icon: LineIcons.userPlus,
onTap: () {},
),
],
),
],
),
);
}
}

View File

@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class HomeFeatureTile extends StatelessWidget {
final String title;
final IconData icon;
final Color color;
final Function() onTap;
const HomeFeatureTile({
super.key,
required this.title,
required this.icon,
required this.color,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Expanded(
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(AppValue.radius),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color.withOpacity(0.1), color.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),
),
const SpaceHeight(12),
Text(
title,
style: AppStyle.sm.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
letterSpacing: -0.2,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
);
}
}

View File

@ -0,0 +1,186 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class HomeHeader extends StatelessWidget {
const HomeHeader({super.key});
@override
Widget build(BuildContext context) {
return Container(
height: 280,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primary,
AppColor.primaryLight,
AppColor.primaryLight.withOpacity(0.8),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
stops: const [0.0, 0.7, 1.0],
),
boxShadow: [
BoxShadow(
color: AppColor.primary.withOpacity(0.3),
blurRadius: 20,
offset: const Offset(0, 10),
),
],
),
child: Stack(
children: [
// Decorative circles
Positioned(
top: -50,
right: -50,
child: Container(
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.white.withOpacity(0.1),
),
),
),
Positioned(
top: 80,
right: -20,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.white.withOpacity(0.05),
),
),
),
SafeArea(child: _buildContent()),
],
),
);
}
Padding _buildContent() {
return Padding(
padding: EdgeInsets.all(AppValue.padding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Top bar
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'AppSkel POS Owner',
style: AppStyle.lg.copyWith(
color: AppColor.white.withOpacity(0.9),
fontWeight: FontWeight.w600,
letterSpacing: 0.3,
),
),
const SpaceHeight(2),
Text(
'Dashboard',
style: AppStyle.sm.copyWith(
color: AppColor.textLight,
fontSize: 11,
fontWeight: FontWeight.w400,
),
),
],
),
),
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppColor.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: AppColor.white.withOpacity(0.3),
width: 1,
),
),
child: const Icon(
Icons.notifications_none_rounded,
color: AppColor.white,
size: 20,
),
),
],
),
const SpaceHeight(24),
// Greeting Section
Text(
'Selamat Pagi,',
style: AppStyle.lg.copyWith(
color: AppColor.white,
fontWeight: FontWeight.w500,
),
),
const SpaceHeight(2),
Text(
'Vira Vania! 👋',
style: AppStyle.h4.copyWith(
color: AppColor.white,
fontWeight: FontWeight.w800,
letterSpacing: -0.5,
),
),
const SpaceHeight(8),
Text(
'Mari tingkatkan performa bisnis Anda hari ini',
style: AppStyle.md.copyWith(
color: AppColor.white.withOpacity(0.85),
fontWeight: FontWeight.w400,
height: 1.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SpaceHeight(16),
// Today's highlight
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppColor.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: AppColor.white.withOpacity(0.3),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.trending_up_rounded,
color: AppColor.white,
size: 14,
),
const SizedBox(width: 6),
Text(
'Penjualan hari ini +25%',
style: AppStyle.sm.copyWith(
color: AppColor.white,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,281 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class HomePerformance extends StatelessWidget {
const HomePerformance({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 24,
horizontal: AppValue.padding,
).copyWith(bottom: 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Performa Minggu Ini',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
letterSpacing: -0.5,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.success.withOpacity(0.1),
AppColor.success.withOpacity(0.05),
],
),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: AppColor.success.withOpacity(0.2),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.arrow_upward_rounded,
color: AppColor.success,
size: 14,
),
const SpaceWidth(4),
Text(
'89%',
style: AppStyle.sm.copyWith(
color: AppColor.success,
fontSize: 12,
fontWeight: FontWeight.w700,
),
),
],
),
),
],
),
const SpaceHeight(20),
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColor.border.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildPerformanceBar(
'Sen',
0.8,
AppColor.primary,
'Rp 2.1M',
),
_buildPerformanceBar(
'Sel',
0.6,
AppColor.primary,
'Rp 1.8M',
),
_buildPerformanceBar(
'Rab',
0.9,
AppColor.success,
'Rp 2.4M',
),
_buildPerformanceBar(
'Kam',
0.7,
AppColor.primary,
'Rp 1.9M',
),
_buildPerformanceBar(
'Jum',
1.0,
AppColor.success,
'Rp 2.5M',
),
_buildPerformanceBar(
'Sab',
0.85,
AppColor.success,
'Rp 2.2M',
),
_buildPerformanceBar(
'Min',
0.4,
AppColor.textLight,
'Rp 1.2M',
),
],
),
const SpaceHeight(24),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColor.primary.withOpacity(0.05),
AppColor.primary.withOpacity(0.02),
],
),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColor.primary.withOpacity(0.1),
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Target Minggu Ini',
style: TextStyle(
fontSize: 12,
color: AppColor.textSecondary,
fontWeight: FontWeight.w500,
),
),
const SpaceHeight(4),
Text(
'Rp 15.000.000',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'Tercapai',
style: TextStyle(
fontSize: 12,
color: AppColor.textSecondary,
fontWeight: FontWeight.w500,
),
),
const SpaceHeight(4),
Row(
children: [
Text(
'89%',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: AppColor.success,
),
),
const SpaceWidth(4),
Icon(
Icons.trending_up_rounded,
color: AppColor.success,
size: 16,
),
],
),
],
),
],
),
),
],
),
),
],
),
);
}
Widget _buildPerformanceBar(
String day,
double percentage,
Color color,
String amount,
) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// Amount label
Text(
amount,
style: AppStyle.xs.copyWith(
fontSize: 10,
fontWeight: FontWeight.w600,
color: AppColor.textSecondary,
),
),
const SpaceHeight(8),
// Performance bar
Container(
height: 80,
width: 12,
decoration: BoxDecoration(
color: AppColor.border.withOpacity(0.3),
borderRadius: BorderRadius.circular(6),
),
child: Align(
alignment: Alignment.bottomCenter,
child: AnimatedContainer(
duration: Duration(milliseconds: 800 + (day.hashCode % 400)),
curve: Curves.easeOutCubic,
height: 80 * percentage,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color, color.withOpacity(0.7)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.circular(6),
boxShadow: [
BoxShadow(
color: color.withOpacity(0.3),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
),
),
),
const SpaceHeight(12),
// Day label
Text(
day,
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
),
),
],
);
}
}

View File

@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
import 'stats_tile.dart';
class HomeStats extends StatelessWidget {
const HomeStats({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 24,
horizontal: AppValue.padding,
).copyWith(bottom: 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Ringkasan Hari Ini',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
letterSpacing: -0.5,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: AppColor.success.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: AppColor.success.withOpacity(0.2),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.trending_up_rounded,
color: AppColor.success,
size: 14,
),
const SpaceWidth(4),
Text(
'Live',
style: AppStyle.sm.copyWith(
color: AppColor.success,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
const SpaceHeight(20),
Row(
children: [
Expanded(
child: HomeStatsTile(
title: 'Total Penjualan',
value: 'Rp 2.450.000',
icon: Icons.trending_up_rounded,
color: AppColor.success,
change: '+12%',
subtitle: 'dari kemarin',
),
),
const SpaceWidth(16),
Expanded(
child: HomeStatsTile(
title: 'Transaksi',
value: '85',
icon: Icons.receipt_long_rounded,
color: AppColor.info,
change: '+8%',
subtitle: 'lebih tinggi',
),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: HomeStatsTile(
title: 'Profit Bersih',
value: 'Rp 735.000',
icon: Icons.account_balance_wallet_rounded,
color: AppColor.warning,
change: '+15%',
subtitle: 'margin sehat',
),
),
const SpaceWidth(16),
Expanded(
child: HomeStatsTile(
title: 'Pelanggan Baru',
value: '42',
icon: Icons.person_add_rounded,
color: AppColor.primary,
change: '+3%',
subtitle: 'bertambah',
),
),
],
),
],
),
);
}
}

View File

@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class HomeStatsTile extends StatelessWidget {
final String title;
final String value;
final IconData icon;
final Color color;
final String change;
final String subtitle;
const HomeStatsTile({
super.key,
required this.title,
required this.value,
required this.icon,
required this.color,
required this.change,
required this.subtitle,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(AppValue.radius),
border: Border.all(color: AppColor.border.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: color.withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, 8),
spreadRadius: 0,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color.withOpacity(0.1), color.withOpacity(0.05)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: color.withOpacity(0.2), width: 1),
),
child: Icon(icon, color: color, size: 20),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColor.success.withOpacity(0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
change,
style: AppStyle.xs.copyWith(
color: AppColor.success,
fontWeight: FontWeight.w700,
),
),
),
],
),
const SpaceHeight(16),
FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text(
value,
style: AppStyle.xxl.copyWith(
fontWeight: FontWeight.w800,
color: AppColor.textPrimary,
letterSpacing: -0.5,
),
),
),
const SpaceHeight(4),
Text(
title,
style: AppStyle.md.copyWith(
fontSize: 13,
color: AppColor.textSecondary,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SpaceHeight(2),
Text(
subtitle,
style: AppStyle.xs.copyWith(
color: AppColor.textLight,
fontWeight: FontWeight.w400,
),
),
],
),
);
}
}

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
import 'task_tile.dart';
class HomeTask extends StatelessWidget {
const HomeTask({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 24,
horizontal: AppValue.padding,
).copyWith(bottom: 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Tugas Harian',
style: AppStyle.h6.copyWith(
fontWeight: FontWeight.w700,
color: AppColor.textPrimary,
letterSpacing: -0.5,
),
),
Text(
'3/5 Selesai',
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.primary,
),
),
],
),
const SpaceHeight(20),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: AppColor.white,
borderRadius: BorderRadius.circular(AppValue.radius),
border: Border.all(color: AppColor.border.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.04),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
child: Column(
children: [
HomeTaskTile(
title: 'Cek stok produk terlaris',
subtitle: 'Pastikan ketersediaan produk favorit pelanggan',
isCompleted: true,
color: AppColor.success,
),
const SpaceHeight(16),
HomeTaskTile(
title: 'Review laporan penjualan kemarin',
subtitle: 'Analisis performa dan identifikasi peluang',
isCompleted: true,
color: AppColor.success,
),
const SpaceHeight(16),
HomeTaskTile(
title: 'Update harga produk musiman',
subtitle: 'Sesuaikan harga berdasarkan demand pasar',
isCompleted: true,
color: AppColor.success,
),
const SpaceHeight(16),
HomeTaskTile(
title: 'Backup data transaksi',
subtitle: 'Pastikan data aman dan tersimpan',
color: AppColor.warning,
),
const SpaceHeight(16),
HomeTaskTile(
title: 'Training tim kasir baru',
subtitle: 'Onboarding karyawan untuk shift sore',
color: AppColor.info,
),
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../components/spacer/spacer.dart';
class HomeTaskTile extends StatelessWidget {
final String title;
final String subtitle;
final bool isCompleted;
final Color color;
const HomeTaskTile({
super.key,
required this.title,
required this.subtitle,
this.isCompleted = false,
required this.color,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: isCompleted ? color : Colors.transparent,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: color, width: 2),
),
child: isCompleted
? Icon(Icons.check_rounded, color: AppColor.white, size: 16)
: null,
),
const SpaceWidth(16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: isCompleted
? AppColor.textSecondary
: AppColor.textPrimary,
decoration: isCompleted
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
const SpaceHeight(2),
Text(
subtitle,
style: AppStyle.sm.copyWith(
color: AppColor.textLight,
decoration: isCompleted
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
],
),
),
],
);
}
}