├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── berkcicekler │ │ │ │ └── langbuddy │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── app_icon │ ├── lang_buddy.png │ ├── lang_buddy_background.png │ └── lang_buddy_clear.png ├── fonts │ └── inter │ │ ├── Inter-Black.ttf │ │ ├── Inter-Bold.ttf │ │ ├── Inter-ExtraBold.ttf │ │ ├── Inter-ExtraLight.ttf │ │ ├── Inter-Light.ttf │ │ ├── Inter-Medium.ttf │ │ ├── Inter-Regular.ttf │ │ ├── Inter-SemiBold.ttf │ │ └── Inter-Thin.ttf ├── photos │ └── profile_picture.jpg └── svg │ └── countryFlags │ ├── ac.svg │ ├── ad.svg │ ├── ae.svg │ ├── af.svg │ ├── ag.svg │ ├── ai.svg │ ├── al.svg │ ├── am.svg │ ├── ao.svg │ ├── aq.svg │ ├── ar.svg │ ├── as.svg │ ├── at.svg │ ├── au.svg │ ├── aw.svg │ ├── ax.svg │ ├── az.svg │ ├── ba.svg │ ├── bb.svg │ ├── bd.svg │ ├── be.svg │ ├── bf.svg │ ├── bg.svg │ ├── bh.svg │ ├── bi.svg │ ├── bj.svg │ ├── bl.svg │ ├── bm.svg │ ├── bn.svg │ ├── bo.svg │ ├── bq.svg │ ├── br.svg │ ├── bs.svg │ ├── bt.svg │ ├── bv.svg │ ├── bw.svg │ ├── by.svg │ ├── bz.svg │ ├── ca.svg │ ├── cc.svg │ ├── cd.svg │ ├── cefta.svg │ ├── cf.svg │ ├── cg.svg │ ├── ch.svg │ ├── ci.svg │ ├── ck.svg │ ├── cl.svg │ ├── cm.svg │ ├── cn.svg │ ├── co.svg │ ├── cp.svg │ ├── cr.svg │ ├── cu.svg │ ├── cv.svg │ ├── cw.svg │ ├── cx.svg │ ├── cy.svg │ ├── cz.svg │ ├── de.svg │ ├── dg.svg │ ├── dj.svg │ ├── dk.svg │ ├── dm.svg │ ├── do.svg │ ├── dz.svg │ ├── ea.svg │ ├── ec.svg │ ├── ee.svg │ ├── eg.svg │ ├── eh.svg │ ├── er.svg │ ├── es-ct.svg │ ├── es-ga.svg │ ├── es.svg │ ├── et.svg │ ├── eu.svg │ ├── fi.svg │ ├── fj.svg │ ├── fk.svg │ ├── fm.svg │ ├── fo.svg │ ├── fr.svg │ ├── ga.svg │ ├── gb-eng.svg │ ├── gb-nir.svg │ ├── gb-sct.svg │ ├── gb-wls.svg │ ├── gb.svg │ ├── gd.svg │ ├── ge.svg │ ├── gf.svg │ ├── gg.svg │ ├── gh.svg │ ├── gi.svg │ ├── gl.svg │ ├── gm.svg │ ├── gn.svg │ ├── gp.svg │ ├── gq.svg │ ├── gr.svg │ ├── gs.svg │ ├── gt.svg │ ├── gu.svg │ ├── gw.svg │ ├── gy.svg │ ├── hk.svg │ ├── hm.svg │ ├── hn.svg │ ├── hr.svg │ ├── ht.svg │ ├── hu.svg │ ├── ic.svg │ ├── id.svg │ ├── ie.svg │ ├── il.svg │ ├── im.svg │ ├── in.svg │ ├── io.svg │ ├── iq.svg │ ├── ir.svg │ ├── is.svg │ ├── it.svg │ ├── je.svg │ ├── jm.svg │ ├── jo.svg │ ├── jp.svg │ ├── ke.svg │ ├── kg.svg │ ├── kh.svg │ ├── ki.svg │ ├── km.svg │ ├── kn.svg │ ├── kp.svg │ ├── kr.svg │ ├── kw.svg │ ├── ky.svg │ ├── kz.svg │ ├── la.svg │ ├── lb.svg │ ├── lc.svg │ ├── li.svg │ ├── lk.svg │ ├── lr.svg │ ├── ls.svg │ ├── lt.svg │ ├── lu.svg │ ├── lv.svg │ ├── ly.svg │ ├── ma.svg │ ├── mc.svg │ ├── md.svg │ ├── me.svg │ ├── mf.svg │ ├── mg.svg │ ├── mh.svg │ ├── mk.svg │ ├── ml.svg │ ├── mm.svg │ ├── mn.svg │ ├── mo.svg │ ├── mp.svg │ ├── mq.svg │ ├── mr.svg │ ├── ms.svg │ ├── mt.svg │ ├── mu.svg │ ├── mv.svg │ ├── mw.svg │ ├── mx.svg │ ├── my.svg │ ├── mz.svg │ ├── na.svg │ ├── nc.svg │ ├── ne.svg │ ├── nf.svg │ ├── ng.svg │ ├── ni.svg │ ├── nl.svg │ ├── no.svg │ ├── np.svg │ ├── nr.svg │ ├── nu.svg │ ├── nz.svg │ ├── om.svg │ ├── pa.svg │ ├── pe.svg │ ├── pf.svg │ ├── pg.svg │ ├── ph.svg │ ├── pk.svg │ ├── pl.svg │ ├── pm.svg │ ├── pn.svg │ ├── pr.svg │ ├── ps.svg │ ├── pt.svg │ ├── pw.svg │ ├── py.svg │ ├── qa.svg │ ├── re.svg │ ├── ro.svg │ ├── rs.svg │ ├── ru.svg │ ├── rw.svg │ ├── sa.svg │ ├── sb.svg │ ├── sc.svg │ ├── sd.svg │ ├── se.svg │ ├── sg.svg │ ├── sh.svg │ ├── si.svg │ ├── sj.svg │ ├── sk.svg │ ├── sl.svg │ ├── sm.svg │ ├── sn.svg │ ├── so.svg │ ├── sr.svg │ ├── ss.svg │ ├── st.svg │ ├── sv.svg │ ├── sx.svg │ ├── sy.svg │ ├── sz.svg │ ├── ta.svg │ ├── tc.svg │ ├── td.svg │ ├── tf.svg │ ├── tg.svg │ ├── th.svg │ ├── tj.svg │ ├── tk.svg │ ├── tl.svg │ ├── tm.svg │ ├── tn.svg │ ├── to.svg │ ├── tr.svg │ ├── tt.svg │ ├── tv.svg │ ├── tw.svg │ ├── tz.svg │ ├── ua.svg │ ├── ug.svg │ ├── um.svg │ ├── un.svg │ ├── us.svg │ ├── uy.svg │ ├── uz.svg │ ├── va.svg │ ├── vc.svg │ ├── ve.svg │ ├── vg.svg │ ├── vi.svg │ ├── vn.svg │ ├── vu.svg │ ├── wf.svg │ ├── ws.svg │ ├── xk.svg │ ├── xx.svg │ ├── ye.svg │ ├── yt.svg │ ├── za.svg │ ├── zm.svg │ └── zw.svg ├── devtools_options.yaml ├── flutter_native_splash.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 1024.png │ │ │ ├── 114.png │ │ │ ├── 120.png │ │ │ ├── 180.png │ │ │ ├── 29.png │ │ │ ├── 40.png │ │ │ ├── 57.png │ │ │ ├── 58.png │ │ │ ├── 60.png │ │ │ ├── 80.png │ │ │ ├── 87.png │ │ │ └── Contents.json │ │ ├── LaunchBackground.imageset │ │ │ ├── Contents.json │ │ │ └── background.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ └── LaunchImage@3x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── Runner-Bridging-Header.h │ └── Runner.entitlements └── RunnerTests │ └── RunnerTests.swift ├── lib ├── feature │ ├── chat_page │ │ ├── chat_page.dart │ │ ├── chat_page_mixin.dart │ │ └── widgets │ │ │ ├── chat_bubble.dart │ │ │ ├── chat_list.dart │ │ │ └── message_text_field.dart │ ├── contacts_page │ │ ├── contacts_page.dart │ │ └── widgets │ │ │ ├── friend_list.dart │ │ │ └── friend_request_bell.dart │ ├── dashboard_page │ │ └── dashboard_page.dart │ ├── language_picker_page │ │ ├── language_picker_mixin.dart │ │ └── language_picker_page.dart │ ├── login_page │ │ ├── login_mixin.dart │ │ ├── login_page.dart │ │ └── widgets │ │ │ ├── create_account_text.dart │ │ │ ├── login_button.dart │ │ │ └── text_fields.dart │ ├── motification_page │ │ ├── notification_page.dart │ │ └── widgets │ │ │ └── friend_request.dart │ ├── profile_page │ │ ├── profile_page.dart │ │ ├── profile_page_mixin.dart │ │ └── widgets │ │ │ ├── avatar_name.dart │ │ │ ├── languages.dart │ │ │ └── top_bar.dart │ ├── search_page │ │ ├── search_page.dart │ │ └── widgets │ │ │ └── searched_users.dart │ ├── sign_up_page │ │ ├── sign_up_mixin.dart │ │ ├── sign_up_page.dart │ │ └── widgets │ │ │ ├── sign_up_button.dart │ │ │ └── text_fields.dart │ └── widgets │ │ ├── button.dart │ │ └── text_input.dart ├── main.dart └── product │ ├── bloc │ ├── chat_cubit │ │ ├── chat_cubit.dart │ │ └── chat_state.dart │ ├── language_picker_cubit │ │ └── language_picker_cubit.dart │ ├── login_bloc │ │ ├── login_bloc.dart │ │ ├── login_event.dart │ │ └── login_state.dart │ ├── notification_bloc │ │ ├── notification_bloc.dart │ │ ├── notification_event.dart │ │ └── notification_state.dart │ ├── search_cubit │ │ ├── search_cubit.dart │ │ └── search_state.dart │ └── sign_up_bloc │ │ ├── sign_up_bloc.dart │ │ ├── sign_up_event.dart │ │ └── sign_up_state.dart │ ├── cache │ ├── base_cache_manager.dart │ └── refresh_token_cache.dart │ ├── constants │ ├── api_constants.dart │ ├── enums │ │ ├── api_request_method_enum.dart │ │ ├── cache_enums.dart │ │ ├── end_point_enums.dart │ │ └── padding_enums.dart │ ├── font_constants.dart │ ├── regexp_constants.dart │ └── typedef.dart │ ├── extension │ ├── asset_extensions.dart │ ├── base_response_extension.dart │ └── dio_exception.dart │ ├── init │ └── product_initialization.dart │ ├── manager │ ├── auth_manager.dart │ └── friend_manager.dart │ ├── models │ ├── base_response_model.dart │ ├── friend.dart │ ├── language.dart │ ├── message.dart │ ├── request_models │ │ ├── friend_request.dart │ │ ├── language_set_model.dart │ │ ├── login_request_model.dart │ │ ├── register_request_model.dart │ │ └── search_friend_request.dart │ ├── response_models │ │ ├── friend_response.dart │ │ ├── search_response.dart │ │ └── tokens_response_model.dart │ └── user.dart │ ├── navigation │ ├── app_router.dart │ ├── auth_guard.dart │ └── language_guard.dart │ ├── services │ ├── authentication │ │ ├── IAuth_request.dart │ │ ├── auth_request.dart │ │ ├── login_auth_service.dart │ │ ├── refresh_auth.dart │ │ └── register_auth_service.dart │ ├── chat_service.dart │ ├── firebase_messaging.dart │ ├── friend_service.dart │ └── network_service.dart │ └── theme │ └── light_theme.dart ├── pubspec.lock └── pubspec.yaml /.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 | 45 | # my custom 46 | firebase_options.dart 47 | firebase.json 48 | *.gr.dart 49 | *.g.dart 50 | *.freezed.dart -------------------------------------------------------------------------------- /.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: "4cf269e36de2573851eaef3c763994f8f9be494d" 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: 4cf269e36de2573851eaef3c763994f8f9be494d 17 | base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 18 | - platform: android 19 | create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 20 | base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 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 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:very_good_analysis/analysis_options.yaml 2 | -------------------------------------------------------------------------------- /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/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | // START: FlutterFire Configuration 4 | id 'com.google.gms.google-services' 5 | // END: FlutterFire Configuration 6 | id "kotlin-android" 7 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 8 | id "dev.flutter.flutter-gradle-plugin" 9 | } 10 | 11 | android { 12 | namespace = "berkcicekler.langbuddy" 13 | compileSdk = flutter.compileSdkVersion 14 | ndkVersion = flutter.ndkVersion 15 | 16 | compileOptions { 17 | sourceCompatibility = JavaVersion.VERSION_1_8 18 | targetCompatibility = JavaVersion.VERSION_1_8 19 | } 20 | 21 | kotlinOptions { 22 | jvmTarget = JavaVersion.VERSION_1_8 23 | } 24 | 25 | defaultConfig { 26 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 27 | applicationId = "com.berkcicekler.langbuddy" 28 | // You can update the following values to match your application needs. 29 | // For more information, see: https://flutter.dev/to/review-gradle-config. 30 | minSdk = 21 31 | targetSdk = flutter.targetSdkVersion 32 | versionCode = flutter.versionCode 33 | versionName = flutter.versionName 34 | multiDexEnabled true 35 | } 36 | 37 | buildTypes { 38 | release { 39 | // TODO: Add your own signing config for the release build. 40 | // Signing with the debug keys for now, so `flutter run --release` works. 41 | signingConfig = signingConfigs.debug 42 | } 43 | } 44 | } 45 | 46 | flutter { 47 | source = "../.." 48 | } 49 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/berkcicekler/langbuddy/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package berkcicekler.langbuddy 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /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.3-all.zip 6 | -------------------------------------------------------------------------------- /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.1.0" apply false 22 | // START: FlutterFire Configuration 23 | id "com.google.gms.google-services" version "4.3.15" apply false 24 | // END: FlutterFire Configuration 25 | id "org.jetbrains.kotlin.android" version "1.8.22" apply false 26 | } 27 | 28 | include ":app" 29 | -------------------------------------------------------------------------------- /assets/app_icon/lang_buddy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/app_icon/lang_buddy.png -------------------------------------------------------------------------------- /assets/app_icon/lang_buddy_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/app_icon/lang_buddy_background.png -------------------------------------------------------------------------------- /assets/app_icon/lang_buddy_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/app_icon/lang_buddy_clear.png -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-ExtraBold.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/inter/Inter-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/fonts/inter/Inter-Thin.ttf -------------------------------------------------------------------------------- /assets/photos/profile_picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/assets/photos/profile_picture.jpg -------------------------------------------------------------------------------- /assets/svg/countryFlags/ae.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ag.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/am.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ao.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/at.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/au.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ax.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/az.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ba.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/be.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bj.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bq.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/bw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ca.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cefta.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ci.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/co.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/cz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/de.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/dj.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/dk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/dz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ee.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/eh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/es-ct.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/et.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/eu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/fi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/fm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/fo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/fr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ga.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gb-eng.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gb-sct.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/gy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/hm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/hn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/hu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/id.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ie.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/il.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/in.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/iq.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/is.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/it.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/jm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/jo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/jp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ke.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/km.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/kn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/kp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/kw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/la.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/lc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/lr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ls.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/lt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/lu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/lv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ly.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ma.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ml.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mq.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/mv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/my.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/na.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/nc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ne.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ng.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/nl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/no.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/np.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/nr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/nu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pa.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ph.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ps.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/pw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/qa.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/re.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ro.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ru.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/rw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/se.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sj.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/so.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ss.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/st.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/sy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/td.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/th.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tl.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/to.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/tz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ua.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/uz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/vc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ve.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/vn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/wf.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ws.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/xx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/ye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/yt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /assets/svg/countryFlags/za.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '13.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | {"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]} -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LaunchImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BerkCicekler/langbuddy/27ec6dd266fbe706cf6aed218e6d8566a6e93824/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyBQ0uJ_DfQitDrXnoHQj3wbcf9kaidO1DU 7 | GCM_SENDER_ID 8 | 222548361840 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | berkcicekler.langbuddy 13 | PROJECT_ID 14 | languagebuddy-3a272 15 | STORAGE_BUCKET 16 | languagebuddy-3a272.appspot.com 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:222548361840:ios:1cd2a9d46c0eeac6362ab5 29 | 30 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/feature/chat_page/chat_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/annotations.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:langbuddy/product/bloc/chat_cubit/chat_cubit.dart'; 5 | import 'package:langbuddy/product/constants/enums/padding_enums.dart'; 6 | import 'package:langbuddy/product/models/friend.dart'; 7 | 8 | part 'chat_page_mixin.dart'; 9 | part 'widgets/chat_bubble.dart'; 10 | part 'widgets/message_text_field.dart'; 11 | part 'widgets/chat_list.dart'; 12 | 13 | @RoutePage() 14 | final class ChatPage extends StatefulWidget { 15 | const ChatPage({ 16 | required this.friend, 17 | super.key, 18 | }); 19 | 20 | final Friend friend; 21 | 22 | @override 23 | State createState() => _ChatPageState(); 24 | } 25 | 26 | class _ChatPageState extends State with ChatPageMixin { 27 | @override 28 | Widget build(BuildContext context) { 29 | return BlocProvider( 30 | create: (context) => _chatCubit, 31 | child: Scaffold( 32 | appBar: AppBar( 33 | title: Text(widget.friend.userName), 34 | ), 35 | body: BlocBuilder( 36 | buildWhen: (previous, current) => previous.state != current.state, 37 | builder: (context, state) { 38 | if (state.state != ChatEnumState.connected) { 39 | return const Center(child: CircularProgressIndicator.adaptive()); 40 | } 41 | return Column( 42 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 43 | children: [ 44 | _ChatList(), 45 | _MessageTextField(), 46 | ], 47 | ); 48 | }, 49 | ), 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/feature/chat_page/chat_page_mixin.dart: -------------------------------------------------------------------------------- 1 | part of 'chat_page.dart'; 2 | 3 | mixin ChatPageMixin on State { 4 | late final TextEditingController _messageTextField; 5 | late final ChatCubit _chatCubit; 6 | @override 7 | void initState() { 8 | super.initState(); 9 | _messageTextField = TextEditingController(); 10 | _chatCubit = ChatCubit( 11 | friend: widget.friend, 12 | messageTextField: _messageTextField, 13 | )..connectToChannel(); 14 | } 15 | 16 | @override 17 | void dispose() { 18 | super.dispose(); 19 | _messageTextField.dispose(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/feature/chat_page/widgets/chat_bubble.dart: -------------------------------------------------------------------------------- 1 | part of '../chat_page.dart'; 2 | 3 | final class _ChatBubble extends StatelessWidget { 4 | const _ChatBubble({required this.message, required this.isBelongToClient}); 5 | 6 | final bool isBelongToClient; 7 | final String message; 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | final width = MediaQuery.of(context).size.width; 12 | return Align( 13 | alignment: 14 | isBelongToClient ? Alignment.centerRight : Alignment.centerLeft, 15 | child: Container( 16 | padding: const EdgeInsets.all(6), 17 | margin: EdgeInsets.only( 18 | right: PaddingConstants.small.value, 19 | left: PaddingConstants.small.value, 20 | top: PaddingConstants.small.value), 21 | constraints: BoxConstraints(maxWidth: width * .7), 22 | decoration: BoxDecoration( 23 | borderRadius: BorderRadius.circular(10), 24 | color: isBelongToClient ? Colors.amber[400] : Colors.grey, 25 | ), 26 | child: Text( 27 | message, 28 | style: const TextStyle( 29 | color: Colors.white, 30 | fontWeight: FontWeight.w500, 31 | fontSize: 16, 32 | letterSpacing: .8, 33 | ), 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/feature/chat_page/widgets/chat_list.dart: -------------------------------------------------------------------------------- 1 | part of '../chat_page.dart'; 2 | 3 | class _ChatList extends StatelessWidget { 4 | const _ChatList({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return BlocBuilder( 9 | builder: (context, state) { 10 | return Expanded( 11 | child: ListView.builder( 12 | itemCount: state.messages.length, 13 | itemBuilder: (context, index) { 14 | final currentMessage = state.messages[index]; 15 | return _ChatBubble( 16 | isBelongToClient: currentMessage.isClient, 17 | message: currentMessage.text, 18 | ); 19 | }, 20 | ), 21 | ); 22 | }, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/feature/contacts_page/contacts_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:get_it/get_it.dart'; 5 | import 'package:langbuddy/product/constants/enums/padding_enums.dart'; 6 | import 'package:langbuddy/product/manager/friend_manager.dart'; 7 | import 'package:langbuddy/product/models/friend.dart'; 8 | import 'package:langbuddy/product/navigation/app_router.dart'; 9 | 10 | part 'widgets/friend_request_bell.dart'; 11 | part 'widgets/friend_list.dart'; 12 | 13 | @RoutePage() 14 | final class ContactsPage extends StatelessWidget { 15 | const ContactsPage({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final friendManager = GetIt.I(); 20 | return SafeArea( 21 | child: Padding( 22 | padding: EdgeInsets.all(PaddingConstants.page.value), 23 | child: ListenableBuilder( 24 | listenable: friendManager, 25 | builder: (context, widget) { 26 | return Column( 27 | children: [ 28 | _FriendRequestBell( 29 | isNotificationExist: friendManager.friendRequests.isNotEmpty, 30 | ), 31 | _FriendList(friends: friendManager.friends), 32 | ], 33 | ); 34 | }, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/feature/contacts_page/widgets/friend_list.dart: -------------------------------------------------------------------------------- 1 | part of '../contacts_page.dart'; 2 | 3 | class _FriendList extends StatelessWidget { 4 | const _FriendList({required this.friends}); 5 | 6 | final List friends; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Expanded( 11 | child: ListView.builder( 12 | itemCount: friends.length, 13 | itemBuilder: (context, index) { 14 | return _friendListTile(friends[index]); 15 | }, 16 | ), 17 | ); 18 | } 19 | 20 | ListTile _friendListTile(Friend friend) { 21 | return ListTile( 22 | onTap: () { 23 | GetIt.I().push(ChatRoute(friend: friend)); 24 | }, 25 | contentPadding: const EdgeInsets.only(top: 7, bottom: 7), 26 | leading: Container( 27 | width: 60, 28 | height: 60, 29 | decoration: const BoxDecoration( 30 | shape: BoxShape.circle, 31 | image: DecorationImage( 32 | image: AssetImage('assets/photos/profile_picture.jpg'), 33 | fit: BoxFit.fill, 34 | ), 35 | ), 36 | ), 37 | title: Text(friend.userName), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/feature/contacts_page/widgets/friend_request_bell.dart: -------------------------------------------------------------------------------- 1 | part of '../contacts_page.dart'; 2 | 3 | final class _FriendRequestBell extends StatelessWidget { 4 | const _FriendRequestBell({required this.isNotificationExist}); 5 | 6 | final bool isNotificationExist; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Row( 11 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 12 | children: [ 13 | Text( 14 | "Contacts", 15 | style: Theme.of(context).textTheme.headlineMedium, 16 | ), 17 | Stack( 18 | children: [ 19 | IconButton( 20 | alignment: AlignmentDirectional.centerEnd, 21 | padding: EdgeInsets.all(0), 22 | onPressed: () { 23 | context.router.push(const NotificationRoute()); 24 | }, 25 | icon: const Icon(CupertinoIcons.bell), 26 | iconSize: 32, 27 | ), 28 | !isNotificationExist 29 | ? const SizedBox.shrink() 30 | : const Positioned( 31 | top: 10, 32 | right: 8, 33 | child: Icon( 34 | Icons.brightness_1, 35 | size: 14, 36 | color: Colors.redAccent, 37 | ), 38 | ), 39 | ], 40 | ), 41 | ], 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/feature/dashboard_page/dashboard_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:langbuddy/product/navigation/app_router.dart'; 5 | 6 | @RoutePage() 7 | class DashBoardPage extends StatelessWidget { 8 | const DashBoardPage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return AutoTabsScaffold( 13 | routes: const [ 14 | SearchRoute(), 15 | ContactsRoute(), 16 | ProfileRoute(), 17 | ], 18 | bottomNavigationBuilder: (context, tabsRouter) { 19 | return BottomNavigationBar( 20 | currentIndex: tabsRouter.activeIndex, 21 | onTap: tabsRouter.setActiveIndex, 22 | iconSize: 30, 23 | showSelectedLabels: false, 24 | showUnselectedLabels: false, 25 | items: const [ 26 | BottomNavigationBarItem( 27 | icon: Icon(CupertinoIcons.search), 28 | label: 'Search', 29 | ), 30 | BottomNavigationBarItem( 31 | icon: Icon(Icons.message_outlined), 32 | label: 'Contacts', 33 | ), 34 | BottomNavigationBarItem( 35 | icon: Icon(Icons.person_2_outlined), 36 | label: 'Profile', 37 | ), 38 | ], 39 | ); 40 | }, 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/feature/language_picker_page/language_picker_mixin.dart: -------------------------------------------------------------------------------- 1 | part of 'language_picker_page.dart'; 2 | 3 | mixin _LanguagePickerMixin on State { 4 | late final List _languages; 5 | late final LanguagePickerCubit _languagePickerCubit; 6 | late final user = GetIt.I().user; 7 | 8 | @override 9 | void initState() { 10 | super.initState(); 11 | _languages = Language.languages; 12 | _languagePickerCubit = LanguagePickerCubit( 13 | languages: _languages, 14 | // language name is not important in this scenario 15 | initialLanguageValue: Language( 16 | isoCode: widget.isSelectingNative 17 | ? user.nativeLanguage 18 | : user.learningLanguage, 19 | name: '', 20 | ), 21 | isSelectingNative: widget.isSelectingNative, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/feature/login_page/login_mixin.dart: -------------------------------------------------------------------------------- 1 | part of 'login_page.dart'; 2 | 3 | mixin _LoginPageMixin on State { 4 | late final TextEditingController _emailController; 5 | late final TextEditingController _passwordController; 6 | late final GlobalKey _formKey; 7 | late final LoginBloc _loginBloc; 8 | 9 | @override 10 | void initState() { 11 | super.initState(); 12 | _emailController = TextEditingController(); 13 | _passwordController = TextEditingController(); 14 | _formKey = GlobalKey(); 15 | _loginBloc = LoginBloc(_formKey, _emailController, _passwordController); 16 | _emailController.text = 'example@gmail.com'; 17 | _passwordController.text = 'test'; 18 | } 19 | 20 | @override 21 | void dispose() { 22 | super.dispose(); 23 | _emailController.dispose(); 24 | _passwordController.dispose(); 25 | } 26 | 27 | void _loginBlocListener(BuildContext context, LoginState state) { 28 | if (state is LoginStateError) { 29 | ScaffoldMessenger.of(context).showSnackBar( 30 | SnackBar( 31 | content: Text(state.error), 32 | ), 33 | ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/feature/login_page/widgets/create_account_text.dart: -------------------------------------------------------------------------------- 1 | part of '../login_page.dart'; 2 | 3 | class _CreateAccountText extends StatelessWidget { 4 | const _CreateAccountText(); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return RichText( 9 | text: TextSpan( 10 | text: 'Create Account', 11 | style: const TextStyle( 12 | color: Colors.black, 13 | fontSize: 16, 14 | fontWeight: FontWeight.w500, 15 | ), 16 | recognizer: TapGestureRecognizer() 17 | ..onTap = () { 18 | context.router.push(const SignUpRoute()); 19 | }, 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/feature/login_page/widgets/login_button.dart: -------------------------------------------------------------------------------- 1 | part of '../login_page.dart'; 2 | 3 | class _LoginButton extends StatelessWidget { 4 | const _LoginButton(); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return CustomFormButton( 9 | onTap: () async { 10 | context.read().add(LoginButtonPressed()); 11 | }, 12 | text: 'CONTINUE', 13 | size: const Size(300, 50), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/feature/login_page/widgets/text_fields.dart: -------------------------------------------------------------------------------- 1 | part of '../login_page.dart'; 2 | 3 | class _TextFields extends StatelessWidget { 4 | const _TextFields(); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | final loginBloc = context.read(); 9 | return Column( 10 | children: [ 11 | CustomTextFormField( 12 | controller: loginBloc.emailController, 13 | validator: (value) { 14 | if (value == null) { 15 | return 'Field can not be empty'; 16 | } 17 | if (!RegExpConstants.email.hasMatch(value)) { 18 | return 'Please enter a valid email address'; 19 | } 20 | return null; 21 | }, 22 | hint: 'example@mail.com', 23 | labelText: 'email', 24 | ), 25 | const SizedBox( 26 | height: 20, 27 | ), 28 | CustomTextFormField( 29 | controller: loginBloc.passwordController, 30 | labelText: 'Password', 31 | ), 32 | ], 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/feature/motification_page/notification_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/annotations.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:get_it/get_it.dart'; 6 | import 'package:langbuddy/product/bloc/notification_bloc/notification_bloc.dart'; 7 | import 'package:langbuddy/product/constants/enums/padding_enums.dart'; 8 | import 'package:langbuddy/product/manager/friend_manager.dart'; 9 | import 'package:langbuddy/product/models/friend.dart'; 10 | 11 | part 'widgets/friend_request.dart'; 12 | 13 | @RoutePage() 14 | final class NotificationPage extends StatelessWidget { 15 | const NotificationPage({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return BlocProvider( 20 | create: (_) => NotificationBloc(friendManager: GetIt.I()) 21 | ..add(NotificationRefreshEvent()), 22 | child: Scaffold( 23 | appBar: AppBar( 24 | title: const Text('Notifications'), 25 | ), 26 | body: Padding( 27 | padding: EdgeInsets.all(PaddingConstants.page.value), 28 | child: const _FriendRequests(), 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/feature/profile_page/profile_page_mixin.dart: -------------------------------------------------------------------------------- 1 | part of 'profile_page.dart'; 2 | 3 | mixin ProfilePageMixin on State { 4 | ///App's authService from GetIt 5 | late final AuthManager authService; 6 | 7 | @override 8 | void initState() { 9 | super.initState(); 10 | authService = GetIt.I(); 11 | } 12 | 13 | @override 14 | void dispose() { 15 | super.dispose(); 16 | } 17 | 18 | void logoutIconButton() { 19 | authService.logout(); 20 | } 21 | 22 | void settingsIconButton() {} 23 | 24 | void nativeLanguageOnTap() { 25 | AutoRouter.of(context).push(LanguagePickerRoute(isSelectingNative: true)); 26 | } 27 | 28 | void learningLanguageOnTap() { 29 | AutoRouter.of(context).push(LanguagePickerRoute(isSelectingNative: false)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/feature/profile_page/widgets/avatar_name.dart: -------------------------------------------------------------------------------- 1 | part of '../profile_page.dart'; 2 | 3 | final class _AvatarAndName extends StatelessWidget { 4 | const _AvatarAndName({required this.name}); 5 | 6 | final String name; 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Column( 11 | children: [ 12 | const CircleAvatar( 13 | backgroundImage: AssetImage('assets/photos/profile_picture.jpg'), 14 | radius: 48, 15 | ), 16 | const SizedBox(height: 20), 17 | Text( 18 | name, 19 | style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w500), 20 | ), 21 | ], 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/feature/profile_page/widgets/top_bar.dart: -------------------------------------------------------------------------------- 1 | part of '../profile_page.dart'; 2 | 3 | final class _TopBar extends StatelessWidget { 4 | const _TopBar({ 5 | required this.settingsIconButton, 6 | required this.logoutIconButton, 7 | }); 8 | 9 | final void Function() settingsIconButton; 10 | final void Function() logoutIconButton; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Row( 15 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 16 | children: [ 17 | Text( 18 | 'Profile', 19 | style: Theme.of(context).textTheme.headlineMedium, 20 | ), 21 | Row( 22 | mainAxisAlignment: MainAxisAlignment.end, 23 | children: [ 24 | IconButton( 25 | alignment: AlignmentDirectional.centerEnd, 26 | padding: EdgeInsets.zero, 27 | onPressed: settingsIconButton, 28 | icon: const Icon(Icons.settings), 29 | ), 30 | IconButton( 31 | alignment: AlignmentDirectional.centerEnd, 32 | padding: EdgeInsets.zero, 33 | onPressed: logoutIconButton, 34 | icon: const Icon(Icons.logout), 35 | ), 36 | ], 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/feature/search_page/search_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/annotations.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:langbuddy/product/bloc/search_cubit/search_cubit.dart'; 5 | import 'package:langbuddy/product/constants/enums/padding_enums.dart'; 6 | import 'package:langbuddy/product/models/friend.dart'; 7 | import 'package:langbuddy/product/services/friend_service.dart'; 8 | 9 | part 'widgets/searched_users.dart'; 10 | 11 | @RoutePage() 12 | final class SearchPage extends StatelessWidget { 13 | const SearchPage({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return BlocProvider( 18 | create: (_) => 19 | SearchCubit(friendService: FriendService())..searchFriend(), 20 | child: SafeArea( 21 | child: Padding( 22 | padding: EdgeInsets.all(PaddingConstants.page.value), 23 | child: Column( 24 | children: [ 25 | Row( 26 | children: [ 27 | Text( 28 | 'Find Langbuddy', 29 | style: Theme.of(context).textTheme.headlineMedium, 30 | ), 31 | ], 32 | ), 33 | const _SearchedUsers(), 34 | ], 35 | ), 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/feature/sign_up_page/sign_up_mixin.dart: -------------------------------------------------------------------------------- 1 | part of 'sign_up_page.dart'; 2 | 3 | mixin SignUpMixin on State { 4 | late final TextEditingController _userNameController; 5 | late final TextEditingController _emailController; 6 | late final TextEditingController _passwordController; 7 | late final GlobalKey _formKey; 8 | 9 | @override 10 | void initState() { 11 | super.initState(); 12 | _userNameController = TextEditingController(); 13 | _emailController = TextEditingController(); 14 | _passwordController = TextEditingController(); 15 | _formKey = GlobalKey(); 16 | _emailController.text = 'example@gmail.com'; 17 | _passwordController.text = 'test'; 18 | } 19 | 20 | void _signUpBlocListener(BuildContext context, SignUpState state) { 21 | if (state is SignUpError) { 22 | ScaffoldMessenger.of(context).showSnackBar( 23 | SnackBar( 24 | content: Text(state.error), 25 | ), 26 | ); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/feature/sign_up_page/widgets/sign_up_button.dart: -------------------------------------------------------------------------------- 1 | part of '../sign_up_page.dart'; 2 | 3 | class _SignUpButton extends StatelessWidget { 4 | const _SignUpButton(); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return CustomFormButton( 9 | text: 'Sign Up', 10 | onTap: () => context.read().add(SignUpButtonPressEvent()), 11 | size: const Size(300, 50), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/feature/sign_up_page/widgets/text_fields.dart: -------------------------------------------------------------------------------- 1 | part of '../sign_up_page.dart'; 2 | 3 | class _TextFields extends StatelessWidget { 4 | const _TextFields(); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | final signUpBloc = context.read(); 9 | return Column( 10 | children: [ 11 | CustomTextFormField( 12 | controller: signUpBloc.userNameController, 13 | labelText: 'Nickname', 14 | ), 15 | const SizedBox(height: 20), 16 | CustomTextFormField( 17 | controller: signUpBloc.emailController, 18 | hint: 'example@mail.com', 19 | labelText: 'email', 20 | ), 21 | const SizedBox(height: 20), 22 | CustomTextFormField( 23 | controller: signUpBloc.passwordController, 24 | labelText: 'Password', 25 | ), 26 | ], 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/feature/widgets/button.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class CustomFormButton extends StatelessWidget { 6 | const CustomFormButton({ 7 | required this.text, 8 | required this.onTap, 9 | this.size, 10 | super.key, 11 | }); 12 | 13 | final String text; 14 | 15 | final Size? size; 16 | 17 | final dynamic Function()? onTap; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return ElevatedButton( 22 | onPressed: onTap, 23 | style: ElevatedButton.styleFrom( 24 | backgroundColor: Colors.black, 25 | disabledBackgroundColor: Colors.black.withAlpha(150), 26 | fixedSize: size, 27 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), 28 | ), 29 | child: Text( 30 | text, 31 | style: const TextStyle( 32 | color: Colors.white, 33 | fontWeight: FontWeight.w700, 34 | fontSize: 22, 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get_it/get_it.dart'; 3 | import 'package:langbuddy/product/init/product_initialization.dart'; 4 | import 'package:langbuddy/product/manager/auth_manager.dart'; 5 | import 'package:langbuddy/product/navigation/app_router.dart'; 6 | import 'package:langbuddy/product/theme/light_theme.dart'; 7 | 8 | void main() async { 9 | await ProductInitialization.mainInit(); 10 | runApp(const _MyApp()); 11 | } 12 | 13 | final class _MyApp extends StatelessWidget { 14 | const _MyApp(); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return MaterialApp.router( 19 | routerConfig: GetIt.instance().config( 20 | reevaluateListenable: GetIt.I(), 21 | ), 22 | title: 'Lang Buddy', 23 | theme: CustomLightTheme.theme, 24 | debugShowCheckedModeBanner: false, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/product/bloc/chat_cubit/chat_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:langbuddy/product/models/message.dart'; 3 | 4 | part 'chat_state.freezed.dart'; 5 | 6 | @freezed 7 | class ChatState with _$ChatState { 8 | const factory ChatState({ 9 | required List messages, 10 | required ChatEnumState state, 11 | }) = _ChatState; 12 | } 13 | 14 | enum ChatEnumState { 15 | initial, 16 | connecting, 17 | connected; 18 | } 19 | -------------------------------------------------------------------------------- /lib/product/bloc/login_bloc/login_event.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | sealed class LoginEvent {} 4 | 5 | class LoginButtonPressed extends LoginEvent {} 6 | -------------------------------------------------------------------------------- /lib/product/bloc/login_bloc/login_state.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | sealed class LoginState {} 4 | 5 | class LoginStateDefault extends LoginState {} 6 | 7 | class LoginStateSuccess extends LoginState {} 8 | 9 | class LoginStateError extends LoginState { 10 | LoginStateError(this.error); 11 | 12 | final String error; 13 | } 14 | -------------------------------------------------------------------------------- /lib/product/bloc/notification_bloc/notification_event.dart: -------------------------------------------------------------------------------- 1 | import 'package:langbuddy/product/models/friend.dart'; 2 | 3 | sealed class NotificationEvent {} 4 | 5 | final class NotificationRefreshEvent extends NotificationEvent {} 6 | 7 | final class NotificationFriendRequestAction extends NotificationEvent { 8 | NotificationFriendRequestAction({ 9 | required this.targetFriend, 10 | required this.action, 11 | }); 12 | 13 | final Friend targetFriend; 14 | final FriendRequestActions action; 15 | } 16 | 17 | enum FriendRequestActions { 18 | ACCEPT, 19 | REJECT; 20 | } 21 | -------------------------------------------------------------------------------- /lib/product/bloc/notification_bloc/notification_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:langbuddy/product/models/friend.dart'; 3 | 4 | final class NotificationState extends Equatable { 5 | NotificationState({required this.friendRequests, required this.state}); 6 | 7 | final List friendRequests; 8 | final NotificationStateEnum state; 9 | 10 | @override 11 | List get props => [state, friendRequests]; 12 | 13 | NotificationState copyWith({ 14 | List? friendRequests, 15 | NotificationStateEnum? state, 16 | }) { 17 | return NotificationState( 18 | friendRequests: friendRequests ?? this.friendRequests, 19 | state: state ?? this.state, 20 | ); 21 | } 22 | } 23 | 24 | enum NotificationStateEnum { 25 | initial, 26 | loading, 27 | loaded, 28 | error; 29 | } 30 | -------------------------------------------------------------------------------- /lib/product/bloc/search_cubit/search_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:langbuddy/product/models/friend.dart'; 4 | 5 | @freezed 6 | final class SearchState extends Equatable { 7 | SearchState({ 8 | required this.users, 9 | required this.state, 10 | required this.sendedFriendRequest, 11 | }); 12 | 13 | /// The users API found for us 14 | /// A.K.A. client's potential friends 15 | final List users; 16 | 17 | /// This value will hold the data of the users 18 | /// the client already send friend request 19 | final List sendedFriendRequest; 20 | final SearchStateEnum state; 21 | 22 | @override 23 | List get props => [users, sendedFriendRequest, state]; 24 | } 25 | 26 | enum SearchStateEnum { 27 | initial, 28 | loading, 29 | loaded, 30 | fail; 31 | } 32 | -------------------------------------------------------------------------------- /lib/product/bloc/sign_up_bloc/sign_up_event.dart: -------------------------------------------------------------------------------- 1 | sealed class SignUpEvent {} 2 | 3 | class SignUpButtonPressEvent extends SignUpEvent {} 4 | -------------------------------------------------------------------------------- /lib/product/bloc/sign_up_bloc/sign_up_state.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | sealed class SignUpState {} 4 | 5 | final class SignUpDefault extends SignUpState {} 6 | 7 | final class SignUpSuccess extends SignUpState {} 8 | 9 | final class SignUpError extends SignUpState { 10 | SignUpError(this.error); 11 | final String error; 12 | } 13 | -------------------------------------------------------------------------------- /lib/product/cache/base_cache_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:langbuddy/product/constants/enums/cache_enums.dart'; 3 | 4 | class BaseCacheManager { 5 | Box? _box; 6 | final CacheBoxNames boxName; 7 | 8 | BaseCacheManager(this.boxName); 9 | 10 | Future openBox() async { 11 | if (!(_box?.isOpen ?? false)) { 12 | _box = await Hive.openBox(boxName.name); 13 | } 14 | } 15 | 16 | Future putItem(CacheKeyNames key, T val) async { 17 | await openBox(); 18 | await _box?.put(key.name, val); 19 | } 20 | 21 | Future getItem(CacheKeyNames key) async { 22 | await openBox(); 23 | return _box?.get(key.name); 24 | } 25 | 26 | List? getAllItems() { 27 | return _box?.values.toList(); 28 | } 29 | 30 | void saveAllItems(List? items) { 31 | if (items != null) { 32 | _box?.addAll(items); 33 | } 34 | } 35 | 36 | Future clear() async { 37 | await openBox(); 38 | await _box?.clear(); 39 | } 40 | 41 | Future containsKey(CacheKeyNames key) async { 42 | await openBox(); 43 | return _box?.containsKey(key.name) ?? false; 44 | } 45 | 46 | Future closeBox() async { 47 | await _box?.close(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/product/cache/refresh_token_cache.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:langbuddy/product/constants/enums/cache_enums.dart'; 3 | 4 | final class RefreshTokenCacheManager { 5 | final _boxName = CacheBoxNames.refreshToken; 6 | 7 | String get _cacheBoxName => _boxName.name; 8 | 9 | String get _refreshTokenKeyName => CacheKeyNames.refreshTokenKey.name; 10 | 11 | late final Box _box; 12 | 13 | Future openBox() async { 14 | _box = await Hive.openBox(_cacheBoxName); 15 | } 16 | 17 | Future isRefreshTokenExist() async { 18 | return _box.containsKey(_refreshTokenKeyName); 19 | } 20 | 21 | Future getRefreshToken() async { 22 | return _box.get(_refreshTokenKeyName) ?? ''; 23 | } 24 | 25 | Future saveRefreshToken(String token) async { 26 | await _box.put(_refreshTokenKeyName, token); 27 | } 28 | 29 | Future removeToken() async { 30 | await _box.clear(); 31 | } 32 | 33 | Future closeBox() async { 34 | await _box.close(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/product/constants/api_constants.dart: -------------------------------------------------------------------------------- 1 | final class ApiConstants { 2 | ApiConstants._(); 3 | static const String url = 'http://localhost:8080/api/v1'; 4 | static const Duration connectTimeout = Duration(seconds: 5); 5 | static const Duration receiveTimeout = Duration(seconds: 3); 6 | } 7 | -------------------------------------------------------------------------------- /lib/product/constants/enums/api_request_method_enum.dart: -------------------------------------------------------------------------------- 1 | /// API request methods 2 | /// these values used in NetworkService class to define the request type/method 3 | enum APIRequestMethod { 4 | GET('get'), 5 | POST('post'); 6 | 7 | final String value; 8 | 9 | const APIRequestMethod(this.value); 10 | } 11 | -------------------------------------------------------------------------------- /lib/product/constants/enums/cache_enums.dart: -------------------------------------------------------------------------------- 1 | enum CacheBoxNames { refreshToken } 2 | 3 | enum CacheKeyNames { refreshTokenKey } 4 | -------------------------------------------------------------------------------- /lib/product/constants/enums/end_point_enums.dart: -------------------------------------------------------------------------------- 1 | /// Back-end's end-points stored in this enum 2 | enum EndPointEnums { 3 | userLogin('/users/login'), 4 | userSignUp('/users/register'), 5 | refreshToken('/users/refreshToken'), 6 | setLanguage('/user/language'), 7 | friendsData('/friends/data'), 8 | friendRequestSend('/friends/sendRequest'), 9 | friendRequestAccept('/friends/accept'), 10 | friendRequestReject('/friends/reject'), 11 | searchFriend('/search/'); 12 | 13 | final String value; 14 | 15 | const EndPointEnums(this.value); 16 | } 17 | -------------------------------------------------------------------------------- /lib/product/constants/enums/padding_enums.dart: -------------------------------------------------------------------------------- 1 | enum PaddingConstants { 2 | // page default 8 3 | page(16), 4 | 5 | /// 8 6 | small(8), 7 | 8 | /// 16 9 | medium(16), 10 | 11 | /// 32 12 | large(32); 13 | 14 | final double value; 15 | const PaddingConstants(this.value); 16 | } 17 | -------------------------------------------------------------------------------- /lib/product/constants/font_constants.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | /// Font family names stored in this class 4 | final class FontConstants { 5 | FontConstants._(); 6 | 7 | static const inter = 'inter'; 8 | } 9 | -------------------------------------------------------------------------------- /lib/product/constants/regexp_constants.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | final class RegExpConstants { 4 | RegExpConstants._(); 5 | static final email = RegExp( 6 | r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+", 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /lib/product/constants/typedef.dart: -------------------------------------------------------------------------------- 1 | typedef JSON = Map; 2 | -------------------------------------------------------------------------------- /lib/product/extension/asset_extensions.dart: -------------------------------------------------------------------------------- 1 | extension AssetExtension on String { 2 | String get languageToFlagSvg { 3 | return 'assets/svg/countryFlags/$this.svg'; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/product/extension/base_response_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:langbuddy/product/models/base_response_model.dart'; 2 | 3 | extension BaseResponseExtension on BaseResponse { 4 | bool get isSucceed => statusCode == 200 || statusCode == 201; 5 | } 6 | -------------------------------------------------------------------------------- /lib/product/extension/dio_exception.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | extension DioExceptionExtension on DioException { 4 | /// This getter tries to get server's error message from the [response] 5 | /// if the server didn't returned a error message getter choses the error message 6 | String get errorMessage { 7 | if (response == null) { 8 | return 'No response found'; 9 | } 10 | if (response!.data is! Map) { 11 | return 'Unknown Data'; 12 | } 13 | if (response!.data['error'] != null) { 14 | return response!.data['error'] as String; 15 | } 16 | return 'Unknown error'; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/product/models/base_response_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | /// [BaseResponse] is used in Network responses 4 | final class BaseResponse { 5 | BaseResponse(this.statusCode, this.data); 6 | final int statusCode; 7 | final Map data; 8 | 9 | factory BaseResponse.fromResponse( 10 | Response> response, 11 | ) { 12 | return BaseResponse(response.statusCode ?? 500, response.data ?? {}); 13 | } 14 | 15 | /// This value will contain the reason of why the request failed 16 | /// if the [statusCode] between 200 and 299 [clientErrorMessage] will be null 17 | String? get clientErrorMessage { 18 | if (statusCode >= 400 && statusCode <= 500) { 19 | if (data['error'] == null) { 20 | return 'Unexpected error'; 21 | } 22 | return data['error'] as String; 23 | } 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/product/models/friend.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'friend.freezed.dart'; 4 | 5 | part 'friend.g.dart'; 6 | 7 | @freezed 8 | class Friend with _$Friend { 9 | const factory Friend({ 10 | required int id, 11 | required String userName, 12 | }) = _Friend; 13 | 14 | factory Friend.fromJson(Map json) => _$FriendFromJson(json); 15 | } 16 | -------------------------------------------------------------------------------- /lib/product/models/message.dart: -------------------------------------------------------------------------------- 1 | /// Message model for Chat page 2 | final class Message { 3 | Message({required this.text, required this.isClient}); 4 | 5 | final String text; 6 | 7 | /// is the message belong to client 8 | final bool isClient; 9 | } 10 | -------------------------------------------------------------------------------- /lib/product/models/request_models/friend_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'friend_request.g.dart'; 4 | 5 | @JsonSerializable() 6 | 7 | /// API service accept this model on any kind of friend request 8 | class FriendRequest { 9 | FriendRequest({required this.targetId}); 10 | 11 | final String targetId; 12 | 13 | Map toJson() => _$FriendRequestToJson(this); 14 | } 15 | -------------------------------------------------------------------------------- /lib/product/models/request_models/language_set_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:langbuddy/product/models/user.dart'; 3 | 4 | part 'language_set_model.g.dart'; 5 | 6 | @JsonSerializable() 7 | final class LanguageSetModel { 8 | LanguageSetModel({required this.native, required this.learning}); 9 | 10 | /// User's native language 11 | final String native; 12 | 13 | /// The language user's want to practice 14 | final String learning; 15 | 16 | Map toJson() => _$LanguageSetModelToJson(this); 17 | 18 | factory LanguageSetModel.fromUser(User user) => LanguageSetModel( 19 | native: user.nativeLanguage, 20 | learning: user.learningLanguage, 21 | ); 22 | 23 | LanguageSetModel copyWith({ 24 | String? native, 25 | String? learning, 26 | }) { 27 | return LanguageSetModel( 28 | native: native ?? this.native, 29 | learning: learning ?? this.learning, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/product/models/request_models/login_request_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'login_request_model.g.dart'; 4 | 5 | @JsonSerializable() 6 | final class LoginRequestModel { 7 | LoginRequestModel({required this.email, required this.password}); 8 | 9 | final String email; 10 | final String password; 11 | 12 | Map toJson() => _$LoginRequestModelToJson(this); 13 | } 14 | -------------------------------------------------------------------------------- /lib/product/models/request_models/register_request_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'register_request_model.g.dart'; 4 | 5 | @JsonSerializable() 6 | 7 | /// register end point data 8 | final class RegisterRequestModel { 9 | RegisterRequestModel( 10 | {required this.username, required this.email, required this.password}); 11 | 12 | final String username; 13 | final String email; 14 | final String password; 15 | 16 | Map toJson() => _$RegisterRequestModelToJson(this); 17 | } 18 | -------------------------------------------------------------------------------- /lib/product/models/request_models/search_friend_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'search_friend_request.g.dart'; 4 | 5 | @JsonSerializable() 6 | class SearchFriendRequest { 7 | SearchFriendRequest({required this.native, required this.learning}); 8 | 9 | final String native; 10 | final String learning; 11 | 12 | Map toJson() => _$SearchFriendRequestToJson(this); 13 | } 14 | -------------------------------------------------------------------------------- /lib/product/models/response_models/friend_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:langbuddy/product/models/friend.dart'; 3 | 4 | part 'friend_response.g.dart'; 5 | 6 | @JsonSerializable() 7 | class FriendResponse { 8 | FriendResponse({required this.friends, required this.friendRequests}); 9 | 10 | final List friends; 11 | final List friendRequests; 12 | 13 | factory FriendResponse.fromJson(Map json) => 14 | _$FriendResponseFromJson(json); 15 | } 16 | -------------------------------------------------------------------------------- /lib/product/models/response_models/search_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:langbuddy/product/models/friend.dart'; 3 | 4 | part 'search_response.g.dart'; 5 | 6 | @JsonSerializable() 7 | final class SearchResponse { 8 | SearchResponse(this.searchedUsers); 9 | final List searchedUsers; 10 | 11 | factory SearchResponse.fromJson(Map json) => 12 | _$SearchResponseFromJson(json); 13 | } 14 | -------------------------------------------------------------------------------- /lib/product/models/response_models/tokens_response_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'tokens_response_model.g.dart'; 4 | 5 | @JsonSerializable() 6 | class TokensResponseModel { 7 | TokensResponseModel(this.accessToken, this.refreshToken); 8 | 9 | final String accessToken; 10 | final String refreshToken; 11 | 12 | factory TokensResponseModel.fromJson(Map json) => 13 | _$TokensResponseModelFromJson(json); 14 | } 15 | -------------------------------------------------------------------------------- /lib/product/models/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'user.freezed.dart'; 4 | 5 | part 'user.g.dart'; 6 | 7 | @freezed 8 | class User with _$User { 9 | const factory User({ 10 | required int id, 11 | required String userName, 12 | required String email, 13 | required String nativeLanguage, 14 | required String learningLanguage, 15 | }) = _User; 16 | 17 | factory User.empty() => const User( 18 | id: -1, 19 | userName: '', 20 | email: '', 21 | nativeLanguage: '', 22 | learningLanguage: '', 23 | ); 24 | 25 | factory User.fromJson(Map json) => _$UserFromJson(json); 26 | } 27 | -------------------------------------------------------------------------------- /lib/product/navigation/auth_guard.dart: -------------------------------------------------------------------------------- 1 | part of 'app_router.dart'; 2 | 3 | final class AuthGuard extends AutoRouteGuard { 4 | @override 5 | void onNavigation(NavigationResolver resolver, StackRouter router) { 6 | final authService = GetIt.I(); 7 | if (resolver.isReevaluating) { 8 | if (authService.isAuthenticated) { 9 | resolver.next(); 10 | if (!router.isRouteActive(DashBoardRoute.name)) { 11 | resolver.redirect(DashBoardRoute()); 12 | } 13 | } else { 14 | resolver.next(false); 15 | resolver.redirect(LoginRoute()); 16 | } 17 | } else { 18 | if (authService.isAuthenticated) { 19 | if (resolver.routeName == LoginRoute.name || 20 | resolver.routeName == SignUpRoute.name) { 21 | resolver.next(false); 22 | resolver.redirect(DashBoardRoute()); 23 | return; 24 | } 25 | resolver.next(); 26 | } else if (resolver.routeName == LoginRoute.name || 27 | resolver.routeName == SignUpRoute.name) { 28 | resolver.next(); 29 | } else { 30 | resolver.next(false); 31 | resolver.redirect(LoginRoute()); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/product/navigation/language_guard.dart: -------------------------------------------------------------------------------- 1 | part of 'app_router.dart'; 2 | 3 | /// This guard will protect the user from using the application 4 | /// with selecting any native language or learning language data 5 | final class LanguageGuard extends AutoRouteGuard { 6 | @override 7 | void onNavigation(NavigationResolver resolver, StackRouter router) { 8 | final authService = GetIt.I(); 9 | if (resolver.isReevaluating) { 10 | if (authService.isAuthenticated && 11 | resolver.routeName == DashBoardRoute.name) { 12 | resolver.next(); 13 | languageProtector(resolver, authService); 14 | return; 15 | } 16 | resolver.next(); 17 | return; 18 | } else { 19 | if (authService.isAuthenticated && 20 | resolver.routeName == DashBoardRoute.name) { 21 | resolver.next(); 22 | languageProtector(resolver, authService); 23 | } else { 24 | resolver.next(); 25 | } 26 | } 27 | } 28 | 29 | void languageProtector(NavigationResolver resolver, AuthManager authService) { 30 | if (authService.user.nativeLanguage.isEmpty) { 31 | resolver.redirect( 32 | LanguagePickerRoute( 33 | isSelectingNative: true, 34 | canPop: false, 35 | ), 36 | ); 37 | } 38 | if (authService.user.learningLanguage.isEmpty) { 39 | resolver.redirect( 40 | LanguagePickerRoute( 41 | isSelectingNative: false, 42 | canPop: false, 43 | ), 44 | ); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/product/services/authentication/IAuth_request.dart: -------------------------------------------------------------------------------- 1 | abstract class IAuthService { 2 | /// [login] function always should return the data of the user 3 | Future?> login(); 4 | } 5 | -------------------------------------------------------------------------------- /lib/product/services/authentication/auth_request.dart: -------------------------------------------------------------------------------- 1 | import 'package:langbuddy/product/constants/enums/api_request_method_enum.dart'; 2 | import 'package:langbuddy/product/constants/enums/end_point_enums.dart'; 3 | import 'package:langbuddy/product/models/request_models/login_request_model.dart'; 4 | import 'package:langbuddy/product/models/request_models/register_request_model.dart'; 5 | import 'package:langbuddy/product/services/authentication/IAuth_request.dart'; 6 | import 'package:langbuddy/product/services/network_service.dart'; 7 | 8 | /// [AuthAPIRequest] class is design for authentication requests 9 | /// 10 | final class AuthAPIRequest implements IAuthService { 11 | AuthAPIRequest({required this.loginRequestModel}); 12 | 13 | final LoginRequestModel loginRequestModel; 14 | 15 | ///Login api request if the login success the 16 | @override 17 | Future?> login() async { 18 | final response = await NetworkService.instance.baseRequest( 19 | EndPointEnums.userLogin, 20 | APIRequestMethod.POST, 21 | loginRequestModel.toJson(), 22 | ); 23 | if (response.statusCode != 200 && response.statusCode != 201) { 24 | return null; 25 | } 26 | return response.data; 27 | } 28 | 29 | static Future?> register( 30 | RegisterRequestModel model) async { 31 | final response = await NetworkService.instance.baseRequest( 32 | EndPointEnums.userSignUp, 33 | APIRequestMethod.POST, 34 | model.toJson(), 35 | ); 36 | if (response.statusCode != 200 && response.statusCode != 201) { 37 | return null; 38 | } 39 | return response.data; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/product/services/authentication/login_auth_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:langbuddy/product/constants/enums/api_request_method_enum.dart'; 2 | import 'package:langbuddy/product/constants/enums/end_point_enums.dart'; 3 | import 'package:langbuddy/product/models/request_models/login_request_model.dart'; 4 | import 'package:langbuddy/product/services/authentication/IAuth_request.dart'; 5 | import 'package:langbuddy/product/services/network_service.dart'; 6 | 7 | final class LoginAuthService implements IAuthService { 8 | LoginAuthService({required this.loginRequestModel}); 9 | 10 | final LoginRequestModel loginRequestModel; 11 | 12 | /// This function will handle the user's login 13 | Future?> login() async { 14 | final response = await NetworkService.instance.baseRequest( 15 | EndPointEnums.userLogin, 16 | APIRequestMethod.POST, 17 | loginRequestModel.toJson(), 18 | ); 19 | if (response.statusCode != 200 && response.statusCode != 201) { 20 | return null; 21 | } 22 | return response.data; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/product/services/authentication/refresh_auth.dart: -------------------------------------------------------------------------------- 1 | import 'package:langbuddy/product/constants/enums/api_request_method_enum.dart'; 2 | import 'package:langbuddy/product/constants/enums/end_point_enums.dart'; 3 | import 'package:langbuddy/product/services/authentication/IAuth_request.dart'; 4 | import 'package:langbuddy/product/services/network_service.dart'; 5 | 6 | /// Authorization with refresh token 7 | final class RefreshTokenAuth implements IAuthService { 8 | RefreshTokenAuth({required this.refreshToken}); 9 | 10 | final String refreshToken; 11 | 12 | @override 13 | Future?> login() async { 14 | final response = await NetworkService.instance.baseRequest( 15 | EndPointEnums.refreshToken, 16 | APIRequestMethod.POST, 17 | {'refreshToken': refreshToken}, 18 | ); 19 | return response.data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/product/services/authentication/register_auth_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:langbuddy/product/constants/enums/api_request_method_enum.dart'; 2 | import 'package:langbuddy/product/constants/enums/end_point_enums.dart'; 3 | import 'package:langbuddy/product/extension/base_response_extension.dart'; 4 | import 'package:langbuddy/product/models/request_models/register_request_model.dart'; 5 | import 'package:langbuddy/product/services/authentication/IAuth_request.dart'; 6 | import 'package:langbuddy/product/services/network_service.dart'; 7 | 8 | final class RegisterAuthService implements IAuthService { 9 | RegisterAuthService({required this.registerRequestModel}); 10 | 11 | final RegisterRequestModel registerRequestModel; 12 | 13 | Future?> login() async { 14 | final response = await NetworkService.instance.baseRequest( 15 | EndPointEnums.userSignUp, 16 | APIRequestMethod.POST, 17 | registerRequestModel.toJson(), 18 | ); 19 | if (!response.isSucceed) { 20 | return null; 21 | } 22 | return response.data; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/product/services/chat_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:web_socket_channel/io.dart'; 2 | import 'package:web_socket_channel/web_socket_channel.dart'; 3 | 4 | final class ChatService { 5 | ChatService._(); 6 | 7 | static ChatService get instance { 8 | return _instance ??= ChatService._(); 9 | } 10 | 11 | static ChatService? _instance; 12 | 13 | String _accessToken = ''; 14 | 15 | void setAccessToken(String accessToken) { 16 | _accessToken = accessToken; 17 | } 18 | 19 | WebSocketChannel connectToChat(String roomId) { 20 | final wsUrl = Uri.parse('ws://localhost:8080/api/v1/chat/$roomId'); 21 | final channel = IOWebSocketChannel.connect( 22 | wsUrl, 23 | headers: {'Authorization': _accessToken}, 24 | ); 25 | return channel; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/product/services/firebase_messaging.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_messaging/firebase_messaging.dart'; 2 | import 'package:logger/web.dart'; 3 | 4 | @pragma('vm:entry-point') 5 | Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { 6 | Logger().i("Handling a background message: ${message.notification!.title}"); 7 | } 8 | 9 | void _firebaseMessagingForegroundHandler(RemoteMessage message) { 10 | Logger().i("Handling a background message: ${message.notification!.title}"); 11 | Logger().i("Handling a background message: ${message.notification!.body}"); 12 | } 13 | 14 | final class FirebaseMessagingService { 15 | FirebaseMessagingService._(); 16 | 17 | static final FirebaseMessaging _messaging = FirebaseMessaging.instance; 18 | 19 | static void _requestPermissionNotification() async { 20 | await _messaging.requestPermission( 21 | alert: true, 22 | sound: true, 23 | badge: true, 24 | ); 25 | } 26 | 27 | static void connectNotification() { 28 | _messaging.setForegroundNotificationPresentationOptions( 29 | alert: true, 30 | sound: true, 31 | badge: true, 32 | ); 33 | _requestPermissionNotification(); 34 | 35 | // notification while application open in screen 36 | FirebaseMessaging.onMessage.listen(_firebaseMessagingForegroundHandler); 37 | 38 | // notification while application closed or inbackground 39 | FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); 40 | } 41 | 42 | static Future getToken() async { 43 | return await _messaging.getToken(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/product/services/network_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:langbuddy/product/constants/api_constants.dart'; 3 | import 'package:langbuddy/product/constants/enums/api_request_method_enum.dart'; 4 | import 'package:langbuddy/product/constants/enums/end_point_enums.dart'; 5 | import 'package:langbuddy/product/models/base_response_model.dart'; 6 | 7 | final class NetworkService { 8 | NetworkService._() { 9 | _dio = Dio( 10 | BaseOptions( 11 | baseUrl: ApiConstants.url, 12 | connectTimeout: ApiConstants.connectTimeout, 13 | receiveTimeout: ApiConstants.receiveTimeout, 14 | ), 15 | ); 16 | } 17 | 18 | static late final Dio _dio; 19 | 20 | //Dio get dio => _dio; 21 | 22 | static final instance = NetworkService._(); 23 | 24 | Future baseRequest( 25 | EndPointEnums endPoint, 26 | APIRequestMethod method, 27 | Map? data, 28 | ) async { 29 | final response = await _dio.request>( 30 | ApiConstants.url + endPoint.value, 31 | data: data, 32 | options: Options( 33 | method: method.value, 34 | ), 35 | ); 36 | return BaseResponse.fromResponse(response); 37 | } 38 | 39 | // change the header's accessToken key 40 | void setAccessToken(String accessToken) { 41 | _dio.options.headers['Authorization'] = accessToken; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/product/theme/light_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:langbuddy/product/constants/font_constants.dart'; 3 | 4 | /// Light theme class 5 | final class CustomLightTheme { 6 | CustomLightTheme._(); 7 | 8 | /// App's main light theme variable 9 | static final ThemeData theme = ThemeData( 10 | useMaterial3: true, 11 | brightness: Brightness.light, 12 | primaryColor: Colors.orange, 13 | colorScheme: const ColorScheme.light( 14 | //primary: Color(0XFF1dab62), 15 | onPrimary: Color(0XFF333333), 16 | background: Color(0XFFf5f5f5), 17 | surface: Color(0XFFfafafa), 18 | ), 19 | textTheme: const TextTheme( 20 | headlineMedium: TextStyle( 21 | fontWeight: FontWeight.w600, 22 | letterSpacing: 1.2, 23 | ), 24 | ), 25 | fontFamily: FontConstants.inter, 26 | ); 27 | } 28 | --------------------------------------------------------------------------------