jelaskan saya secara detail terkait pengembangan app ini, seperti library, fitur2, witdget yang digunakan. yang mana itu jika ingin melakukan sebuah pengeditan dalam proses integrasi map dan algoritma logika fungsinya itu dimana aja
Saya akan menjelaskan secara detail pengembangan aplikasi FitTrack ini, mulai dari arsitektur hingga lokasi file untuk editing.
provider: ^6.1.1lib/providers/user_provider.dart, activity_provider.dartsqflite: ^2.3.0
path: ^1.8.3lib/services/database_service.dartgoogle_maps_flutter: ^2.5.0
location: ^5.0.3
geolocator: ^10.1.0lib/widgets/map_widget.dart, lib/services/location_service.dartimage_picker: ^1.0.4
cached_network_image: ^3.3.0
intl: ^0.18.1
shared_preferences: ^2.2.2lib/models/)user_model.dart - Data model untuk user
activity_model.dart - Data model untuk aktivitas larilib/services/)database_service.dart - CRUD operations SQLite
location_service.dart - GPS tracking & distance calculationlib/providers/)user_provider.dart - State management untuk user
activity_provider.dart - State management untuk activitieslib/screens/ & lib/widgets/)screens/
├── main_screen.dart - Bottom navigation
├── home_screen.dart - Dashboard
├── profile_screen.dart - User profile
├── edit_profile_screen.dart - Edit profile form
├── tracking_screen.dart - GPS tracking
└── tracking_summary_screen.dart - Hasil tracking
widgets/
├── map_widget.dart - Google Maps component
├── stat_card.dart - Statistics card
└── activity_grid.dart - Grid aktivitaslib/widgets/map_widget.dart)// Komponen utama Google Maps
class MapWidget extends StatefulWidget {
final bool isStatic; // Static untuk history, dynamic untuk tracking
final List<LatLng>? routePoints; // Route points untuk polyline
final bool showCurrentLocation; // Show user location
final Function(LatLng)? onLocationUpdate; // Callback location update
}Fungsi utama:
Untuk edit map:
// Edit map style (line 25-150)
static const String _mapStyle = '''[...]''';
// Edit polyline appearance (line 200-210)
_polylines.add(
Polyline(
polylineId: const PolylineId('current_route'),
points: points,
color: Colors.blue, // Ubah warna rute
width: 5, // Ubah ketebalan garis
),
);lib/services/location_service.dart)class LocationService {
// GPS tracking configuration
Function(LocationData)? onLocationUpdate;
Function(double)? onDistanceUpdate;
// Tracking methods
Future<void> startTracking()
void stopTracking()
double _calculateTotalDistance()
}Untuk edit tracking logic:
// Edit GPS accuracy (line 45-50)
await location.changeSettings(
accuracy: LocationAccuracy.high, // Ubah akurasi GPS
interval: 1000, // Ubah interval update (ms)
distanceFilter: 1, // Ubah minimum distance (meter)
);
// Edit distance calculation (line 80-95)
double _calculateTotalDistance() {
// Custom algorithm untuk kalkulasi jarak
}lib/screens/tracking_screen.dart)Lokasi edit tracking logic:
// Setup location callbacks (line 40-70)
void _setupLocationService() {
locationService.onLocationUpdate = (LocationData locationData) {
// Edit: Logic saat ada update lokasi baru
if (_isTracking && !_isPaused) {
LatLng newPoint = LatLng(locationData.latitude!, locationData.longitude!);
setState(() {
_routePoints.add(newPoint); // Tambah point ke rute
});
_updateMapRoute(); // Update visual map
}
};
locationService.onDistanceUpdate = (double distance) {
// Edit: Logic kalkulasi statistik
setState(() {
_distance = distance;
_pace = _duration > 0 && _distance > 0 ? (_duration / 60) / _distance : 0;
_calories = (_distance * 65).round(); // Edit: Formula kalori
});
};
}Untuk edit algoritma pace/kalori:
// Edit pace calculation (line 85)
_pace = _duration > 0 && _distance > 0 ? (_duration / 60) / _distance : 0;
// Edit calorie calculation (line 86)
_calories = (_distance * 65).round(); // 65 kalori per kmlib/services/location_service.dart)Lokasi edit algoritma jarak:
double _calculateTotalDistance() {
double totalDistance = 0.0;
// Edit: Custom distance algorithm
for (int i = 1; i < _routePoints.length; i++) {
double distance = Geolocator.distanceBetween(
_routePoints[i - 1].latitude!,
_routePoints[i - 1].longitude!,
_routePoints[i].latitude!,
_routePoints[i].longitude!,
);
totalDistance += distance;
}
return totalDistance / 1000; // Convert to kilometers
}lib/services/database_service.dart)Lokasi edit database schema:
// Edit table structure (line 30-60)
Future _createDB(Database db, int version) async {
await db.execute('''
CREATE TABLE activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
activity_name TEXT NOT NULL,
distance REAL NOT NULL,
duration INTEGER NOT NULL,
pace REAL NOT NULL,
calories INTEGER NOT NULL,
date INTEGER NOT NULL,
route_points TEXT, // Edit: Tambah field baru
FOREIGN KEY (user_id) REFERENCES users (id)
)
''');
}File: lib/widgets/map_widget.dart
Line: 25-150 (Map style)
Line: 200-220 (Polyline style)
Line: 250-280 (Marker configuration)File: lib/services/location_service.dart
Line: 45-55 (GPS settings)
Line: 80-100 (Distance calculation)
File: lib/screens/tracking_screen.dart
Line: 40-90 (Tracking callbacks)
Line: 150-200 (Start/stop logic)File: lib/screens/tracking_screen.dart
Line: 85-90 (Pace & calorie formula)
File: lib/utils/helpers.dart
Line: 50-80 (Helper functions untuk format)File: lib/services/database_service.dart
Line: 30-80 (Table creation)
Line: 100-200 (CRUD operations)File: lib/screens/tracking_screen.dart
Line: 300-400 (Tracking UI)
File: lib/widgets/stat_card.dart
Line: 20-50 (Statistics display)// StatCard - Menampilkan statistik
StatCard(
title: 'Distance',
value: '5.2 km',
subtitle: '+ 1.2 km',
color: AppTheme.lightBlue,
)
// MapWidget - Google Maps component
MapWidget(
isStatic: false, // Dynamic untuk tracking
routePoints: _routePoints, // Route yang dilalui
showCurrentLocation: true, // Show user location
)MainScreen (Bottom Navigation)
├── HomeScreen (Dashboard)
├── TrackingScreen (GPS Tracking)
│ └── TrackingSummaryScreen (Hasil)
└── ProfileScreen
└── EditProfileScreenGPS Location → LocationService → TrackingScreen → MapWidget
↓
Database ← ActivityProvider ← TrackingScreenUser Action → Provider → Database → UI UpdateDengan struktur ini, Anda bisa dengan mudah memodifikasi fitur spesifik sesuai kebutuhan! 🚀
tambahkan screen baru yang muncul pertama kali untuk pengguna. yang mana itu digunakan sebagai user untuk memasukkan sebuah username, dan nama yang dimasukkan itu dimunculkan dalam profile
Saya akan menambahkan screen onboarding yang muncul pertama kali untuk pengguna memasukkan username dan nama mereka. Data ini akan disimpan dan ditampilkan di profile.
Saya telah menambahkan screen onboarding baru yang akan muncul pertama kali saat pengguna membuka aplikasi. Berikut adalah detail implementasinya:
Screen ini meminta pengguna memasukkan:
username pada tabel usersUserModel untuk menyimpan usernamemain.dart memeriksa SharedPreferences untuk menentukan apakah onboarding sudah selesaiDengan fitur ini, pengguna dapat mempersonalisasi aplikasi mereka sejak awal dan data tersebut akan ditampilkan di profil mereka.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/user_provider.dart';
import '../providers/activity_provider.dart';
import '../utils/app_theme.dart';
import '../widgets/activity_grid.dart';
import 'edit_profile_screen.dart';
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.backgroundColor,
appBar: AppBar(
title: const Text('PROFILE'),
backgroundColor: AppTheme.backgroundColor,
foregroundColor: Colors.black,
elevation: 0,
actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EditProfileScreen(),
),
);
},
icon: const Icon(Icons.edit),
),
],
),
body: Consumer2<UserProvider, ActivityProvider>(
builder: (context, userProvider, activityProvider, child) {
if (userProvider.isLoading) {
return const Center(child: CircularProgressIndicator());
}
final user = userProvider.currentUser;
if (user == null) {
return const Center(child: Text('User not found'));
}
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
children: [
// Profile Header
CircleAvatar(
radius: 50,
// backgroundColor: AppTheme.lightBlue,
backgroundImage: user.photoPath != null
? AssetImage(user.photoPath!)
: null,
child: user.photoPath == null
? Text(
user.firstName[0].toUpperCase(),
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.white,
),
)
: null,
),
const SizedBox(height: 16),
Text(
user.fullName.toUpperCase(),
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
const SizedBox(height: 8),
Text(
user.bio,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
// Followers/Following
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildFollowStat('${user.followersCount} FOLLOWERS'),
const SizedBox(width: 24),
_buildFollowStat('${user.followingCount} FOLLOWING'),
],
),
const SizedBox(height: 32),
// Activities Section
Align(
alignment: Alignment.centerLeft,
child: Text(
'ACTIVITIES',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
),
),
const SizedBox(height: 16),
// Activities Grid
ActivityGrid(activities: activityProvider.activities),
],
),
);
},
),
);
}
Widget _buildFollowStat(String text) {
return Text(
text,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
),
);
}
}
tolong buatkan snapshot activity dalam profile tersebut, jadi bukan map yang ditunjukan tetapi hanya riwayat tracknya aja dalam bentuk foto