data sync overflow

This commit is contained in:
efrilm 2025-09-20 03:12:32 +07:00
parent f104390141
commit 04811015b6

View File

@ -1,15 +1,9 @@
// ========================================
// DATA SYNC PAGE - POST LOGIN SYNC
// lib/presentation/sync/pages/data_sync_page.dart
// ========================================
import 'dart:async';
import 'package:enaklo_pos/core/extensions/build_context_ext.dart';
import 'package:enaklo_pos/presentation/home/pages/dashboard_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/components/buttons.dart';
import '../../../core/components/spaces.dart';
import '../../../core/constants/colors.dart';
import '../bloc/data_sync_bloc.dart';
@ -54,6 +48,11 @@ class _DataSyncPageState extends State<DataSyncPage>
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
final isLandscape = mediaQuery.orientation == Orientation.landscape;
final screenHeight = mediaQuery.size.height;
final screenWidth = mediaQuery.size.width;
return Scaffold(
backgroundColor: Colors.grey.shade50,
body: SafeArea(
@ -77,71 +76,121 @@ class _DataSyncPageState extends State<DataSyncPage>
);
},
builder: (context, state) {
return Padding(
padding: EdgeInsets.all(24),
child: Column(
children: [
SpaceHeight(60),
// Header
_buildHeader(),
SpaceHeight(60),
// Sync progress
Expanded(
child: state.when(
initial: () => _buildInitialState(),
syncing: (step, progress, message) =>
_buildSyncingState(step, progress, message),
completed: (stats) => _buildCompletedState(stats),
error: (message) => _buildErrorState(message),
),
),
SpaceHeight(40),
// Actions
_buildActions(state),
],
),
);
if (isLandscape) {
return _buildLandscapeLayout(state, screenWidth, screenHeight);
} else {
return _buildPortraitLayout(state, screenHeight);
}
},
),
),
);
}
Widget _buildHeader() {
// Portrait layout (original)
Widget _buildPortraitLayout(DataSyncState state, double screenHeight) {
return Padding(
padding: EdgeInsets.all(24),
child: Column(
children: [
SizedBox(height: screenHeight * 0.08), // Responsive spacing
// Header
_buildHeader(false),
SizedBox(height: screenHeight * 0.08),
// Sync progress
Expanded(
child: state.when(
initial: () => _buildInitialState(false),
syncing: (step, progress, message) =>
_buildSyncingState(step, progress, message, false),
completed: (stats) => _buildCompletedState(stats, false),
error: (message) => _buildErrorState(message, false),
),
),
SizedBox(height: 20),
// Actions
_buildActions(state),
],
),
);
}
// Landscape layout (side by side)
Widget _buildLandscapeLayout(
DataSyncState state, double screenWidth, double screenHeight) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
child: Row(
children: [
// Left side - Header and info
Expanded(
flex: 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildHeader(true),
SizedBox(height: 20),
_buildActions(state),
],
),
),
SizedBox(width: 40),
// Right side - Sync progress
Expanded(
flex: 3,
child: Container(
height: screenHeight * 0.8,
child: state.when(
initial: () => _buildInitialState(true),
syncing: (step, progress, message) =>
_buildSyncingState(step, progress, message, true),
completed: (stats) => _buildCompletedState(stats, true),
error: (message) => _buildErrorState(message, true),
),
),
),
],
),
);
}
Widget _buildHeader(bool isLandscape) {
return Column(
children: [
Container(
width: 80,
height: 80,
width: isLandscape ? 60 : 80,
height: isLandscape ? 60 : 80,
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(isLandscape ? 15 : 20),
),
child: Icon(
Icons.sync,
size: 40,
size: isLandscape ? 30 : 40,
color: AppColors.primary,
),
),
SpaceHeight(20),
SizedBox(height: isLandscape ? 12 : 20),
Text(
'Sinkronisasi Data',
style: TextStyle(
fontSize: 24,
fontSize: isLandscape ? 20 : 24,
fontWeight: FontWeight.bold,
color: Colors.grey.shade800,
),
),
SpaceHeight(8),
SizedBox(height: isLandscape ? 4 : 8),
Text(
'Mengunduh data terbaru ke perangkat',
style: TextStyle(
fontSize: 16,
fontSize: isLandscape ? 14 : 16,
color: Colors.grey.shade600,
),
textAlign: TextAlign.center,
@ -150,117 +199,125 @@ class _DataSyncPageState extends State<DataSyncPage>
);
}
Widget _buildInitialState() {
Widget _buildInitialState(bool isLandscape) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.download_rounded,
size: 64,
size: isLandscape ? 48 : 64,
color: Colors.grey.shade400,
),
SpaceHeight(20),
SizedBox(height: isLandscape ? 12 : 20),
Text(
'Siap untuk sinkronisasi',
style: TextStyle(
fontSize: 18,
fontSize: isLandscape ? 16 : 18,
fontWeight: FontWeight.w500,
),
),
SpaceHeight(8),
SizedBox(height: isLandscape ? 4 : 8),
Text(
'Tekan tombol mulai untuk mengunduh data',
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
color: Colors.grey.shade600,
),
textAlign: TextAlign.center,
),
],
);
}
Widget _buildSyncingState(SyncStep step, double progress, String message) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Progress circle
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: 120,
height: 120,
child: AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
return CircularProgressIndicator(
value: _progressAnimation.value,
strokeWidth: 8,
backgroundColor: Colors.grey.shade200,
valueColor:
AlwaysStoppedAnimation<Color>(AppColors.primary),
);
},
),
),
Column(
children: [
Icon(
_getSyncIcon(step),
size: 32,
color: AppColors.primary,
),
SpaceHeight(4),
AnimatedBuilder(
Widget _buildSyncingState(
SyncStep step, double progress, String message, bool isLandscape) {
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Progress circle
Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: isLandscape ? 100 : 120,
height: isLandscape ? 100 : 120,
child: AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
return Text(
'${(_progressAnimation.value * 100).toInt()}%',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
return CircularProgressIndicator(
value: _progressAnimation.value,
strokeWidth: isLandscape ? 6 : 8,
backgroundColor: Colors.grey.shade200,
valueColor:
AlwaysStoppedAnimation<Color>(AppColors.primary),
);
},
),
],
),
],
),
SpaceHeight(30),
// Step indicator
_buildStepIndicator(step),
SpaceHeight(20),
// Current message
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
Column(
children: [
Icon(
_getSyncIcon(step),
size: isLandscape ? 24 : 32,
color: AppColors.primary,
),
SizedBox(height: 2),
AnimatedBuilder(
animation: _progressAnimation,
builder: (context, child) {
return Text(
'${(_progressAnimation.value * 100).toInt()}%',
style: TextStyle(
fontSize: isLandscape ? 14 : 16,
fontWeight: FontWeight.bold,
color: AppColors.primary,
),
);
},
),
],
),
],
),
child: Text(
message,
style: TextStyle(
color: Colors.blue.shade700,
fontWeight: FontWeight.w500,
SizedBox(height: isLandscape ? 20 : 30),
// Step indicator
_buildStepIndicator(step, isLandscape),
SizedBox(height: isLandscape ? 12 : 20),
// Current message
Container(
padding: EdgeInsets.symmetric(
horizontal: isLandscape ? 16 : 20,
vertical: isLandscape ? 8 : 12),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Text(
message,
style: TextStyle(
color: Colors.blue.shade700,
fontSize: isLandscape ? 12 : 14,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
textAlign: TextAlign.center,
),
),
SpaceHeight(20),
SizedBox(height: isLandscape ? 12 : 20),
// Sync details
_buildSyncDetails(step, progress),
],
// Sync details
_buildSyncDetails(step, progress, isLandscape),
],
),
);
}
Widget _buildStepIndicator(SyncStep currentStep) {
Widget _buildStepIndicator(SyncStep currentStep, bool isLandscape) {
final steps = [
('Produk', SyncStep.products, Icons.inventory_2),
('Kategori', SyncStep.categories, Icons.category),
@ -268,59 +325,111 @@ class _DataSyncPageState extends State<DataSyncPage>
('Selesai', SyncStep.completed, Icons.check_circle),
];
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: steps.map((stepData) {
final (label, step, icon) = stepData;
final isActive = step == currentStep;
final isCompleted = step.index < currentStep.index;
if (isLandscape) {
// Vertical layout for landscape
return Column(
children: steps.map((stepData) {
final (label, step, icon) = stepData;
final isActive = step == currentStep;
final isCompleted = step.index < currentStep.index;
return Container(
margin: EdgeInsets.symmetric(horizontal: 4),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isActive
? AppColors.primary.withOpacity(0.1)
: isCompleted
? Colors.green.shade50
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isCompleted ? Icons.check : icon,
size: 14,
color: isActive
? AppColors.primary
: isCompleted
? Colors.green.shade600
: Colors.grey.shade500,
),
SizedBox(width: 4),
Text(
label,
style: TextStyle(
fontSize: 11,
fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
return Container(
margin: EdgeInsets.symmetric(vertical: 2),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isActive
? AppColors.primary.withOpacity(0.1)
: isCompleted
? Colors.green.shade50
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isCompleted ? Icons.check : icon,
size: 12,
color: isActive
? AppColors.primary
: isCompleted
? Colors.green.shade600
: Colors.grey.shade600,
: Colors.grey.shade500,
),
),
],
),
);
}).toList(),
);
SizedBox(width: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
color: isActive
? AppColors.primary
: isCompleted
? Colors.green.shade600
: Colors.grey.shade600,
),
),
],
),
);
}).toList(),
);
} else {
// Horizontal layout for portrait
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: steps.map((stepData) {
final (label, step, icon) = stepData;
final isActive = step == currentStep;
final isCompleted = step.index < currentStep.index;
return Container(
margin: EdgeInsets.symmetric(horizontal: 4),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: isActive
? AppColors.primary.withOpacity(0.1)
: isCompleted
? Colors.green.shade50
: Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isCompleted ? Icons.check : icon,
size: 14,
color: isActive
? AppColors.primary
: isCompleted
? Colors.green.shade600
: Colors.grey.shade500,
),
SizedBox(width: 4),
Text(
label,
style: TextStyle(
fontSize: 11,
fontWeight: isActive ? FontWeight.w600 : FontWeight.normal,
color: isActive
? AppColors.primary
: isCompleted
? Colors.green.shade600
: Colors.grey.shade600,
),
),
],
),
);
}).toList(),
);
}
}
Widget _buildSyncDetails(SyncStep step, double progress) {
Widget _buildSyncDetails(SyncStep step, double progress, bool isLandscape) {
return Container(
padding: EdgeInsets.all(16),
padding: EdgeInsets.all(isLandscape ? 12 : 16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
@ -334,6 +443,7 @@ class _DataSyncPageState extends State<DataSyncPage>
Text(
'Status:',
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
fontWeight: FontWeight.w500,
color: Colors.grey.shade700,
),
@ -341,19 +451,21 @@ class _DataSyncPageState extends State<DataSyncPage>
Text(
_getStepLabel(step),
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
fontWeight: FontWeight.w600,
color: AppColors.primary,
),
),
],
),
SpaceHeight(8),
SizedBox(height: isLandscape ? 6 : 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Progress:',
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
fontWeight: FontWeight.w500,
color: Colors.grey.shade700,
),
@ -361,6 +473,7 @@ class _DataSyncPageState extends State<DataSyncPage>
Text(
'${(progress * 100).toInt()}%',
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
fontWeight: FontWeight.w600,
color: AppColors.primary,
),
@ -372,135 +485,141 @@ class _DataSyncPageState extends State<DataSyncPage>
);
}
Widget _buildCompletedState(SyncStats stats) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Success icon
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(50),
),
child: Icon(
Icons.check_circle,
size: 60,
color: Colors.green.shade600,
),
),
SpaceHeight(30),
Text(
'Sinkronisasi Berhasil!',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.green.shade700,
),
),
SpaceHeight(16),
Text(
'Data berhasil diunduh ke perangkat',
style: TextStyle(
fontSize: 16,
color: Colors.grey.shade600,
),
),
SpaceHeight(30),
// Stats cards
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
children: [
Text(
'Data yang Diunduh',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
SpaceHeight(16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem(
'Produk',
'${stats.totalProducts}',
Icons.inventory_2,
Colors.blue,
),
_buildStatItem(
'Kategori',
'${stats.totalCategories}',
Icons.category,
Colors.green,
),
_buildStatItem(
'Variant',
'${stats.totalVariants}',
Icons.tune,
Colors.orange,
),
],
),
],
),
),
SpaceHeight(20),
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Text(
'Mengalihkan ke halaman utama...',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
Widget _buildCompletedState(SyncStats stats, bool isLandscape) {
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Success icon
Container(
width: isLandscape ? 80 : 100,
height: isLandscape ? 80 : 100,
decoration: BoxDecoration(
color: Colors.green.shade50,
borderRadius: BorderRadius.circular(isLandscape ? 40 : 50),
),
child: Icon(
Icons.check_circle,
size: isLandscape ? 48 : 60,
color: Colors.green.shade600,
),
),
),
],
SizedBox(height: isLandscape ? 20 : 30),
Text(
'Sinkronisasi Berhasil!',
style: TextStyle(
fontSize: isLandscape ? 18 : 22,
fontWeight: FontWeight.bold,
color: Colors.green.shade700,
),
),
SizedBox(height: isLandscape ? 8 : 16),
Text(
'Data berhasil diunduh ke perangkat',
style: TextStyle(
fontSize: isLandscape ? 14 : 16,
color: Colors.grey.shade600,
),
),
SizedBox(height: isLandscape ? 20 : 30),
// Stats cards
Container(
padding: EdgeInsets.all(isLandscape ? 16 : 20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Column(
children: [
Text(
'Data yang Diunduh',
style: TextStyle(
fontSize: isLandscape ? 14 : 16,
fontWeight: FontWeight.w600,
color: Colors.grey.shade700,
),
),
SizedBox(height: isLandscape ? 12 : 16),
if (isLandscape)
// Vertical layout for landscape
Column(
children: [
_buildStatItem('Produk', '${stats.totalProducts}',
Icons.inventory_2, Colors.blue, isLandscape),
SizedBox(height: 8),
_buildStatItem('Kategori', '${stats.totalCategories}',
Icons.category, Colors.green, isLandscape),
SizedBox(height: 8),
_buildStatItem('Variant', '${stats.totalVariants}',
Icons.tune, Colors.orange, isLandscape),
],
)
else
// Horizontal layout for portrait
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('Produk', '${stats.totalProducts}',
Icons.inventory_2, Colors.blue, isLandscape),
_buildStatItem('Kategori', '${stats.totalCategories}',
Icons.category, Colors.green, isLandscape),
_buildStatItem('Variant', '${stats.totalVariants}',
Icons.tune, Colors.orange, isLandscape),
],
),
],
),
),
SizedBox(height: isLandscape ? 12 : 20),
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Text(
'Mengalihkan ke halaman utama...',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: isLandscape ? 10 : 12,
),
),
),
],
),
);
}
Widget _buildErrorState(String message) {
Widget _buildErrorState(String message, bool isLandscape) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.error_outline,
size: 64,
size: isLandscape ? 48 : 64,
color: Colors.red.shade400,
),
SpaceHeight(20),
SizedBox(height: isLandscape ? 12 : 20),
Text(
'Sinkronisasi Gagal',
style: TextStyle(
fontSize: 20,
fontSize: isLandscape ? 16 : 20,
fontWeight: FontWeight.bold,
color: Colors.red.shade600,
),
),
SpaceHeight(12),
SizedBox(height: isLandscape ? 8 : 12),
Container(
padding: EdgeInsets.all(16),
padding: EdgeInsets.all(isLandscape ? 12 : 16),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(8),
@ -508,15 +627,17 @@ class _DataSyncPageState extends State<DataSyncPage>
child: Text(
message,
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
color: Colors.red.shade700,
),
textAlign: TextAlign.center,
),
),
SpaceHeight(20),
SizedBox(height: isLandscape ? 12 : 20),
Text(
'Periksa koneksi internet dan coba lagi',
style: TextStyle(
fontSize: isLandscape ? 12 : 14,
color: Colors.grey.shade600,
),
textAlign: TextAlign.center,
@ -525,40 +646,75 @@ class _DataSyncPageState extends State<DataSyncPage>
);
}
Widget _buildStatItem(
String label, String value, IconData icon, Color color) {
return Column(
children: [
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(
icon,
size: 24,
color: color,
),
Widget _buildStatItem(String label, String value, IconData icon, Color color,
bool isLandscape) {
if (isLandscape) {
// Horizontal layout for landscape
return Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
SpaceHeight(8),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: color,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 20, color: color),
SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
value,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: color,
),
),
Text(
label,
style: TextStyle(
fontSize: 10,
color: Colors.grey.shade600,
),
),
],
),
],
),
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
);
} else {
// Vertical layout for portrait
return Column(
children: [
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: 24, color: color),
),
),
],
);
SizedBox(height: 8),
Text(
value,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: color,
),
),
Text(
label,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
);
}
}
Widget _buildActions(DataSyncState state) {
@ -577,7 +733,7 @@ class _DataSyncPageState extends State<DataSyncPage>
),
completed: (stats) => Button.filled(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/home');
context.pushReplacement(DashboardPage());
},
label: 'Lanjutkan ke Aplikasi',
),
@ -586,7 +742,7 @@ class _DataSyncPageState extends State<DataSyncPage>
Expanded(
child: Button.outlined(
onPressed: () {
Navigator.of(context).pushReplacementNamed('/home');
context.pushReplacement(DashboardPage());
},
label: 'Lewati',
),