15 KiB
15 KiB
📝 Code Integration Guide - AR Viewer & Favorit
Panduan untuk mengintegrasikan fitur-fitur tambahan ke halaman Beranda.
🔍 Integrasi AR Viewer
1. Di destination_detail_page.dart
Ganti tombol "Lihat AR" yang sekarang menampilkan SnackBar dengan navigasi ke AR viewer:
// Current implementation (di beranda_screen.dart)
ElevatedButton.icon(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Model 3D: ${widget.destination.modelPath}',
),
duration: const Duration(seconds: 2),
),
);
},
// ...
)
// Dapat diubah menjadi:
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArViewerPage(
modelPath: widget.destination.modelPath,
destinationName: widget.destination.nama,
),
),
);
},
// ...
)
2. Buat file ArViewerPage
Buat file baru: lib/pages/ar_viewer_page.dart
import 'package:flutter/material.dart';
class ArViewerPage extends StatefulWidget {
final String modelPath;
final String destinationName;
const ArViewerPage({
Key? key,
required this.modelPath,
required this.destinationName,
}) : super(key: key);
@override
State<ArViewerPage> createState() => _ArViewerPageState();
}
class _ArViewerPageState extends State<ArViewerPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.destinationName),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.view_in_ar,
size: 80,
color: Colors.grey[400],
),
const SizedBox(height: 16),
Text(
'AR Viewer',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.grey[600],
),
),
const SizedBox(height: 8),
Text(
'Model: ${widget.modelPath}',
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
),
),
const SizedBox(height: 32),
ElevatedButton(
onPressed: () {
// TODO: Implement actual AR viewer
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('AR Viewer akan segera hadir'),
),
);
},
child: const Text('Buka AR Viewer'),
),
],
),
),
);
}
}
❤️ Integrasi Favorit
1. Update destination_model.dart
Tambahkan isFavorite property:
class Destination {
final int id;
final String nama;
// ... existing properties ...
bool isFavorite; // Tambahan
Destination({
required this.id,
required this.nama,
// ... existing parameters ...
this.isFavorite = false,
});
}
2. Buat FavoriteProvider
Buat file baru: lib/providers/favorite_provider.dart
import 'package:flutter/material.dart';
import '../models/destination_model.dart';
class FavoriteProvider extends ChangeNotifier {
final Set<int> _favorites = {};
Set<int> get favorites => _favorites;
void toggleFavorite(Destination destination) {
if (_favorites.contains(destination.id)) {
_favorites.remove(destination.id);
destination.isFavorite = false;
} else {
_favorites.add(destination.id);
destination.isFavorite = true;
}
notifyListeners();
}
bool isFavorite(int id) {
return _favorites.contains(id);
}
}
3. Update destination_card_widget.dart
Tambahkan favorite button:
// Di bagian bottom card
Container(
padding: const EdgeInsets.all(14),
child: Column(
children: [
// ... existing content ...
Row(
children: [
Expanded(
child: ElevatedButton(
// ... existing button ...
),
),
const SizedBox(width: 8),
Container(
width: 48,
height: 36,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.grey[300] ?? Colors.grey,
),
),
child: Icon(
widget.destination.isFavorite
? Icons.favorite
: Icons.favorite_border,
color: const Color(0xFFE28D42),
),
),
],
),
],
),
)
🎯 Integrasi dengan Provider
Update main.dart
import 'package:provider/provider.dart';
import 'providers/favorite_provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => FavoriteProvider()),
],
child: const ExploreLumajangApp(),
),
);
}
🔖 Integrasi Favorit Page
Buat file baru: lib/pages/favorite_page.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/destination_data.dart';
import '../providers/favorite_provider.dart';
import '../widgets/destination_card_widget.dart';
import 'destination_detail_page.dart';
class FavoritePage extends StatelessWidget {
const FavoritePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 100,
floating: false,
pinned: true,
elevation: 0,
backgroundColor: const Color(0xFF1F8F5F),
flexibleSpace: FlexibleSpaceBar(
background: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xFF1F8F5F),
Color(0xFF2FA86B),
],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(20, 0, 20, 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Favorit Saya',
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
),
),
),
SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 20),
sliver: Consumer<FavoriteProvider>(
builder: (context, favoriteProvider, _) {
final favorites = destinationList
.where((d) => favoriteProvider.isFavorite(d.id))
.toList();
if (favorites.isEmpty) {
return SliverToBoxAdapter(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 100),
child: Column(
children: [
Icon(
Icons.favorite_outline,
size: 64,
color: Colors.grey[300],
),
const SizedBox(height: 16),
Text(
'Belum ada favorit',
style: TextStyle(
fontSize: 16,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
],
),
),
),
);
}
return SliverGrid(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 20,
childAspectRatio: 0.75,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
final destination = favorites[index];
return DestinationCardWidget(
destination: destination,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DestinationDetailPage(
destination: destination,
),
),
);
},
);
},
childCount: favorites.length,
),
);
},
),
),
],
),
);
}
}
👤 Integrasi Profile Page
Buat file baru: lib/pages/profile_page.dart
import 'package:flutter/material.dart';
class ProfilePage extends StatelessWidget {
const ProfilePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 100,
pinned: true,
backgroundColor: const Color(0xFF1F8F5F),
flexibleSpace: FlexibleSpaceBar(
background: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color(0xFF1F8F5F),
Color(0xFF2FA86B),
],
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.fromLTRB(20, 0, 20, 20),
child: Text(
'Profil Saya',
style: TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Profile info here
CircleAvatar(
radius: 50,
backgroundColor: Colors.grey[300],
child: const Icon(
Icons.person,
size: 50,
color: Colors.grey,
),
),
const SizedBox(height: 16),
const Text(
'Wisatawan',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'wisatawan@example.com',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
const SizedBox(height: 32),
// Add profile options here
],
),
),
),
],
),
);
}
}
🔄 Update beranda_screen.dart
import 'package:flutter/material.dart';
import '../pages/home_page.dart';
import '../pages/favorite_page.dart';
import '../pages/profile_page.dart';
class BerandaScreen extends StatefulWidget {
const BerandaScreen({Key? key}) : super(key: key);
static const routeName = '/beranda';
@override
State<BerandaScreen> createState() => _BerandaScreenState();
}
class _BerandaScreenState extends State<BerandaScreen> {
int _selectedIndex = 0;
final List<Widget> _pages = [
const HomePage(),
const FavoritePage(),
const ProfilePage(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_selectedIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: _onItemTapped,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Beranda',
),
NavigationDestination(
icon: Icon(Icons.favorite_outline),
selectedIcon: Icon(Icons.favorite),
label: 'Favorit',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profil',
),
],
),
);
}
}
✅ Checklist Integrasi
- Update main.dart dengan Provider
- Create FavoriteProvider
- Create ArViewerPage
- Create FavoritePage
- Create ProfilePage
- Update DestinationCardWidget dengan favorite button
- Update destination_detail_page.dart untuk AR viewer
- Test favorit functionality
- Test AR viewer navigation
- Test profile page
🚀 Testing
Setelah integrasi:
-
Test favorit:
- Buka halaman Beranda
- Tap card untuk buka detail
- Tap favorite button
- Navigasi ke tab Favorit
- Verify card muncul di favorite page
-
Test AR Viewer:
- Buka halaman detail
- Tap "Lihat AR"
- Verify navigasi ke AR viewer page
-
Test Profile:
- Navigasi ke tab Profil
- Verify profile info muncul
Notes: Code snippets ini adalah referensi. Sesuaikan dengan implementasi yang sudah ada di project Anda.