Daehan Lim

Header Project Icon

ShareLingo - Language Exchange Social Media App

πŸ“ Overview

πŸ“Œ App Introduction: Social media platform connecting language learners to share posts and interact with each other
πŸ•’ Duration: May 16, 2025 ~ May 27, 2025 (2 weeks)
πŸ“± Platform: Flutter cross-platform app (Android, iOS)
πŸ‘₯ Team Size: 4 developers
πŸ’Ό Role: Team Leader, CI/CD pipeline, authentication system, profile management, feed filtering, Google Maps integration, onboarding flow
πŸ› οΈ Key Technologies: Flutter Firebase Riverpod Clean Architecture Google OAuth Firestore Cloud Functions GitHub Actions VWorld API
πŸ”— GitHub: zero-to-one-flutter/flutter-share-lingo
πŸ”— Play Store: ShareLingo on Play Store

1 login screen 7 my profile screen 8 my profile posts screen 9 edit profile screen 2 onboarding date screen 3 onboarding name screen 4 onboarding language screen 5 onboarding language selection screen 11 self intro screen 6 onboarding location screen 10 settings screen

πŸ“– Project Background

πŸ› οΈ Tech Stack

Flutter Dart Riverpod Clean Architecture Firebase Firestore Cloud Functions Google OAuth Crashlytics GitHub Actions Google Maps MVVM Firebase Auth VWorld API Geolocator Cached Network Image Dio Mocktail Firebase Storage Image Picker SharedPreferences URL Launcher

πŸ“‹ Project Structure

β”œβ”€β”€ app/                               # App-wide settings, constants, and themes
β”‚   β”œβ”€β”€ constants/                     # App constant definitions
β”‚   β”‚   β”œβ”€β”€ app_colors.dart            # Color scheme definitions
β”‚   β”‚   β”œβ”€β”€ app_constants.dart         # Constant values (language lists, tags, etc.)
β”‚   β”‚   └── app_styles.dart            # Style definitions
β”‚   └── theme.dart                     # App theme configuration

β”œβ”€β”€ core/                              # Core functionality and utilities
β”‚   β”œβ”€β”€ exceptions/                    # App-wide exception classes
β”‚   β”œβ”€β”€ extensions/                    # Extension method definitions
β”‚   β”œβ”€β”€ providers/                     # Common providers
β”‚   β”œβ”€β”€ ui_validators/                 # UI validation utilities
β”‚   └── utils/                         # Utility functions
β”‚       β”œβ”€β”€ dialogue_util.dart         # Dialog utilities
β”‚       β”œβ”€β”€ format_time_ago.dart       # Time formatting utilities
β”‚       β”œβ”€β”€ general_utils.dart         # General utility functions
β”‚       β”œβ”€β”€ geolocator_util.dart       # Location utilities
β”‚       β”œβ”€β”€ logger.dart                # Logging utilities
β”‚       β”œβ”€β”€ map_url_util.dart          # Map URL generation utilities
β”‚       β”œβ”€β”€ navigation_util.dart       # Navigation utilities
β”‚       β”œβ”€β”€ snackbar_util.dart         # Snackbar utilities
β”‚       └── throttler_util.dart        # Throttling utilities

β”œβ”€β”€ data/                              # Data layer and data access
β”‚   β”œβ”€β”€ data_source/                   # Data source classes
β”‚   β”œβ”€β”€ dto/                           # Data Transfer Objects
β”‚   └── repository/                    # Repository implementations

β”œβ”€β”€ domain/                            # Business logic and entities
β”‚   β”œβ”€β”€ entity/                        # Domain entities
β”‚   β”œβ”€β”€ repository/                    # Repository interfaces
β”‚   └── usecase/                       # Use cases

β”œβ”€β”€ presentation/                      # UI layer
β”‚   β”œβ”€β”€ pages/                         # App screens
β”‚   β”‚   β”œβ”€β”€ home/                      # Home screen (example)
β”‚   β”‚   β”‚   β”œβ”€β”€ home_page.dart         # Home page
β”‚   β”‚   β”‚   β”œβ”€β”€ home_view_model.dart   # Home view model
β”‚   β”‚   β”‚   └── widgets/               # Home screen widgets
β”‚   β”œβ”€β”€ widgets/                       # Common widgets
β”‚   └── user_global_view_model.dart    # Global user view model

β”œβ”€β”€ main.dart                          # App entry point

🌟 Key Contributions

Project Leadership and Development Process Management

CI/CD Pipeline Implementation

Google OAuth Authentication and User Management System

Multi-step Onboarding and Location-based Matching

Customized Feed Filtering with 4 Targeted Tabs

Firebase Backend Automation and Security

Terms of Service and Privacy Policy

Architecture, Testing, and Error Handling

🧭 Technical Decision-Making

1. Clean Architecture and Dependency Injection Pattern

// Repository Interface (Domain Layer)
abstract class AuthRepository {
  Future<AppUser?> signInWithGoogle();
  Future<void> signOut();
  Stream<String?> authStateChanges();
}

// Repository Implementation (Data Layer)
class AuthRepositoryImpl implements AuthRepository {
  final GoogleSignInDataSource _googleSignIn;
  final FirebaseAuthDataSource _firebaseAuth;
  final UserDataSource _userDataSource;

  AuthRepositoryImpl(this._googleSignIn, this._firebaseAuth, this._userDataSource);
  // Implementation...
}

// Dependency Injection Setup
final authRepositoryProvider = Provider<AuthRepository>(
  (ref) => AuthRepositoryImpl(
    ref.read(googleSignInDataSourceProvider),
    ref.read(firebaseAuthDataSourceProvider),
    ref.read(userFirestoreDataSourceProvider),
  ),
);

2. GeoPoint Extension for Distance Calculations

extension GeoPointExtensions on GeoPoint {
  double distanceFrom(GeoPoint other) {
    final distanceInMeters = Geolocator.distanceBetween(
      latitude, longitude, other.latitude, other.longitude,
    );
    return distanceInMeters / 1000;
  }
}

// Usage in UserGlobalViewModel
String? calculateDistanceFrom(GeoPoint? otherLocation) {
  final userLocation = state?.location;
  if (userLocation == null || otherLocation == null) return null;
  final distanceKm = userLocation.distanceFrom(otherLocation);
  return '${distanceKm.toStringAsFixed(1)} km';
}

🌱 Problem Solving

1. Onboarding User Experience Optimization

enum LocationStatus { success, deniedTemporarily, deniedForever, error }

Future<(LocationStatus, Position?)> getPosition() async {
  try {
    final permission = await Geolocator.checkPermission();
    
    if (permission == LocationPermission.denied ||
        permission == LocationPermission.deniedForever) {
      final requested = await Geolocator.requestPermission();
      
      if (requested == LocationPermission.denied) {
        return (LocationStatus.deniedTemporarily, null);
      }
      
      if (requested == LocationPermission.deniedForever) {
        return (LocationStatus.deniedForever, null);
      }
    }
    
    final position = await Geolocator.getCurrentPosition(
      locationSettings: const LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 100,
      ),
    );
    
    return (LocationStatus.success, position);
  } catch (e) {
    return (LocationStatus.error, null);
  }
}

2. Firebase Configuration Files Missing in GitHub Actions

- name: Decode firebase_options.dart
  run: |
    mkdir -p lib
    echo "$" | base64 --decode > lib/firebase_options.dart

3. Profile Update Data Synchronization

🎞️ Video

Watch the Video