Skip to content
burakJsPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Lokaly 📍

Drop yourself into Türkiye's most famous places with AI, then share it.

Lokaly is a social photo app. You upload a photo of yourself, pick a city and a landmark, and AI realistically places you at that spot — keeping your face and appearance intact. The generated image lands in a social feed where it can be liked, commented on, and shared.


Screenshots

Auth

Login Google Sign-In
Login Google Sign-In

Create Flow

1 · Select City 2 · Select Place 3 · Select Photo
Select City Select Place Select Photo
Generating Result Native Share
Generating Result Share

Social

Feed Comments Profile
Feed Comments Profile

Account & Settings

Edit Profile Settings (EN) Settings (TR)
Edit Profile Settings English Settings Turkish

Features

  • 📸 AI placement — upload your photo and get realistically placed at a famous Turkish landmark
  • 🏙️ 81 cities — landmark suggestions are fetched live per city via Gemini
  • 🤖 Person-preserving generation — your face and appearance stay identical to the reference
  • 🧭 Guided 4-step create flow — City → Place → Photo → Result with a progress bar
  • ❤️ Social feed — like, comment, and follow other users
  • 📤 Share — share generated photos through the native share sheet
  • 👤 Profiles — stats, post grid, editable username / bio / avatar
  • 🌗 Theme — Light / Dark / System
  • 🌍 Localization — Turkish & English (easy_localization)
  • 🔐 Auth — email/password and Google Sign-In via Firebase

Tech Stack

Layer Technology
Framework Flutter (Dart SDK ^3.12)
Page state flutter_bloc (Cubit)
Global state provider (AppState)
Navigation go_router
Extensions kartal
Auth firebase_auth + google_sign_in
Database cloud_firestore
Storage firebase_storage
AI (landmarks) firebase_ai — Gemini 2.5 Flash
AI (image) firebase_ai — Gemini 2.5 Flash Image
Images image_picker, cached_network_image, flutter_image_compress
Sharing share_plus
Localization easy_localization
Fonts google_fonts (Manrope)
Env flutter_dotenv
Models freezed + json_serializable
Unit tests flutter_test, mockito, bloc_test
Integration tests patrol

Getting Started

Prerequisites

  • Flutter SDK (latest stable, Dart ^3.12)
  • A Firebase project with Authentication (Email + Google), Firestore, and Storage enabled
  • Firebase AI (Gemini) enabled on the project
  • flutterfire_cli installed

Setup

git clone https://github.com/burakJs/Lokaly.git
cd lokaly
flutter pub get

Configure Firebase (generates firebase_options.dart and platform config):

dart pub global activate flutterfire_cli
flutterfire configure

Create a .env file in the project root (it is referenced as an asset in pubspec.yaml):

# Optional — only needed for Google Sign-In on Android.
# The Google "Web client" OAuth ID (serverClientId). Leave empty to rely on
# platform config (google-services.json / Info.plist).
GOOGLE_SERVER_CLIENT_ID=

# Gemini access is provided through the Firebase project (firebase_ai),
# so no separate Gemini API key is required at runtime.

The app loads .env on startup, so the file must exist (it can be empty).

Run code generation (freezed / json_serializable / mockito):

dart run build_runner build --delete-conflicting-outputs

Run the app:

flutter run

Running Tests

# Unit & widget tests
flutter test

# With coverage
flutter test --coverage

# Patrol integration / E2E tests (Android)
patrol test

# Patrol integration / E2E tests (iOS)
patrol test --ios

Project Structure

The codebase follows a core / feature / product layering convention.

lib/
├── main.dart          # App bootstrap: init + global providers + runApp
├── app/               # LokalyApp (MaterialApp) + go_router config
├── core/              # Infrastructure: theme, init, constants (UI-agnostic)
│   ├── theme/         #   AppColors, text styles, ThemeData (light/dark)
│   ├── constants/     #   App constants + Turkey cities
│   └── init/          #   dotenv + Firebase initialization
├── feature/           # One folder per screen: cubit/ (state) + view/ (UI)
│   ├── splash/        #   auth-state redirect
│   ├── auth/          #   login + register
│   ├── feed/          #   home feed
│   ├── create/        #   city → landmark → photo → generating → result
│   ├── comments/      #   realtime comments sheet
│   ├── profile/       #   profile + edit profile
│   ├── post_detail/   #   single post view
│   └── settings/      #   theme + language + account
└── product/           # Cross-feature shared code
    ├── state/         #   AppState (global ChangeNotifier)
    ├── models/        #   user / post / comment models
    ├── services/      #   auth, firestore, gemini, image gen, share, etc.
    ├── widgets/       #   shared widgets (logo, post card, badges...)
    └── navigation/    #   route paths

test/                  # Unit + widget + cubit tests (flutter_test)
integration_test/      # Patrol E2E tests
screenshots/           # Images used in this README

Architecture

Two-layer state management:

  • Page state → each screen owns a Cubit (flutter_bloc) under feature/<x>/cubit/. Holds screen-local concerns: form, loading, error, lists, selection.
  • Global state → AppState (provider / ChangeNotifier) under product/state/. Holds app-wide concerns: theme mode, current user, locale.
View (feature/<x>/view)
        ↓  (BlocBuilder / BlocProvider)
Cubit (feature/<x>/cubit)  ←→  AppState (global, via provider)
        ↓  (constructor injection)
Services (product/services: Auth, Firestore, Gemini, Image, Share)
        ↓
Firebase  ·  Gemini (firebase_ai)

Rule: service calls happen inside Cubits, never in the view. Cubits receive their services via constructor injection so they can be mocked in tests.


Firestore Data Model

users/{uid}                                  → username, email, photoURL, bio,
                                               followersCount, followingCount, createdAt
posts/{postId}                               → userId, userUsername, userPhotoURL,
                                               imageURL, city, landmark,
                                               likesCount, commentsCount, createdAt
posts/{postId}/likes/{uid}                   → createdAt
posts/{postId}/comments/{commentId}          → userId, userUsername, text, createdAt
following/{uid}/userFollowing/{targetUid}    → createdAt
followers/{uid}/userFollowers/{targetUid}    → createdAt

Likes and follows are written via Firestore transactions so the denormalized counters stay consistent.


Built with Flutter · Firebase · Gemini.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages