Skip to content

#811: Splash screen added #812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"cmake.sourceDirectory": "E:/ultimate_alarm_clock/linux"
"cmake.sourceDirectory": "E:/ultimate_alarm_clock/linux",
"java.configuration.updateBuildConfiguration": "automatic"
}
6 changes: 4 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -33,6 +33,8 @@ if (keystorePropertiesFile.exists()) {
}

android {
// namespace "com.example.fl_location"
namespace "com.ccextractor.ultimate_alarm_clock"
compileSdkVersion 34
ndkVersion flutter.ndkVersion

@@ -94,8 +96,8 @@ flutter {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.7.10"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"
// implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.22"
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"
implementation platform('com.google.firebase:firebase-bom:32.0.0') // Updated Firebase BOM to latest
implementation 'com.google.firebase:protolite-well-known-types:18.0.0'
implementation("com.android.volley:volley:1.2.1")
9 changes: 9 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -122,6 +122,15 @@
android:hardwareAccelerated="true"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 16 additions & 5 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '1.8.22'
ext {
compileSdkVersion = 34
targetSdkVersion = 34
compileSdkVersion = 35
targetSdkVersion = 35
appCompatVersion = "1.6.1"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'com.android.tools.build:gradle:8.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.15'
}
@@ -27,10 +27,21 @@ rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}

subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace project.group
}
}
}
}
}
subprojects {
project.evaluationDependsOn(':app')
}

tasks.register("clean", Delete) {
delete rootProject.buildDir
}
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
35 changes: 21 additions & 14 deletions lib/app/data/providers/isar_provider.dart
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:ultimate_alarm_clock/app/data/models/alarm_model.dart';
import 'package:ultimate_alarm_clock/app/data/models/profile_model.dart';
import 'package:ultimate_alarm_clock/app/data/models/ringtone_model.dart';
@@ -12,7 +13,6 @@ import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart';
import 'package:ultimate_alarm_clock/app/data/providers/firestore_provider.dart';
import 'package:ultimate_alarm_clock/app/data/providers/get_storage_provider.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
import 'package:sqflite/sqflite.dart';

class IsarDb {
static final IsarDb _instance = IsarDb._internal();
@@ -177,7 +177,8 @@ class IsarDb {
static Future<ProfileModel?> getProfile(String name) async {
final isarProvider = IsarDb();
final db = await isarProvider.db;
final a = await db.profileModels.filter().profileNameEqualTo(name).findFirst();
final a =
await db.profileModels.filter().profileNameEqualTo(name).findFirst();
print('$a appkle');
return a;
}
@@ -196,7 +197,8 @@ class IsarDb {
static Future<bool> profileExists(String name) async {
final isarProvider = IsarDb();
final db = await isarProvider.db;
final a = await db.profileModels.filter().profileNameEqualTo(name).findFirst();
final a =
await db.profileModels.filter().profileNameEqualTo(name).findFirst();

return a != null;
}
@@ -372,7 +374,7 @@ class IsarDb {
'profileName': currentProfileName,
'profileData': ProfileModel.toMap(currentProfile!),
'alarmData': alarmMaps,
'owner': ''
'owner': '',
};
return profileSet;
}
@@ -633,30 +635,35 @@ class IsarDb {
if (ringtoneCount.isEmpty) {
await db.writeTxn(() async {
await db.ringtoneModels.importJson([
{'isarId' : fastHash('Digital Alarm 1'),
{
'isarId': fastHash('Digital Alarm 1'),
'ringtoneName': 'Digital Alarm 1',
'ringtonePath': 'ringtones/digialarm.mp3',
'currentCounterOfUsage': 0
'currentCounterOfUsage': 0,
},
{'isarId' : fastHash('Digital Alarm 2'),
{
'isarId': fastHash('Digital Alarm 2'),
'ringtoneName': 'Digital Alarm 2',
'ringtonePath': 'ringtones/digialarm2.mp3',
'currentCounterOfUsage': 0
'currentCounterOfUsage': 0,
},
{'isarId' : fastHash('Digital Alarm 3'),
{
'isarId': fastHash('Digital Alarm 3'),
'ringtoneName': 'Digital Alarm 3',
'ringtonePath': 'ringtones/digialarm3.mp3',
'currentCounterOfUsage': 0
'currentCounterOfUsage': 0,
},
{'isarId' : fastHash('Mystery'),
{
'isarId': fastHash('Mystery'),
'ringtoneName': 'Mystery',
'ringtonePath': 'ringtones/mystery.mp3',
'currentCounterOfUsage': 0
'currentCounterOfUsage': 0,
},
{'isarId' : fastHash('New Day'),
{
'isarId': fastHash('New Day'),
'ringtoneName': 'New Day',
'ringtonePath': 'ringtones/newday.mp3',
'currentCounterOfUsage': 0
'currentCounterOfUsage': 0,
},
]);
});
47 changes: 21 additions & 26 deletions lib/app/modules/settings/views/customize_undo_duration.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/modules/home/controllers/home_controller.dart';
@@ -7,10 +6,10 @@ import '../../../utils/constants.dart';
import '../../../utils/utils.dart';
import '../controllers/theme_controller.dart';

class CustomizeUndoDuration extends StatelessWidget{
HomeController homeController = Get.find<HomeController>();
class CustomizeUndoDuration extends StatelessWidget {
final HomeController homeController = Get.find<HomeController>();
CustomizeUndoDuration({
super.key ,
super.key,
required this.themeController,
required this.height,
required this.width,
@@ -23,7 +22,7 @@ class CustomizeUndoDuration extends StatelessWidget{
Widget build(BuildContext context) {
int duration;
return Obx(
() => InkWell(
() => InkWell(
onTap: () {
Utils.hapticFeedback();
duration = homeController.duration.value;
@@ -34,12 +33,13 @@ class CustomizeUndoDuration extends StatelessWidget{
homeController.duration.value = duration;
return true;
},
titlePadding: const EdgeInsets.symmetric(vertical: 20, horizontal: 5),
// titlePadding:
// const EdgeInsets.symmetric(vertical: 20, horizontal: 5),
backgroundColor: themeController.secondaryBackgroundColor.value,
title: 'Customize Undo Duration'.tr,
titleStyle: Theme.of(context).textTheme.displaySmall,
content: Obx(
() => Column(
() => Column(
children: [
Text(
'${homeController.duration.value} seconds'.tr,
@@ -50,15 +50,14 @@ class CustomizeUndoDuration extends StatelessWidget{
onChanged: (double value) {
homeController.selecteddurationDouble.value = value;
homeController.duration.value = value.toInt();

},
min: 0.0,
max: 20.0,
divisions: 20,
label: homeController.duration.value.toString(),
),
// Replace the volMin Slider with RangeSlider

ElevatedButton(
onPressed: () {
Get.back();
@@ -79,33 +78,30 @@ class CustomizeUndoDuration extends StatelessWidget{
);
},
child: Container(
padding: containerPadding,
width: width * 0.91,
height: height * 0.09,
decoration: Utils.getCustomTileBoxDecoration(
isLightMode: themeController.currentTheme.value == ThemeMode.light,
),
child: Center(
child: Padding(
padding: EdgeInsets.only(left: 10, right: 10),
child: ListTile(
tileColor: themeController.secondaryBackgroundColor.value,
title: Text(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Undo Duration'.tr,
style: TextStyle(
color: themeController.primaryTextColor.value,
fontSize: 15
),
style: Theme.of(context).textTheme.bodyLarge,
),
trailing: Wrap(
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Obx(
() => Text(
() => Text(
'${homeController.duration.value.round().toInt()} seconds',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: themeController.primaryTextColor.value,
fontSize: 13
),
color: themeController.primaryTextColor.value,
fontSize: 13,
),
),
),
Icon(
@@ -114,12 +110,11 @@ class CustomizeUndoDuration extends StatelessWidget{
),
],
),
),
],
),
),
),
),
);
}

}
}
7 changes: 4 additions & 3 deletions lib/app/modules/settings/views/enable_24Hour_format.dart
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
import 'package:ultimate_alarm_clock/app/utils/constants.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';

class Enable24HourFormat extends StatefulWidget {
const Enable24HourFormat({
@@ -31,10 +31,11 @@ class _Enable24HourFormatState extends State<Enable24HourFormat> {
width: widget.width * 0.91,
height: widget.height * 0.1,
decoration: Utils.getCustomTileBoxDecoration(
isLightMode: widget.themeController.currentTheme.value == ThemeMode.light,
isLightMode:
widget.themeController.currentTheme.value == ThemeMode.light,
),
child: Padding(
padding: const EdgeInsets.only(left: 30, right: 20),
padding: containerPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
4 changes: 2 additions & 2 deletions lib/app/modules/settings/views/enable_haptic_feedback.dart
Original file line number Diff line number Diff line change
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/settings_controller.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';
import 'package:ultimate_alarm_clock/app/utils/constants.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';

class EnableHapticFeedback extends StatefulWidget {
const EnableHapticFeedback({
@@ -35,7 +35,7 @@ class _EnableHapticFeedbackState extends State<EnableHapticFeedback> {
widget.themeController.currentTheme.value == ThemeMode.light,
),
child: Padding(
padding: const EdgeInsets.only(left: 30, right: 20),
padding: containerPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
5 changes: 3 additions & 2 deletions lib/app/modules/settings/views/enable_sorted_alarm_list.dart
Original file line number Diff line number Diff line change
@@ -38,15 +38,16 @@ class _EnableSortedAlarmListState extends State<EnableSortedAlarmList> {
color: widget.themeController.secondaryBackgroundColor.value,
),
child: Padding(
padding: EdgeInsets.only(left: 30, right: 20),
padding: containerPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
'Enable Sorted Alarm List'.tr,
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: widget.themeController.primaryTextColor.value),
color: widget.themeController.primaryTextColor.value,
),
),
),
Obx(
6 changes: 2 additions & 4 deletions lib/app/modules/settings/views/settings_view.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import 'package:flutter/material.dart';

import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/views/customize_undo_duration.dart';

import 'package:ultimate_alarm_clock/app/modules/settings/views/enable_24Hour_format.dart';

import 'package:ultimate_alarm_clock/app/modules/settings/views/enable_haptic_feedback.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/views/enable_sorted_alarm_list.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/views/language_menu.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/views/theme_value_tile.dart';
import 'package:ultimate_alarm_clock/app/utils/utils.dart';

import '../controllers/settings_controller.dart';
import 'google_sign_in.dart';

@@ -49,7 +47,7 @@ class SettingsView extends GetView<SettingsController> {
body: SingleChildScrollView(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 25.0),
padding: const EdgeInsets.all(16),
child: Column(
children: [
GoogleSignIn(
2 changes: 1 addition & 1 deletion lib/app/modules/settings/views/theme_value_tile.dart
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ class _ThemeValueTileState extends State<ThemeValueTile> {
widget.themeController.currentTheme.value == ThemeMode.light,
),
child: Padding(
padding: EdgeInsets.only(left: 30, right: 20),
padding: containerPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Original file line number Diff line number Diff line change
@@ -21,20 +21,18 @@ class SplashScreenController extends GetxController {
homeController.genFakeAlarmModel().obs;

getCurrentlyRingingAlarm() async {
AlarmModel _alarmRecord = homeController.genFakeAlarmModel();
AlarmModel latestAlarm =
await IsarDb.getLatestAlarm(_alarmRecord, false);
AlarmModel alarmRecord = homeController.genFakeAlarmModel();
AlarmModel latestAlarm = await IsarDb.getLatestAlarm(alarmRecord, false);
debugPrint('CURRENT RINGING : ${latestAlarm.alarmTime}');
return latestAlarm;
}

getNextAlarm() async {
UserModel? _userModel = await SecureStorageProvider().retrieveUserModel();
AlarmModel _alarmRecord = homeController.genFakeAlarmModel();
AlarmModel isarLatestAlarm =
await IsarDb.getLatestAlarm(_alarmRecord, true);
UserModel? userModel = await SecureStorageProvider().retrieveUserModel();
AlarmModel alarmRecord = homeController.genFakeAlarmModel();
AlarmModel isarLatestAlarm = await IsarDb.getLatestAlarm(alarmRecord, true);
AlarmModel firestoreLatestAlarm =
await FirestoreDb.getLatestAlarm(_userModel, _alarmRecord, true);
await FirestoreDb.getLatestAlarm(userModel, alarmRecord, true);
AlarmModel latestAlarm =
Utils.getFirstScheduledAlarm(isarLatestAlarm, firestoreLatestAlarm);
debugPrint('LATEST : ${latestAlarm.alarmTime}');
@@ -103,7 +101,7 @@ class SplashScreenController extends GetxController {
alarmChannel.setMethodCallHandler((call) async {
if (call.method == 'appStartup') {
bool shouldAlarmRing = call.arguments['shouldAlarmRing'];
print("shouldring: $shouldAlarmRing");
print('shouldring: $shouldAlarmRing');
// This indicates the app was started through native code
if (shouldAlarmRing == true) {
shouldNavigate = false;
@@ -115,7 +113,10 @@ class SplashScreenController extends GetxController {
} else {
if (shouldAlarmRing) {
currentlyRingingAlarm.value = await getCurrentlyRingingAlarm();
Get.offNamed('/alarm-ring',arguments: currentlyRingingAlarm.value);
Get.offNamed(
'/alarm-ring',
arguments: currentlyRingingAlarm.value,
);
} else {
currentlyRingingAlarm.value = await getCurrentlyRingingAlarm();
// If the alarm is set to NEVER repeat, then it will be chosen as
@@ -172,11 +173,11 @@ class SplashScreenController extends GetxController {
try {
await alarmChannel.invokeMethod('scheduleAlarm', {
'milliSeconds': intervaltoAlarm,
'activityMonitor': latestAlarm.activityMonitor
'activityMonitor': latestAlarm.activityMonitor,
});
print("Scheduled...");
print('Scheduled...');
} on PlatformException catch (e) {
print("Failed to schedule alarm: ${e.message}");
print('Failed to schedule alarm: ${e.message}');
}
}
SystemNavigator.pop();
@@ -189,7 +190,7 @@ class SplashScreenController extends GetxController {
}
});
// Necessary when hot restarting
Future.delayed(const Duration(seconds: 0), () {
Future.delayed(const Duration(seconds: 3), () {
if (shouldNavigate == true) {
Get.offNamed('/bottom-navigation-bar');
}
58 changes: 46 additions & 12 deletions lib/app/modules/splashScreen/views/splash_screen_view.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,54 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/modules/splashScreen/controllers/splash_screen_controller.dart';

class SplashScreenView extends GetView<SplashScreenController> {
const SplashScreenView({Key? key}) : super(key: key);
class SplashScreenView extends StatefulWidget {
const SplashScreenView({super.key});

@override
State<SplashScreenView> createState() => _SplashScreenViewState();
}

class _SplashScreenViewState extends State<SplashScreenView>
with SingleTickerProviderStateMixin<SplashScreenView> {
AnimationController? controller;
Animation<double>? colorAnimation;

@override
void initState() {
super.initState();

// ignore: avoid_print
print('this is callling in initstate');
controller =
AnimationController(vsync: this, duration: const Duration(seconds: 2));
colorAnimation = Tween<double>(begin: 0.0, end: 1.0)
.animate(CurvedAnimation(parent: controller!, curve: Curves.bounceOut));
WidgetsBinding.instance.scheduleFrameCallback((_) async {
controller!.addListener(() {
setState(() {});
});

controller!.addStatusListener((status) async {
if (status == AnimationStatus.completed) {}
});

controller!.forward();
});
}

@override
void dispose() {
controller!.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
// ignore: unused_local_variable
var width = Get.width;
return const Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
),
return Scaffold(
backgroundColor: Colors.black87,
body: Center(
child: SizedBox(
height: 250 * colorAnimation!.value,
child: Image.asset('assets/images/splashscreen.png'),
),
),
);
102 changes: 102 additions & 0 deletions lib/app/modules/stopwatch/local_notification.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/modules/stopwatch/controllers/stopwatch_controller.dart';

class LocalNotification with WidgetsBindingObserver {
static final FlutterLocalNotificationsPlugin
_flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

LocalNotification() {
WidgetsBinding.instance.addObserver(this);
}

/// Initialize Notification
static Future<void> initNotification() async {
const AndroidInitializationSettings androidSettings =
AndroidInitializationSettings('notificationicon');

const DarwinInitializationSettings iosSettings =
DarwinInitializationSettings();

const InitializationSettings settings = InitializationSettings(
android: androidSettings,
iOS: iosSettings,
);

await _flutterLocalNotificationsPlugin.initialize(
settings,
onDidReceiveNotificationResponse: onNotificationTap,
onDidReceiveBackgroundNotificationResponse: onNotificationTap,
);
}

/// Show Stopwatch Notification when app is in background
Future<void> showStopwatchNotification() async {
final StopwatchController stopwatchController = Get.find();

const AndroidNotificationDetails androidDetails =
AndroidNotificationDetails(
'stopwatch_channel',
'Stopwatch',
channelDescription: 'Controls stopwatch',
importance: Importance.high,
priority: Priority.high,
ongoing: true, // Keeps the notification persistent
onlyAlertOnce: true,
actions: [
AndroidNotificationAction('pause', 'Pause', showsUserInterface: true),
AndroidNotificationAction('start', 'Start', showsUserInterface: true),
AndroidNotificationAction('reset', 'Reset', showsUserInterface: true),
],
);

const NotificationDetails details =
NotificationDetails(android: androidDetails);

await _flutterLocalNotificationsPlugin.show(
0,
'Stopwatch Running',
stopwatchController.result,
details,
);
}

/// Cancel the notification
Future<void> cancelNotification() async {
await _flutterLocalNotificationsPlugin.cancel(0);
}

/// Handle Notification Tap Actions
static void onNotificationTap(NotificationResponse response) {
final StopwatchController stopwatchController =
Get.put(StopwatchController(), permanent: true);

switch (response.actionId) {
case 'pause':
stopwatchController.stopTimer();
break;
case 'start':
stopwatchController.startTimer();
break;
case 'reset':
stopwatchController.resetTime();
break;
}
}

/// Show notification when app goes to background and hide when foregrounded
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final StopwatchController stopwatchController = Get.find();

if (state == AppLifecycleState.paused ||
state == AppLifecycleState.detached) {
if (!stopwatchController.isTimerPaused.value) {
showStopwatchNotification();
}
} else if (state == AppLifecycleState.resumed) {
cancelNotification();
}
}
}
342 changes: 172 additions & 170 deletions lib/app/modules/stopwatch/views/stopwatch_view.dart
Original file line number Diff line number Diff line change
@@ -3,213 +3,215 @@ import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart';
import 'package:ultimate_alarm_clock/app/modules/stopwatch/controllers/stopwatch_controller.dart';
import 'package:ultimate_alarm_clock/app/modules/stopwatch/local_notification.dart';
import 'package:ultimate_alarm_clock/app/utils/end_drawer.dart';

import '../../../utils/utils.dart';

// ignore: must_be_immutable
class StopwatchView extends GetView<StopwatchController> {
StopwatchView({Key? key}) : super(key: key);
StopwatchView({super.key});
ThemeController themeController = Get.find<ThemeController>();
@override
Widget build(BuildContext context) {
// ignore: unused_local_variable
final double width = MediaQuery.of(context).size.width;
final double height = MediaQuery.of(context).size.height;
LocalNotification();
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(height / 7.9),
child: AppBar(
toolbarHeight: height / 7.9,
elevation: 0.0,
centerTitle: true,
actions: [
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Obx(
() => IconButton(
onPressed: () {
Utils.hapticFeedback();
Scaffold.of(context).openEndDrawer();
},
icon: const Icon(
Icons.menu,
),
color: themeController.primaryTextColor.value
.withOpacity(0.75),
iconSize: 27,
// splashRadius: 0.000001,
appBar: PreferredSize(
preferredSize: Size.fromHeight(height / 7.9),
child: AppBar(
toolbarHeight: height / 7.9,
elevation: 0.0,
centerTitle: true,
actions: [
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Obx(
() => IconButton(
onPressed: () {
Utils.hapticFeedback();
Scaffold.of(context).openEndDrawer();
},
icon: const Icon(
Icons.menu,
),
);
},
),
],
),
color: themeController.primaryTextColor.value
.withOpacity(0.75),
iconSize: 27,
// splashRadius: 0.000001,
),
);
},
),
],
),
body: Column(
children: [
Obx(
() => AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: controller.hasFlags.value ? height * 0.1 : height * 0.3,
),
),
body: Column(
children: [
Obx(
() => AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: controller.hasFlags.value ? height * 0.1 : height * 0.3,
),
Obx(
() => Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Center(
child: Text(
controller.result.split(':')[0],
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
Obx(
() => Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Center(
child: Text(
controller.result.split(':')[0],
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
),
const Text(
':',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
const Text(
':',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
Expanded(
child: Center(
child: Text(
controller.result.split(':')[1],
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
Expanded(
child: Center(
child: Text(
controller.result.split(':')[1],
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
),
const Text(
':',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
const Text(
':',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
Expanded(
child: Center(
child: Text(
controller.result.split(':')[2],
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
Expanded(
child: Center(
child: Text(
controller.result.split(':')[2],
style: const TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
const SizedBox(
height: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton(
heroTag: "flag",
onPressed: controller.addFlag,
child: Icon(
Icons.flag,
size: 33,
color: themeController.secondaryTextColor.value,
),
),
FloatingActionButton(
heroTag: "start",
onPressed: controller.toggleTimer,
child: Obx(
() => Icon(
controller.isTimerPaused.value
? Icons.play_arrow_rounded
: Icons.pause_rounded,
size: 33,
),
),
],
),
),
const SizedBox(
height: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton(
heroTag: 'flag',
onPressed: controller.addFlag,
child: Icon(
Icons.flag,
size: 33,
color: themeController.secondaryTextColor.value,
),
// Reset button
FloatingActionButton(
heroTag: "stop",
onPressed: controller.resetTime,
child: Icon(
Icons.stop_rounded,
),
FloatingActionButton(
heroTag: 'start',
onPressed: controller.toggleTimer,
child: Obx(
() => Icon(
controller.isTimerPaused.value
? Icons.play_arrow_rounded
: Icons.pause_rounded,
size: 33,
),
),
],
),
const SizedBox(
height: 10.0,
),
Expanded(
child: Obx(
() => AnimatedList(
key: controller.listKey, // Add this key to controller
initialItemCount: controller.flags.length,
padding: const EdgeInsets.symmetric(horizontal: 12),
itemBuilder: (context, index, animation) {
final reversedIndex = controller.flags.length - 1 - index;
return SlideTransition(
position: animation.drive(
Tween<Offset>(
begin: const Offset(0, -0.3),
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeInOut)),
),
// Reset button
FloatingActionButton(
heroTag: 'stop',
onPressed: controller.resetTime,
child: const Icon(
Icons.stop_rounded,
size: 33,
),
),
],
),
const SizedBox(
height: 10.0,
),
Expanded(
child: Obx(
() => AnimatedList(
key: controller.listKey, // Add this key to controller
initialItemCount: controller.flags.length,
padding: const EdgeInsets.symmetric(horizontal: 12),
itemBuilder: (context, index, animation) {
final reversedIndex = controller.flags.length - 1 - index;
return SlideTransition(
position: animation.drive(
Tween<Offset>(
begin: const Offset(0, -0.3),
end: Offset.zero,
).chain(CurveTween(curve: Curves.easeInOut)),
),
child: ListTile(
minVerticalPadding: 0,
title: Text(
'${controller.flags[reversedIndex].number}',
style: const TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.bold,
height: 1,
),
),
child: ListTile(
minVerticalPadding: 0,
title: Text(
'${controller.flags[reversedIndex].number}',
style: const TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.bold,
height: 1,
),
trailing: Text(
"+${DateFormat('mm:ss:SS').format(
DateTime(0)
.add(controller.flags[reversedIndex].lapTime),
)}",
style: TextStyle(
fontSize: 12.0,
height: -0.5,
color: themeController.primaryDisabledTextColor.value,
),
trailing: Text(
"+${DateFormat('mm:ss:SS').format(
DateTime(0)
.add(controller.flags[reversedIndex].lapTime),
)}",
style: TextStyle(
fontSize: 12.0,
height: -0.5,
color:
themeController.primaryDisabledTextColor.value,
),
),
subtitle: Text(
DateFormat('mm:ss:SS').format(
DateTime(0)
.add(controller.flags[reversedIndex].totalTime),
),
subtitle: Text(
DateFormat('mm:ss:SS').format(
DateTime(0)
.add(controller.flags[reversedIndex].totalTime),
),
style: TextStyle(
fontSize: 14.0,
color:
themeController.primaryDisabledTextColor.value,
fontWeight: FontWeight.bold,
),
style: TextStyle(
fontSize: 14.0,
color: themeController.primaryDisabledTextColor.value,
fontWeight: FontWeight.bold,
),
),
);
},
),
),
);
},
),
),
const SizedBox(
height: 10.0,
),
],
),
endDrawer: buildEndDrawer(context));
),
const SizedBox(
height: 10.0,
),
],
),
endDrawer: buildEndDrawer(context),
);
}
}
101 changes: 58 additions & 43 deletions lib/app/modules/timer/views/timer_animation.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:ultimate_alarm_clock/app/data/models/timer_model.dart';
@@ -29,8 +30,8 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>

Timer? _timerCounter;
void startTimer() {
_timerCounter = Timer.periodic(Duration(seconds: 1), (timer) {
print('${widget.timer.timerName}');
_timerCounter = Timer.periodic(const Duration(seconds: 1), (timer) {
print(widget.timer.timerName);
if (widget.timer.timeElapsed < widget.timer.timerValue) {
setState(() {
widget.timer.timeElapsed += 1000;
@@ -51,18 +52,25 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
void initState() {
super.initState();
if (Utils.getDifferenceMillisFromNow(
widget.timer.startedOn, widget.timer.timerValue) <=
widget.timer.startedOn,
widget.timer.timerValue,
) <=
0 &&
widget.timer.isPaused == 0) {
widget.timer.isPaused = 1;
widget.timer.timeElapsed = 0;
IsarDb.updateTimerPauseStatus(widget.timer);
} else if (Utils.getDifferenceMillisFromNow(
widget.timer.startedOn, widget.timer.timerValue) <
widget.timer.startedOn,
widget.timer.timerValue,
) <
widget.timer.timerValue &&
widget.timer.isPaused == 0) {
widget.timer.timeElapsed = widget.timer.timerValue - Utils.getDifferenceMillisFromNow(
widget.timer.startedOn, widget.timer.timerValue);
widget.timer.timeElapsed = widget.timer.timerValue -
Utils.getDifferenceMillisFromNow(
widget.timer.startedOn,
widget.timer.timerValue,
);
IsarDb.updateTimerPauseStatus(widget.timer);
}
if (widget.timer.isPaused == 0) {
@@ -82,8 +90,8 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
padding: const EdgeInsets.symmetric(
horizontal: 10.0,
),
child: Container(
height: context.height / 3.0,
child: SizedBox(
height: context.height / 3.0,
width: context.width,
child: Obx(
() => Card(
@@ -102,9 +110,10 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
children: [
AnimatedContainer(
decoration: BoxDecoration(
color: kprimaryDisabledTextColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(18)),
duration: Duration(milliseconds: 1000),
color: kprimaryDisabledTextColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(18),
),
duration: const Duration(milliseconds: 1000),
height: context.height / 3.3,
width: context.width *
((widget.timer.timeElapsed) /
@@ -127,9 +136,10 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
style: Theme.of(
context,
).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w500,
color: kprimaryColor,
fontSize: 18),
fontWeight: FontWeight.w500,
color: kprimaryColor,
fontSize: 18,
),
),
),
Row(
@@ -152,7 +162,7 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
}
});
},
icon: Icon(
icon: const Icon(
Icons.refresh,
size: 18,
color: Colors.white,
@@ -161,11 +171,13 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
IconButton(
onPressed: () {
controller.stopRinger(
widget.timer.timerId);
widget.timer.timerId,
);
controller.deleteTimer(
widget.timer.timerId);
widget.timer.timerId,
);
},
icon: Icon(
icon: const Icon(
Icons.close,
size: 18,
color: Colors.white,
@@ -175,23 +187,24 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
),
],
),
Spacer(),
const Spacer(),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 10),
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Obx(
() => AnimatedContainer(
duration: Duration(seconds: 1),
duration: const Duration(seconds: 1),
child: Text(
'${Utils.formatMilliseconds(widget.timer.timerValue - widget.timer.timeElapsed)}',
Utils.formatMilliseconds(
widget.timer.timerValue -
widget.timer.timeElapsed),
style: Theme.of(
context,
).textTheme.displayLarge!.copyWith(
color: themeController.primaryTextColor.value,
color: themeController
.primaryTextColor.value,
fontSize: 44,
),
),
@@ -204,52 +217,54 @@ class _TimerAnimatedCardState extends State<TimerAnimatedCard>
? stopTimer()
: startTimer();
widget.timer.isPaused =
widget.timer.isPaused == 0
? 1
: 0;
widget.timer.isPaused == 0 ? 1 : 0;
IsarDb.updateTimerPauseStatus(
widget.timer);
widget.timer,
);
});
if (widget.timer.timeElapsed >=
widget.timer.timerValue) {
controller.stopRinger(
widget.timer.timerId);
widget.timer.timerId,
);
setState(() {
widget.timer.timeElapsed = 0;
IsarDb.updateTimerTick(
widget.timer)
.then((value) => IsarDb
.updateTimerPauseStatus(
widget.timer));
widget.timer,
).then(
(value) =>
IsarDb.updateTimerPauseStatus(
widget.timer,
),
);
widget.timer.isPaused = 1;
});
}
},
child: Container(
decoration: BoxDecoration(
color: kprimaryColor,
borderRadius:
BorderRadius.circular(80)),
color: kprimaryColor,
borderRadius: BorderRadius.circular(80),
),
width: 80,
height: 80,
child: Icon(
widget.timer.isPaused == 0
? Icons.pause
: Icons.play_arrow,
size: 30,
color:
Colors.white,
color: Colors.white,
),
),
)
),
],
),
),
Spacer(),
const Spacer(),
],
),
),
)
),
],
),
),
32 changes: 20 additions & 12 deletions lib/app/utils/constants.dart
Original file line number Diff line number Diff line change
@@ -33,7 +33,9 @@ const Color kLightSecondaryBackgroundColor = Color(0xffF9F9F9);
const Color kLightPrimaryTextColor = Color(0xff444444);
const Color kLightSecondaryTextColor = Color(0xff444444);
const Color kLightPrimaryDisabledTextColor = Color(0xffACACAB);

// padding
EdgeInsetsGeometry containerPadding =
const EdgeInsets.only(left: 30, right: 20);
// Dark ThemeData
ThemeData kThemeData = ThemeData(
canvasColor: kprimaryBackgroundColor,
@@ -46,8 +48,8 @@ ThemeData kThemeData = ThemeData(
),
fontFamily: 'poppins',
checkboxTheme: CheckboxThemeData(
checkColor: MaterialStateProperty.all(kprimaryTextColor),
fillColor: MaterialStateProperty.all(kprimaryBackgroundColor),
checkColor: WidgetStateProperty.all(kprimaryTextColor),
fillColor: WidgetStateProperty.all(kprimaryBackgroundColor),
),
textTheme: const TextTheme(
titleSmall: TextStyle(color: kprimaryTextColor, letterSpacing: 0.15),
@@ -84,7 +86,7 @@ ThemeData kThemeData = ThemeData(
appBarTheme: const AppBarTheme(backgroundColor: kprimaryBackgroundColor),
colorScheme: ColorScheme.fromSwatch().copyWith(
secondary: ksecondaryColor,
background: kprimaryBackgroundColor,
surface: kprimaryBackgroundColor,
onPrimaryContainer: ksecondaryBackgroundColor,
),
inputDecorationTheme: InputDecorationTheme(
@@ -112,8 +114,8 @@ ThemeData kThemeData = ThemeData(
),
outlinedButtonTheme: const OutlinedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.transparent),
side: MaterialStatePropertyAll(BorderSide(color: kprimaryColor)),
backgroundColor: WidgetStatePropertyAll(Colors.transparent),
side: WidgetStatePropertyAll(BorderSide(color: kprimaryColor)),
),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
@@ -189,8 +191,8 @@ ThemeData kLightThemeData = ThemeData(
),
fontFamily: 'poppins',
checkboxTheme: CheckboxThemeData(
checkColor: MaterialStateProperty.all(kprimaryTextColor),
fillColor: MaterialStateProperty.all(kLightPrimaryBackgroundColor),
checkColor: WidgetStateProperty.all(kprimaryTextColor),
fillColor: WidgetStateProperty.all(kLightPrimaryBackgroundColor),
),
textTheme: const TextTheme(
titleSmall: TextStyle(color: kLightPrimaryTextColor, letterSpacing: 0.15),
@@ -227,7 +229,7 @@ ThemeData kLightThemeData = ThemeData(
appBarTheme: const AppBarTheme(backgroundColor: kLightPrimaryBackgroundColor),
colorScheme: ColorScheme.fromSwatch().copyWith(
secondary: kLightSecondaryColor,
background: kLightPrimaryBackgroundColor,
surface: kLightPrimaryBackgroundColor,
onPrimaryContainer: kLightSecondaryBackgroundColor,
),
inputDecorationTheme: InputDecorationTheme(
@@ -255,8 +257,8 @@ ThemeData kLightThemeData = ThemeData(
),
outlinedButtonTheme: const OutlinedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStatePropertyAll(Colors.transparent),
side: MaterialStatePropertyAll(BorderSide(color: kprimaryColor)),
backgroundColor: WidgetStatePropertyAll(Colors.transparent),
side: WidgetStatePropertyAll(BorderSide(color: kprimaryColor)),
),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
@@ -321,7 +323,13 @@ ThemeData kLightThemeData = ThemeData(
),
);

const List<String> defaultRingtones = ['Digital Alarm 1','Digital Alarm 2','Digital Alarm 3','Mystery','New Day'];
const List<String> defaultRingtones = [
'Digital Alarm 1',
'Digital Alarm 2',
'Digital Alarm 3',
'Mystery',
'New Day',
];

const Map<String, String> Holidays = {
'Christian Holidays': 'en.christian#holiday@group.v.calendar.google.com',
11 changes: 6 additions & 5 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:ultimate_alarm_clock/app/data/providers/get_storage_provider.dart';
import 'package:ultimate_alarm_clock/app/modules/settings/controllers/theme_controller.dart';

import 'package:ultimate_alarm_clock/app/utils/language.dart';
import 'package:ultimate_alarm_clock/app/modules/stopwatch/local_notification.dart';
import 'package:ultimate_alarm_clock/app/utils/constants.dart';
import 'package:ultimate_alarm_clock/app/utils/custom_error_screen.dart';
import 'package:ultimate_alarm_clock/app/utils/language.dart';

import 'app/routes/app_pages.dart';

Locale? loc;
@@ -28,6 +29,7 @@ void main() async {

final storage = Get.find<GetStorageProvider>();
loc = await storage.readLocale();
await LocalNotification.initNotification();

final ThemeController themeController = Get.put(ThemeController());

@@ -54,7 +56,6 @@ void main() async {
);
}


class UltimateAlarmClockApp extends StatelessWidget {
const UltimateAlarmClockApp({super.key});
@override
@@ -68,7 +69,7 @@ class UltimateAlarmClockApp extends StatelessWidget {
getPages: AppPages.routes,
translations: AppTranslations(),
locale: loc,
fallbackLocale: Locale('en', 'US'),
fallbackLocale: const Locale('en', 'US'),
builder: (BuildContext context, Widget? error) {
ErrorWidget.builder = (FlutterErrorDetails? error) {
return CustomErrorScreen(errorDetails: error!);
4 changes: 4 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -57,6 +57,7 @@ dependencies:
intl_phone_number_input: ^0.7.4
firebase_messaging: ^14.7.19
shared_preferences: ^2.2.3
flutter_local_notifications: ^18.0.1

dev_dependencies:
flutter_lints: ^4.0.0
@@ -65,6 +66,9 @@ dev_dependencies:
flutter_test:
sdk: flutter

dependency_overrides:
sensors_plus: ^6.1.1

flutter_native_splash:
android: true
ios: true