├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json ├── screenshots ├── 0.png ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png └── cover.png ├── assets ├── images │ ├── icon │ │ └── icon.png │ └── svg │ │ ├── ic_notification.svg │ │ ├── ic_search.svg │ │ └── ic_setting.svg └── fonts │ ├── ubuntu_regular.ttf │ ├── yekan_bakh_regular.ttf │ ├── yekan_bakh_no_en_regular.ttf │ └── yekan_bakh_regular_numfa.ttf ├── android ├── app │ ├── src │ │ ├── main │ │ │ ├── ic_launcher-playstore.png │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── drawable-hdpi │ │ │ │ │ ├── ic_stat_name.png │ │ │ │ │ └── ic_action_name.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ ├── ic_stat_name.png │ │ │ │ │ └── ic_action_name.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ ├── ic_action_name.png │ │ │ │ │ └── ic_stat_name.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ ├── ic_stat_name.png │ │ │ │ │ └── ic_action_name.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ ├── ic_stat_name.png │ │ │ │ │ └── ic_action_name.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── values │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── ir │ │ │ │ │ └── umut │ │ │ │ │ └── on_time │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── build.gradle └── settings.gradle ├── devtools_options.yaml ├── lib ├── resource │ ├── app_dimens.dart │ ├── constants.dart │ ├── widgets │ │ ├── restart.dart │ │ ├── into_vc.dart │ │ ├── mobile_wrapper_in_desktop.dart │ │ ├── empty_vc.dart │ │ ├── dialog.dart │ │ ├── input_feild.dart │ │ ├── custom_app_bar.dart │ │ ├── note_card.dart │ │ └── color_item.dart │ ├── utils │ │ ├── notification_helper.dart │ │ ├── utils.dart │ │ └── extensions.dart │ ├── themes │ │ └── themes.dart │ └── components │ │ └── text_style.dart ├── screens │ ├── home │ │ ├── bloc │ │ │ ├── home_event.dart │ │ │ ├── home_bloc.dart │ │ │ └── home_state.dart │ │ ├── note │ │ │ ├── bloc │ │ │ │ ├── note_event.dart │ │ │ │ ├── note_state.dart │ │ │ │ └── note_bloc.dart │ │ │ ├── notes.dart │ │ │ ├── note_list.dart │ │ │ ├── add_note_screen.dart │ │ │ └── edit_note_screen.dart │ │ ├── task │ │ │ ├── bloc │ │ │ │ ├── task_event.dart │ │ │ │ ├── task_state.dart │ │ │ │ └── task_bloc.dart │ │ │ ├── task_list.dart │ │ │ └── tasks.dart │ │ └── home_page.dart │ ├── settings │ │ ├── bloc │ │ │ ├── settings_event.dart │ │ │ ├── settings_state.dart │ │ │ └── settings_bloc.dart │ │ └── settings_page.dart │ ├── splash_screen.dart │ ├── my_app.dart │ └── intro │ │ └── intro_screen.dart ├── data │ ├── models │ │ ├── language_model.dart │ │ ├── note_model.dart │ │ ├── task_model.dart │ │ ├── note_model.g.dart │ │ └── task_model.g.dart │ ├── repositories │ │ ├── repositories.dart │ │ └── local_repo.dart │ └── sources │ │ ├── data_source.dart │ │ └── local_data_src.dart ├── gen │ ├── fonts.gen.dart │ └── assets.gen.dart ├── l10n │ ├── intl_en.arb │ └── intl_fa.arb ├── generated │ ├── intl │ │ ├── messages_all.dart │ │ ├── messages_en.dart │ │ └── messages_fa.dart │ └── l10n.dart ├── main.dart └── index.dart ├── .gitignore ├── .metadata ├── analysis_options.yaml ├── pubspec.yaml ├── README.md └── LICENSE /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/web/favicon.png -------------------------------------------------------------------------------- /screenshots/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/0.png -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/5.png -------------------------------------------------------------------------------- /screenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/6.png -------------------------------------------------------------------------------- /screenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/7.png -------------------------------------------------------------------------------- /screenshots/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/screenshots/cover.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /assets/images/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/assets/images/icon/icon.png -------------------------------------------------------------------------------- /assets/fonts/ubuntu_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/assets/fonts/ubuntu_regular.ttf -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /assets/fonts/yekan_bakh_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/assets/fonts/yekan_bakh_regular.ttf -------------------------------------------------------------------------------- /assets/fonts/yekan_bakh_no_en_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/assets/fonts/yekan_bakh_no_en_regular.ttf -------------------------------------------------------------------------------- /assets/fonts/yekan_bakh_regular_numfa.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/assets/fonts/yekan_bakh_regular_numfa.ttf -------------------------------------------------------------------------------- /android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-hdpi/ic_stat_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-mdpi/ic_stat_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-hdpi/ic_action_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-mdpi/ic_action_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-xhdpi/ic_action_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-xhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_action_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_action_name.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OmidHaqi/on_time/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #DAE5EB 4 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/ir/umut/on_time/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package ir.umut.on_time 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /lib/resource/app_dimens.dart: -------------------------------------------------------------------------------- 1 | part of '../index.dart'; 2 | 3 | class AppDimens { 4 | AppDimens._(); 5 | static const double small = 8; 6 | static const double medium = 16; 7 | static const double large = 24; 8 | } 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /lib/screens/home/bloc/home_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | abstract class HomeEvent extends Equatable { 4 | const HomeEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class ChangePage extends HomeEvent { 11 | final int pageIndex; 12 | 13 | const ChangePage(this.pageIndex); 14 | 15 | @override 16 | List get props => [pageIndex]; 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/resource/constants.dart: -------------------------------------------------------------------------------- 1 | part of '../index.dart'; 2 | 3 | 4 | class AppColors { 5 | AppColors._(); 6 | 7 | static const Color appPrimaryDark = Color(0xff161928); 8 | static const Color appOnPrimaryDark = Color(0xffDAE5EB); 9 | static const Color appPrimaryLight = Color(0xffDAE5EB); 10 | static const Color appOPrimaryLight = Color(0xff161928); 11 | } 12 | 13 | const String taskBoxName = 'task_box'; 14 | const String noteBoxName = 'note_box'; 15 | 16 | -------------------------------------------------------------------------------- /lib/screens/home/bloc/home_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:on_time/screens/home/bloc/home_event.dart'; 3 | import 'package:on_time/screens/home/bloc/home_state.dart'; 4 | 5 | class HomeBloc extends Bloc { 6 | HomeBloc() : super(HomeState.initial()) { 7 | on(_onChangePage); 8 | } 9 | 10 | void _onChangePage(ChangePage event, Emitter emit) { 11 | emit(state.copyWith(selectedPageIndex: event.pageIndex)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/screens/home/bloc/home_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | 3 | class HomeState extends Equatable { 4 | final int selectedPageIndex; 5 | 6 | const HomeState({required this.selectedPageIndex}); 7 | 8 | factory HomeState.initial() { 9 | return const HomeState(selectedPageIndex: 0); 10 | } 11 | 12 | HomeState copyWith({int? selectedPageIndex}) { 13 | return HomeState( 14 | selectedPageIndex: selectedPageIndex ?? this.selectedPageIndex, 15 | ); 16 | } 17 | 18 | @override 19 | List get props => [selectedPageIndex]; 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/models/language_model.dart: -------------------------------------------------------------------------------- 1 | class LanguageModel { 2 | String language; 3 | String subLanguage; 4 | String languageCode; 5 | LanguageModel({ 6 | required this.language, 7 | required this.subLanguage, 8 | required this.languageCode, 9 | }); 10 | } 11 | 12 | List get languageModel => [ 13 | LanguageModel( 14 | language: 'فارسی', 15 | subLanguage: 'Farsi', 16 | languageCode: 'fa', 17 | ), 18 | LanguageModel( 19 | language: 'English', 20 | subLanguage: 'English', 21 | languageCode: 'en', 22 | ), 23 | ]; 24 | -------------------------------------------------------------------------------- /lib/gen/fonts.gen.dart: -------------------------------------------------------------------------------- 1 | /// GENERATED CODE - DO NOT MODIFY BY HAND 2 | /// ***************************************************** 3 | /// FlutterGen 4 | /// ***************************************************** 5 | 6 | // coverage:ignore-file 7 | // ignore_for_file: type=lint 8 | // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use 9 | 10 | class FontFamily { 11 | FontFamily._(); 12 | 13 | /// Font family: Ubuntu 14 | static const String ubuntu = 'Ubuntu'; 15 | 16 | /// Font family: YekanBakhNumFa 17 | static const String yekanBakhNumFa = 'YekanBakhNumFa'; 18 | } 19 | -------------------------------------------------------------------------------- /lib/screens/settings/bloc/settings_event.dart: -------------------------------------------------------------------------------- 1 | part of 'settings_bloc.dart'; 2 | 3 | sealed class SettingsEvent extends Equatable { 4 | const SettingsEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class LoadLocalizations extends SettingsEvent { 11 | final Locale locale; 12 | 13 | const LoadLocalizations(this.locale); 14 | 15 | @override 16 | List get props => [locale]; 17 | } 18 | 19 | class LoadSavedLocalizations extends SettingsEvent {} 20 | 21 | class ThemeChanged extends SettingsEvent { 22 | final bool isDark; 23 | 24 | const ThemeChanged({required this.isDark}); 25 | } 26 | 27 | class LoadTheme extends SettingsEvent {} 28 | -------------------------------------------------------------------------------- /lib/screens/settings/bloc/settings_state.dart: -------------------------------------------------------------------------------- 1 | part of 'settings_bloc.dart'; 2 | 3 | class SettingsState extends Equatable { 4 | final Locale locale; 5 | final ThemeMode themeMode; 6 | 7 | const SettingsState({required this.locale, required this.themeMode}); 8 | 9 | // Default state 10 | factory SettingsState.initial() { 11 | return const SettingsState( 12 | locale: Locale('en'), themeMode: ThemeMode.light); 13 | } 14 | 15 | SettingsState copyWith({Locale? locale, ThemeMode? themeMode}) { 16 | return SettingsState( 17 | locale: locale ?? this.locale, 18 | themeMode: themeMode ?? this.themeMode, 19 | ); 20 | } 21 | 22 | @override 23 | List get props => [locale, themeMode]; 24 | } 25 | -------------------------------------------------------------------------------- /lib/data/repositories/repositories.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | abstract class ITaskRepo { 4 | Future> getAllTasks(); 5 | Future> saveTask(TaskModel task); 6 | Future> updateTask(int id, TaskModel task); 7 | Future> deleteTask(int id); 8 | Future> deleteAllTasks(); 9 | Future> isComplateTask(int id, TaskModel task,bool isComplated); 10 | } 11 | 12 | abstract class INoteRepo { 13 | Future> getAllNotes(); 14 | Future> saveNote(NoteModel note); 15 | Future> updateNote(int id, NoteModel note); 16 | Future> deleteNote(int id); 17 | Future> deleteAllNotes(); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/sources/data_source.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | abstract class ITaskDataSrc { 4 | Future> getAllTasks(); 5 | Future> saveTask(TaskModel task); 6 | Future> updateTask(int id, TaskModel task); 7 | Future> deleteTask(int id); 8 | Future> deleteAllTasks(); 9 | Future> isComplateTask(int id , TaskModel task,bool isComplated); 10 | } 11 | 12 | abstract class INoteDataSrc { 13 | Future> getAllNotes(); 14 | Future> saveNote(NoteModel note); 15 | Future> updateNote(int id, NoteModel note); 16 | Future> deleteNote(int id); 17 | Future> deleteAllNotes(); 18 | } 19 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.6.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.22" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /lib/resource/widgets/restart.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | class RestartWidget extends StatefulWidget { 3 | final Widget child; 4 | 5 | const RestartWidget({super.key, required this.child}); 6 | 7 | static void restartApp(BuildContext context) { 8 | final RestartWidgetState? state = context.findAncestorStateOfType(); 9 | state?.restartApp(); 10 | } 11 | 12 | @override 13 | RestartWidgetState createState() => RestartWidgetState(); 14 | } 15 | 16 | class RestartWidgetState extends State { 17 | Key key = UniqueKey(); 18 | 19 | void restartApp() { 20 | setState(() { 21 | key = UniqueKey(); 22 | }); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return KeyedSubtree( 28 | key: key, 29 | child: widget.child, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | on_time 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | # Android Studio will place build artifacts here 41 | /android/app/debug 42 | /android/app/profile 43 | /android/app/release 44 | /android/app/.cxx 45 | -------------------------------------------------------------------------------- /lib/resource/widgets/into_vc.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class IntoVC extends StatelessWidget { 4 | final String text; 5 | final String darkVC; 6 | final String lightVC; 7 | const IntoVC({ 8 | super.key, 9 | required this.text, 10 | required this.darkVC, 11 | required this.lightVC, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return BlocBuilder( 17 | builder: (context, state) { 18 | return Column( 19 | children: [ 20 | SvgPicture.asset( 21 | state.themeMode == ThemeMode.dark ? darkVC : lightVC, 22 | ), 23 | AppDimens.large.height, 24 | Text( 25 | text, 26 | style: AppTextStyles.introScreenDesTextStyle.apply( 27 | color: Theme.of(context).colorScheme.onSurface, 28 | ), 29 | ) 30 | ], 31 | ); 32 | }, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "on_time", 3 | "short_name": "on_time", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#161928", 7 | "theme_color": "#161928", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "ea121f8859e4b13e47a8f845e4586164519588bc" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 17 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 18 | - platform: web 19 | create_revision: ea121f8859e4b13e47a8f845e4586164519588bc 20 | base_revision: ea121f8859e4b13e47a8f845e4586164519588bc 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/screens/home/note/bloc/note_event.dart: -------------------------------------------------------------------------------- 1 | part of 'note_bloc.dart'; 2 | 3 | sealed class NoteEvent extends Equatable { 4 | const NoteEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class NoteInit extends NoteEvent {} 11 | 12 | class NoteLoadedEvent extends NoteEvent { 13 | final List allNoteList; 14 | const NoteLoadedEvent(this.allNoteList); 15 | @override 16 | List get props => [allNoteList]; 17 | } 18 | 19 | class DeleteNoteEvent extends NoteEvent { 20 | final int id; 21 | const DeleteNoteEvent(this.id); 22 | 23 | @override 24 | List get props => [id]; 25 | } 26 | 27 | class SaveNoteEvent extends NoteEvent { 28 | final NoteModel saveNote; 29 | const SaveNoteEvent(this.saveNote); 30 | 31 | @override 32 | List get props => [saveNote]; 33 | } 34 | 35 | class UpdateNoteEvent extends NoteEvent { 36 | final int id; 37 | final NoteModel updateNote; 38 | 39 | const UpdateNoteEvent(this.updateNote, this.id); 40 | 41 | @override 42 | List get props => [updateNote, id]; 43 | } 44 | 45 | final class DeleteAllNotesEvent extends NoteEvent {} 46 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | #dae5eb 19 | 20 | -------------------------------------------------------------------------------- /lib/resource/widgets/mobile_wrapper_in_desktop.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MobileWrapperInDesktop extends StatelessWidget { 4 | const MobileWrapperInDesktop({ 5 | super.key, 6 | required this.mobileWidth, 7 | required this.child, 8 | }); 9 | 10 | final double mobileWidth; 11 | final Widget child; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | color: Colors.grey, 17 | child: Center( 18 | child: Container( 19 | decoration: const BoxDecoration( 20 | color: Colors.white, 21 | ), 22 | width: mobileWidth, 23 | height: double.infinity, 24 | child: ClipRRect( 25 | child: MediaQuery( 26 | data: MediaQuery.of(context).copyWith( 27 | size: Size(mobileWidth, MediaQuery.of(context).size.height), 28 | padding: EdgeInsets.zero, 29 | viewInsets: EdgeInsets.zero, 30 | viewPadding: EdgeInsets.zero, 31 | ), 32 | child: child, 33 | ), 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /assets/images/svg/ic_notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/screens/home/note/bloc/note_state.dart: -------------------------------------------------------------------------------- 1 | part of 'note_bloc.dart'; 2 | 3 | sealed class NoteState extends Equatable { 4 | const NoteState(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | final class NoteLoadingState extends NoteState {} 11 | 12 | final class NoteLoadedState extends NoteState { 13 | final List noteList; 14 | const NoteLoadedState(this.noteList); 15 | @override 16 | List get props => [noteList]; 17 | } 18 | 19 | final class DeleteNoteState extends NoteState { 20 | final List noteList; 21 | const DeleteNoteState(this.noteList); 22 | 23 | @override 24 | List get props => [noteList]; 25 | } 26 | 27 | final class SaveNoteState extends NoteState { 28 | final List noteList; 29 | const SaveNoteState(this.noteList); 30 | 31 | @override 32 | List get props => [noteList]; 33 | } 34 | 35 | final class UpdateNoteState extends NoteState { 36 | final List noteList; 37 | const UpdateNoteState(this.noteList); 38 | 39 | @override 40 | List get props => [noteList]; 41 | } 42 | 43 | final class DeleteAllNotesState extends NoteState { 44 | final List noteList; 45 | const DeleteAllNotesState(this.noteList); 46 | 47 | @override 48 | List get props => [noteList]; 49 | } 50 | 51 | final class NoteError extends NoteState {} 52 | -------------------------------------------------------------------------------- /assets/images/svg/ic_search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/resource/widgets/empty_vc.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class EmptyVC extends StatelessWidget { 4 | const EmptyVC({ 5 | super.key, 6 | required this.text, 7 | }); 8 | 9 | final String text; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return BlocBuilder( 14 | builder: (context, state) { 15 | return Center( 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | SvgPicture.asset( 20 | state.themeMode == ThemeMode.dark 21 | ? Assets.images.svg.vcEmptyDark 22 | : Assets.images.svg.vcEmptyLight, 23 | height: 200, 24 | ), 25 | AppDimens.medium.height, 26 | Text( 27 | text, 28 | style: AppTextStyles.emptyTextStyle.copyWith( 29 | background: Paint() 30 | ..strokeJoin = StrokeJoin.round 31 | ..style = PaintingStyle.stroke 32 | ..color = Theme.of(context) 33 | .colorScheme 34 | .onSurface 35 | .withValues(alpha: 0.1) 36 | ..strokeWidth = 25), 37 | ) 38 | ], 39 | ), 40 | ); 41 | }, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/screens/home/task/bloc/task_event.dart: -------------------------------------------------------------------------------- 1 | part of 'task_bloc.dart'; 2 | 3 | sealed class TaskEvent extends Equatable { 4 | const TaskEvent(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | class TaskInit extends TaskEvent {} 11 | 12 | class TaskLoadedEvent extends TaskEvent { 13 | final List allTaskList; 14 | const TaskLoadedEvent(this.allTaskList); 15 | @override 16 | List get props => [allTaskList]; 17 | } 18 | 19 | class DeleteTaskEvent extends TaskEvent { 20 | final int id; 21 | const DeleteTaskEvent(this.id); 22 | 23 | @override 24 | List get props => [id]; 25 | } 26 | 27 | class SaveTaskEvent extends TaskEvent { 28 | final TaskModel saveTask; 29 | const SaveTaskEvent(this.saveTask); 30 | 31 | @override 32 | List get props => [saveTask]; 33 | } 34 | 35 | class UpdateTaskEvent extends TaskEvent { 36 | final int id; 37 | final TaskModel updateTask; 38 | 39 | const UpdateTaskEvent(this.updateTask, this.id); 40 | 41 | @override 42 | List get props => [updateTask, id]; 43 | } 44 | 45 | class IsComplatedTaskEvent extends TaskEvent { 46 | final int id; 47 | final TaskModel isComplatedTask; 48 | final bool isCompleted; 49 | 50 | const IsComplatedTaskEvent( 51 | this.isComplatedTask, 52 | this.id, 53 | this.isCompleted, 54 | ); 55 | 56 | @override 57 | List get props => [isComplatedTask, id,isCompleted]; 58 | } 59 | 60 | final class DeleteAllTasksEvent extends TaskEvent {} 61 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /lib/screens/home/task/bloc/task_state.dart: -------------------------------------------------------------------------------- 1 | part of 'task_bloc.dart'; 2 | 3 | sealed class TaskState extends Equatable { 4 | const TaskState(); 5 | 6 | @override 7 | List get props => []; 8 | } 9 | 10 | final class TaskLoadingState extends TaskState {} 11 | 12 | final class TaskLoadedState extends TaskState { 13 | final List taskList; 14 | const TaskLoadedState(this.taskList); 15 | @override 16 | List get props => [taskList]; 17 | } 18 | 19 | final class DeleteTaskState extends TaskState { 20 | final List taskTask; 21 | const DeleteTaskState(this.taskTask); 22 | 23 | @override 24 | List get props => [taskTask]; 25 | } 26 | 27 | final class SaveTaskState extends TaskState { 28 | final List taskList; 29 | const SaveTaskState(this.taskList); 30 | 31 | @override 32 | List get props => [taskList]; 33 | } 34 | 35 | final class UpdateTaskState extends TaskState { 36 | final List taskList; 37 | const UpdateTaskState(this.taskList); 38 | 39 | @override 40 | List get props => [taskList]; 41 | } 42 | 43 | final class DeleteAllTasksState extends TaskState { 44 | final List taskList; 45 | const DeleteAllTasksState(this.taskList); 46 | 47 | @override 48 | List get props => [taskList]; 49 | } 50 | 51 | final class IsComplatedTaskState extends TaskState { 52 | final List taskList; 53 | const IsComplatedTaskState(this.taskList); 54 | 55 | @override 56 | List get props => [taskList]; 57 | } 58 | 59 | final class TaskError extends TaskState {} 60 | -------------------------------------------------------------------------------- /lib/screens/home/note/notes.dart: -------------------------------------------------------------------------------- 1 | part of '../../../../../index.dart'; 2 | 3 | Box noteBox = Hive.box(noteBoxName); 4 | 5 | class Notes extends StatelessWidget { 6 | const Notes({ 7 | super.key, 8 | }); 9 | @override 10 | Widget build(BuildContext context) { 11 | return BlocConsumer( 12 | listener: (context, state) { 13 | if (state is DeleteNoteState) { 14 | ScaffoldMessenger.of(context).showSnackBar( 15 | SnackBar( 16 | duration: const Duration(seconds: 1), 17 | content: Text( 18 | S.current.deleteNoteSnackBar, 19 | textDirection: TextDirection.rtl, 20 | ), 21 | ), 22 | ); 23 | } 24 | }, 25 | builder: (context, state) { 26 | if (state is NoteLoadedState) { 27 | return const NoteList(); 28 | } else if (state is DeleteNoteState || 29 | state is UpdateNoteState || 30 | state is SaveNoteState || 31 | state is DeleteAllNotesState) { 32 | return const NoteList(); 33 | } else if (state is NoteLoadingState) { 34 | return const LinearProgressIndicator(); 35 | } else if (state is NoteError) { 36 | return Text(S.current.error); 37 | } else { 38 | return ElevatedButton( 39 | onPressed: () { 40 | BlocProvider.of(context).add(TaskInit()); 41 | }, 42 | child: Text(S.current.tryAgain), 43 | ); 44 | } 45 | }, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/data/models/note_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'note_model.g.dart'; 4 | 5 | @HiveType(typeId: 2) 6 | class NoteModel extends HiveObject { 7 | @HiveField(0) 8 | int id; 9 | 10 | @HiveField(1) 11 | final String title; 12 | 13 | @HiveField(2) 14 | final String description; 15 | 16 | @HiveField(3) 17 | final NoteColor color; 18 | 19 | @HiveField(4) 20 | final String dateTime; 21 | 22 | NoteModel({ 23 | required this.id, 24 | required this.title, 25 | required this.description, 26 | required this.color, 27 | required this.dateTime, 28 | }); 29 | 30 | factory NoteModel.fromJson(Map json) { 31 | return NoteModel( 32 | id: json['id'], 33 | title: json['title'], 34 | description: json['description'], 35 | color: json['color'], 36 | dateTime: json['dateTime'], 37 | ); 38 | } 39 | 40 | Map toJson() { 41 | return { 42 | 'id': id, 43 | 'title': title, 44 | 'description': description, 45 | 'color': color, 46 | 'dateTime': dateTime, 47 | }; 48 | } 49 | } 50 | 51 | @HiveType(typeId: 3) 52 | enum NoteColor { 53 | @HiveField(0) 54 | one(0xffe6ee9b), 55 | 56 | @HiveField(1) 57 | two(0xff80deea), 58 | 59 | @HiveField(2) 60 | three(0xffcf93d9), 61 | 62 | @HiveField(3) 63 | four(0xffffab91), 64 | 65 | @HiveField(4) 66 | five(0xffffcc80), 67 | 68 | @HiveField(5) 69 | six(0xffC4BBF0), 70 | 71 | @HiveField(6) 72 | seven(0xffFFD717), 73 | 74 | @HiveField(7) 75 | eight(0xffFFC7C7), 76 | 77 | @HiveField(8) 78 | nine(0xffCCF6C8); 79 | 80 | final int code; 81 | 82 | const NoteColor(this.code); 83 | } 84 | -------------------------------------------------------------------------------- /lib/screens/splash_screen.dart: -------------------------------------------------------------------------------- 1 | part of '../index.dart'; 2 | 3 | class SplashScreen extends StatefulWidget { 4 | const SplashScreen({super.key}); 5 | 6 | @override 7 | State createState() => _SplashScreenState(); 8 | } 9 | 10 | class _SplashScreenState extends State { 11 | late bool _isFirstRun; 12 | 13 | void _checkFirstRun() async { 14 | bool ifr = await IsFirstRun.isFirstRun(); 15 | setState(() { 16 | _isFirstRun = ifr; 17 | }); 18 | } 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _checkFirstRun(); 24 | WidgetsBinding.instance.addPostFrameCallback( 25 | (_) { 26 | Future.delayed( 27 | const Duration(seconds: 2), 28 | () { 29 | if (mounted) { 30 | Navigator.pushReplacement( 31 | context, 32 | MaterialPageRoute( 33 | builder: (_) => 34 | _isFirstRun ? const IntroScreen() : const HomePage(), 35 | ), 36 | ); 37 | } 38 | }, 39 | ); 40 | }, 41 | ); 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return SafeArea( 47 | child: Scaffold( 48 | body: Center( 49 | child: Text( 50 | S.current.name, 51 | style: AppTextStyles.splashScreenTextStyle.apply( 52 | color: Theme.of(context).colorScheme.onSurface, 53 | ), 54 | ).animate().fadeIn( 55 | duration: const Duration( 56 | milliseconds: 1000, 57 | ), 58 | ), 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/data/repositories/local_repo.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | final taskLocalRepo = TaskLocalRepo(TaskLocalDataSrc()); 4 | final noteLocalRepo = NoteLocalRepo(NoteLocalDataSrc()); 5 | 6 | class TaskLocalRepo implements ITaskRepo { 7 | final ITaskDataSrc _iTaskDataSrc; 8 | 9 | TaskLocalRepo(this._iTaskDataSrc); 10 | 11 | @override 12 | Future> deleteAllTasks() => _iTaskDataSrc.deleteAllTasks(); 13 | 14 | @override 15 | Future> deleteTask(int id) => _iTaskDataSrc.deleteTask(id); 16 | 17 | @override 18 | Future> getAllTasks() => _iTaskDataSrc.getAllTasks(); 19 | 20 | @override 21 | Future> saveTask(TaskModel task) => 22 | _iTaskDataSrc.saveTask(task); 23 | 24 | @override 25 | Future> updateTask(int id, TaskModel task) => 26 | _iTaskDataSrc.updateTask(id, task); 27 | 28 | @override 29 | Future> isComplateTask(int id, TaskModel task, bool isCompleted) => _iTaskDataSrc.isComplateTask(id, task,isCompleted); 30 | } 31 | 32 | class NoteLocalRepo implements INoteRepo { 33 | final INoteDataSrc _iNoteDataSrc; 34 | 35 | NoteLocalRepo(this._iNoteDataSrc); 36 | @override 37 | Future> deleteAllNotes() => _iNoteDataSrc.deleteAllNotes(); 38 | 39 | @override 40 | Future> deleteNote(int id) => _iNoteDataSrc.deleteNote(id); 41 | 42 | @override 43 | Future> getAllNotes() => _iNoteDataSrc.getAllNotes(); 44 | 45 | @override 46 | Future> saveNote(NoteModel note) => 47 | _iNoteDataSrc.saveNote(note); 48 | 49 | @override 50 | Future> updateNote(int id, NoteModel note) => 51 | _iNoteDataSrc.updateNote(id, note); 52 | } 53 | -------------------------------------------------------------------------------- /lib/resource/widgets/dialog.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | customeDialogee( 4 | BuildContext context, { 5 | required final String content, 6 | required final String primaryBtn, 7 | required final Function() onTapPrimaryBtn, 8 | required final Function() onTapSecendaryBtn, 9 | required final String secendaryBtn, 10 | final GestureTapCallback? onTap, 11 | final List? actions, 12 | }) { 13 | return showDialog( 14 | context: context, 15 | builder: (context) { 16 | return AlertDialog( 17 | actionsAlignment: MainAxisAlignment.center, 18 | scrollable: true, 19 | actions: [ 20 | TextButton( 21 | style: ButtonStyle( 22 | backgroundColor: 23 | WidgetStatePropertyAll(Theme.of(context).colorScheme.error), 24 | ), 25 | onPressed: onTapPrimaryBtn, 26 | child: Text( 27 | primaryBtn, 28 | style: TextStyle(color: Theme.of(context).colorScheme.surface), 29 | ), 30 | ), 31 | TextButton( 32 | style: ButtonStyle( 33 | backgroundColor: WidgetStatePropertyAll( 34 | Theme.of(context).colorScheme.onSurface), 35 | ), 36 | onPressed: onTapSecendaryBtn, 37 | child: Text( 38 | secendaryBtn, 39 | style: TextStyle(color: Theme.of(context).colorScheme.surface), 40 | ), 41 | ), 42 | ], 43 | content: FittedBox( 44 | child: Column( 45 | children: [ 46 | const SizedBox(height: 15), 47 | Text(content), 48 | ], 49 | ), 50 | ), 51 | ); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /lib/resource/utils/notification_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 2 | import 'package:on_time/index.dart'; 3 | import 'package:timezone/timezone.dart' as tz; 4 | import 'package:timezone/data/latest.dart' as tz; 5 | 6 | class NotificationHelper { 7 | static final FlutterLocalNotificationsPlugin 8 | _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); 9 | 10 | static void initialize() { 11 | final initializationSettingsAndroid = 12 | // ignore: prefer_const_constructors 13 | AndroidInitializationSettings('@mipmap/ic_launcher'); 14 | final initializationSettings = 15 | InitializationSettings(android: initializationSettingsAndroid); 16 | _flutterLocalNotificationsPlugin.initialize(initializationSettings); 17 | tz.initializeTimeZones(); 18 | } 19 | 20 | static Future scheduleNotification( 21 | int id, String title, String body, DateTime scheduledTime) async { 22 | final scheduledDate = 23 | tz.TZDateTime.from(scheduledTime, tz.getLocation('Asia/Tehran')) 24 | .subtract(const Duration(minutes: 15)); 25 | await _flutterLocalNotificationsPlugin.zonedSchedule( 26 | id, 27 | title, 28 | body, 29 | scheduledDate, 30 | const NotificationDetails( 31 | android: AndroidNotificationDetails( 32 | 'on_time', 33 | 'Task Channel', 34 | colorized: true, 35 | color: AppColors.appOnPrimaryDark, 36 | importance: Importance.max, 37 | priority: Priority.max, 38 | showWhen: true, 39 | ), 40 | ), 41 | androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, 42 | matchDateTimeComponents: DateTimeComponents.dateAndTime, 43 | ); 44 | } 45 | 46 | static Future cancelNotification(int id) async { 47 | await _flutterLocalNotificationsPlugin.cancel(id); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/resource/themes/themes.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class AppTheme { 4 | static ThemeData darkTheme(BuildContext context) { 5 | var lang = BlocProvider.of(context).state.locale.languageCode; 6 | return ThemeData( 7 | fontFamily: lang == 'fa' ? 'YekanBakhNumFa' : 'Ubuntu', 8 | colorScheme: const ColorScheme( 9 | brightness: Brightness.dark, 10 | primary: Color(0xffDAE5EB), 11 | onPrimary: Color(0xff161928), 12 | secondary: Color(0xff161928), 13 | onSecondary: Color(0xffDAE5EB), 14 | error: Color(0xffFF636C), 15 | onError: Color(0xffDAE5EB), 16 | surface: Color(0xff161928), 17 | onSurface: Color(0xffDAE5EB), 18 | ), 19 | dialogTheme: DialogTheme( 20 | shape: RoundedRectangleBorder( 21 | borderRadius: BorderRadius.circular(20), 22 | side: const BorderSide( 23 | color: Color(0xffDAE5EB), 24 | ), 25 | ), 26 | ), 27 | ); 28 | } 29 | 30 | static ThemeData lightTheme(BuildContext context) { 31 | var lang = BlocProvider.of(context).state.locale.languageCode; 32 | return ThemeData( 33 | fontFamily: lang == 'fa' ? 'YekanBakhNumFa': 'Ubuntu', 34 | colorScheme: const ColorScheme( 35 | brightness: Brightness.light, 36 | primary: Color(0xff161928), 37 | onPrimary: Color(0xffDAE5EB), 38 | secondary: Color(0xffDAE5EB), 39 | onSecondary: Color(0xff161928), 40 | error: Color(0xffFF636C), 41 | onError: Color(0xffDAE5EB), 42 | surface: Color(0xffDAE5EB), 43 | onSurface: Color(0xff161928), 44 | ), 45 | dialogTheme: DialogTheme( 46 | shape: RoundedRectangleBorder( 47 | borderRadius: BorderRadius.circular(20), 48 | side: const BorderSide( 49 | color: Color(0xff161928), 50 | )), 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: on_time 2 | description: "A planner and note application" 3 | 4 | publish_to: 'none' 5 | 6 | version: 0.0.1+1 7 | 8 | environment: 9 | sdk: '>=3.4.1 <4.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_localizations: 15 | sdk: flutter 16 | cupertino_icons: ^1.0.6 17 | flutter_svg: ^2.0.10+1 18 | zoom_tap_animation: ^1.1.0 19 | intl: ^0.19.0 20 | bloc: ^9.0.0 21 | flutter_bloc: ^9.1.1 22 | equatable: ^2.0.5 23 | shared_preferences: ^2.2.3 24 | hive: ^2.2.3 25 | hive_flutter: ^1.1.0 26 | hive_generator: ^2.0.1 27 | path_provider: ^2.1.3 28 | persian_datetime_picker: ^3.1.0 29 | flutter_staggered_animations: ^1.1.1 30 | persian_horizontal_date_picker: ^2.0.0 31 | uuid: ^4.4.2 32 | auto_size_text: ^3.0.0 33 | flutter_staggered_grid_view: ^0.7.0 34 | flutter_animate: ^4.5.0 35 | flutter_local_notifications: ^19.1.0 36 | device_preview: ^1.2.0 37 | timezone: ^0.10.1 38 | 39 | dev_dependencies: 40 | flutter_test: 41 | sdk: flutter 42 | flutter_lints: ^5.0.0 43 | build_runner: ^2.4.11 44 | flutter_launcher_icons: "^0.14.3" 45 | 46 | flutter_launcher_icons: 47 | image_path: "assets/images/icon/icon.png" 48 | web: 49 | generate: true 50 | image_path: "assets/images/icon/icon.png" 51 | background_color: "#DAE5EB" 52 | theme_color: "#DAE5EB" 53 | 54 | flutter_intl: 55 | enabled: true 56 | 57 | flutter: 58 | uses-material-design: true 59 | generate: true 60 | 61 | 62 | 63 | 64 | assets: 65 | - assets/images/svg/ 66 | - assets/images/icon/ 67 | fonts: 68 | - family: Ubuntu 69 | fonts: 70 | - asset: assets/fonts/ubuntu_regular.ttf 71 | - family: YekanBakh 72 | fonts: 73 | - asset: assets/fonts/yekan_bakh_regular.ttf 74 | - family: YekanBakhNumFa 75 | fonts: 76 | - asset: assets/fonts/yekan_bakh_regular_numfa.ttf 77 | - family: YekanBakhNoEn 78 | fonts: 79 | - asset: assets/fonts/yekan_bakh_no_en_regular.ttf 80 | 81 | 82 | -------------------------------------------------------------------------------- /lib/resource/utils/utils.dart: -------------------------------------------------------------------------------- 1 | part of ' ../../../../index.dart'; 2 | 3 | Iterable taskMapTOlist(Box box) { 4 | return box.values.map( 5 | (t) => TaskModel( 6 | id: t.id, 7 | title: t.title, 8 | note: t.note, 9 | isCompleted: t.isCompleted, 10 | dateTime: t.dateTime, 11 | color: t.color, 12 | addToNote: t.addToNote 13 | ), 14 | ); 15 | } 16 | 17 | Iterable noteMapToList(Box box) { 18 | return box.values.map( 19 | (n) => NoteModel( 20 | id: n.id, 21 | title: n.title, 22 | description: n.description, 23 | color: n.color, 24 | dateTime: n.dateTime, 25 | ), 26 | ); 27 | } 28 | 29 | class IsFirstRun { 30 | static const _firstRunSettingsKey = 'is_first_run'; 31 | static const _firstCallSettingsKey = 'is_first_call'; 32 | 33 | static bool? _isFirstRun; 34 | 35 | static Future isFirstCall() async { 36 | SharedPreferences prefs = await SharedPreferences.getInstance(); 37 | bool firstCall; 38 | try { 39 | firstCall = prefs.getBool(_firstCallSettingsKey) ?? true; 40 | } on Exception { 41 | firstCall = true; 42 | } 43 | await prefs.setBool(_firstCallSettingsKey, false); 44 | return firstCall; 45 | } 46 | 47 | static Future isFirstRun() async { 48 | if (_isFirstRun != null) { 49 | return _isFirstRun!; 50 | } else { 51 | SharedPreferences prefs = await SharedPreferences.getInstance(); 52 | bool isFirstRun; 53 | try { 54 | isFirstRun = prefs.getBool(_firstRunSettingsKey) ?? true; 55 | } on Exception { 56 | isFirstRun = true; 57 | } 58 | await prefs.setBool(_firstRunSettingsKey, false); 59 | _isFirstRun ??= isFirstRun; 60 | return isFirstRun; 61 | } 62 | } 63 | 64 | static Future reset() async { 65 | SharedPreferences prefs = await SharedPreferences.getInstance(); 66 | prefs.setBool(_firstRunSettingsKey, true); 67 | prefs.setBool(_firstCallSettingsKey, true); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/data/models/task_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | 3 | part 'task_model.g.dart'; 4 | 5 | @HiveType(typeId: 0) 6 | class TaskModel extends HiveObject { 7 | @HiveField(0) 8 | int id; 9 | 10 | @HiveField(1) 11 | final String title; 12 | 13 | @HiveField(2) 14 | final String note; 15 | 16 | @HiveField(3) 17 | bool isCompleted; 18 | 19 | @HiveField(4) 20 | final DateTime dateTime; 21 | 22 | @HiveField(5) 23 | final TaskColor color; 24 | 25 | @HiveField(6) 26 | final bool addToNote; 27 | 28 | TaskModel( 29 | {required this.id, 30 | required this.title, 31 | required this.note, 32 | required this.isCompleted, 33 | required this.dateTime, 34 | required this.color, 35 | required this.addToNote}); 36 | 37 | factory TaskModel.fromJson(Map json) { 38 | return TaskModel( 39 | id: json['id'], 40 | title: json['title'], 41 | note: json['note'], 42 | isCompleted: json['isCompleted'], 43 | dateTime: json['date'], 44 | color: json['color'], 45 | addToNote: json['place']); 46 | } 47 | 48 | Map toJson() { 49 | final Map data = {}; 50 | data['id'] = id; 51 | data['title'] = title; 52 | data['date'] = dateTime; 53 | data['note'] = note; 54 | data['isCompleted'] = isCompleted; 55 | data['color'] = color; 56 | data['place'] = addToNote; 57 | return data; 58 | } 59 | } 60 | 61 | @HiveType(typeId: 1) 62 | enum TaskColor { 63 | @HiveField(0) 64 | one(0xffe6ee9b), 65 | 66 | @HiveField(1) 67 | two(0xff80deea), 68 | 69 | @HiveField(2) 70 | three(0xffcf93d9), 71 | 72 | @HiveField(3) 73 | four(0xffffab91), 74 | 75 | @HiveField(4) 76 | five(0xffffcc80), 77 | 78 | @HiveField(5) 79 | six(0xffC4BBF0), 80 | 81 | @HiveField(6) 82 | seven(0xffFFD717), 83 | 84 | @HiveField(7) 85 | eight(0xffFFC7C7), 86 | 87 | @HiveField(8) 88 | nine(0xffCCF6C8); 89 | 90 | final int code; 91 | 92 | const TaskColor(this.code); 93 | } 94 | -------------------------------------------------------------------------------- /lib/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "en", 3 | "name": "On . Time", 4 | "language": "Language", 5 | "settings": "Settings", 6 | "theme": "Theme", 7 | "darkMode": "Dark Mode", 8 | "planning": "Planning", 9 | "note": "Note", 10 | "well": "Ok", 11 | "letsGo": "Let''s Go...", 12 | "goal": "Set a goal !", 13 | "plan": "Plan for tomorrow!", 14 | "target": "Touch it!", 15 | "done": "Done", 16 | "tasks": "Tasks", 17 | "emptyNote": "You have not added a note yet", 18 | "emptyPlan": "You have no plans today", 19 | "newTask": "New Task", 20 | "taskTitle": "Title", 21 | "taskTitleHint": "Write your title here", 22 | "taskNoteTitle": "Note", 23 | "taskNoteHint": "Write your note here", 24 | "taskPlaceTitle": "Place", 25 | "taskPlaceHint": "What is the name of the place you want to go?", 26 | "time": "Time", 27 | "addTaskSnackBar": "Task added successfully", 28 | "deleteTaskSnackBar": "The task successfully deleted", 29 | "emptyTextFieldError": "You must fill in the title", 30 | "save": "Save", 31 | "discard": "Discard", 32 | "editTask": "Edit Task", 33 | "editTaskSnackBar": "The task edited successfully", 34 | "deleteTaskDialogContent": "Are you sure you want to delete this task?", 35 | "deleteDialogPrimaryBtn": "Delete it", 36 | "deleteDialogSecendaryBtn": "Cancel", 37 | "noteTextFieldTitleHint": "Title", 38 | "noteTextFieldNoteHint": "Write your note here...", 39 | "addNoteSnackBar": "Note added successfully", 40 | "editNoteSnackBar": "Note edited successfully", 41 | "createAt": "Created at :", 42 | "deleteNoteDialogContent": "Are you sure you want to delete this note?", 43 | "deleteNoteSnackBar": "Note deleted successfully", 44 | "error": "ERROR", 45 | "tryAgain": "Try Again", 46 | "taskCardTime": "Time:", 47 | "taskCardPlace": "Place: ", 48 | "taskCardNote": "Note: ", 49 | "appVersion": "0.0.1+1", 50 | "author": "Made with ☕ by Umut", 51 | "addToNote": "Add the task note to notes!", 52 | "editNote": "Edit the note!" 53 | } -------------------------------------------------------------------------------- /lib/l10n/intl_fa.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "fa", 3 | "name": "آن . تایم", 4 | "language": "زبان", 5 | "settings": "تنظیمات", 6 | "theme": "تم", 7 | "darkMode": "حالت تاریک", 8 | "planning": "برنامه ریزی", 9 | "note": "یادداشت", 10 | "well": "!خب", 11 | "letsGo": "!...بزن بریم", 12 | "goal": "! هدف گذاری کن", 13 | "plan": "!برنامه ریزی کن", 14 | "target": "!برس بهش", 15 | "done": "انجام شد", 16 | "tasks": "برنامه‌ها", 17 | "emptyNote":"هنوز یادداشتی اضافه نکردی", 18 | "emptyPlan":"امروز برنامه‌ای نداری", 19 | "newTask":"برنامه جدید", 20 | "taskTitle":"عنوان", 21 | "taskTitleHint": "عنوان خود را اینجا وارد کنید", 22 | "taskNoteTitle": "یادداشت", 23 | "taskNoteHint": "یادداشت خود را در اینجا وارد کنید", 24 | "taskPlaceTitle": "مکان", 25 | "taskPlaceHint": "اسم جایی که میخای بری چیه؟", 26 | "time":"ساعت", 27 | "addTaskSnackBar":"تسک با موفقیت اضافه شد", 28 | "deleteTaskSnackBar":"تسک با موفقیت حذف شد", 29 | "emptyTextFieldError":"حداقل عنوان رو پر کن", 30 | "save":"ثبت", 31 | "discard":"بیخیال", 32 | "editTask":"ویرایش برنامه", 33 | "editTaskSnackBar": "تسک با موفقیت ویرایش شد", 34 | "deleteTaskDialogContent":"مطمئنی میخوای این تسک رو حذف کنی؟", 35 | "deleteDialogPrimaryBtn":"حذفش کن", 36 | "deleteDialogSecendaryBtn":"دستم خورد", 37 | "noteTextFieldTitleHint":"عنوان", 38 | "noteTextFieldNoteHint":"یادداشت خودتو اینجا بنویس...", 39 | "addNoteSnackBar":"یادداشت با موفقیت اضافه شد...", 40 | "editNoteSnackBar":"یادداشت با موفقیت ویرایش شد...", 41 | "createAt":"ایجاد شده در :", 42 | "deleteNoteDialogContent":"مطمئنی میخوای این یادداشت رو حذف کنی؟", 43 | "deleteNoteSnackBar":"یادداشت با موفقیت حذف شد", 44 | "error":"خطا", 45 | "tryAgain":"تلاش مجدد" , 46 | "taskCardTime":"ساعت:", 47 | "taskCardPlace":"مکان:", 48 | "taskCardNote":"یادداشت:", 49 | "appVersion":"0.0.1+1", 50 | "author":"ساخته شده با ☕ توسط امید", 51 | "addToNote":"یادداشت تسک رو به یادداشت ها اضافه کن!", 52 | "editNote":"یادداشت رو هم ویرایش کن!" 53 | 54 | } -------------------------------------------------------------------------------- /lib/screens/home/note/bloc/note_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:on_time/data/models/note_model.dart'; 4 | import 'package:on_time/index.dart'; 5 | 6 | part 'note_event.dart'; 7 | part 'note_state.dart'; 8 | 9 | class NoteBloc extends Bloc { 10 | final NoteLocalRepo noteRepo; 11 | NoteBloc(this.noteRepo) : super(NoteLoadingState()) { 12 | on((event, emit) async { 13 | if (event is NoteInit) { 14 | try { 15 | emit(NoteLoadingState()); 16 | final noteList = await noteRepo.getAllNotes(); 17 | emit(NoteLoadedState(noteList)); 18 | } catch (e) { 19 | emit(NoteError()); 20 | } 21 | } else if (event is DeleteNoteEvent) { 22 | try { 23 | emit(NoteLoadingState()); 24 | await noteRepo.deleteNote(event.id).then( 25 | (val) => emit( 26 | DeleteNoteState(val), 27 | ), 28 | ); 29 | } catch (e) { 30 | emit(NoteError()); 31 | } 32 | } else if (event is DeleteAllNotesEvent) { 33 | emit(NoteLoadingState()); 34 | await noteRepo.deleteAllNotes().then( 35 | (val) => emit( 36 | DeleteAllNotesState(val), 37 | ), 38 | ); 39 | } else if (event is SaveNoteEvent) { 40 | emit(NoteLoadingState()); 41 | await noteRepo 42 | .saveNote( 43 | event.saveNote, 44 | ) 45 | .then( 46 | (val) => emit( 47 | SaveNoteState(val), 48 | ), 49 | ); 50 | } else if (event is UpdateNoteEvent) { 51 | emit(NoteLoadingState()); 52 | await noteRepo 53 | .updateNote( 54 | event.id, 55 | event.updateNote, 56 | ) 57 | .then( 58 | (val) => emit( 59 | UpdateNoteState(val), 60 | ), 61 | ); 62 | } 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/resource/widgets/input_feild.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class InputField extends StatelessWidget { 4 | final String? title; 5 | final TextEditingController? controller; 6 | final String hint; 7 | final Widget? widget; 8 | final Function()? onTap; 9 | final bool readOnly; 10 | 11 | final Widget? prefixIcon; 12 | final Widget? suffixIcon; 13 | final int? maxLines; 14 | final int? minLines; 15 | 16 | const InputField({ 17 | super.key, 18 | this.title, 19 | this.controller, 20 | required this.hint, 21 | this.widget, 22 | this.onTap, 23 | this.readOnly = false, 24 | this.prefixIcon, 25 | this.suffixIcon, this.maxLines, this.minLines, 26 | }); 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Container( 31 | margin: const EdgeInsets.only(top: 16.0), 32 | child: TextFormField( 33 | textAlignVertical: TextAlignVertical.top, 34 | maxLines: maxLines, 35 | minLines: minLines, 36 | style: const TextStyle(color: AppColors.appPrimaryDark), 37 | autofocus: false, 38 | 39 | onTap: onTap, 40 | readOnly: readOnly, 41 | controller: controller, 42 | decoration: InputDecoration( 43 | counterText: '', 44 | enabledBorder: const OutlineInputBorder( 45 | borderSide: BorderSide(color: AppColors.appPrimaryDark)), 46 | disabledBorder: const OutlineInputBorder( 47 | borderSide: BorderSide(color: AppColors.appPrimaryDark)), 48 | focusedBorder: const OutlineInputBorder( 49 | borderSide: BorderSide(color: AppColors.appPrimaryDark)), 50 | hintStyle: const TextStyle(color: AppColors.appPrimaryDark), 51 | labelStyle: const TextStyle(color: AppColors.appPrimaryDark), 52 | labelText: title, 53 | alignLabelWithHint: true, 54 | 55 | hintText: hint, 56 | border: const OutlineInputBorder( 57 | borderSide: BorderSide(color: AppColors.appPrimaryDark)), 58 | prefixIcon: prefixIcon, 59 | suffixIcon: suffixIcon), 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/screens/my_app.dart: -------------------------------------------------------------------------------- 1 | part of '../index.dart'; 2 | 3 | 4 | final GlobalKey navigatorKey = GlobalKey(); 5 | 6 | class MyApp extends StatelessWidget { 7 | const MyApp({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return BlocBuilder( 12 | builder: (context, state) { 13 | return MaterialApp( 14 | title: 'On Time', 15 | navigatorKey: navigatorKey, 16 | debugShowCheckedModeBanner: false, 17 | theme: AppTheme.lightTheme(context), 18 | darkTheme: AppTheme.darkTheme(context), 19 | builder: (_, child) { 20 | if (kIsWeb) { 21 | final mediaQuery = MediaQuery.of(context); 22 | final isDesktop = mediaQuery.size.width > 768; 23 | if (isDesktop) { 24 | const double mobileWidth = 480.0; 25 | return MobileWrapperInDesktop( 26 | mobileWidth: mobileWidth, 27 | child: MediaQuery( 28 | data: MediaQuery.of(context).copyWith( 29 | textScaler: MediaQuery.of(context) 30 | .textScaler 31 | .clamp(minScaleFactor: 0.8, maxScaleFactor: 0.8), 32 | ), 33 | child: child!, 34 | ), 35 | ); 36 | } 37 | } 38 | return MediaQuery( 39 | data: MediaQuery.of(context).copyWith( 40 | textScaler: MediaQuery.of(context) 41 | .textScaler 42 | .clamp(minScaleFactor: 0.8, maxScaleFactor: 0.8), 43 | ), 44 | child: child!, 45 | ); 46 | }, 47 | themeMode: state.themeMode, 48 | localizationsDelegates: const [ 49 | S.delegate, 50 | GlobalMaterialLocalizations.delegate, 51 | GlobalWidgetsLocalizations.delegate, 52 | GlobalCupertinoLocalizations.delegate, 53 | ], 54 | supportedLocales: S.delegate.supportedLocales, 55 | locale: state.locale, 56 | home: const SplashScreen(), 57 | ); 58 | }, 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/resource/widgets/custom_app_bar.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class CustomAppBar extends StatelessWidget implements PreferredSize { 4 | @override 5 | final Widget child; 6 | 7 | final double appBarHeight; 8 | 9 | const CustomAppBar( 10 | {super.key, required this.child, required this.appBarHeight}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return PreferredSize( 15 | preferredSize: preferredSize, 16 | child: SizedBox( 17 | height: 100, 18 | child: Row( 19 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 20 | crossAxisAlignment: CrossAxisAlignment.center, 21 | children: [ 22 | Padding( 23 | padding: const EdgeInsets.only(right: AppDimens.medium,left: AppDimens.medium), 24 | child: child, 25 | ), 26 | Padding( 27 | padding: const EdgeInsets.only(right: AppDimens.small), 28 | child: AppIcon( 29 | icon: SvgPicture.asset( 30 | Assets.images.svg.icSetting, 31 | colorFilter: ColorFilter.mode( 32 | Theme.of(context).colorScheme.onSurface, 33 | BlendMode.srcIn, 34 | ), 35 | ), 36 | onTap: () { 37 | Navigator.push( 38 | context, 39 | CupertinoPageRoute( 40 | builder: (context) => const SettingsPage())); 41 | }, 42 | ), 43 | ), 44 | 45 | ], 46 | ), 47 | ), 48 | ); 49 | } 50 | 51 | @override 52 | Size get preferredSize => Size.fromHeight(appBarHeight); 53 | } 54 | 55 | class AppIcon extends StatelessWidget { 56 | final void Function() onTap; 57 | final Widget icon; 58 | const AppIcon({ 59 | super.key, 60 | required this.onTap, 61 | required this.icon, 62 | }); 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return ZoomTapAnimation( 67 | onTap: onTap, 68 | child: Padding( 69 | padding: const EdgeInsets.all(AppDimens.small), 70 | child: SizedBox( 71 | height: 30, 72 | width: 30, 73 | child: icon, 74 | ), 75 | ), 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/screens/settings/bloc/settings_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:on_time/index.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | 7 | part 'settings_event.dart'; 8 | part 'settings_state.dart'; 9 | 10 | class SettingsBloc extends Bloc { 11 | SettingsBloc() : super(SettingsState.initial()) { 12 | on(_getLanguage); 13 | on(_changeLanguage); 14 | on(_onThemeChanged); 15 | on(_onLoadTheme); 16 | add(LoadSavedLocalizations()); 17 | add(LoadTheme()); 18 | } 19 | 20 | void _changeLanguage( 21 | LoadLocalizations event, Emitter emit) async { 22 | if (event.locale == state.locale) return; 23 | await _saveLocale(event.locale); 24 | emit(state.copyWith(locale: event.locale)); 25 | RestartWidget.restartApp(navigatorKey.currentContext!); 26 | } 27 | 28 | Future _getLanguage( 29 | LoadSavedLocalizations event, Emitter emit) async { 30 | Locale savedLocale = await _getLocale(); 31 | emit(state.copyWith(locale: savedLocale)); 32 | } 33 | 34 | Future _onThemeChanged( 35 | ThemeChanged event, Emitter emit) async { 36 | final prefs = await SharedPreferences.getInstance(); 37 | await prefs.setBool('isDark', event.isDark); 38 | emit(state.copyWith( 39 | themeMode: event.isDark ? ThemeMode.dark : ThemeMode.light)); 40 | RestartWidget.restartApp(navigatorKey.currentContext!); 41 | } 42 | 43 | Future _onLoadTheme( 44 | LoadTheme event, Emitter emit) async { 45 | final prefs = await SharedPreferences.getInstance(); 46 | final isDark = prefs.getBool('isDark') ?? false; 47 | emit(state.copyWith(themeMode: isDark ? ThemeMode.dark : ThemeMode.light)); 48 | } 49 | 50 | Future _saveLocale(Locale locale) async { 51 | SharedPreferences prefs = await SharedPreferences.getInstance(); 52 | prefs.setString('language', locale.languageCode); 53 | } 54 | 55 | Future _getLocale() async { 56 | SharedPreferences prefs = await SharedPreferences.getInstance(); 57 | String languageCode = prefs.getString('language') ?? 'fa'; 58 | return Locale(languageCode); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/resource/components/text_style.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class AppTextStyles { 4 | AppTextStyles._(); 5 | 6 | static const TextStyle appNameTextStyle = TextStyle( 7 | fontSize: 36, 8 | fontWeight: FontWeight.bold, 9 | ); 10 | static const TextStyle chipTextStyle = TextStyle( 11 | fontSize: 14, 12 | fontWeight: FontWeight.bold, 13 | ); 14 | static const TextStyle bodyTitleTextStyle = TextStyle( 15 | fontSize: 26, 16 | fontWeight: FontWeight.bold, 17 | ); 18 | static const TextStyle taskTitleTextStyle = TextStyle( 19 | fontSize: 20, 20 | fontWeight: FontWeight.bold, 21 | ); 22 | static const TextStyle taskInfoTitleTextStyle = TextStyle( 23 | fontSize: 18, 24 | fontWeight: FontWeight.bold, 25 | ); 26 | static const TextStyle taskInfoTextStyle = TextStyle( 27 | fontSize: 16, 28 | ); 29 | static const TextStyle taskDateTimeTextStyle = TextStyle( 30 | fontSize: 14, 31 | fontWeight: FontWeight.bold, 32 | ); 33 | static const TextStyle appBarTitle = TextStyle( 34 | fontSize: 24, 35 | fontWeight: FontWeight.bold, 36 | ); 37 | static const TextStyle dateTimeTextStyle = TextStyle( 38 | fontSize: 24, 39 | ); 40 | 41 | static const TextStyle appTopText = TextStyle( 42 | fontSize: 26, 43 | fontWeight: FontWeight.bold, 44 | ); 45 | 46 | static const TextStyle emptyTextStyle = TextStyle( 47 | fontSize: 18, 48 | ); 49 | 50 | static const TextStyle noteTitleTextStyle = TextStyle( 51 | fontSize: 24, 52 | fontWeight: FontWeight.bold, 53 | ); 54 | static const TextStyle introScreenBtnTextStyle = TextStyle( 55 | fontSize: 22, 56 | fontWeight: FontWeight.bold, 57 | ); 58 | static const TextStyle noteDecTextStyle = TextStyle( 59 | fontSize: 20, 60 | ); 61 | static const TextStyle noteDateTimeTextStyle = TextStyle( 62 | fontSize: 18, 63 | ); 64 | static const TextStyle splashScreenTextStyle = TextStyle( 65 | fontWeight: FontWeight.w800, 66 | fontSize: 45, 67 | ); 68 | static const TextStyle introScreenDesTextStyle = TextStyle( 69 | fontWeight: FontWeight.w400, 70 | fontSize: 22, 71 | ); 72 | static const TextStyle isComplatedTextStyle = TextStyle( 73 | fontWeight: FontWeight.w700, 74 | ); 75 | static const TextStyle selectedTextStyle = TextStyle( 76 | fontWeight: FontWeight.bold, 77 | ); 78 | static const TextStyle appInfoTextStyle = TextStyle( 79 | fontWeight: FontWeight.bold, 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | def localProperties = new Properties() 9 | def localPropertiesFile = rootProject.file("local.properties") 10 | if (localPropertiesFile.exists()) { 11 | localPropertiesFile.withReader("UTF-8") { reader -> 12 | localProperties.load(reader) 13 | } 14 | } 15 | 16 | def flutterVersionCode = localProperties.getProperty("flutter.versionCode") 17 | if (flutterVersionCode == null) { 18 | flutterVersionCode = "1" 19 | } 20 | 21 | def flutterVersionName = localProperties.getProperty("flutter.versionName") 22 | if (flutterVersionName == null) { 23 | flutterVersionName = "1.0" 24 | } 25 | 26 | android { 27 | namespace = "ir.umut.on_time" 28 | compileSdk = 35 29 | ndkVersion = flutter.ndkVersion 30 | 31 | defaultConfig { 32 | multiDexEnabled true 33 | } 34 | 35 | compileOptions { 36 | coreLibraryDesugaringEnabled true 37 | sourceCompatibility JavaVersion.VERSION_11 38 | targetCompatibility JavaVersion.VERSION_11 39 | } 40 | 41 | kotlinOptions { 42 | jvmTarget = "11" 43 | } 44 | 45 | defaultConfig { 46 | multiDexEnabled true 47 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 48 | applicationId = "ir.umut.on_time" 49 | // You can update the following values to match your application needs. 50 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 51 | minSdk = 21 52 | targetSdk = flutter.targetSdkVersion 53 | versionCode = flutterVersionCode.toInteger() 54 | versionName = flutterVersionName 55 | } 56 | 57 | buildTypes { 58 | release { 59 | // TODO: Add your own signing config for the release build. 60 | // Signing with the debug keys for now, so `flutter run --release` works. 61 | signingConfig = signingConfigs.debug 62 | } 63 | } 64 | } 65 | 66 | dependencies { 67 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' 68 | implementation 'androidx.window:window:1.0.0' 69 | implementation 'androidx.window:window-java:1.0.0' 70 | } 71 | 72 | flutter { 73 | source = "../.." 74 | } 75 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:flutter/foundation.dart'; 15 | import 'package:intl/intl.dart'; 16 | import 'package:intl/message_lookup_by_library.dart'; 17 | import 'package:intl/src/intl_helpers.dart'; 18 | 19 | import 'messages_en.dart' as messages_en; 20 | import 'messages_fa.dart' as messages_fa; 21 | 22 | typedef Future LibraryLoader(); 23 | Map _deferredLibraries = { 24 | 'en': () => new SynchronousFuture(null), 25 | 'fa': () => new SynchronousFuture(null), 26 | }; 27 | 28 | MessageLookupByLibrary? _findExact(String localeName) { 29 | switch (localeName) { 30 | case 'en': 31 | return messages_en.messages; 32 | case 'fa': 33 | return messages_fa.messages; 34 | default: 35 | return null; 36 | } 37 | } 38 | 39 | /// User programs should call this before using [localeName] for messages. 40 | Future initializeMessages(String localeName) { 41 | var availableLocale = Intl.verifiedLocale( 42 | localeName, 43 | (locale) => _deferredLibraries[locale] != null, 44 | onFailure: (_) => null, 45 | ); 46 | if (availableLocale == null) { 47 | return new SynchronousFuture(false); 48 | } 49 | var lib = _deferredLibraries[availableLocale]; 50 | lib == null ? new SynchronousFuture(false) : lib(); 51 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 52 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 53 | return new SynchronousFuture(true); 54 | } 55 | 56 | bool _messagesExistFor(String locale) { 57 | try { 58 | return _findExact(locale) != null; 59 | } catch (e) { 60 | return false; 61 | } 62 | } 63 | 64 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 65 | var actualLocale = Intl.verifiedLocale( 66 | locale, 67 | _messagesExistFor, 68 | onFailure: (_) => null, 69 | ); 70 | if (actualLocale == null) return null; 71 | return _findExact(actualLocale); 72 | } 73 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter/foundation.dart' show kIsWeb; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:hive_flutter/hive_flutter.dart'; 7 | import 'package:on_time/data/models/note_model.dart'; 8 | import 'package:on_time/data/models/task_model.dart'; 9 | import 'package:on_time/index.dart'; 10 | import 'package:on_time/resource/utils/notification_helper.dart'; 11 | import 'package:on_time/screens/home/bloc/home_bloc.dart'; 12 | import 'package:on_time/screens/home/note/bloc/note_bloc.dart'; 13 | import 'package:on_time/screens/home/task/bloc/task_bloc.dart'; 14 | import 'package:on_time/screens/settings/bloc/settings_bloc.dart'; 15 | import 'package:path_provider/path_provider.dart'; 16 | 17 | 18 | main() async { 19 | WidgetsFlutterBinding.ensureInitialized(); 20 | NotificationHelper.initialize(); 21 | 22 | 23 | if (kIsWeb) { 24 | 25 | await Hive.initFlutter(); 26 | } else { 27 | 28 | Directory directory = await getApplicationDocumentsDirectory(); 29 | Hive.init(directory.path); 30 | } 31 | 32 | Hive.registerAdapter(TaskModelAdapter()); 33 | Hive.registerAdapter(TaskColorAdapter()); 34 | Hive.registerAdapter(NoteModelAdapter()); 35 | Hive.registerAdapter(NoteColorAdapter()); 36 | 37 | await Hive.openBox(taskBoxName); 38 | await Hive.openBox(noteBoxName); 39 | 40 | SystemChrome.setSystemUIOverlayStyle( 41 | const SystemUiOverlayStyle( 42 | statusBarColor: Color(0xff161928), 43 | statusBarIconBrightness: Brightness.light, 44 | systemNavigationBarColor: Color(0xff161928), 45 | systemNavigationBarIconBrightness: Brightness.light, 46 | ), 47 | ); 48 | runApp( 49 | MultiBlocProvider( 50 | providers: [ 51 | BlocProvider( 52 | create: (_) { 53 | final taskBloc = TaskBloc(taskLocalRepo); 54 | taskBloc.add(TaskInit()); 55 | return taskBloc; 56 | }, 57 | ), 58 | BlocProvider(create: (_) { 59 | final noteBloc = NoteBloc(noteLocalRepo); 60 | noteBloc.add(NoteInit()); 61 | return noteBloc; 62 | }), 63 | BlocProvider( 64 | create: (_) { 65 | final settingsBloc = SettingsBloc(); 66 | settingsBloc.add(LoadSavedLocalizations()); 67 | return settingsBloc; 68 | }, 69 | ), 70 | BlocProvider(create: (_) => HomeBloc()) 71 | ], 72 | child: const RestartWidget(child: MyApp()), 73 | ), 74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /lib/screens/home/task/bloc/task_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:on_time/data/models/task_model.dart'; 4 | import '../../../../index.dart'; 5 | 6 | part 'task_event.dart'; 7 | part 'task_state.dart'; 8 | 9 | class TaskBloc extends Bloc { 10 | final TaskLocalRepo taskRepo; 11 | TaskBloc(this.taskRepo) : super(TaskLoadingState()) { 12 | on( 13 | (event, emit) async { 14 | if (event is TaskInit) { 15 | try { 16 | emit(TaskLoadingState()); 17 | final taskList = await taskRepo.getAllTasks(); 18 | emit(TaskLoadedState(taskList)); 19 | } catch (e) { 20 | emit(TaskError()); 21 | } 22 | } else if (event is DeleteTaskEvent) { 23 | try { 24 | emit(TaskLoadingState()); 25 | await taskRepo.deleteTask(event.id).then( 26 | (val) => emit( 27 | DeleteTaskState(val), 28 | ), 29 | ); 30 | } catch (e) { 31 | emit(TaskError()); 32 | } 33 | } else if (event is DeleteAllTasksEvent) { 34 | emit(TaskLoadingState()); 35 | await taskRepo.deleteAllTasks().then( 36 | (val) => emit( 37 | DeleteAllTasksState(val), 38 | ), 39 | ); 40 | } else if (event is SaveTaskEvent) { 41 | emit(TaskLoadingState()); 42 | await taskRepo 43 | .saveTask( 44 | event.saveTask, 45 | ) 46 | .then( 47 | (val) => emit( 48 | SaveTaskState(val), 49 | ), 50 | ); 51 | } else if (event is UpdateTaskEvent) { 52 | emit(TaskLoadingState()); 53 | await taskRepo 54 | .updateTask( 55 | event.id, 56 | event.updateTask, 57 | ) 58 | .then( 59 | (val) => emit( 60 | UpdateTaskState(val), 61 | ), 62 | ); 63 | } else if (event is IsComplatedTaskEvent) { 64 | try { 65 | emit(TaskLoadingState()); 66 | final updatedTaskList = await taskRepo.isComplateTask( 67 | event.id, 68 | event.isComplatedTask, 69 | event.isCompleted 70 | ); 71 | emit(TaskLoadedState(updatedTaskList)); 72 | } catch (e) { 73 | emit(TaskError()); 74 | } 75 | } 76 | }, 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/resource/widgets/note_card.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class NoteCard extends StatelessWidget { 4 | const NoteCard({ 5 | super.key, 6 | required this.note, 7 | required this.onTap, 8 | required this.onLongPress, 9 | }); 10 | 11 | final NoteModel note; 12 | final Function() onTap; 13 | final Function() onLongPress; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Material( 18 | clipBehavior: Clip.antiAlias, 19 | borderRadius: BorderRadius.circular(AppDimens.small), 20 | color: Color(note.color.code), 21 | child: InkWell( 22 | onTap: onTap, 23 | onLongPress: onLongPress, 24 | child: Container( 25 | constraints: const BoxConstraints( 26 | maxHeight: 300, 27 | minHeight: 100, 28 | ), 29 | decoration: BoxDecoration( 30 | border: 31 | Border.all(color: Theme.of(context).colorScheme.onSurface), 32 | borderRadius: BorderRadius.circular(AppDimens.small)), 33 | padding: const EdgeInsets.symmetric( 34 | horizontal: AppDimens.medium, 35 | vertical: AppDimens.medium, 36 | ), 37 | child: Column( 38 | mainAxisSize: MainAxisSize.min, 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | children: [ 42 | Flexible( 43 | child: AutoSizeText( 44 | note.title, 45 | presetFontSizes: const [16, 14, 12, 10, 8], 46 | textScaleFactor: 1.3, 47 | softWrap: true, 48 | style: AppTextStyles.noteTitleTextStyle 49 | .apply(color: AppColors.appPrimaryDark), 50 | group: AutoSizeGroup(), 51 | overflow: TextOverflow.fade, 52 | ), 53 | ), 54 | if (note.description.isNotEmpty) 55 | Flexible( 56 | child: Text( 57 | note.description, 58 | overflow: TextOverflow.clip, 59 | style: AppTextStyles.noteDecTextStyle.copyWith( 60 | color: AppColors.appPrimaryDark, 61 | ), 62 | ), 63 | ), 64 | AppDimens.medium.height, 65 | Text( 66 | note.dateTime, 67 | style: AppTextStyles.noteDecTextStyle 68 | .copyWith(color: AppColors.appPrimaryDark), 69 | ), 70 | ], 71 | ), 72 | ), 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/screens/intro/intro_screen.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class IntroScreen extends StatefulWidget { 4 | const IntroScreen({super.key}); 5 | 6 | @override 7 | State createState() => _IntroScreenState(); 8 | } 9 | 10 | class _IntroScreenState extends State { 11 | int pageIndex = 0; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SafeArea( 16 | child: Scaffold( 17 | body: Center( 18 | child: Column( 19 | mainAxisAlignment: MainAxisAlignment.center, 20 | crossAxisAlignment: CrossAxisAlignment.center, 21 | children: [ 22 | IndexedStack( 23 | alignment: Alignment.center, 24 | index: pageIndex, 25 | children: [ 26 | IntoVC( 27 | text:S.current.goal, 28 | darkVC: Assets.images.svg.vcIntroOneDark, 29 | lightVC: Assets.images.svg.vcIntroOneLight, 30 | ), 31 | IntoVC( 32 | text: S.current.plan, 33 | darkVC: Assets.images.svg.vcIntroTwoDark, 34 | lightVC: Assets.images.svg.vcIntroTwoLight, 35 | ), 36 | IntoVC( 37 | text:S.current.target, 38 | darkVC: Assets.images.svg.vcIntroThreeDark, 39 | lightVC: Assets.images.svg.vcIntroThreeLight, 40 | ), 41 | ], 42 | ), 43 | Padding( 44 | padding: const EdgeInsets.symmetric( 45 | horizontal: AppDimens.large, vertical: AppDimens.large), 46 | child: SizedBox( 47 | width: double.infinity, 48 | height: 60, 49 | child: ElevatedButton( 50 | style: ButtonStyle( 51 | backgroundColor: WidgetStatePropertyAll( 52 | Theme.of(context).colorScheme.onSurface, 53 | ), 54 | ), 55 | onPressed: () { 56 | if (pageIndex == 2) { 57 | Navigator.pushReplacement( 58 | context, 59 | CupertinoPageRoute( 60 | builder: (context) => const HomePage()), 61 | ); 62 | } else { 63 | setState(() { 64 | pageIndex++; 65 | }); 66 | } 67 | }, 68 | child: Text( 69 | pageIndex == 2 ? S.current.letsGo :S.current.well, 70 | style: AppTextStyles.introScreenBtnTextStyle 71 | .apply(color: Theme.of(context).colorScheme.surface), 72 | ), 73 | ), 74 | ), 75 | ), 76 | ], 77 | ), 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/screens/home/note/note_list.dart: -------------------------------------------------------------------------------- 1 | part of '../../../index.dart'; 2 | 3 | class NoteList extends StatelessWidget { 4 | const NoteList({ 5 | super.key, 6 | }); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Column( 11 | children: [ 12 | Expanded( 13 | flex: 10, 14 | child: ValueListenableBuilder>( 15 | valueListenable: noteBox.listenable(), 16 | builder: (contxt, builderNotesBox, _) { 17 | final notesList = builderNotesBox.values.toList(); 18 | if (notesList.isEmpty) { 19 | return EmptyVC( 20 | text: S.current.emptyNote, 21 | ); 22 | } else { 23 | return MasonryGridView.count( 24 | padding: const EdgeInsets.symmetric( 25 | horizontal: 16, 26 | vertical: 16, 27 | ), 28 | crossAxisCount: 2, 29 | itemCount: notesList.length, 30 | itemBuilder: (BuildContext context, int index) { 31 | return NoteCard( 32 | note: notesList[index], 33 | onTap: () { 34 | Navigator.push( 35 | context, 36 | CupertinoPageRoute( 37 | builder: (context) => EditNoteScreen( 38 | note: NoteModel( 39 | id: notesList[index].id, 40 | title: notesList[index].title, 41 | description: notesList[index].description, 42 | color: notesList[index].color, 43 | dateTime: notesList[index].dateTime, 44 | ), 45 | ), 46 | ), 47 | ); 48 | }, 49 | onLongPress: () { 50 | customeDialogee( 51 | context, 52 | content: S.current.deleteNoteDialogContent, 53 | primaryBtn:S.current.deleteDialogPrimaryBtn, 54 | onTapPrimaryBtn: () { 55 | BlocProvider.of(context) 56 | .add(DeleteNoteEvent(notesList[index].id)); 57 | Navigator.pop(context); 58 | }, 59 | onTapSecendaryBtn: () { 60 | Navigator.pop(context); 61 | }, 62 | secendaryBtn: S.current.deleteDialogSecendaryBtn, 63 | ); 64 | }, 65 | ) 66 | .animate() 67 | .fadeIn(delay: 100.ms * index) 68 | .moveX(delay: 100.ms * index); 69 | }, 70 | mainAxisSpacing: 12.0, 71 | crossAxisSpacing: 12.0, 72 | ); 73 | } 74 | }, 75 | ), 76 | ), 77 | ], 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/index.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:math'; 3 | 4 | import 'package:auto_size_text/auto_size_text.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter/foundation.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_animate/flutter_animate.dart'; 9 | import 'package:flutter_bloc/flutter_bloc.dart'; 10 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 11 | import 'package:flutter_localizations/flutter_localizations.dart'; 12 | import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; 13 | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; 14 | import 'package:flutter_svg/svg.dart'; 15 | import 'package:hive_flutter/hive_flutter.dart'; 16 | import 'package:on_time/data/models/language_model.dart'; 17 | import 'package:on_time/data/models/note_model.dart'; 18 | import 'package:on_time/data/models/task_model.dart'; 19 | import 'package:on_time/gen/assets.gen.dart'; 20 | import 'package:on_time/generated/l10n.dart'; 21 | import 'package:on_time/resource/utils/notification_helper.dart'; 22 | import 'package:on_time/resource/widgets/mobile_wrapper_in_desktop.dart'; 23 | import 'package:on_time/screens/home/bloc/home_bloc.dart'; 24 | import 'package:on_time/screens/home/bloc/home_event.dart'; 25 | import 'package:on_time/screens/home/bloc/home_state.dart'; 26 | import 'package:on_time/screens/home/note/bloc/note_bloc.dart'; 27 | import 'package:on_time/screens/home/task/bloc/task_bloc.dart'; 28 | import 'package:on_time/screens/settings/bloc/settings_bloc.dart'; 29 | import 'package:persian_datetime_picker/persian_datetime_picker.dart'; 30 | import 'package:persian_horizontal_date_picker/persian_horizontal_date_picker.dart'; 31 | import 'package:shared_preferences/shared_preferences.dart'; 32 | import 'package:uuid/uuid.dart'; 33 | import 'package:zoom_tap_animation/zoom_tap_animation.dart'; 34 | 35 | part 'screens/my_app.dart'; 36 | part 'screens/settings/settings_page.dart'; 37 | part 'screens/home/task/add_task_screen.dart'; 38 | part 'screens/home/home_page.dart'; 39 | part 'screens/home/task/tasks.dart'; 40 | part 'screens/home/task/task_list.dart'; 41 | part 'resource/constants.dart'; 42 | part 'resource/app_dimens.dart'; 43 | part 'resource/widgets/custom_app_bar.dart'; 44 | part 'resource/widgets/input_feild.dart'; 45 | part 'resource/utils/extensions.dart'; 46 | part 'resource/themes/themes.dart'; 47 | part 'resource/components/text_style.dart'; 48 | part 'data/repositories/repositories.dart'; 49 | part 'data/repositories/local_repo.dart'; 50 | part 'data/sources/data_source.dart'; 51 | part 'data/sources/local_data_src.dart'; 52 | part 'resource/widgets/color_item.dart'; 53 | part 'resource/widgets/empty_vc.dart'; 54 | part 'resource/widgets/task_card.dart'; 55 | part 'screens/home/task/edit_task_screen.dart'; 56 | part 'resource/utils/utils.dart'; 57 | part 'screens/home/note/notes.dart'; 58 | part 'resource/widgets/note_card.dart'; 59 | part 'screens/home/note/add_note_screen.dart'; 60 | part 'screens/home/note/edit_note_screen.dart'; 61 | part 'screens/home/note/note_list.dart'; 62 | part 'screens/splash_screen.dart'; 63 | part 'screens/intro/intro_screen.dart'; 64 | part 'resource/widgets/into_vc.dart'; 65 | part 'resource/widgets/dialog.dart'; 66 | part 'resource/widgets/restart.dart'; 67 | -------------------------------------------------------------------------------- /lib/resource/widgets/color_item.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class ColorItem extends StatelessWidget { 4 | const ColorItem({ 5 | super.key, 6 | required this.onTap, 7 | required this.isSelected, 8 | required this.colorCode, 9 | }); 10 | 11 | final Function() onTap; 12 | final bool isSelected; 13 | final int colorCode; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return GestureDetector( 18 | onTap: onTap, 19 | child: Container( 20 | width: 45, 21 | height: 45, 22 | alignment: Alignment.center, 23 | margin: const EdgeInsets.all(3), 24 | decoration: const BoxDecoration( 25 | shape: BoxShape.circle, color: AppColors.appPrimaryDark), 26 | child: Container( 27 | width: 43, 28 | height: 43, 29 | alignment: Alignment.center, 30 | margin: const EdgeInsets.all(4), 31 | padding: const EdgeInsets.all(3), 32 | decoration: BoxDecoration( 33 | shape: BoxShape.circle, 34 | color: Color(colorCode), 35 | border: Border.all(color: AppColors.appOnPrimaryDark)), 36 | child: isSelected 37 | ? const Icon( 38 | Icons.check, 39 | color: AppColors.appPrimaryDark, 40 | ) 41 | : null, 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | 48 | TaskColor _taskSelectedColor = TaskColor.one; 49 | 50 | class TaskColorSelector extends StatelessWidget { 51 | final TaskColor selectedColor; 52 | final ValueChanged onColorSelected; 53 | 54 | const TaskColorSelector({ 55 | super.key, 56 | required this.selectedColor, 57 | required this.onColorSelected, 58 | }); 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return SingleChildScrollView( 63 | scrollDirection: Axis.horizontal, 64 | child: Row( 65 | mainAxisAlignment: MainAxisAlignment.spaceAround, 66 | children: TaskColor.values.map((noteColor) { 67 | return ColorItem( 68 | onTap: () => onColorSelected(noteColor), 69 | isSelected: selectedColor == noteColor, 70 | colorCode: noteColor.code, 71 | ); 72 | }).toList(), 73 | ), 74 | ); 75 | } 76 | } 77 | 78 | NoteColor _noteSelectedColor = NoteColor.one; 79 | 80 | class NoteColorSelector extends StatelessWidget { 81 | final NoteColor selectedColor; 82 | final ValueChanged onColorSelected; 83 | 84 | const NoteColorSelector({ 85 | super.key, 86 | required this.selectedColor, 87 | required this.onColorSelected, 88 | }); 89 | 90 | @override 91 | Widget build(BuildContext context) { 92 | return SingleChildScrollView( 93 | scrollDirection: Axis.horizontal, 94 | child: Row( 95 | mainAxisAlignment: MainAxisAlignment.spaceAround, 96 | children: NoteColor.values.map((noteColor) { 97 | return ColorItem( 98 | onTap: () => onColorSelected(noteColor), 99 | isSelected: selectedColor == noteColor, 100 | colorCode: noteColor.code, 101 | ); 102 | }).toList(), 103 | ), 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/resource/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | String getFarsiNumberStr(String number) { 4 | const en = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; 5 | const fa = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; 6 | for (var element in en) { 7 | number = number.replaceAll(element, fa[en.indexOf(element)]); 8 | } 9 | 10 | return number; 11 | } 12 | 13 | String getFarsiNumberInt(int number) { 14 | String numStr = number.toString(); 15 | const en = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; 16 | const fa = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; 17 | for (var element in en) { 18 | numStr = numStr.replaceAll(element, fa[en.indexOf(element)]); 19 | } 20 | 21 | return numStr; 22 | } 23 | 24 | String getPersianMonthStr(String month) { 25 | const en = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']; 26 | 27 | const perMonth = [ 28 | 'فروردین', 29 | 'اردیبهشت', 30 | 'خرداد', 31 | 'تیر', 32 | 'مرداد', 33 | 'شهریور', 34 | 'مهر', 35 | 'آبان', 36 | 'آذر', 37 | 'دی', 38 | 'بهمن', 39 | 'اسفند' 40 | ]; 41 | 42 | for (var i = 0; i < en.length; i++) { 43 | if (month == en[i]) { 44 | return perMonth[i]; 45 | } 46 | } 47 | 48 | return month; 49 | } 50 | 51 | String getPersianMonthInt(int month) { 52 | String strMonth = month.toString(); 53 | const en = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']; 54 | 55 | const perMonth = [ 56 | 'فروردین', 57 | 'اردیبهشت', 58 | 'خرداد', 59 | 'تیر', 60 | 'مرداد', 61 | 'شهریور', 62 | 'مهر', 63 | 'آبان', 64 | 'آذر', 65 | 'دی', 66 | 'بهمن', 67 | 'اسفند' 68 | ]; 69 | 70 | for (var i = 0; i < en.length; i++) { 71 | if (strMonth == en[i]) { 72 | return perMonth[i]; 73 | } 74 | } 75 | 76 | return strMonth; 77 | } 78 | 79 | String getGregorianMonthInt(int month) { 80 | String strMonth = month.toString(); 81 | const en = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']; 82 | 83 | const perMonth = [ 84 | 'January', 85 | 'February', 86 | 'March', 87 | 'April', 88 | 'May', 89 | 'June', 90 | 'July', 91 | 'August', 92 | 'September', 93 | 'October', 94 | 'November', 95 | 'December' 96 | ]; 97 | 98 | for (var i = 0; i < en.length; i++) { 99 | if (strMonth == en[i]) { 100 | return perMonth[i]; 101 | } 102 | } 103 | 104 | return strMonth; 105 | } 106 | 107 | extension PersianNumbersStr on String { 108 | String toPersianNumber() { 109 | return getFarsiNumberStr(this); 110 | } 111 | 112 | String toPesianMonth() { 113 | return getPersianMonthStr(this); 114 | } 115 | } 116 | 117 | extension PersianNumbersInt on int { 118 | String toPersianNumberInt() { 119 | return getFarsiNumberInt(this); 120 | } 121 | 122 | String toPesianMonth() { 123 | return getPersianMonthInt(this); 124 | } 125 | 126 | String toGregorianMonth() { 127 | return getGregorianMonthInt(this); 128 | } 129 | } 130 | 131 | extension SizedBoxExtention on double { 132 | SizedBox get height => SizedBox( 133 | height: toDouble(), 134 | ); 135 | SizedBox get width => SizedBox( 136 | width: toDouble(), 137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # On . Time 2 | 3 | ![on.time Logo](/screenshots/cover.png) 4 | 5 | On.Time is a handy app for your daily notes and planning, built with the Flutter framework. It uses the BloC pattern for state management and Hive for data storage. 6 | 7 | ### Download and Install from GitHub Releases 8 | 9 | You can download and install the app from the [GitHub Releases](https://github.com/omidhaqi/on_time/releases) page. Currently, a preview version is available for download. 10 | 11 | 1. Go to the [Releases](https://github.com/omidhaqi/on_time/releases) page. 12 | 2. Download the latest preview version. 13 | 3. Follow the installation instructions provided in the release notes. 14 | 15 | ## Features 16 | 17 | - **Create and manage daily notes** 18 | - **Plan tasks and events** 19 | - **Reminders for important tasks** 20 | - **Simple and user-friendly interface** 21 | 22 | ## Build and Setup 23 | 24 | ### Prerequisites 25 | 26 | - Flutter SDK: [Download link](https://flutter.dev/docs/get-started/install) 27 | - Dart: [Download link](https://dart.dev/get-dart) 28 | - A text editor like VS Code or Android Studio 29 | 30 | ### Build Steps 31 | 32 | 1. Clone the repository: 33 | 34 | ```bash 35 | git clone https://github.com/omidhaqi/on_time.git 36 | ``` 37 | 38 | 2. Navigate to the project directory: 39 | 40 | ```bash 41 | cd on_time 42 | ``` 43 | 44 | 3. Install the dependencies: 45 | 46 | ```bash 47 | flutter pub get 48 | ``` 49 | 50 | 4. Run the project: 51 | 52 | ```bash 53 | flutter run 54 | ``` 55 | 56 | ## Usage 57 | 58 | To use on.time, simply run the app and use the interface to create and manage your daily notes and tasks. 59 | 60 | ## Architecture 61 | 62 | This application is developed using the BloC architecture for state management and the Hive library for data storage. 63 | 64 | - **BloC (Business Logic Component):** This pattern helps separate business logic from the UI, making the code more testable and scalable. 65 | - **Hive:** A fast and lightweight database for Flutter and Dart, used for local storage. 66 | 67 | 68 | 69 | ## Contributing 70 | 71 | If you are interested in contributing to on.time, feel free to submit pull requests and report issues on our GitHub repository. 72 | 73 | ## License 74 | 75 | This project is licensed under the Apache License. For more information, see the [LICENSE](./LICENSE) file. 76 | 77 | ## Contact 78 | 79 | For any questions or suggestions, you can contact us at : 80 | - **Telegram:** @Omid_Haqi 81 | - **Email:** omid.haqi@gmail.com 82 | 83 | #### Screenshots 84 | 85 | | | | 86 | | :-------------------------------------------: | :-------------------------------------------: | 87 | | | | 88 | | | | 89 | | | | 90 | | | | 91 | 92 | 93 | --- 94 | 95 |
96 | 97 | Version [0.0.1+1](https://github.com/omidhaqi/) 98 | 99 |
100 |
101 | 102 | Developed with ☕ by [Umut](https://github.com/omidhaqi/) 103 | 104 |
105 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 31 | 35 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 49 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /lib/data/models/note_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'note_model.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class NoteModelAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 2; 12 | 13 | @override 14 | NoteModel read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return NoteModel( 20 | id: fields[0] as int, 21 | title: fields[1] as String, 22 | description: fields[2] as String, 23 | color: fields[3] as NoteColor, 24 | dateTime: fields[4] as String, 25 | ); 26 | } 27 | 28 | @override 29 | void write(BinaryWriter writer, NoteModel obj) { 30 | writer 31 | ..writeByte(5) 32 | ..writeByte(0) 33 | ..write(obj.id) 34 | ..writeByte(1) 35 | ..write(obj.title) 36 | ..writeByte(2) 37 | ..write(obj.description) 38 | ..writeByte(3) 39 | ..write(obj.color) 40 | ..writeByte(4) 41 | ..write(obj.dateTime); 42 | } 43 | 44 | @override 45 | int get hashCode => typeId.hashCode; 46 | 47 | @override 48 | bool operator ==(Object other) => 49 | identical(this, other) || 50 | other is NoteModelAdapter && 51 | runtimeType == other.runtimeType && 52 | typeId == other.typeId; 53 | } 54 | 55 | class NoteColorAdapter extends TypeAdapter { 56 | @override 57 | final int typeId = 3; 58 | 59 | @override 60 | NoteColor read(BinaryReader reader) { 61 | switch (reader.readByte()) { 62 | case 0: 63 | return NoteColor.one; 64 | case 1: 65 | return NoteColor.two; 66 | case 2: 67 | return NoteColor.three; 68 | case 3: 69 | return NoteColor.four; 70 | case 4: 71 | return NoteColor.five; 72 | case 5: 73 | return NoteColor.six; 74 | case 6: 75 | return NoteColor.seven; 76 | case 7: 77 | return NoteColor.eight; 78 | case 8: 79 | return NoteColor.nine; 80 | default: 81 | return NoteColor.one; 82 | } 83 | } 84 | 85 | @override 86 | void write(BinaryWriter writer, NoteColor obj) { 87 | switch (obj) { 88 | case NoteColor.one: 89 | writer.writeByte(0); 90 | break; 91 | case NoteColor.two: 92 | writer.writeByte(1); 93 | break; 94 | case NoteColor.three: 95 | writer.writeByte(2); 96 | break; 97 | case NoteColor.four: 98 | writer.writeByte(3); 99 | break; 100 | case NoteColor.five: 101 | writer.writeByte(4); 102 | break; 103 | case NoteColor.six: 104 | writer.writeByte(5); 105 | break; 106 | case NoteColor.seven: 107 | writer.writeByte(6); 108 | break; 109 | case NoteColor.eight: 110 | writer.writeByte(7); 111 | break; 112 | case NoteColor.nine: 113 | writer.writeByte(8); 114 | break; 115 | } 116 | } 117 | 118 | @override 119 | int get hashCode => typeId.hashCode; 120 | 121 | @override 122 | bool operator ==(Object other) => 123 | identical(this, other) || 124 | other is NoteColorAdapter && 125 | runtimeType == other.runtimeType && 126 | typeId == other.typeId; 127 | } 128 | -------------------------------------------------------------------------------- /lib/data/models/task_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'task_model.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class TaskModelAdapter extends TypeAdapter { 10 | @override 11 | final int typeId = 0; 12 | 13 | @override 14 | TaskModel read(BinaryReader reader) { 15 | final numOfFields = reader.readByte(); 16 | final fields = { 17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 18 | }; 19 | return TaskModel( 20 | id: fields[0] as int, 21 | title: fields[1] as String, 22 | note: fields[2] as String, 23 | isCompleted: fields[3] as bool, 24 | dateTime: fields[4] as DateTime, 25 | color: fields[5] as TaskColor, 26 | addToNote: fields[6] as bool, 27 | ); 28 | } 29 | 30 | @override 31 | void write(BinaryWriter writer, TaskModel obj) { 32 | writer 33 | ..writeByte(7) 34 | ..writeByte(0) 35 | ..write(obj.id) 36 | ..writeByte(1) 37 | ..write(obj.title) 38 | ..writeByte(2) 39 | ..write(obj.note) 40 | ..writeByte(3) 41 | ..write(obj.isCompleted) 42 | ..writeByte(4) 43 | ..write(obj.dateTime) 44 | ..writeByte(5) 45 | ..write(obj.color) 46 | ..writeByte(6) 47 | ..write(obj.addToNote); 48 | } 49 | 50 | @override 51 | int get hashCode => typeId.hashCode; 52 | 53 | @override 54 | bool operator ==(Object other) => 55 | identical(this, other) || 56 | other is TaskModelAdapter && 57 | runtimeType == other.runtimeType && 58 | typeId == other.typeId; 59 | } 60 | 61 | class TaskColorAdapter extends TypeAdapter { 62 | @override 63 | final int typeId = 1; 64 | 65 | @override 66 | TaskColor read(BinaryReader reader) { 67 | switch (reader.readByte()) { 68 | case 0: 69 | return TaskColor.one; 70 | case 1: 71 | return TaskColor.two; 72 | case 2: 73 | return TaskColor.three; 74 | case 3: 75 | return TaskColor.four; 76 | case 4: 77 | return TaskColor.five; 78 | case 5: 79 | return TaskColor.six; 80 | case 6: 81 | return TaskColor.seven; 82 | case 7: 83 | return TaskColor.eight; 84 | case 8: 85 | return TaskColor.nine; 86 | default: 87 | return TaskColor.one; 88 | } 89 | } 90 | 91 | @override 92 | void write(BinaryWriter writer, TaskColor obj) { 93 | switch (obj) { 94 | case TaskColor.one: 95 | writer.writeByte(0); 96 | break; 97 | case TaskColor.two: 98 | writer.writeByte(1); 99 | break; 100 | case TaskColor.three: 101 | writer.writeByte(2); 102 | break; 103 | case TaskColor.four: 104 | writer.writeByte(3); 105 | break; 106 | case TaskColor.five: 107 | writer.writeByte(4); 108 | break; 109 | case TaskColor.six: 110 | writer.writeByte(5); 111 | break; 112 | case TaskColor.seven: 113 | writer.writeByte(6); 114 | break; 115 | case TaskColor.eight: 116 | writer.writeByte(7); 117 | break; 118 | case TaskColor.nine: 119 | writer.writeByte(8); 120 | break; 121 | } 122 | } 123 | 124 | @override 125 | int get hashCode => typeId.hashCode; 126 | 127 | @override 128 | bool operator ==(Object other) => 129 | identical(this, other) || 130 | other is TaskColorAdapter && 131 | runtimeType == other.runtimeType && 132 | typeId == other.typeId; 133 | } 134 | -------------------------------------------------------------------------------- /lib/data/sources/local_data_src.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class TaskLocalDataSrc implements ITaskDataSrc { 4 | TaskLocalDataSrc._constructor(); 5 | 6 | static final _instance = TaskLocalDataSrc._constructor(); 7 | 8 | factory TaskLocalDataSrc() => _instance; 9 | 10 | void close() { 11 | Hive.close(); 12 | } 13 | 14 | @override 15 | Future> getAllTasks() async { 16 | final box = await Hive.openBox(taskBoxName); 17 | return taskMapTOlist(box).toList(); 18 | } 19 | 20 | @override 21 | Future> deleteAllTasks() async { 22 | final box = await Hive.openBox(taskBoxName); 23 | await box.clear(); 24 | return taskMapTOlist(box).toList(); 25 | } 26 | 27 | @override 28 | Future> deleteTask(int id) async { 29 | final box = await Hive.openBox(taskBoxName); 30 | NotificationHelper.cancelNotification(id); 31 | await box.delete(id); 32 | 33 | return taskMapTOlist(box).toList(); 34 | } 35 | 36 | @override 37 | Future> saveTask(TaskModel task) async { 38 | 39 | final box = await Hive.openBox(taskBoxName); 40 | NotificationHelper.scheduleNotification( 41 | task.id, 42 | task.title, 43 | task.note, 44 | task.dateTime, 45 | ); 46 | await box.put(task.id, task); 47 | 48 | return taskMapTOlist(box).toList(); 49 | } 50 | 51 | @override 52 | Future> updateTask(int id, TaskModel task) async { 53 | NotificationHelper.cancelNotification(id); 54 | final box = await Hive.openBox(taskBoxName); 55 | NotificationHelper.scheduleNotification( 56 | task.id, 57 | task.title, 58 | task.note, 59 | task.dateTime, 60 | ); 61 | box.put(id, task); 62 | 63 | return taskMapTOlist(box).toList(); 64 | } 65 | 66 | @override 67 | Future> isComplateTask( 68 | int id, TaskModel task, bool isComplated) async { 69 | final box = await Hive.openBox(taskBoxName); 70 | isComplated = task.isCompleted; 71 | 72 | box.put(id, task); 73 | if (isComplated) { 74 | NotificationHelper.cancelNotification(id); 75 | } else { 76 | NotificationHelper.scheduleNotification( 77 | task.id, 78 | task.title, 79 | task.note, 80 | task.dateTime, 81 | ); 82 | } 83 | 84 | return taskMapTOlist(box).toList(); 85 | } 86 | } 87 | 88 | class NoteLocalDataSrc implements INoteDataSrc { 89 | NoteLocalDataSrc._constructor(); 90 | 91 | static final _instance = NoteLocalDataSrc._constructor(); 92 | 93 | factory NoteLocalDataSrc() => _instance; 94 | 95 | @override 96 | Future> deleteAllNotes() async { 97 | final box = await Hive.openBox(noteBoxName); 98 | box.clear(); 99 | return noteMapToList(box).toList(); 100 | } 101 | 102 | @override 103 | Future> deleteNote(int id) async { 104 | final box = await Hive.openBox(noteBoxName); 105 | box.delete(id); 106 | return noteMapToList(box).toList(); 107 | } 108 | 109 | @override 110 | Future> getAllNotes() async { 111 | final box = await Hive.openBox(noteBoxName); 112 | 113 | return noteMapToList(box).toList(); 114 | } 115 | 116 | @override 117 | Future> saveNote(NoteModel note) async { 118 | final box = await Hive.openBox(noteBoxName); 119 | 120 | box.put(note.id, note); 121 | return noteMapToList(box).toList(); 122 | } 123 | 124 | @override 125 | Future> updateNote(int id, NoteModel note) async { 126 | final box = await Hive.openBox(noteBoxName); 127 | box.put(id, note); 128 | return noteMapToList(box).toList(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /assets/images/svg/ic_setting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lib/screens/home/task/task_list.dart: -------------------------------------------------------------------------------- 1 | // TaskList.dart 2 | 3 | part of '../../../index.dart'; 4 | 5 | class TaskList extends StatelessWidget { 6 | final DateTime selectedDate; 7 | 8 | const TaskList({ 9 | super.key, 10 | required this.selectedDate, 11 | }); 12 | 13 | bool isSameDay(DateTime date1, DateTime date2) { 14 | return date1.year == date2.year && 15 | date1.month == date2.month && 16 | date1.day == date2.day; 17 | } 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Expanded( 22 | child: ValueListenableBuilder>( 23 | valueListenable: taskBox.listenable(), 24 | builder: (context, builderTaskBox, _) { 25 | final todoList = builderTaskBox.values.toList(); 26 | final tasksForSelectedDate = todoList 27 | .where((task) => isSameDay(task.dateTime, selectedDate)) 28 | .toList(); 29 | 30 | if (todoList.isEmpty || tasksForSelectedDate.isEmpty) { 31 | return EmptyVC( 32 | text: S.current.emptyPlan, 33 | ); 34 | } else { 35 | return ListView.builder( 36 | itemCount: tasksForSelectedDate.length, 37 | itemBuilder: (contxt, index) { 38 | var taskList = tasksForSelectedDate[index]; 39 | 40 | return BlocBuilder( 41 | builder: (context, state) { 42 | return TaskCard( 43 | taskList: taskList, 44 | index: index, 45 | editOnTap: () { 46 | Navigator.push( 47 | context, 48 | CupertinoPageRoute( 49 | builder: (context) => EditTaskScreen( 50 | 51 | taskModel: TaskModel( 52 | id: taskList.id, 53 | title: taskList.title, 54 | note: taskList.note, 55 | isCompleted: taskList.isCompleted, 56 | dateTime: taskList.dateTime, 57 | color: taskList.color, 58 | addToNote: taskList.addToNote, 59 | ), 60 | ), 61 | ), 62 | ); 63 | }, 64 | deleteOnTap: () { 65 | customeDialogee( 66 | context, 67 | content: S.current.deleteNoteDialogContent, 68 | primaryBtn: S.current.deleteDialogPrimaryBtn, 69 | secendaryBtn: S.current.deleteDialogSecendaryBtn, 70 | onTapPrimaryBtn: () { 71 | BlocProvider.of(context) 72 | .add(DeleteTaskEvent(taskList.id)); 73 | 74 | Navigator.pop(context); 75 | }, 76 | onTapSecendaryBtn: () { 77 | Navigator.pop(context); 78 | }, 79 | ); 80 | }, 81 | onChanged: (val) { 82 | BlocProvider.of(context) 83 | .add(IsComplatedTaskEvent( 84 | TaskModel( 85 | id: taskList.id, 86 | title: taskList.title, 87 | note: taskList.note, 88 | isCompleted: val!, 89 | dateTime: taskList.dateTime, 90 | color: taskList.color, 91 | addToNote: taskList.addToNote, 92 | ), 93 | taskList.id, 94 | val, 95 | )); 96 | }, 97 | value: taskList.isCompleted, 98 | ); 99 | }, 100 | ); 101 | }, 102 | ); 103 | } 104 | }, 105 | ), 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/gen/assets.gen.dart: -------------------------------------------------------------------------------- 1 | /// GENERATED CODE - DO NOT MODIFY BY HAND 2 | /// ***************************************************** 3 | /// FlutterGen 4 | /// ***************************************************** 5 | 6 | // coverage:ignore-file 7 | // ignore_for_file: type=lint 8 | // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use 9 | 10 | import 'package:flutter/widgets.dart'; 11 | 12 | class $AssetsImagesGen { 13 | const $AssetsImagesGen(); 14 | 15 | /// Directory path: assets/images/icon 16 | $AssetsImagesIconGen get icon => const $AssetsImagesIconGen(); 17 | 18 | /// Directory path: assets/images/svg 19 | $AssetsImagesSvgGen get svg => const $AssetsImagesSvgGen(); 20 | } 21 | 22 | class $AssetsImagesIconGen { 23 | const $AssetsImagesIconGen(); 24 | 25 | /// File path: assets/images/icon/icon.png 26 | AssetGenImage get icon => const AssetGenImage('assets/images/icon/icon.png'); 27 | 28 | /// List of all assets 29 | List get values => [icon]; 30 | } 31 | 32 | class $AssetsImagesSvgGen { 33 | const $AssetsImagesSvgGen(); 34 | 35 | /// File path: assets/images/svg/ic_notification.svg 36 | String get icNotification => 'assets/images/svg/ic_notification.svg'; 37 | 38 | /// File path: assets/images/svg/ic_search.svg 39 | String get icSearch => 'assets/images/svg/ic_search.svg'; 40 | 41 | /// File path: assets/images/svg/ic_setting.svg 42 | String get icSetting => 'assets/images/svg/ic_setting.svg'; 43 | 44 | /// File path: assets/images/svg/vc_empty_dark.svg 45 | String get vcEmptyDark => 'assets/images/svg/vc_empty_dark.svg'; 46 | 47 | /// File path: assets/images/svg/vc_empty_light.svg 48 | String get vcEmptyLight => 'assets/images/svg/vc_empty_light.svg'; 49 | 50 | /// File path: assets/images/svg/vc_intro_one_dark.svg 51 | String get vcIntroOneDark => 'assets/images/svg/vc_intro_one_dark.svg'; 52 | 53 | /// File path: assets/images/svg/vc_intro_one_light.svg 54 | String get vcIntroOneLight => 'assets/images/svg/vc_intro_one_light.svg'; 55 | 56 | /// File path: assets/images/svg/vc_intro_three_dark.svg 57 | String get vcIntroThreeDark => 'assets/images/svg/vc_intro_three_dark.svg'; 58 | 59 | /// File path: assets/images/svg/vc_intro_three_light.svg 60 | String get vcIntroThreeLight => 'assets/images/svg/vc_intro_three_light.svg'; 61 | 62 | /// File path: assets/images/svg/vc_intro_two_dark.svg 63 | String get vcIntroTwoDark => 'assets/images/svg/vc_intro_two_dark.svg'; 64 | 65 | /// File path: assets/images/svg/vc_intro_two_light.svg 66 | String get vcIntroTwoLight => 'assets/images/svg/vc_intro_two_light.svg'; 67 | 68 | /// List of all assets 69 | List get values => [ 70 | icNotification, 71 | icSearch, 72 | icSetting, 73 | vcEmptyDark, 74 | vcEmptyLight, 75 | vcIntroOneDark, 76 | vcIntroOneLight, 77 | vcIntroThreeDark, 78 | vcIntroThreeLight, 79 | vcIntroTwoDark, 80 | vcIntroTwoLight 81 | ]; 82 | } 83 | 84 | class Assets { 85 | Assets._(); 86 | 87 | static const $AssetsImagesGen images = $AssetsImagesGen(); 88 | } 89 | 90 | class AssetGenImage { 91 | const AssetGenImage( 92 | this._assetName, { 93 | this.size, 94 | this.flavors = const {}, 95 | }); 96 | 97 | final String _assetName; 98 | 99 | final Size? size; 100 | final Set flavors; 101 | 102 | Image image({ 103 | Key? key, 104 | AssetBundle? bundle, 105 | ImageFrameBuilder? frameBuilder, 106 | ImageErrorWidgetBuilder? errorBuilder, 107 | String? semanticLabel, 108 | bool excludeFromSemantics = false, 109 | double? scale, 110 | double? width, 111 | double? height, 112 | Color? color, 113 | Animation? opacity, 114 | BlendMode? colorBlendMode, 115 | BoxFit? fit, 116 | AlignmentGeometry alignment = Alignment.center, 117 | ImageRepeat repeat = ImageRepeat.noRepeat, 118 | Rect? centerSlice, 119 | bool matchTextDirection = false, 120 | bool gaplessPlayback = false, 121 | bool isAntiAlias = false, 122 | String? package, 123 | FilterQuality filterQuality = FilterQuality.low, 124 | int? cacheWidth, 125 | int? cacheHeight, 126 | }) { 127 | return Image.asset( 128 | _assetName, 129 | key: key, 130 | bundle: bundle, 131 | frameBuilder: frameBuilder, 132 | errorBuilder: errorBuilder, 133 | semanticLabel: semanticLabel, 134 | excludeFromSemantics: excludeFromSemantics, 135 | scale: scale, 136 | width: width, 137 | height: height, 138 | color: color, 139 | opacity: opacity, 140 | colorBlendMode: colorBlendMode, 141 | fit: fit, 142 | alignment: alignment, 143 | repeat: repeat, 144 | centerSlice: centerSlice, 145 | matchTextDirection: matchTextDirection, 146 | gaplessPlayback: gaplessPlayback, 147 | isAntiAlias: isAntiAlias, 148 | package: package, 149 | filterQuality: filterQuality, 150 | cacheWidth: cacheWidth, 151 | cacheHeight: cacheHeight, 152 | ); 153 | } 154 | 155 | ImageProvider provider({ 156 | AssetBundle? bundle, 157 | String? package, 158 | }) { 159 | return AssetImage( 160 | _assetName, 161 | bundle: bundle, 162 | package: package, 163 | ); 164 | } 165 | 166 | String get path => _assetName; 167 | 168 | String get keyName => _assetName; 169 | } 170 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_en.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "addNoteSnackBar": MessageLookupByLibrary.simpleMessage( 26 | "Note added successfully", 27 | ), 28 | "addTaskSnackBar": MessageLookupByLibrary.simpleMessage( 29 | "Task added successfully", 30 | ), 31 | "addToNote": MessageLookupByLibrary.simpleMessage( 32 | "Add the task note to notes!", 33 | ), 34 | "appVersion": MessageLookupByLibrary.simpleMessage("0.0.1+1"), 35 | "author": MessageLookupByLibrary.simpleMessage("Made with ☕ by Umut"), 36 | "createAt": MessageLookupByLibrary.simpleMessage("Created at :"), 37 | "darkMode": MessageLookupByLibrary.simpleMessage("Dark Mode"), 38 | "deleteDialogPrimaryBtn": MessageLookupByLibrary.simpleMessage("Delete it"), 39 | "deleteDialogSecendaryBtn": MessageLookupByLibrary.simpleMessage("Cancel"), 40 | "deleteNoteDialogContent": MessageLookupByLibrary.simpleMessage( 41 | "Are you sure you want to delete this note?", 42 | ), 43 | "deleteNoteSnackBar": MessageLookupByLibrary.simpleMessage( 44 | "Note deleted successfully", 45 | ), 46 | "deleteTaskDialogContent": MessageLookupByLibrary.simpleMessage( 47 | "Are you sure you want to delete this task?", 48 | ), 49 | "deleteTaskSnackBar": MessageLookupByLibrary.simpleMessage( 50 | "The task successfully deleted", 51 | ), 52 | "discard": MessageLookupByLibrary.simpleMessage("Discard"), 53 | "done": MessageLookupByLibrary.simpleMessage("Done"), 54 | "editNote": MessageLookupByLibrary.simpleMessage("Edit the note!"), 55 | "editNoteSnackBar": MessageLookupByLibrary.simpleMessage( 56 | "Note edited successfully", 57 | ), 58 | "editTask": MessageLookupByLibrary.simpleMessage("Edit Task"), 59 | "editTaskSnackBar": MessageLookupByLibrary.simpleMessage( 60 | "The task edited successfully", 61 | ), 62 | "emptyNote": MessageLookupByLibrary.simpleMessage( 63 | "You have not added a note yet", 64 | ), 65 | "emptyPlan": MessageLookupByLibrary.simpleMessage( 66 | "You have no plans today", 67 | ), 68 | "emptyTextFieldError": MessageLookupByLibrary.simpleMessage( 69 | "You must fill in the title", 70 | ), 71 | "error": MessageLookupByLibrary.simpleMessage("ERROR"), 72 | "goal": MessageLookupByLibrary.simpleMessage("Set a goal !"), 73 | "language": MessageLookupByLibrary.simpleMessage("Language"), 74 | "letsGo": MessageLookupByLibrary.simpleMessage("Let\'\'s Go..."), 75 | "name": MessageLookupByLibrary.simpleMessage("On . Time"), 76 | "newTask": MessageLookupByLibrary.simpleMessage("New Task"), 77 | "note": MessageLookupByLibrary.simpleMessage("Note"), 78 | "noteTextFieldNoteHint": MessageLookupByLibrary.simpleMessage( 79 | "Write your note here...", 80 | ), 81 | "noteTextFieldTitleHint": MessageLookupByLibrary.simpleMessage("Title"), 82 | "plan": MessageLookupByLibrary.simpleMessage("Plan for tomorrow!"), 83 | "planning": MessageLookupByLibrary.simpleMessage("Planning"), 84 | "save": MessageLookupByLibrary.simpleMessage("Save"), 85 | "settings": MessageLookupByLibrary.simpleMessage("Settings"), 86 | "target": MessageLookupByLibrary.simpleMessage("Touch it!"), 87 | "taskCardNote": MessageLookupByLibrary.simpleMessage("Note: "), 88 | "taskCardPlace": MessageLookupByLibrary.simpleMessage("Place: "), 89 | "taskCardTime": MessageLookupByLibrary.simpleMessage("Time:"), 90 | "taskNoteHint": MessageLookupByLibrary.simpleMessage( 91 | "Write your note here", 92 | ), 93 | "taskNoteTitle": MessageLookupByLibrary.simpleMessage("Note"), 94 | "taskPlaceHint": MessageLookupByLibrary.simpleMessage( 95 | "What is the name of the place you want to go?", 96 | ), 97 | "taskPlaceTitle": MessageLookupByLibrary.simpleMessage("Place"), 98 | "taskTitle": MessageLookupByLibrary.simpleMessage("Title"), 99 | "taskTitleHint": MessageLookupByLibrary.simpleMessage( 100 | "Write your title here", 101 | ), 102 | "tasks": MessageLookupByLibrary.simpleMessage("Tasks"), 103 | "theme": MessageLookupByLibrary.simpleMessage("Theme"), 104 | "time": MessageLookupByLibrary.simpleMessage("Time"), 105 | "tryAgain": MessageLookupByLibrary.simpleMessage("Try Again"), 106 | "well": MessageLookupByLibrary.simpleMessage("Ok"), 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_fa.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a fa locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'fa'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "addNoteSnackBar": MessageLookupByLibrary.simpleMessage( 26 | "یادداشت با موفقیت اضافه شد...", 27 | ), 28 | "addTaskSnackBar": MessageLookupByLibrary.simpleMessage( 29 | "تسک با موفقیت اضافه شد", 30 | ), 31 | "addToNote": MessageLookupByLibrary.simpleMessage( 32 | "یادداشت تسک رو به یادداشت ها اضافه کن!", 33 | ), 34 | "appVersion": MessageLookupByLibrary.simpleMessage("0.0.1+1"), 35 | "author": MessageLookupByLibrary.simpleMessage("ساخته شده با ☕ توسط امید"), 36 | "createAt": MessageLookupByLibrary.simpleMessage("ایجاد شده در :"), 37 | "darkMode": MessageLookupByLibrary.simpleMessage("حالت تاریک"), 38 | "deleteDialogPrimaryBtn": MessageLookupByLibrary.simpleMessage("حذفش کن"), 39 | "deleteDialogSecendaryBtn": MessageLookupByLibrary.simpleMessage( 40 | "دستم خورد", 41 | ), 42 | "deleteNoteDialogContent": MessageLookupByLibrary.simpleMessage( 43 | "مطمئنی میخوای این یادداشت رو حذف کنی؟", 44 | ), 45 | "deleteNoteSnackBar": MessageLookupByLibrary.simpleMessage( 46 | "یادداشت با موفقیت حذف شد", 47 | ), 48 | "deleteTaskDialogContent": MessageLookupByLibrary.simpleMessage( 49 | "مطمئنی میخوای این تسک رو حذف کنی؟", 50 | ), 51 | "deleteTaskSnackBar": MessageLookupByLibrary.simpleMessage( 52 | "تسک با موفقیت حذف شد", 53 | ), 54 | "discard": MessageLookupByLibrary.simpleMessage("بیخیال"), 55 | "done": MessageLookupByLibrary.simpleMessage("انجام شد"), 56 | "editNote": MessageLookupByLibrary.simpleMessage( 57 | "یادداشت رو هم ویرایش کن!", 58 | ), 59 | "editNoteSnackBar": MessageLookupByLibrary.simpleMessage( 60 | "یادداشت با موفقیت ویرایش شد...", 61 | ), 62 | "editTask": MessageLookupByLibrary.simpleMessage("ویرایش برنامه"), 63 | "editTaskSnackBar": MessageLookupByLibrary.simpleMessage( 64 | "تسک با موفقیت ویرایش شد", 65 | ), 66 | "emptyNote": MessageLookupByLibrary.simpleMessage( 67 | "هنوز یادداشتی اضافه نکردی", 68 | ), 69 | "emptyPlan": MessageLookupByLibrary.simpleMessage("امروز برنامه‌ای نداری"), 70 | "emptyTextFieldError": MessageLookupByLibrary.simpleMessage( 71 | "حداقل عنوان رو پر کن", 72 | ), 73 | "error": MessageLookupByLibrary.simpleMessage("خطا"), 74 | "goal": MessageLookupByLibrary.simpleMessage("! هدف گذاری کن"), 75 | "language": MessageLookupByLibrary.simpleMessage("زبان"), 76 | "letsGo": MessageLookupByLibrary.simpleMessage("!...بزن بریم"), 77 | "name": MessageLookupByLibrary.simpleMessage("آن . تایم"), 78 | "newTask": MessageLookupByLibrary.simpleMessage("برنامه جدید"), 79 | "note": MessageLookupByLibrary.simpleMessage("یادداشت"), 80 | "noteTextFieldNoteHint": MessageLookupByLibrary.simpleMessage( 81 | "یادداشت خودتو اینجا بنویس...", 82 | ), 83 | "noteTextFieldTitleHint": MessageLookupByLibrary.simpleMessage("عنوان"), 84 | "plan": MessageLookupByLibrary.simpleMessage("!برنامه ریزی کن"), 85 | "planning": MessageLookupByLibrary.simpleMessage("برنامه ریزی"), 86 | "save": MessageLookupByLibrary.simpleMessage("ثبت"), 87 | "settings": MessageLookupByLibrary.simpleMessage("تنظیمات"), 88 | "target": MessageLookupByLibrary.simpleMessage("!برس بهش"), 89 | "taskCardNote": MessageLookupByLibrary.simpleMessage("یادداشت:"), 90 | "taskCardPlace": MessageLookupByLibrary.simpleMessage("مکان:"), 91 | "taskCardTime": MessageLookupByLibrary.simpleMessage("ساعت:"), 92 | "taskNoteHint": MessageLookupByLibrary.simpleMessage( 93 | "یادداشت خود را در اینجا وارد کنید", 94 | ), 95 | "taskNoteTitle": MessageLookupByLibrary.simpleMessage("یادداشت"), 96 | "taskPlaceHint": MessageLookupByLibrary.simpleMessage( 97 | "اسم جایی که میخای بری چیه؟", 98 | ), 99 | "taskPlaceTitle": MessageLookupByLibrary.simpleMessage("مکان"), 100 | "taskTitle": MessageLookupByLibrary.simpleMessage("عنوان"), 101 | "taskTitleHint": MessageLookupByLibrary.simpleMessage( 102 | "عنوان خود را اینجا وارد کنید", 103 | ), 104 | "tasks": MessageLookupByLibrary.simpleMessage("برنامه‌ها"), 105 | "theme": MessageLookupByLibrary.simpleMessage("تم"), 106 | "time": MessageLookupByLibrary.simpleMessage("ساعت"), 107 | "tryAgain": MessageLookupByLibrary.simpleMessage("تلاش مجدد"), 108 | "well": MessageLookupByLibrary.simpleMessage("!خب"), 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /lib/screens/settings/settings_page.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class SettingsPage extends StatelessWidget { 4 | const SettingsPage({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | var groupValue = context.read().state.locale.languageCode; 9 | return SafeArea( 10 | child: BlocConsumer( 11 | listener: (context, state) { 12 | groupValue = state.locale.languageCode; 13 | }, 14 | builder: (context, state) { 15 | return Scaffold( 16 | appBar: AppBar( 17 | automaticallyImplyLeading: false, 18 | centerTitle: true, 19 | title: Text( 20 | S.current.settings, 21 | style: AppTextStyles.appBarTitle, 22 | ), 23 | ), 24 | body: Directionality( 25 | textDirection: TextDirection.ltr, 26 | child: Column( 27 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 28 | crossAxisAlignment: CrossAxisAlignment.center, 29 | children: [ 30 | Column( 31 | mainAxisAlignment: MainAxisAlignment.start, 32 | crossAxisAlignment: CrossAxisAlignment.end, 33 | children: [ 34 | Padding( 35 | padding: const EdgeInsets.only(right: AppDimens.medium), 36 | child: Text(S.current.language), 37 | ), 38 | const Padding( 39 | padding: 40 | EdgeInsets.symmetric(horizontal: AppDimens.small), 41 | child: Divider(), 42 | ), 43 | SizedBox( 44 | width: double.infinity, 45 | height: 150, 46 | child: ListView.builder( 47 | itemCount: languageModel.length, 48 | itemBuilder: (context, index) { 49 | var item = languageModel[index]; 50 | return RadioListTile( 51 | value: item.languageCode, 52 | groupValue: groupValue, 53 | onChanged: (value) { 54 | BlocProvider.of(context).add( 55 | LoadLocalizations( 56 | Locale( 57 | item.languageCode, 58 | ), 59 | ), 60 | ); 61 | }, 62 | title: Row( 63 | mainAxisAlignment: MainAxisAlignment.end, 64 | children: [ 65 | Text(item.language), 66 | ], 67 | ), 68 | subtitle: Row( 69 | mainAxisAlignment: MainAxisAlignment.end, 70 | children: [ 71 | Text(item.subLanguage), 72 | ], 73 | ), 74 | ); 75 | }), 76 | ), 77 | Padding( 78 | padding: const EdgeInsets.only(right: AppDimens.medium), 79 | child: Text(S.current.theme), 80 | ), 81 | const Padding( 82 | padding: 83 | EdgeInsets.symmetric(horizontal: AppDimens.small), 84 | child: Divider(), 85 | ), 86 | Padding( 87 | padding: const EdgeInsets.symmetric( 88 | horizontal: AppDimens.medium), 89 | child: Directionality( 90 | textDirection: TextDirection.rtl, 91 | child: Row( 92 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 93 | crossAxisAlignment: CrossAxisAlignment.center, 94 | children: [ 95 | Padding( 96 | padding: const EdgeInsets.symmetric( 97 | horizontal: AppDimens.medium), 98 | child: Text(S.current.darkMode), 99 | ), 100 | Switch( 101 | value: state.themeMode == ThemeMode.dark, 102 | onChanged: (value) { 103 | context 104 | .read() 105 | .add(ThemeChanged(isDark: value)); 106 | }, 107 | ) 108 | ], 109 | ), 110 | ), 111 | ), 112 | ], 113 | ), 114 | Padding( 115 | padding:const EdgeInsets.only(bottom: AppDimens.large), 116 | child: Column( 117 | children: [ 118 | Text(S.current.appVersion,style: AppTextStyles.appInfoTextStyle,), 119 | Text(S.current.author,style: AppTextStyles.appInfoTextStyle,), 120 | ], 121 | ), 122 | ) 123 | ], 124 | ), 125 | ), 126 | ); 127 | }, 128 | ), 129 | ); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /lib/screens/home/home_page.dart: -------------------------------------------------------------------------------- 1 | part of '../../index.dart'; 2 | 3 | class HomePage extends StatelessWidget { 4 | const HomePage({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = 9 | FlutterLocalNotificationsPlugin(); 10 | flutterLocalNotificationsPlugin 11 | .resolvePlatformSpecificImplementation< 12 | AndroidFlutterLocalNotificationsPlugin>() 13 | ?.requestNotificationsPermission(); 14 | Size size = MediaQuery.sizeOf(context); 15 | return SafeArea( 16 | child: Scaffold( 17 | appBar: CustomAppBar( 18 | appBarHeight: 100, 19 | child: Text( 20 | S.current.name, 21 | style: AppTextStyles.appNameTextStyle, 22 | ), 23 | ), 24 | body: Column( 25 | children: [ 26 | Expanded( 27 | flex: 1, 28 | child: Row( 29 | mainAxisAlignment: MainAxisAlignment.center, 30 | children: [ 31 | ConstrainedBox( 32 | constraints: BoxConstraints( 33 | maxWidth: min(size.width * 0.9, 400), 34 | ), 35 | child: Container( 36 | height: size.height / 16.4, 37 | decoration: BoxDecoration( 38 | color: Theme.of(context).colorScheme.primary, 39 | borderRadius: BorderRadius.circular(10), 40 | ), 41 | child: Row( 42 | mainAxisAlignment: MainAxisAlignment.spaceAround, 43 | children: [ 44 | Expanded( 45 | child: ZoomTapAnimation( 46 | onTap: () { 47 | context.read().add(const ChangePage(0)); 48 | }, 49 | child: BlocBuilder( 50 | builder: (context, state) { 51 | return Container( 52 | margin: const EdgeInsets.symmetric(horizontal: 4), 53 | height: size.height / 19, 54 | decoration: BoxDecoration( 55 | borderRadius: BorderRadius.circular(8), 56 | color: state.selectedPageIndex == 0 57 | ? Theme.of(context).colorScheme.onPrimary 58 | : Colors.transparent, 59 | ), 60 | child: Center( 61 | child: Text(S.current.planning, 62 | style: AppTextStyles.chipTextStyle.apply( 63 | color: state.selectedPageIndex == 0 64 | ? Theme.of(context) 65 | .colorScheme 66 | .primary 67 | : Theme.of(context) 68 | .colorScheme 69 | .onPrimary, 70 | )), 71 | ), 72 | ); 73 | }, 74 | ), 75 | ), 76 | ), 77 | Expanded( 78 | child: ZoomTapAnimation( 79 | onTap: () { 80 | context.read().add(const ChangePage(1)); 81 | }, 82 | child: BlocBuilder( 83 | builder: (context, state) { 84 | return Container( 85 | margin: const EdgeInsets.symmetric(horizontal: 4), 86 | height: size.height / 19, 87 | decoration: BoxDecoration( 88 | borderRadius: BorderRadius.circular(8), 89 | color: state.selectedPageIndex == 1 90 | ? Theme.of(context).colorScheme.onPrimary 91 | : Colors.transparent, 92 | ), 93 | child: Center( 94 | child: Text( 95 | S.current.note, 96 | style: AppTextStyles.chipTextStyle.apply( 97 | color: state.selectedPageIndex == 1 98 | ? Theme.of(context) 99 | .colorScheme 100 | .primary 101 | : Theme.of(context) 102 | .colorScheme 103 | .onPrimary, 104 | ), 105 | ), 106 | ), 107 | ); 108 | }, 109 | ), 110 | ), 111 | ), 112 | ], 113 | ), 114 | ), 115 | ), 116 | ], 117 | ), 118 | ), 119 | Expanded( 120 | flex: 13, 121 | child: BlocBuilder( 122 | builder: (context, state) { 123 | return IndexedStack( 124 | alignment: Alignment.topCenter, 125 | index: state.selectedPageIndex, 126 | children: const [ 127 | Tasks(), 128 | Notes(), 129 | ], 130 | ); 131 | }, 132 | ), 133 | ), 134 | ], 135 | ), 136 | floatingActionButton: BlocBuilder( 137 | builder: (context, state) { 138 | return state.selectedPageIndex == 1 139 | ? FloatingActionButton( 140 | child: Icon( 141 | Icons.add, 142 | color: Theme.of(context).colorScheme.surface, 143 | ), 144 | onPressed: () { 145 | Navigator.push( 146 | context, 147 | CupertinoPageRoute( 148 | builder: (context) => const AddNoteScreen(), 149 | ), 150 | ); 151 | }, 152 | ) 153 | : const SizedBox.shrink(); 154 | }, 155 | ), 156 | ), 157 | ); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /lib/screens/home/task/tasks.dart: -------------------------------------------------------------------------------- 1 | // Tasks.dart 2 | 3 | part of '../../../index.dart'; 4 | 5 | Box taskBox = Hive.box(taskBoxName); 6 | 7 | class Tasks extends StatefulWidget { 8 | const Tasks({super.key}); 9 | 10 | @override 11 | State createState() => _TasksState(); 12 | } 13 | 14 | class _TasksState extends State { 15 | DateTime _selectedDate = DateTime.now(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final todoList = taskBox.values.toList(); 20 | final taskDates = todoList 21 | .map((task) => DateTime( 22 | task.dateTime.year, task.dateTime.month, task.dateTime.day)) 23 | .toSet() 24 | .toList(); 25 | 26 | String day = DateTimeExtensions(_selectedDate).toJalali().day.toPersianNumberInt(); 27 | String month = DateTimeExtensions(_selectedDate).toJalali().month.toPesianMonth(); 28 | String year = DateTimeExtensions(_selectedDate).toJalali().year.toPersianNumberInt(); 29 | 30 | return BlocBuilder( 31 | builder: (context, state) { 32 | return Column( 33 | children: [ 34 | if (state.locale.languageCode == "fa") 35 | Column( 36 | children: [ 37 | Container( 38 | margin: const EdgeInsets.only(top: AppDimens.medium), 39 | padding: const EdgeInsets.symmetric( 40 | horizontal: AppDimens.medium), 41 | child: Text( 42 | ' $day, $month , $year', 43 | style: AppTextStyles.appTopText, 44 | textDirection: TextDirection.rtl, 45 | ), 46 | ), 47 | Padding( 48 | padding: const EdgeInsets.only(top: AppDimens.medium), 49 | child: PersianHorizontalDatePicker( 50 | weekDayTextStyle: AppTextStyles.selectedTextStyle.apply( 51 | color: Theme.of(context).colorScheme.onSurface, 52 | ), 53 | dayTextStyle: AppTextStyles.selectedTextStyle.apply( 54 | color: Theme.of(context).colorScheme.onSurface, 55 | ), 56 | monthTextStyle: AppTextStyles.selectedTextStyle.apply( 57 | color: Theme.of(context).colorScheme.onSurface, 58 | ), 59 | markedDates: taskDates, 60 | hasShadow: false, 61 | initialSelectedDate: DateTime.now(), 62 | datePickerHeight: 90, 63 | startDate: DateTime.now().subtract(const Duration(days: 30)), 64 | endDate: DateTime.now().add(const Duration(days: 30)), 65 | backgroundColor: Colors.transparent, 66 | onBackgroundColor: Theme.of(context).colorScheme.onSurface, 67 | selectedOnBackgroundColor: Theme.of(context).colorScheme.surface, 68 | selectedBackgroundColor: 69 | Theme.of(context).colorScheme.onSurface, 70 | onDateSelected: (date) { 71 | setState(() { 72 | _selectedDate = date!; 73 | }); 74 | }, 75 | ), 76 | ), 77 | ], 78 | ), 79 | if (state.locale.languageCode == "en") 80 | Column( 81 | children: [ 82 | Container( 83 | margin: const EdgeInsets.only(top: AppDimens.medium), 84 | padding: const EdgeInsets.symmetric( 85 | horizontal: AppDimens.medium), 86 | child: Text( 87 | ' ${_selectedDate.day}, ${_selectedDate.month.toGregorianMonth()} , ${_selectedDate.year}', 88 | style: AppTextStyles.appTopText, 89 | ), 90 | ), 91 | Padding( 92 | padding: const EdgeInsets.only(top: AppDimens.medium), 93 | child: PersianHorizontalDatePicker( 94 | weekDayTextStyle: AppTextStyles.selectedTextStyle.apply( 95 | color: Theme.of(context).colorScheme.onSurface, 96 | ), 97 | dayTextStyle: AppTextStyles.selectedTextStyle.apply( 98 | color: Theme.of(context).colorScheme.onSurface, 99 | ), 100 | monthTextStyle: AppTextStyles.selectedTextStyle.apply( 101 | color: Theme.of(context).colorScheme.onSurface, 102 | ), 103 | markedDates: taskDates, 104 | isPersianDate: false, 105 | hasShadow: false, 106 | initialSelectedDate: DateTime.now(), 107 | datePickerHeight: 90, 108 | startDate: DateTime(2024, 6, 6), 109 | endDate: DateTime(2050, 6, 6), 110 | backgroundColor: Colors.transparent, 111 | onBackgroundColor: Theme.of(context).colorScheme.onSurface, 112 | selectedOnBackgroundColor: Theme.of(context).colorScheme.surface, 113 | selectedBackgroundColor: 114 | Theme.of(context).colorScheme.onSurface, 115 | onDateSelected: (date) { 116 | setState(() { 117 | _selectedDate = date!; 118 | }); 119 | }, 120 | ), 121 | ), 122 | ], 123 | ), 124 | Row( 125 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 126 | children: [ 127 | Padding( 128 | padding: const EdgeInsets.all(AppDimens.medium), 129 | child: Text( 130 | S.current.tasks, 131 | style: AppTextStyles.bodyTitleTextStyle.apply( 132 | color: Theme.of(context).colorScheme.onSurface, 133 | ), 134 | ), 135 | ), 136 | Padding( 137 | padding: 138 | const EdgeInsets.symmetric(horizontal: AppDimens.medium), 139 | child: IconButton.filled( 140 | iconSize: 35, 141 | onPressed: () { 142 | Navigator.push( 143 | context, 144 | CupertinoPageRoute( 145 | builder: (context) => AddTaskScreen( 146 | date: _selectedDate, 147 | ), 148 | ), 149 | ); 150 | }, 151 | icon: const Icon(Icons.add_rounded), 152 | ), 153 | ), 154 | ], 155 | ), 156 | BlocConsumer( 157 | listener: (context, state) { 158 | if (state is DeleteTaskState) { 159 | ScaffoldMessenger.of(context).showSnackBar( 160 | SnackBar( 161 | duration: const Duration(seconds: 1), 162 | content: Text( 163 | S.current.deleteTaskSnackBar, 164 | ), 165 | ), 166 | ); 167 | } 168 | }, 169 | builder: (context, state) { 170 | if (state is TaskLoadedState) { 171 | 172 | return TaskList( 173 | selectedDate: _selectedDate, 174 | ); 175 | } else if (state is DeleteAllTasksState || 176 | state is DeleteTaskState || 177 | state is SaveTaskState || 178 | state is UpdateTaskState || 179 | state is IsComplatedTaskState) { 180 | return TaskList( 181 | selectedDate: _selectedDate, 182 | ); 183 | } else if (state is TaskError) { 184 | return Text(S.current.error); 185 | } else if (state is TaskLoadingState) { 186 | return const LinearProgressIndicator(); 187 | } else { 188 | return ElevatedButton( 189 | onPressed: () { 190 | BlocProvider.of(context).add(TaskInit()); 191 | }, 192 | child: Text(S.current.tryAgain), 193 | ); 194 | } 195 | }, 196 | ), 197 | ], 198 | ); 199 | }, 200 | ); 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /lib/screens/home/note/add_note_screen.dart: -------------------------------------------------------------------------------- 1 | part of '../../../index.dart'; 2 | 3 | class AddNoteScreen extends StatefulWidget { 4 | const AddNoteScreen({ 5 | super.key, 6 | }); 7 | 8 | @override 9 | State createState() => _AddNoteScreenState(); 10 | } 11 | 12 | class _AddNoteScreenState extends State { 13 | final TextEditingController _titleController = TextEditingController(); 14 | 15 | final TextEditingController _descriptionController = TextEditingController(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | var lang = BlocProvider.of(context).state.locale.languageCode; 20 | var uuid = const Uuid(); 21 | String uuidString = uuid.v4(); 22 | List bytes = utf8.encode(uuidString); 23 | int noteId = bytes.fold(0, (previousValue, element) { 24 | return previousValue + element; 25 | }); 26 | return SafeArea( 27 | child: Scaffold( 28 | backgroundColor: Color(_noteSelectedColor.code), 29 | body: Column( 30 | children: [ 31 | Expanded( 32 | child: ListView( 33 | keyboardDismissBehavior: 34 | ScrollViewKeyboardDismissBehavior.onDrag, 35 | padding: const EdgeInsets.symmetric( 36 | horizontal: 20, 37 | vertical: 20, 38 | ), 39 | children: [ 40 | TextField( 41 | controller: _titleController, 42 | style: AppTextStyles.noteTitleTextStyle 43 | .apply(color: AppColors.appPrimaryDark), 44 | decoration: InputDecoration( 45 | hintText: S.current.noteTextFieldTitleHint, 46 | border: InputBorder.none, 47 | contentPadding: EdgeInsets.zero, 48 | hintStyle: const TextStyle( 49 | color: Color.fromARGB(132, 22, 25, 40)), 50 | counterText: '', 51 | ), 52 | minLines: 1, 53 | maxLines: 8, 54 | maxLength: 140, 55 | ), 56 | const SizedBox(height: 20), 57 | TextField( 58 | controller: _descriptionController, 59 | style: AppTextStyles.noteDecTextStyle 60 | .apply(color: AppColors.appPrimaryDark), 61 | decoration: InputDecoration( 62 | hintText: S.current.noteTextFieldNoteHint, 63 | border: InputBorder.none, 64 | hintStyle: const TextStyle( 65 | color: Color.fromARGB(132, 22, 25, 40)), 66 | contentPadding: EdgeInsets.zero, 67 | ), 68 | minLines: 2, 69 | maxLines: 100, 70 | ), 71 | ], 72 | ), 73 | ), 74 | Column( 75 | children: [ 76 | Padding( 77 | padding: const EdgeInsets.only(right: AppDimens.small), 78 | child: NoteColorSelector( 79 | selectedColor: _noteSelectedColor, 80 | onColorSelected: (color) { 81 | setState(() { 82 | _noteSelectedColor = color; 83 | }); 84 | }, 85 | ), 86 | ), 87 | Padding( 88 | padding: const EdgeInsets.all(AppDimens.small), 89 | child: Row( 90 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 91 | children: [ 92 | Expanded( 93 | child: SizedBox( 94 | height: 50, 95 | child: BlocConsumer( 96 | listener: (context, state) { 97 | if (state is SaveNoteState) { 98 | ScaffoldMessenger.of(context).showSnackBar( 99 | SnackBar( 100 | duration: const Duration(milliseconds: 500), 101 | content: Text( 102 | S.current.addNoteSnackBar, 103 | ), 104 | ), 105 | ); 106 | } 107 | }, 108 | builder: (context, state) { 109 | return TextButton( 110 | style: const ButtonStyle( 111 | backgroundColor: WidgetStatePropertyAll( 112 | AppColors.appPrimaryDark), 113 | ), 114 | onPressed: () { 115 | if (_titleController.text.isEmpty) { 116 | ScaffoldMessenger.of(context).showSnackBar( 117 | SnackBar( 118 | content: Text( 119 | S.current.emptyTextFieldError, 120 | ), 121 | ), 122 | ); 123 | } else { 124 | if (lang == 'fa') { 125 | String dateTime = 126 | '${DateTime.now().hour.toPersianNumberInt()}:${DateTime.now().minute.toPersianNumberInt()} / ${Jalali.now().day.toPersianNumberInt()} - ${Jalali.now().month.toPesianMonth()} '; 127 | BlocProvider.of(context).add( 128 | SaveNoteEvent( 129 | NoteModel( 130 | id: noteId, 131 | title: _titleController.text, 132 | description: 133 | _descriptionController.text, 134 | color: _noteSelectedColor, 135 | dateTime: dateTime), 136 | ), 137 | ); 138 | } else { 139 | String gDateTime = 140 | '${DateTime.now().hour}:${DateTime.now().minute} / ${DateTime.now().day} - ${DateTime.now().month.toGregorianMonth()} '; 141 | 142 | BlocProvider.of(context).add( 143 | SaveNoteEvent( 144 | NoteModel( 145 | id: noteId, 146 | title: _titleController.text, 147 | description: 148 | _descriptionController.text, 149 | color: _noteSelectedColor, 150 | dateTime: gDateTime), 151 | ), 152 | ); 153 | } 154 | 155 | Navigator.pop(context); 156 | } 157 | }, 158 | child: Text( 159 | S.current.save, 160 | style: const TextStyle( 161 | color: AppColors.appOnPrimaryDark), 162 | ), 163 | ); 164 | }, 165 | ), 166 | ), 167 | ), 168 | AppDimens.small.width, 169 | Expanded( 170 | child: SizedBox( 171 | height: 50, 172 | child: TextButton( 173 | style: const ButtonStyle( 174 | backgroundColor: WidgetStatePropertyAll( 175 | AppColors.appPrimaryDark, 176 | ), 177 | ), 178 | onPressed: () { 179 | Navigator.pop(context); 180 | }, 181 | child: Text( 182 | S.current.discard, 183 | style: const TextStyle( 184 | color: AppColors.appOnPrimaryDark), 185 | ), 186 | ), 187 | ), 188 | ), 189 | ], 190 | ), 191 | ) 192 | ], 193 | ) 194 | ], 195 | ), 196 | ), 197 | ); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2024 [OmidHaqi] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /lib/generated/l10n.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'intl/messages_all.dart'; 5 | 6 | // ************************************************************************** 7 | // Generator: Flutter Intl IDE plugin 8 | // Made by Localizely 9 | // ************************************************************************** 10 | 11 | // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars 12 | // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each 13 | // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes 14 | 15 | class S { 16 | S(); 17 | 18 | static S? _current; 19 | 20 | static S get current { 21 | assert( 22 | _current != null, 23 | 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.', 24 | ); 25 | return _current!; 26 | } 27 | 28 | static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); 29 | 30 | static Future load(Locale locale) { 31 | final name = 32 | (locale.countryCode?.isEmpty ?? false) 33 | ? locale.languageCode 34 | : locale.toString(); 35 | final localeName = Intl.canonicalizedLocale(name); 36 | return initializeMessages(localeName).then((_) { 37 | Intl.defaultLocale = localeName; 38 | final instance = S(); 39 | S._current = instance; 40 | 41 | return instance; 42 | }); 43 | } 44 | 45 | static S of(BuildContext context) { 46 | final instance = S.maybeOf(context); 47 | assert( 48 | instance != null, 49 | 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?', 50 | ); 51 | return instance!; 52 | } 53 | 54 | static S? maybeOf(BuildContext context) { 55 | return Localizations.of(context, S); 56 | } 57 | 58 | /// `On . Time` 59 | String get name { 60 | return Intl.message('On . Time', name: 'name', desc: '', args: []); 61 | } 62 | 63 | /// `Language` 64 | String get language { 65 | return Intl.message('Language', name: 'language', desc: '', args: []); 66 | } 67 | 68 | /// `Settings` 69 | String get settings { 70 | return Intl.message('Settings', name: 'settings', desc: '', args: []); 71 | } 72 | 73 | /// `Theme` 74 | String get theme { 75 | return Intl.message('Theme', name: 'theme', desc: '', args: []); 76 | } 77 | 78 | /// `Dark Mode` 79 | String get darkMode { 80 | return Intl.message('Dark Mode', name: 'darkMode', desc: '', args: []); 81 | } 82 | 83 | /// `Planning` 84 | String get planning { 85 | return Intl.message('Planning', name: 'planning', desc: '', args: []); 86 | } 87 | 88 | /// `Note` 89 | String get note { 90 | return Intl.message('Note', name: 'note', desc: '', args: []); 91 | } 92 | 93 | /// `Ok` 94 | String get well { 95 | return Intl.message('Ok', name: 'well', desc: '', args: []); 96 | } 97 | 98 | /// `Let''s Go...` 99 | String get letsGo { 100 | return Intl.message('Let\'\'s Go...', name: 'letsGo', desc: '', args: []); 101 | } 102 | 103 | /// `Set a goal !` 104 | String get goal { 105 | return Intl.message('Set a goal !', name: 'goal', desc: '', args: []); 106 | } 107 | 108 | /// `Plan for tomorrow!` 109 | String get plan { 110 | return Intl.message('Plan for tomorrow!', name: 'plan', desc: '', args: []); 111 | } 112 | 113 | /// `Touch it!` 114 | String get target { 115 | return Intl.message('Touch it!', name: 'target', desc: '', args: []); 116 | } 117 | 118 | /// `Done` 119 | String get done { 120 | return Intl.message('Done', name: 'done', desc: '', args: []); 121 | } 122 | 123 | /// `Tasks` 124 | String get tasks { 125 | return Intl.message('Tasks', name: 'tasks', desc: '', args: []); 126 | } 127 | 128 | /// `You have not added a note yet` 129 | String get emptyNote { 130 | return Intl.message( 131 | 'You have not added a note yet', 132 | name: 'emptyNote', 133 | desc: '', 134 | args: [], 135 | ); 136 | } 137 | 138 | /// `You have no plans today` 139 | String get emptyPlan { 140 | return Intl.message( 141 | 'You have no plans today', 142 | name: 'emptyPlan', 143 | desc: '', 144 | args: [], 145 | ); 146 | } 147 | 148 | /// `New Task` 149 | String get newTask { 150 | return Intl.message('New Task', name: 'newTask', desc: '', args: []); 151 | } 152 | 153 | /// `Title` 154 | String get taskTitle { 155 | return Intl.message('Title', name: 'taskTitle', desc: '', args: []); 156 | } 157 | 158 | /// `Write your title here` 159 | String get taskTitleHint { 160 | return Intl.message( 161 | 'Write your title here', 162 | name: 'taskTitleHint', 163 | desc: '', 164 | args: [], 165 | ); 166 | } 167 | 168 | /// `Note` 169 | String get taskNoteTitle { 170 | return Intl.message('Note', name: 'taskNoteTitle', desc: '', args: []); 171 | } 172 | 173 | /// `Write your note here` 174 | String get taskNoteHint { 175 | return Intl.message( 176 | 'Write your note here', 177 | name: 'taskNoteHint', 178 | desc: '', 179 | args: [], 180 | ); 181 | } 182 | 183 | /// `Place` 184 | String get taskPlaceTitle { 185 | return Intl.message('Place', name: 'taskPlaceTitle', desc: '', args: []); 186 | } 187 | 188 | /// `What is the name of the place you want to go?` 189 | String get taskPlaceHint { 190 | return Intl.message( 191 | 'What is the name of the place you want to go?', 192 | name: 'taskPlaceHint', 193 | desc: '', 194 | args: [], 195 | ); 196 | } 197 | 198 | /// `Time` 199 | String get time { 200 | return Intl.message('Time', name: 'time', desc: '', args: []); 201 | } 202 | 203 | /// `Task added successfully` 204 | String get addTaskSnackBar { 205 | return Intl.message( 206 | 'Task added successfully', 207 | name: 'addTaskSnackBar', 208 | desc: '', 209 | args: [], 210 | ); 211 | } 212 | 213 | /// `The task successfully deleted` 214 | String get deleteTaskSnackBar { 215 | return Intl.message( 216 | 'The task successfully deleted', 217 | name: 'deleteTaskSnackBar', 218 | desc: '', 219 | args: [], 220 | ); 221 | } 222 | 223 | /// `You must fill in the title` 224 | String get emptyTextFieldError { 225 | return Intl.message( 226 | 'You must fill in the title', 227 | name: 'emptyTextFieldError', 228 | desc: '', 229 | args: [], 230 | ); 231 | } 232 | 233 | /// `Save` 234 | String get save { 235 | return Intl.message('Save', name: 'save', desc: '', args: []); 236 | } 237 | 238 | /// `Discard` 239 | String get discard { 240 | return Intl.message('Discard', name: 'discard', desc: '', args: []); 241 | } 242 | 243 | /// `Edit Task` 244 | String get editTask { 245 | return Intl.message('Edit Task', name: 'editTask', desc: '', args: []); 246 | } 247 | 248 | /// `The task edited successfully` 249 | String get editTaskSnackBar { 250 | return Intl.message( 251 | 'The task edited successfully', 252 | name: 'editTaskSnackBar', 253 | desc: '', 254 | args: [], 255 | ); 256 | } 257 | 258 | /// `Are you sure you want to delete this task?` 259 | String get deleteTaskDialogContent { 260 | return Intl.message( 261 | 'Are you sure you want to delete this task?', 262 | name: 'deleteTaskDialogContent', 263 | desc: '', 264 | args: [], 265 | ); 266 | } 267 | 268 | /// `Delete it` 269 | String get deleteDialogPrimaryBtn { 270 | return Intl.message( 271 | 'Delete it', 272 | name: 'deleteDialogPrimaryBtn', 273 | desc: '', 274 | args: [], 275 | ); 276 | } 277 | 278 | /// `Cancel` 279 | String get deleteDialogSecendaryBtn { 280 | return Intl.message( 281 | 'Cancel', 282 | name: 'deleteDialogSecendaryBtn', 283 | desc: '', 284 | args: [], 285 | ); 286 | } 287 | 288 | /// `Title` 289 | String get noteTextFieldTitleHint { 290 | return Intl.message( 291 | 'Title', 292 | name: 'noteTextFieldTitleHint', 293 | desc: '', 294 | args: [], 295 | ); 296 | } 297 | 298 | /// `Write your note here...` 299 | String get noteTextFieldNoteHint { 300 | return Intl.message( 301 | 'Write your note here...', 302 | name: 'noteTextFieldNoteHint', 303 | desc: '', 304 | args: [], 305 | ); 306 | } 307 | 308 | /// `Note added successfully` 309 | String get addNoteSnackBar { 310 | return Intl.message( 311 | 'Note added successfully', 312 | name: 'addNoteSnackBar', 313 | desc: '', 314 | args: [], 315 | ); 316 | } 317 | 318 | /// `Note edited successfully` 319 | String get editNoteSnackBar { 320 | return Intl.message( 321 | 'Note edited successfully', 322 | name: 'editNoteSnackBar', 323 | desc: '', 324 | args: [], 325 | ); 326 | } 327 | 328 | /// `Created at :` 329 | String get createAt { 330 | return Intl.message('Created at :', name: 'createAt', desc: '', args: []); 331 | } 332 | 333 | /// `Are you sure you want to delete this note?` 334 | String get deleteNoteDialogContent { 335 | return Intl.message( 336 | 'Are you sure you want to delete this note?', 337 | name: 'deleteNoteDialogContent', 338 | desc: '', 339 | args: [], 340 | ); 341 | } 342 | 343 | /// `Note deleted successfully` 344 | String get deleteNoteSnackBar { 345 | return Intl.message( 346 | 'Note deleted successfully', 347 | name: 'deleteNoteSnackBar', 348 | desc: '', 349 | args: [], 350 | ); 351 | } 352 | 353 | /// `ERROR` 354 | String get error { 355 | return Intl.message('ERROR', name: 'error', desc: '', args: []); 356 | } 357 | 358 | /// `Try Again` 359 | String get tryAgain { 360 | return Intl.message('Try Again', name: 'tryAgain', desc: '', args: []); 361 | } 362 | 363 | /// `Time:` 364 | String get taskCardTime { 365 | return Intl.message('Time:', name: 'taskCardTime', desc: '', args: []); 366 | } 367 | 368 | /// `Place: ` 369 | String get taskCardPlace { 370 | return Intl.message('Place: ', name: 'taskCardPlace', desc: '', args: []); 371 | } 372 | 373 | /// `Note: ` 374 | String get taskCardNote { 375 | return Intl.message('Note: ', name: 'taskCardNote', desc: '', args: []); 376 | } 377 | 378 | /// `0.0.1+1` 379 | String get appVersion { 380 | return Intl.message('0.0.1+1', name: 'appVersion', desc: '', args: []); 381 | } 382 | 383 | /// `Made with ☕ by Umut` 384 | String get author { 385 | return Intl.message( 386 | 'Made with ☕ by Umut', 387 | name: 'author', 388 | desc: '', 389 | args: [], 390 | ); 391 | } 392 | 393 | /// `Add the task note to notes!` 394 | String get addToNote { 395 | return Intl.message( 396 | 'Add the task note to notes!', 397 | name: 'addToNote', 398 | desc: '', 399 | args: [], 400 | ); 401 | } 402 | 403 | /// `Edit the note!` 404 | String get editNote { 405 | return Intl.message('Edit the note!', name: 'editNote', desc: '', args: []); 406 | } 407 | } 408 | 409 | class AppLocalizationDelegate extends LocalizationsDelegate { 410 | const AppLocalizationDelegate(); 411 | 412 | List get supportedLocales { 413 | return const [ 414 | Locale.fromSubtags(languageCode: 'en'), 415 | Locale.fromSubtags(languageCode: 'fa'), 416 | ]; 417 | } 418 | 419 | @override 420 | bool isSupported(Locale locale) => _isSupported(locale); 421 | @override 422 | Future load(Locale locale) => S.load(locale); 423 | @override 424 | bool shouldReload(AppLocalizationDelegate old) => false; 425 | 426 | bool _isSupported(Locale locale) { 427 | for (var supportedLocale in supportedLocales) { 428 | if (supportedLocale.languageCode == locale.languageCode) { 429 | return true; 430 | } 431 | } 432 | return false; 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /lib/screens/home/note/edit_note_screen.dart: -------------------------------------------------------------------------------- 1 | part of '../../../index.dart'; 2 | 3 | class EditNoteScreen extends StatefulWidget { 4 | const EditNoteScreen({super.key, required this.note}); 5 | 6 | final NoteModel note; 7 | 8 | @override 9 | State createState() => _EditNoteScreenState(); 10 | } 11 | 12 | class _EditNoteScreenState extends State { 13 | 14 | late TextEditingController _titleController; 15 | late TextEditingController _descriptionController; 16 | 17 | @override 18 | void initState() { 19 | _titleController = TextEditingController(text: widget.note.title); 20 | _descriptionController = 21 | TextEditingController(text: widget.note.description); 22 | super.initState(); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | var lang = BlocProvider.of(context).state.locale.languageCode; 28 | 29 | return SafeArea( 30 | child: Scaffold( 31 | backgroundColor: Color(_noteSelectedColor.code), 32 | body: Column( 33 | children: [ 34 | Expanded( 35 | child: ListView( 36 | keyboardDismissBehavior: 37 | ScrollViewKeyboardDismissBehavior.onDrag, 38 | padding: const EdgeInsets.symmetric( 39 | horizontal: 20, 40 | vertical: 20, 41 | ), 42 | children: [ 43 | Stack(children: [ 44 | TextField( 45 | controller: _titleController, 46 | style: AppTextStyles.noteTitleTextStyle 47 | .apply(color: AppColors.appPrimaryDark), 48 | decoration: InputDecoration( 49 | hintText: S.current.noteTextFieldTitleHint, 50 | hintStyle: const TextStyle( 51 | color: Color.fromARGB(132, 22, 25, 40)), 52 | border: InputBorder.none, 53 | contentPadding: EdgeInsets.zero, 54 | counterText: '', 55 | ), 56 | minLines: 1, 57 | maxLines: 8, 58 | maxLength: 140, 59 | ), 60 | Positioned( 61 | left: lang == 'fa' ? 0 : null, 62 | right: lang == 'en' ? 0 : null, 63 | child: IconButton( 64 | onPressed: () { 65 | customeDialogee( 66 | context, 67 | content: S.current.deleteNoteDialogContent, 68 | primaryBtn: S.current.deleteDialogPrimaryBtn, 69 | onTapPrimaryBtn: () { 70 | BlocProvider.of(context).add( 71 | DeleteNoteEvent( 72 | widget.note.id, 73 | ), 74 | ); 75 | Navigator.pop(context); 76 | Navigator.pop(context); 77 | }, 78 | onTapSecendaryBtn: () { 79 | Navigator.pop(context); 80 | }, 81 | secendaryBtn: S.current.deleteDialogSecendaryBtn, 82 | ); 83 | 84 | 85 | }, 86 | icon: const Icon( 87 | Icons.delete_rounded, 88 | color: AppColors.appPrimaryDark, 89 | ), 90 | ), 91 | ), 92 | ]), 93 | const SizedBox(height: 20), 94 | TextField( 95 | controller: _descriptionController, 96 | style: AppTextStyles.noteDecTextStyle 97 | .apply(color: AppColors.appPrimaryDark), 98 | decoration: InputDecoration( 99 | hintText: S.current.noteTextFieldTitleHint, 100 | border: InputBorder.none, 101 | hintStyle: const TextStyle( 102 | color: Color.fromARGB(132, 22, 25, 40), 103 | ), 104 | contentPadding: EdgeInsets.zero, 105 | ), 106 | minLines: 2, 107 | maxLines: 100, 108 | onChanged: (value) {}, 109 | ), 110 | const SizedBox(height: 20), 111 | Row( 112 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 113 | children: [ 114 | Text( 115 | S.current.createAt, 116 | style: AppTextStyles.noteDecTextStyle 117 | .apply(color: AppColors.appPrimaryDark), 118 | ), 119 | Text( 120 | widget.note.dateTime, 121 | style: AppTextStyles.noteDecTextStyle 122 | .apply(color: AppColors.appPrimaryDark), 123 | ), 124 | ], 125 | ), 126 | ], 127 | ), 128 | ), 129 | Column( 130 | children: [ 131 | Padding( 132 | padding: const EdgeInsets.only(right: AppDimens.small), 133 | child: NoteColorSelector( 134 | selectedColor: _noteSelectedColor, 135 | onColorSelected: (color) { 136 | setState(() { 137 | _noteSelectedColor = color; 138 | }); 139 | }, 140 | ), 141 | ), 142 | Padding( 143 | padding: const EdgeInsets.all(AppDimens.small), 144 | child: Row( 145 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 146 | children: [ 147 | Expanded( 148 | child: SizedBox( 149 | height: 50, 150 | child: BlocConsumer( 151 | listener: (context, state) { 152 | if (state is UpdateNoteState) { 153 | ScaffoldMessenger.of(context).showSnackBar( 154 | SnackBar( 155 | duration: const Duration(milliseconds: 500), 156 | content: Text( 157 | S.current.editNoteSnackBar, 158 | ), 159 | ), 160 | ); 161 | } 162 | }, 163 | builder: (context, state) { 164 | return TextButton( 165 | style: const ButtonStyle( 166 | backgroundColor: WidgetStatePropertyAll( 167 | AppColors.appPrimaryDark, 168 | ), 169 | ), 170 | onPressed: () { 171 | if (_titleController.text.isEmpty) { 172 | ScaffoldMessenger.of(context).showSnackBar( 173 | SnackBar( 174 | content: Text( 175 | S.current.emptyTextFieldError, 176 | ), 177 | ), 178 | ); 179 | } else { 180 | if (lang == 'fa') { 181 | String persianDateTime = 182 | '${DateTime.now().hour.toPersianNumberInt()}:${DateTime.now().minute.toPersianNumberInt()} / ${Jalali.now().day.toPersianNumberInt()} - ${Jalali.now().month.toPesianMonth()} '; 183 | BlocProvider.of(context).add( 184 | UpdateNoteEvent( 185 | NoteModel( 186 | id: widget.note.id, 187 | title: _titleController.text, 188 | description: 189 | _descriptionController.text, 190 | color: _noteSelectedColor, 191 | dateTime: persianDateTime), 192 | widget.note.id), 193 | ); 194 | } else { 195 | String gDateTime = 196 | '${DateTime.now().hour}:${DateTime.now().minute} / ${DateTime.now().day} - ${DateTime.now().month.toGregorianMonth()} '; 197 | BlocProvider.of(context).add( 198 | UpdateNoteEvent( 199 | NoteModel( 200 | id: widget.note.id, 201 | title: _titleController.text, 202 | description: 203 | _descriptionController.text, 204 | color: _noteSelectedColor, 205 | dateTime: gDateTime), 206 | widget.note.id), 207 | ); 208 | } 209 | 210 | Navigator.pop(context); 211 | } 212 | }, 213 | child: Text( 214 | S.current.save, 215 | style: const TextStyle( 216 | color: AppColors.appOnPrimaryDark, 217 | ), 218 | ), 219 | ); 220 | }, 221 | ), 222 | ), 223 | ), 224 | AppDimens.small.width, 225 | Expanded( 226 | child: SizedBox( 227 | height: 50, 228 | child: TextButton( 229 | style: const ButtonStyle( 230 | backgroundColor: WidgetStatePropertyAll( 231 | AppColors.appPrimaryDark, 232 | ), 233 | ), 234 | onPressed: () { 235 | Navigator.pop(context); 236 | }, 237 | child: Text( 238 | S.current.discard, 239 | style: const TextStyle( 240 | color: AppColors.appOnPrimaryDark), 241 | ), 242 | ), 243 | ), 244 | ), 245 | ], 246 | ), 247 | ), 248 | ], 249 | ), 250 | ], 251 | ), 252 | ), 253 | ); 254 | } 255 | } 256 | --------------------------------------------------------------------------------