first commit

This commit is contained in:
ytc1012
2025-11-22 18:17:35 +08:00
commit d427916c6a
169 changed files with 15241 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
import 'dart:convert';
import 'dart:math';
import 'package:flutter/services.dart';
/// Service to manage encouragement messages
class EncouragementService {
List<String> _messages = [];
final Random _random = Random();
/// Load encouragement messages from assets
Future<void> loadMessages() async {
try {
final String jsonString =
await rootBundle.loadString('assets/encouragements.json');
final List<dynamic> jsonList = json.decode(jsonString);
_messages = jsonList.cast<String>();
} catch (e) {
// Fallback messages if file can't be loaded
_messages = [
"Showing up is half the battle.",
"Every minute counts.",
"You're learning, not failing.",
"Gentleness is strength.",
"Progress over perfection.",
];
}
}
/// Get a random encouragement message
String getRandomMessage() {
if (_messages.isEmpty) {
return "You're doing great!";
}
return _messages[_random.nextInt(_messages.length)];
}
/// Get all messages (for testing)
List<String> getAllMessages() => List.from(_messages);
}

View File

@@ -0,0 +1,194 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter/foundation.dart';
/// Notification Service - Handles local notifications
class NotificationService {
static final NotificationService _instance = NotificationService._internal();
factory NotificationService() => _instance;
NotificationService._internal();
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
bool _initialized = false;
/// Initialize notification service
Future<void> initialize() async {
if (_initialized) return;
// Skip initialization on web platform
if (kIsWeb) {
if (kDebugMode) {
print('Notifications not supported on web platform');
}
return;
}
try {
// Android initialization settings
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
// iOS initialization settings
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
const initSettings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);
await _notifications.initialize(
initSettings,
onDidReceiveNotificationResponse: _onNotificationTapped,
);
_initialized = true;
if (kDebugMode) {
print('Notification service initialized successfully');
}
} catch (e) {
if (kDebugMode) {
print('Failed to initialize notifications: $e');
}
}
}
/// Handle notification tap
void _onNotificationTapped(NotificationResponse response) {
if (kDebugMode) {
print('Notification tapped: ${response.payload}');
}
// TODO: Navigate to appropriate screen if needed
}
/// Request notification permissions (iOS only, Android auto-grants)
Future<bool> requestPermissions() async {
if (kIsWeb) return false;
try {
final result = await _notifications
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
return result ?? true; // Android always returns true
} catch (e) {
if (kDebugMode) {
print('Failed to request permissions: $e');
}
return false;
}
}
/// Show focus session completed notification
Future<void> showFocusCompletedNotification({
required int minutes,
required int distractionCount,
}) async {
if (kIsWeb || !_initialized) return;
try {
const androidDetails = AndroidNotificationDetails(
'focus_completed',
'Focus Session Completed',
channelDescription: 'Notifications for completed focus sessions',
importance: Importance.high,
priority: Priority.high,
enableVibration: true,
playSound: true,
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
const notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
// Create notification message
final title = '🎉 Focus session complete!';
final body = distractionCount == 0
? 'You focused for $minutes ${minutes == 1 ? 'minute' : 'minutes'} without distractions!'
: 'You focused for $minutes ${minutes == 1 ? 'minute' : 'minutes'}. Great effort!';
await _notifications.show(
0, // Notification ID
title,
body,
notificationDetails,
payload: 'focus_completed',
);
if (kDebugMode) {
print('Notification shown: $title - $body');
}
} catch (e) {
if (kDebugMode) {
print('Failed to show notification: $e');
}
}
}
/// Show reminder notification (optional feature for future)
Future<void> showReminderNotification({
required String message,
}) async {
if (kIsWeb || !_initialized) return;
try {
const androidDetails = AndroidNotificationDetails(
'reminders',
'Focus Reminders',
channelDescription: 'Gentle reminders to focus',
importance: Importance.defaultImportance,
priority: Priority.defaultPriority,
);
const iosDetails = DarwinNotificationDetails();
const notificationDetails = NotificationDetails(
android: androidDetails,
iOS: iosDetails,
);
await _notifications.show(
1, // Different ID from completion notifications
'💚 FocusBuddy',
message,
notificationDetails,
payload: 'reminder',
);
} catch (e) {
if (kDebugMode) {
print('Failed to show reminder: $e');
}
}
}
/// Cancel all notifications
Future<void> cancelAll() async {
if (kIsWeb || !_initialized) return;
try {
await _notifications.cancelAll();
} catch (e) {
if (kDebugMode) {
print('Failed to cancel notifications: $e');
}
}
}
/// Check if notifications are supported on this platform
bool get isSupported => !kIsWeb;
}

View File

@@ -0,0 +1,80 @@
import 'package:hive_flutter/hive_flutter.dart';
import '../models/focus_session.dart';
/// Service to manage local storage using Hive
class StorageService {
static const String _focusSessionBox = 'focus_sessions';
/// Initialize Hive
static Future<void> init() async {
await Hive.initFlutter();
// Register adapters
Hive.registerAdapter(FocusSessionAdapter());
// Open boxes
await Hive.openBox<FocusSession>(_focusSessionBox);
}
/// Get the focus sessions box
Box<FocusSession> get _sessionsBox => Hive.box<FocusSession>(_focusSessionBox);
/// Save a focus session
Future<void> saveFocusSession(FocusSession session) async {
await _sessionsBox.add(session);
}
/// Get all focus sessions
List<FocusSession> getAllSessions() {
return _sessionsBox.values.toList();
}
/// Get today's focus sessions
List<FocusSession> getTodaySessions() {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
return _sessionsBox.values.where((session) {
final sessionDate = DateTime(
session.startTime.year,
session.startTime.month,
session.startTime.day,
);
return sessionDate == today;
}).toList();
}
/// Get total focus minutes for today
int getTodayTotalMinutes() {
return getTodaySessions()
.fold<int>(0, (sum, session) => sum + session.actualMinutes);
}
/// Get total distractions for today
int getTodayDistractionCount() {
return getTodaySessions()
.fold<int>(0, (sum, session) => sum + session.distractionCount);
}
/// Get total completed sessions for today
int getTodayCompletedCount() {
return getTodaySessions()
.where((session) => session.completed)
.length;
}
/// Delete a focus session
Future<void> deleteSession(FocusSession session) async {
await session.delete();
}
/// Clear all sessions (for testing/debugging)
Future<void> clearAllSessions() async {
await _sessionsBox.clear();
}
/// Close all boxes
static Future<void> close() async {
await Hive.close();
}
}