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
This project follows the GetX architecture pattern which consists of Models, Views, Controllers, and Services.
├── 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
AppUser { uid: str, email: str, photo_url: str }
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}'; } }
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}'; } }
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}'; } }
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"; }
###
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 }