additional-docs

Implementation Details

Project Dependencies

dependencies:
    flutter:
        sdk: flutter

    cupertino_icons: ^1.0.2
    get: ^4.6.5
    firebase_auth: ^4.2.6
    cloud_firestore: ^4.3.2
    firebase_core: ^2.8.0
    google_sign_in: ^5.4.4
    url_launcher: ^6.1.9
    flutter_signin_button: ^2.0.0
    flutter_osm_plugin: ^0.40.3
    http: ^0.13.5
    flutter_svg: ^1.1.6
    google_fonts: ^4.0.3
    carousel_slider: ^4.2.1
    carousel_indicator: ^1.0.6
    bottom_navy_bar: ^6.0.0
    geoflutterfire2: ^2.3.15
    geolocator: ^9.0.2
    material_tag_editor: ^0.1.2

Project Architecture

This project follows the GetX architecture pattern which consists of Models, Views, Controllers, and Services.

Folder Structure

├── assets
│   ├── Frame-1.svg
│   ├── Frame-2.svg
│   ├── Frame-3.svg
│   └── icons
│       ├── bx_bxs-star.svg
│       ├── cooker.svg
│       ├── filter_icon.svg
│       ├── google.svg
│       ├── ic_baseline-location-on.svg
│       ├── non-veg-icon.svg
│       └── veg-icon.svg
├── controllers
│   └── app_logic_controller.dart
├── firebase_options.dart
├── generated_plugin_registrant.dart
├── main.dart
├── models
│   ├── location_model.dart
│   └── restaurant_model.dart
├── utils
│   ├── formatters.dart
│   ├── size_util.dart
│   └── theme.dart
└── views
    ├── app_root.dart
    ├── components
    │   ├── buttons.dart
    │   ├── error_screen.dart
    │   ├── header.dart
    │   ├── informationBox.dart
    │   ├── location_selector.dart
    │   ├── menuItemTile.dart
    │   └── restaurantInformationTile.dart
    ├── contact_us_page.dart
    ├── landing_page.dart
    ├── partner_views
    │   ├── add_menu_form.dart
    │   ├── buisness_control_root.dart
    │   ├── partner_onboarding_form.dart
    │   ├── update_menu_card.dart
    │   └── update_notice_board.dart
    ├── restaurant_details_view.dart
    └── sub_views
        ├── home.dart
        ├── profile.dart
        └── saved_places.dart

Models

User Model

AppUser {
    uid: str,
    email: str,
    photo_url: str
}

Owner Model

class Owner {
  String? name;
  String? email;
  String? uid;
 
  Owner(this.name, this.email, this.uid);
 
  Owner.fromMap(Map<String, dynamic> data) {
    name = data["name"];
    email = data["email"];
    uid = data["uid"];
  }
 
  Map<String, dynamic> toMap() {
    return {"name": name, "email": email, "uid": uid};
  }
 
  @override
  String toString() {
    return 'Owner{name: $name, email: $email, uid: $uid}';
  }
}

FoodItem Model

class FoodItem {
  String? name;
  String? description;
  double? price;
  int? pos;
 
 
  FoodItem(this.name, this.description, this.price, [this.pos = 0]);
 
  FoodItem.fromMap(Map<String, dynamic> data) {
    name = data["name"];
    description = data["desc"];
    price = data["price"].toDouble();
    pos = data["pos"];
  }
 
  Map<String, dynamic> toMap() {
    return {
      "name" : name,
      "desc" : description,
      "price" : price,
      "pos" : pos
    };
  }
 
  @override
  String toString() {
    return 'MenuItem{name: $name, description: $description, price: $price, pos: $pos}';
  }
}
class Menu {
  List<FoodItem> items = [];
 
  Menu();
 
  Menu.fromList(List data) {
    items = data.map((e) => FoodItem.fromMap(e)).toList();
  }
 
  List<Map<String, dynamic>> mapCompatible() {
    return items.map((e) => e.toMap()).toList();
  }
 
  @override
  String toString() {
    return 'Menu{items: $items}';
  }
}

Restaurant Model

class Restaurant {
  String? address;
  ApplicationStatus? applicationStatus;
  Owner? owner;
 
  bool? isNonVeg;
  bool? isVeg;
  GeoFirePoint? location;
 
  String? name;
  String? mobile;
 
  Menu? menu = Menu();
 
  int? seatingCapacity;
  String? status;
 
  List<String> tags = [];
 
 
  String? noticeBoard;
 
  Restaurant.onBoardingInstance(
      this.owner,
      this.name,
      this.mobile,
      this.address,
      this.location,
      this.isVeg,
      this.isNonVeg,
      this.seatingCapacity) {
    applicationStatus =
        ApplicationStatus(Status.under_verification, DateTime.now());
    status = Status.blocked;
  }
 
  Restaurant.fromFirestoreDocument(
      DocumentSnapshot<Map<String, dynamic>> documentSnapshot) {
    Map<String, dynamic> dataFromDoc = documentSnapshot.data()!;
 
    address = dataFromDoc["address"];
    applicationStatus =
        ApplicationStatus.fromMap(dataFromDoc["application_status"]);
 
    isVeg = dataFromDoc["isVeg"];
    isNonVeg = dataFromDoc["isNonVeg"];
 
    GeoPoint locCoord = (dataFromDoc["location"]["geopoint"] as GeoPoint);
    location = GeoFirePoint(locCoord.latitude, locCoord.longitude);
 
    name = dataFromDoc["name"];
    mobile = dataFromDoc["mobile"];
 
    owner = Owner.fromMap(dataFromDoc["owner"]);
 
    seatingCapacity = dataFromDoc["seating_capacity"];
    status = dataFromDoc["status"];
 
    noticeBoard = dataFromDoc["notice_board"];
 
    menu = Menu.fromList(dataFromDoc["menu"]);
 
    tags = (dataFromDoc["tags"] as List<dynamic>).map((e) => e.toString()).toList();
  }
 
  Map<String, dynamic> toMap() {
    return {
      "address": address,
      "application_status": applicationStatus!.toMap(),
      "isNonVeg": isNonVeg,
      "isVeg": isVeg,
      "location": location!.data,
      "name": name,
      "mobile": mobile,
      "owner": owner!.toMap(),
      "seating_capacity": seatingCapacity,
      "status": status,
      "tags" : tags,
      "notice_board" : noticeBoard,
      "menu" : menu?.mapCompatible()
    };
  }
 
  @override
  String toString() {
    return 'Restaurant{address: $address, applicationStatus: $applicationStatus, owner: $owner, isNonVeg: $isNonVeg, isVeg: $isVeg, location: $location, name: $name, mobile: $mobile, menu: $menu, seatingCapacity: $seatingCapacity, status: $status, tags: $tags, noticeBoard: $noticeBoard}';
  }
}
 

Status Model

class ApplicationStatus {
  String? status;
  DateTime? timestamp;
 
  ApplicationStatus(this.status, this.timestamp);
 
  ApplicationStatus.fromMap(Map<String, dynamic> data) {
    status = data["status"];
    timestamp = data["timestamp"].toDate();
  }
 
  Map<String, dynamic> toMap() {
    return {"status": status, "timestamp": timestamp};
  }
 
  @override
  String toString() {
    return 'ApplicationStatus{status: $status, timestamp: $timestamp}';
  }
}
 
class Status {
  static String under_verification = "under_verification";
  static String verified = "verified";
  static String blocked = "blocked";
  static String active = "active" ;
  static String shadow_banned = "shadow_banned";
}

###

Controllers

  1. AppLogicController: Handles the business logic for the app using Firebase Authentication, Database etc.

Services

  1. FirebaseService: Handles the communication with Firebase Authentication and Cloud Firestore.
class AppLogicController extends GetxController {
  static const Widget _loadingWidget = Center(
    child: CircularProgressIndicator(),
  );
 
  final _authentication = FirebaseAuth.instance;
  final _database = FirebaseFirestore.instance;
 
  final Rx<User?> userFromFirebase = Rx<User?>(null);
  final Rx<Restaurant?> myRestaurant = Rx<Restaurant?>(null);
 
  final Rx<LocationInfo?> myLocation = Rx<LocationInfo?>(null);
  final RxList<Restaurant> nearbyRestaurants = RxList<Restaurant>([]);
  final RxList<Restaurant> filteredOutRestaurants = RxList<Restaurant>([]);
 
  static double defaultRadius = 5;
 
  /// User editable parameters
  final Rx<double> radius = Rx<double>(5);
  final Rx<FoodType> foodType = Rx<FoodType>(FoodType.any);
  final Rx<String> query = Rx<String>("");
 
  void _actionOnFirebaseUserChange(User? user)
  void _actionOnQuerySnapshotChange(Object? querySnapshot)
  void _actionOnLocationChange(LocationInfo? location)
  void _actionOnNearbyRestaurantsChange(Object? restaurantList)
  void _actionOnRadiusChange(double radius)
  void _actionOnFoodTypeChange(FoodType foodType)
  void _actionOnSearchQueryChange(String query)
 
  Future handleGoogleSSO() 
 
  Future handleSignOut()
 
  Future handleGoToContactUsPage()
 
  Future handleGoToPartnerOnBoardingPage()
 
  Future handleBecomeAPartner(
      String name,
      String contact,
      String address,
      double latitude,
      double longitude,
      bool isVeg,
      bool isNonVeg,
      int seatingCapacity)
 
  Future handleCustomerQueryMessage(String message)
 
  Future handleNoticeBoardUpdate(String updatedText)
 
  Future handleAddNewItem(String name, double price, String description)
 
  Future handleFoodItemDelete(FoodItem item)
 
  handleSetLocation(LocationInfo locationInfo) 
 
  handleRadiusUpdate(double radius)
 
  handleFoodTypeUpdate(FoodType foodType) 
 
  handleSearchQueryUpdate(String query) 
 
 
  double getDistanceToRestaurant(Restaurant restaurant)
 
  Future<Position?> _determinePosition()
}
 
enum FoodType { veg, nonVeg, any }