sync page
This commit is contained in:
@@ -1,43 +1,173 @@
|
||||
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 StatelessWidget {
|
||||
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: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [_buildHeader()],
|
||||
),
|
||||
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();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SpaceWidth(40),
|
||||
SizedBox(width: 40),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Container(height: context.deviceHeight * 0.8),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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: [
|
||||
@@ -68,4 +198,58 @@ class SyncPage extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user