256 lines
7.6 KiB
Dart
256 lines
7.6 KiB
Dart
import 'package:auto_route/auto_route.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../../../application/sync/sync_bloc.dart';
|
|
import '../../../common/extension/extension.dart';
|
|
import '../../../common/theme/theme.dart';
|
|
import '../../../injection.dart';
|
|
import '../../components/button/button.dart';
|
|
import '../../components/spaces/space.dart';
|
|
import '../../router/app_router.gr.dart';
|
|
import 'widgets/sync_completed.dart';
|
|
import 'widgets/sync_initial.dart';
|
|
import 'widgets/sync_state.dart';
|
|
|
|
@RoutePage()
|
|
class SyncPage extends StatefulWidget implements AutoRouteWrapper {
|
|
const SyncPage({super.key});
|
|
|
|
@override
|
|
State<SyncPage> createState() => _SyncPageState();
|
|
|
|
@override
|
|
Widget wrappedRoute(BuildContext context) =>
|
|
BlocProvider(create: (context) => getIt<SyncBloc>(), child: this);
|
|
}
|
|
|
|
class _SyncPageState extends State<SyncPage> with TickerProviderStateMixin {
|
|
late AnimationController _animationController;
|
|
late Animation<double> _progressAnimation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_animationController = AnimationController(
|
|
duration: Duration(milliseconds: 500),
|
|
vsync: this,
|
|
);
|
|
_progressAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
|
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_animationController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: AppColor.background,
|
|
body: SafeArea(
|
|
child: BlocConsumer<SyncBloc, SyncState>(
|
|
listener: (context, state) {
|
|
// Kalau lagi syncing, update progress animasi
|
|
if (state.isSyncing) {
|
|
_animationController.animateTo(state.progress);
|
|
}
|
|
// Kalau sudah selesai sukses
|
|
else if (state.stats != null && state.errorMessage == null) {
|
|
_animationController.animateTo(1.0);
|
|
|
|
// Tunggu sebentar lalu pindah ke dashboard
|
|
// Future.delayed(const Duration(seconds: 2), () {
|
|
// context.pushReplacement(DashboardPage());
|
|
// });
|
|
}
|
|
// Kalau error
|
|
else if (state.errorMessage != null) {
|
|
_animationController.stop();
|
|
}
|
|
},
|
|
builder: (context, state) {
|
|
return Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
flex: 2,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildHeader(),
|
|
SizedBox(height: 20),
|
|
_buildActions(state),
|
|
],
|
|
),
|
|
),
|
|
SpaceWidth(40),
|
|
SizedBox(width: 40),
|
|
Expanded(
|
|
flex: 3,
|
|
child: SizedBox(
|
|
height: context.deviceHeight * 0.8,
|
|
child: Builder(
|
|
builder: (context) {
|
|
// Kondisi 1: error
|
|
if (state.errorMessage != null) {
|
|
return _buildErrorState(state.errorMessage!);
|
|
}
|
|
|
|
// Kondisi 2: sudah selesai
|
|
if (state.stats != null) {
|
|
return SyncCompletedWidget(stats: state.stats!);
|
|
}
|
|
|
|
// Kondisi 3: sedang syncing
|
|
if (state.isSyncing) {
|
|
return SyncStateWidget(
|
|
step: state.currentStep ?? SyncStep.categories,
|
|
progress: state.progress,
|
|
message: state.errorMessage ?? '',
|
|
progressAnimation: _progressAnimation,
|
|
);
|
|
}
|
|
|
|
// Kondisi default: initial
|
|
return SyncInitialWidget();
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildErrorState(String message) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Icon(Icons.error_outline, size: 48, color: Colors.red.shade400),
|
|
SizedBox(height: 12),
|
|
Text(
|
|
'Sinkronisasi Gagal',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.red.shade600,
|
|
),
|
|
),
|
|
SizedBox(height: 8),
|
|
Container(
|
|
padding: EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.red.shade50,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Text(
|
|
message,
|
|
style: TextStyle(fontSize: 12, color: Colors.red.shade700),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
SizedBox(height: 12),
|
|
Text(
|
|
'Periksa koneksi internet dan coba lagi',
|
|
style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildHeader() {
|
|
return Column(
|
|
children: [
|
|
Container(
|
|
width: 60,
|
|
height: 60,
|
|
decoration: BoxDecoration(
|
|
color: AppColor.primary.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(15),
|
|
),
|
|
child: Icon(Icons.sync, size: 30, color: AppColor.primary),
|
|
),
|
|
SizedBox(height: 12),
|
|
Text(
|
|
'Sinkronisasi Data',
|
|
style: TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.grey.shade800,
|
|
),
|
|
),
|
|
SizedBox(height: 8),
|
|
Text(
|
|
'Mengunduh kategori dan produk terbaru',
|
|
style: TextStyle(fontSize: 16, color: Colors.grey.shade600),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildActions(SyncState state) {
|
|
if (state.isSyncing) {
|
|
return AppElevatedButton.outlined(
|
|
onPressed: () {
|
|
context.read<SyncBloc>().add(const SyncEvent.cancelSync());
|
|
},
|
|
label: 'Batalkan',
|
|
);
|
|
}
|
|
|
|
// Completed state
|
|
if (state.stats != null && state.errorMessage == null) {
|
|
return AppElevatedButton.filled(
|
|
onPressed: () {
|
|
context.router.replace(MainRoute());
|
|
},
|
|
label: 'Lanjutkan ke Aplikasi',
|
|
);
|
|
}
|
|
|
|
// Error state
|
|
if (state.errorMessage != null) {
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: AppElevatedButton.outlined(
|
|
onPressed: () {
|
|
context.router.replace(MainRoute());
|
|
},
|
|
label: 'Lewati',
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: AppElevatedButton.filled(
|
|
onPressed: () {
|
|
context.read<SyncBloc>().add(const SyncEvent.startSync());
|
|
},
|
|
label: 'Coba Lagi',
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// Default (initial)
|
|
return AppElevatedButton.filled(
|
|
onPressed: () {
|
|
context.read<SyncBloc>().add(const SyncEvent.startSync());
|
|
},
|
|
label: 'Mulai Sinkronisasi',
|
|
);
|
|
}
|
|
}
|