Update page_intro.dart
This commit is contained in:
parent
77fc37ee52
commit
96d1c1ea3c
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:harvest_guard_app/routes/app_routes.dart';
|
import 'package:harvest_guard_app/routes/app_routes.dart';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
class IntroScreen extends StatefulWidget {
|
class IntroScreen extends StatefulWidget {
|
||||||
const IntroScreen({super.key});
|
const IntroScreen({super.key});
|
||||||
|
@ -9,274 +10,566 @@ class IntroScreen extends StatefulWidget {
|
||||||
State<IntroScreen> createState() => _IntroScreenState();
|
State<IntroScreen> createState() => _IntroScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IntroScreenState extends State<IntroScreen> {
|
class _IntroScreenState extends State<IntroScreen>
|
||||||
final PageController _pageController = PageController();
|
with TickerProviderStateMixin {
|
||||||
|
// Controller untuk animasi
|
||||||
|
late final AnimationController _backgroundAnimController;
|
||||||
|
late final AnimationController _contentAnimController;
|
||||||
|
|
||||||
|
// Halaman saat ini
|
||||||
int _currentPage = 0;
|
int _currentPage = 0;
|
||||||
|
|
||||||
final List<IntroPage> _introPages = [
|
// Data halaman intro
|
||||||
IntroPage(
|
final List<IntroData> _introPages = [
|
||||||
|
IntroData(
|
||||||
title: 'Pilih Varietas Padi',
|
title: 'Pilih Varietas Padi',
|
||||||
description:
|
description:
|
||||||
'Pilih jenis padi yang ingin Anda periksa dari daftar varietas yang tersedia',
|
'Pilih jenis padi yang ingin Anda periksa dari berbagai varietas tanaman padi yang tersedia',
|
||||||
icon: Icons.grass_rounded,
|
image: 'icons/ic_rice_plant.png',
|
||||||
color: Colors.green.shade400,
|
bgColor: const Color(0xFF4CAF50),
|
||||||
|
overlayColor: const Color(0xFF2E7D32),
|
||||||
|
lightColor: const Color(0xFFA5D6A7),
|
||||||
),
|
),
|
||||||
IntroPage(
|
IntroData(
|
||||||
title: 'Scan Tanaman Padi',
|
title: 'Scan Tanaman Padi',
|
||||||
description:
|
description:
|
||||||
'Arahkan kamera ponsel Anda ke bagian tanaman padi yang ingin diperiksa',
|
'Arahkan kamera ke bagian tanaman yang ingin diperiksa untuk analisis dan deteksi penyakit',
|
||||||
icon: Icons.document_scanner_rounded,
|
image: 'icons/ic_camera.png',
|
||||||
color: Colors.blue.shade400,
|
bgColor: const Color(0xFF2196F3),
|
||||||
|
overlayColor: const Color(0xFF1565C0),
|
||||||
|
lightColor: const Color(0xFF90CAF9),
|
||||||
),
|
),
|
||||||
IntroPage(
|
IntroData(
|
||||||
title: 'Cek Hasil Diagnosis',
|
title: 'Hasil Diagnosis',
|
||||||
description:
|
description:
|
||||||
'Dapatkan hasil diagnosis penyakit dan rekomendasi penanganannya',
|
'Lihat hasil diagnosis penyakit beserta rekomendasi cara penanganan dan pencegahannya',
|
||||||
icon: Icons.checklist_rounded,
|
image: 'icons/ic_result.png',
|
||||||
color: Colors.orange.shade400,
|
bgColor: const Color(0xFFFF9800),
|
||||||
|
overlayColor: const Color(0xFFEF6C00),
|
||||||
|
lightColor: const Color(0xFFFFCC80),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// PageController tunggal hanya untuk pergantian halaman
|
||||||
|
late final PageController _pageController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// Inisialisasi page controller
|
||||||
|
_pageController = PageController();
|
||||||
|
_pageController.addListener(_handlePageChange);
|
||||||
|
|
||||||
|
// Inisialisasi animasi controller
|
||||||
|
_backgroundAnimController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(seconds: 20),
|
||||||
|
)..repeat();
|
||||||
|
|
||||||
|
_contentAnimController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 800),
|
||||||
|
)..forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handlePageChange() {
|
||||||
|
// Jika halaman saat ini berbeda dari _currentPage
|
||||||
|
final page = (_pageController.page ?? 0).round();
|
||||||
|
if (page != _currentPage) {
|
||||||
|
setState(() {
|
||||||
|
_currentPage = page;
|
||||||
|
// Reset dan mulai animasi konten
|
||||||
|
_contentAnimController.reset();
|
||||||
|
_contentAnimController.forward();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_pageController.removeListener(_handlePageChange);
|
||||||
_pageController.dispose();
|
_pageController.dispose();
|
||||||
|
_backgroundAnimController.dispose();
|
||||||
|
_contentAnimController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final Size screenSize = MediaQuery.of(context).size;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
// Background animasi bergerak
|
// Background animasi
|
||||||
AnimatedPositioned(
|
_buildAnimatedBackground(),
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
top: -100 + (_currentPage * 50),
|
|
||||||
right: -100 + (_currentPage * 40),
|
|
||||||
child: Container(
|
|
||||||
height: 300,
|
|
||||||
width: 300,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: RadialGradient(
|
|
||||||
colors: [
|
|
||||||
_introPages[_currentPage].color.withOpacity(0.7),
|
|
||||||
_introPages[_currentPage].color.withOpacity(0.0),
|
|
||||||
],
|
|
||||||
stops: const [0.1, 1.0],
|
|
||||||
),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
AnimatedPositioned(
|
|
||||||
duration: const Duration(milliseconds: 500),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
bottom: -80 + (_currentPage * 30),
|
|
||||||
left: -50 + (_currentPage * 20),
|
|
||||||
child: Container(
|
|
||||||
height: 250,
|
|
||||||
width: 250,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: RadialGradient(
|
|
||||||
colors: [
|
|
||||||
_introPages[_currentPage].color.withOpacity(0.5),
|
|
||||||
_introPages[_currentPage].color.withOpacity(0.0),
|
|
||||||
],
|
|
||||||
stops: const [0.1, 1.0],
|
|
||||||
),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Content
|
// Konten utama dengan PageView
|
||||||
Column(
|
PageView.builder(
|
||||||
children: [
|
controller: _pageController,
|
||||||
Expanded(
|
itemCount: _introPages.length,
|
||||||
child: PageView.builder(
|
onPageChanged: (page) {
|
||||||
controller: _pageController,
|
setState(() {
|
||||||
itemCount: _introPages.length,
|
_currentPage = page;
|
||||||
onPageChanged: (int page) {
|
// Reset animasi konten saat halaman berubah
|
||||||
setState(() {
|
_contentAnimController.reset();
|
||||||
_currentPage = page;
|
_contentAnimController.forward();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return _buildIntroPage(_introPages[index]);
|
return _buildFullPageContent(_introPages[index], screenSize);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Pagination indicator
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 30),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: List.generate(
|
|
||||||
_introPages.length,
|
|
||||||
(index) => _buildDotIndicator(index),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Bottom buttons
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
// Next/Finish button
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (_currentPage < _introPages.length - 1) {
|
|
||||||
_pageController.nextPage(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
Get.offNamed(AppRoutes.dashboard);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: _introPages[_currentPage].color,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 30, vertical: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(30),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_currentPage < _introPages.length - 1
|
|
||||||
? 'Lanjut'
|
|
||||||
: 'Mulai',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildIntroPage(IntroPage page) {
|
// Background dengan animasi
|
||||||
return Padding(
|
Widget _buildAnimatedBackground() {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30.0),
|
return AnimatedBuilder(
|
||||||
child: Column(
|
animation: _backgroundAnimController,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
builder: (context, child) {
|
||||||
children: [
|
return Stack(
|
||||||
// Icon with animated container
|
children: [
|
||||||
TweenAnimationBuilder<double>(
|
// Base background color
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 600),
|
duration: const Duration(milliseconds: 500),
|
||||||
builder: (context, value, child) {
|
color: _introPages[_currentPage].bgColor,
|
||||||
return Transform.scale(
|
),
|
||||||
scale: value,
|
|
||||||
child: Container(
|
// Animated shapes - circles
|
||||||
padding: const EdgeInsets.all(25),
|
Positioned(
|
||||||
|
right: -100,
|
||||||
|
top: -50,
|
||||||
|
child: Transform.rotate(
|
||||||
|
angle: _backgroundAnimController.value * 2 * math.pi,
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
width: 300,
|
||||||
|
height: 300,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: page.color.withOpacity(0.2),
|
shape: BoxShape.circle,
|
||||||
|
color:
|
||||||
|
_introPages[_currentPage].lightColor.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Animated shapes - blob
|
||||||
|
Positioned(
|
||||||
|
left: -150 +
|
||||||
|
(math.sin(_backgroundAnimController.value * math.pi) * 50),
|
||||||
|
bottom: 100 +
|
||||||
|
(math.cos(_backgroundAnimController.value * math.pi) * 50),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
width: 350,
|
||||||
|
height: 350,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color:
|
||||||
|
_introPages[_currentPage].overlayColor.withOpacity(0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Small decorative elements
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
Positioned(
|
||||||
|
top: (i * 100) +
|
||||||
|
(math.sin(
|
||||||
|
_backgroundAnimController.value * 2 * math.pi + i) *
|
||||||
|
20),
|
||||||
|
right: (i % 2 == 0) ? 40 + (i * 30) : null,
|
||||||
|
left: (i % 2 != 0) ? 40 + (i * 20) : null,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: 0.3,
|
||||||
|
child: Container(
|
||||||
|
width: 15 + (i * 3),
|
||||||
|
height: 15 + (i * 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.white.withOpacity(0.6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget untuk konten halaman penuh
|
||||||
|
Widget _buildFullPageContent(IntroData data, Size screenSize) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
// Bagian Header - Occupies 40% of screen
|
||||||
|
SizedBox(
|
||||||
|
height: screenSize.height * 0.45,
|
||||||
|
child: _buildHeaderContent(data),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Bagian konten bawah
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(30),
|
||||||
|
topRight: Radius.circular(30),
|
||||||
|
),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, -5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
// Indikator halaman
|
||||||
|
_buildPageIndicator(),
|
||||||
|
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
// Konten deskripsi
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: const BouncingScrollPhysics(),
|
||||||
|
child: _buildDescriptionContent(data),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Tombol navigasi
|
||||||
|
_buildNavigationButtons(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget untuk bagian header
|
||||||
|
Widget _buildHeaderContent(IntroData data) {
|
||||||
|
return SafeArea(
|
||||||
|
bottom: false,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
// Logo atau branding
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.eco_rounded,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Text(
|
||||||
|
'Harvest Guard',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const Spacer(),
|
||||||
|
|
||||||
|
// Ilustrasi tengah
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _contentAnimController,
|
||||||
|
builder: (context, child) {
|
||||||
|
// Scale up animation
|
||||||
|
return Transform.scale(
|
||||||
|
scale: _contentAnimController.value,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: _contentAnimController.value,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 180,
|
||||||
|
height: 180,
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.2),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.3),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Container(
|
||||||
page.icon,
|
decoration: const BoxDecoration(
|
||||||
size: 80,
|
color: Colors.white,
|
||||||
color: page.color,
|
shape: BoxShape.circle,
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
|
|
||||||
// Title
|
|
||||||
TweenAnimationBuilder<double>(
|
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
|
||||||
duration: const Duration(milliseconds: 800),
|
|
||||||
builder: (context, value, child) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: value,
|
|
||||||
child: Transform.translate(
|
|
||||||
offset: Offset(0, 20 * (1 - value)),
|
|
||||||
child: Text(
|
|
||||||
page.title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 28,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
child: Icon(
|
||||||
),
|
_getIconForImage(data.image),
|
||||||
),
|
size: 60,
|
||||||
);
|
color: data.bgColor,
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Description
|
|
||||||
TweenAnimationBuilder<double>(
|
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
|
||||||
duration: const Duration(milliseconds: 1000),
|
|
||||||
builder: (context, value, child) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: value,
|
|
||||||
child: Transform.translate(
|
|
||||||
offset: Offset(0, 30 * (1 - value)),
|
|
||||||
child: Text(
|
|
||||||
page.description,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black54,
|
|
||||||
height: 1.5,
|
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
|
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget untuk konten deskripsi
|
||||||
|
Widget _buildDescriptionContent(IntroData data) {
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: _contentAnimController,
|
||||||
|
builder: (context, child) {
|
||||||
|
// Slide up and fade in animation
|
||||||
|
return Transform.translate(
|
||||||
|
offset: Offset(0, 50 * (1 - _contentAnimController.value)),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: _contentAnimController.value,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Title
|
||||||
|
Text(
|
||||||
|
data.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: data.bgColor,
|
||||||
|
height: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Description
|
||||||
|
Text(
|
||||||
|
data.description,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.grey.shade700,
|
||||||
|
height: 1.6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Feature points - menambahkan beberapa fitur spesifik
|
||||||
|
...(_getFeaturesForPage(data))
|
||||||
|
.map((feature) => Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: data.bgColor.withOpacity(0.1),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
feature.icon,
|
||||||
|
size: 16,
|
||||||
|
color: data.bgColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
feature.text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
|
||||||
|
// Padding di bawah untuk memastikan scroll aman
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget untuk indikator halaman
|
||||||
|
Widget _buildPageIndicator() {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: List.generate(_introPages.length, (index) {
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
height: 8,
|
||||||
|
width: _currentPage == index ? 30 : 8,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _currentPage == index
|
||||||
|
? _introPages[_currentPage].bgColor
|
||||||
|
: Colors.grey.shade300,
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Widget untuk tombol navigasi
|
||||||
|
Widget _buildNavigationButtons() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(30, 20, 30, 40),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
|
||||||
|
// Next/Finish button
|
||||||
|
AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
width: _currentPage < _introPages.length - 1 ? 60 : 160,
|
||||||
|
height: 60,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_currentPage < _introPages.length - 1) {
|
||||||
|
_pageController.animateToPage(
|
||||||
|
_currentPage + 1,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Get.offNamed(AppRoutes.dashboard);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: _introPages[_currentPage].bgColor,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 2,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: _currentPage < _introPages.length - 1
|
||||||
|
? const Icon(Icons.arrow_forward_rounded, size: 24)
|
||||||
|
: const Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Mulai Sekarang',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Icon(Icons.arrow_forward_rounded, size: 20),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDotIndicator(int index) {
|
// Helper untuk mendapatkan ikon berdasarkan nama file
|
||||||
return AnimatedContainer(
|
IconData _getIconForImage(String imagePath) {
|
||||||
duration: const Duration(milliseconds: 300),
|
if (imagePath.contains('rice_plant')) {
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
return Icons.grass_rounded;
|
||||||
height: 8,
|
} else if (imagePath.contains('camera')) {
|
||||||
width: _currentPage == index ? 24 : 8,
|
return Icons.camera_alt_rounded;
|
||||||
decoration: BoxDecoration(
|
} else if (imagePath.contains('result')) {
|
||||||
color: _currentPage == index
|
return Icons.fact_check_rounded;
|
||||||
? _introPages[_currentPage].color
|
}
|
||||||
: Colors.grey.shade300,
|
return Icons.eco_rounded;
|
||||||
borderRadius: BorderRadius.circular(4),
|
}
|
||||||
),
|
|
||||||
);
|
// Helper untuk mendapatkan fitur spesifik berdasarkan halaman
|
||||||
|
List<FeatureItem> _getFeaturesForPage(IntroData data) {
|
||||||
|
if (data.title.contains('Pilih Varietas')) {
|
||||||
|
return [
|
||||||
|
FeatureItem(Icons.category_rounded, 'Berbagai varietas padi tersedia'),
|
||||||
|
FeatureItem(Icons.info_rounded, 'Informasi detail setiap varietas'),
|
||||||
|
FeatureItem(Icons.bookmark_rounded, 'Simpan favorit untuk akses cepat'),
|
||||||
|
];
|
||||||
|
} else if (data.title.contains('Scan')) {
|
||||||
|
return [
|
||||||
|
FeatureItem(Icons.autorenew_rounded, 'Deteksi cepat & akurat'),
|
||||||
|
FeatureItem(Icons.photo_library_rounded, 'Gunakan foto dari galeri'),
|
||||||
|
FeatureItem(Icons.crop_rounded, 'Crop gambar untuk hasil terbaik'),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
FeatureItem(Icons.timeline_rounded, 'Analisis detail penyakit'),
|
||||||
|
FeatureItem(Icons.healing_rounded, 'Rekomendasi penanganan'),
|
||||||
|
FeatureItem(Icons.history_rounded, 'Riwayat pemeriksaan'),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntroPage {
|
// Model untuk data intro page
|
||||||
|
class IntroData {
|
||||||
final String title;
|
final String title;
|
||||||
final String description;
|
final String description;
|
||||||
final IconData icon;
|
final String image;
|
||||||
final Color color;
|
final Color bgColor;
|
||||||
|
final Color overlayColor;
|
||||||
|
final Color lightColor;
|
||||||
|
|
||||||
IntroPage({
|
IntroData({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.icon,
|
required this.image,
|
||||||
required this.color,
|
required this.bgColor,
|
||||||
|
required this.overlayColor,
|
||||||
|
required this.lightColor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Model untuk item fitur
|
||||||
|
class FeatureItem {
|
||||||
|
final IconData icon;
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
FeatureItem(this.icon, this.text);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue