From a94eb5248122a0c681b309a15ce80ba5b5b263a9 Mon Sep 17 00:00:00 2001
From: Jodamco
Date: Fri, 12 Apr 2024 19:14:23 -0300
Subject: [PATCH 01/19] chore(android): change kotlin version due to conflict
with gradle version
---
android/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/android/build.gradle b/android/build.gradle
index 24047dce..6bfb6ff0 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.3.50'
+ ext.kotlin_version = '1.6.0'
repositories {
google()
mavenCentral()
@@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
-task clean(type: Delete) {
+tasks.register("clean", Delete) {
delete rootProject.buildDir
}
From 44998c90d07daa7ae419a8ac34510a54dc7baae1 Mon Sep 17 00:00:00 2001
From: Jodamco
Date: Fri, 12 Apr 2024 19:15:11 -0300
Subject: [PATCH 02/19] chore(general): initial setup; sets yelp appKey
---
.fvmrc | 4 ++
lib/repositories/yelp_repository.dart | 5 ++-
pubspec.lock | 54 ++++++++++++---------------
3 files changed, 30 insertions(+), 33 deletions(-)
create mode 100644 .fvmrc
diff --git a/.fvmrc b/.fvmrc
new file mode 100644
index 00000000..6108f14a
--- /dev/null
+++ b/.fvmrc
@@ -0,0 +1,4 @@
+{
+ "flutter": "3.13.9",
+ "flavors": {}
+}
\ No newline at end of file
diff --git a/lib/repositories/yelp_repository.dart b/lib/repositories/yelp_repository.dart
index f251d7b4..6de569ee 100644
--- a/lib/repositories/yelp_repository.dart
+++ b/lib/repositories/yelp_repository.dart
@@ -2,7 +2,8 @@ import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:restaurantour/models/restaurant.dart';
-const _apiKey = '';
+const _apiKey =
+ 'X-Me22MBBw6lIYs9fV-ntllINQ_8rfQXuHojnDY8UxXUpuSR0zHY5TSaPmyKY8qwqirh2LWdew6yjgxPSk2eiPWA77TgJHXRC4En1JJ0Kn2ej0vNHBTsuDfLVKMZZnYx';
class YelpRepository {
late Dio dio;
@@ -102,7 +103,7 @@ query getRestaurants {
}
}
}
-}
+ }
''';
}
}
diff --git a/pubspec.lock b/pubspec.lock
index 0b052c68..53e31b51 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -149,10 +149,10 @@ packages:
dependency: transitive
description:
name: collection
- sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
- version: "1.18.0"
+ version: "1.17.1"
convert:
dependency: transitive
description:
@@ -295,10 +295,10 @@ packages:
dependency: transitive
description:
name: js
- sha256: d9bdfd70d828eeb352390f81b18d6a354ef2044aa28ef25682079797fa7cd174
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
- version: "0.6.3"
+ version: "0.6.7"
json_annotation:
dependency: "direct main"
description:
@@ -335,26 +335,26 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
source: hosted
- version: "0.12.16"
+ version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
- version: "0.5.0"
+ version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
- sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.9.1"
mime:
dependency: transitive
description:
@@ -391,10 +391,10 @@ packages:
dependency: transitive
description:
name: petitparser
- sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
+ sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
url: "https://pub.dev"
source: hosted
- version: "6.0.2"
+ version: "5.4.0"
pool:
dependency: transitive
description:
@@ -460,26 +460,26 @@ packages:
dependency: transitive
description:
name: source_span
- sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
- sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
- version: "1.11.1"
+ version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
- sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.1"
stream_transform:
dependency: transitive
description:
@@ -508,10 +508,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
+ sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
source: hosted
- version: "0.6.1"
+ version: "0.5.1"
timing:
dependency: transitive
description:
@@ -568,14 +568,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
- web:
- dependency: transitive
- description:
- name: web
- sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
- url: "https://pub.dev"
- source: hosted
- version: "0.3.0"
web_socket_channel:
dependency: transitive
description:
@@ -588,10 +580,10 @@ packages:
dependency: transitive
description:
name: xml
- sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
+ sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
url: "https://pub.dev"
source: hosted
- version: "6.5.0"
+ version: "6.3.0"
yaml:
dependency: transitive
description:
@@ -601,5 +593,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
- dart: ">=3.2.0 <4.0.0"
+ dart: ">=3.0.0 <4.0.0"
flutter: ">=3.7.0-0"
From 1e8c49711325b2c49dcba67c1cd5d69269658c73 Mon Sep 17 00:00:00 2001
From: Jodamco
Date: Fri, 12 Apr 2024 23:47:50 -0300
Subject: [PATCH 03/19] feat(all restaurants): wip - creates restaurants bloc;
move models and respositories to data folder; split main file
---
lib/{ => data}/models/restaurant.dart | 0
lib/{ => data}/models/restaurant.g.dart | 0
.../repositories/yelp_repository.dart | 40 +----
lib/logic/restaurants/restaurants_bloc.dart | 31 ++++
lib/logic/restaurants/restaurants_event.dart | 9 ++
lib/logic/restaurants/restaurants_state.dart | 18 +++
lib/main.dart | 66 ++------
lib/presentation/views/home/home_page.dart | 77 +++++++++
.../views/restaurant/restaurant_page.dart | 0
lib/restaurantour.dart | 40 +++++
pubspec.lock | 146 +++++++++++++++++-
pubspec.yaml | 4 +
test/widget_test.dart | 5 +-
13 files changed, 342 insertions(+), 94 deletions(-)
rename lib/{ => data}/models/restaurant.dart (100%)
rename lib/{ => data}/models/restaurant.g.dart (100%)
rename lib/{ => data}/repositories/yelp_repository.dart (53%)
create mode 100644 lib/logic/restaurants/restaurants_bloc.dart
create mode 100644 lib/logic/restaurants/restaurants_event.dart
create mode 100644 lib/logic/restaurants/restaurants_state.dart
create mode 100644 lib/presentation/views/home/home_page.dart
create mode 100644 lib/presentation/views/restaurant/restaurant_page.dart
create mode 100644 lib/restaurantour.dart
diff --git a/lib/models/restaurant.dart b/lib/data/models/restaurant.dart
similarity index 100%
rename from lib/models/restaurant.dart
rename to lib/data/models/restaurant.dart
diff --git a/lib/models/restaurant.g.dart b/lib/data/models/restaurant.g.dart
similarity index 100%
rename from lib/models/restaurant.g.dart
rename to lib/data/models/restaurant.g.dart
diff --git a/lib/repositories/yelp_repository.dart b/lib/data/repositories/yelp_repository.dart
similarity index 53%
rename from lib/repositories/yelp_repository.dart
rename to lib/data/repositories/yelp_repository.dart
index 6de569ee..e9042d18 100644
--- a/lib/repositories/yelp_repository.dart
+++ b/lib/data/repositories/yelp_repository.dart
@@ -1,6 +1,6 @@
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
-import 'package:restaurantour/models/restaurant.dart';
+import 'package:restaurantour/data/models/restaurant.dart';
const _apiKey =
'X-Me22MBBw6lIYs9fV-ntllINQ_8rfQXuHojnDY8UxXUpuSR0zHY5TSaPmyKY8qwqirh2LWdew6yjgxPSk2eiPWA77TgJHXRC4En1JJ0Kn2ej0vNHBTsuDfLVKMZZnYx';
@@ -21,44 +21,6 @@ class YelpRepository {
),
);
- /// Returns a response in this shape
- /// {
- /// "data": {
- /// "search": {
- /// "total": 5056,
- /// "business": [
- /// {
- /// "id": "faPVqws-x-5k2CQKDNtHxw",
- /// "name": "Yardbird Southern Table & Bar",
- /// "price": "$$",
- /// "rating": 4.5,
- /// "photos": [
- /// "https:///s3-media4.fl.yelpcdn.com/bphoto/_zXRdYX4r1OBfF86xKMbDw/o.jpg"
- /// ],
- /// "reviews": [
- /// {
- /// "id": "sjZoO8wcK1NeGJFDk5i82Q",
- /// "rating": 5,
- /// "user": {
- /// "id": "BuBCkWFNT_O2dbSnBZvpoQ",
- /// "image_url": "https:///s3-media2.fl.yelpcdn.com/photo/v8tbTjYaFvkzh1d7iE-pcQ/o.jpg",
- /// "name": "Gina T."
- /// }
- /// },
- /// {
- /// "id": "okpO9hfpxQXssbTZTKq9hA",
- /// "rating": 5,
- /// "user": {
- /// "id": "0x9xu_b0Ct_6hG6jaxpztw",
- /// "image_url": "https:///s3-media3.fl.yelpcdn.com/photo/gjz8X6tqE3e4praK4HfCiA/o.jpg",
- /// "name": "Crystal L."
- /// }
- /// },
- /// ...
- /// ]
- /// }
- /// }
- ///
Future getRestaurants({int offset = 0}) async {
try {
final response = await dio.post
@@ -65,9 +64,14 @@ Go to `Preferences > Languages & Frameworks > Flutter` and set the Flutter SDK p
-## Requirements
+## App Structure
+
+### Design
+
+The app is based on the following [Figma File](https://www.figma.com/file/KsEhQUp66m9yeVkvQ0hSZm/Flutter-Test?node-id=0%3A1). Improvements and new features are welcolme as long as they follow the same visual aspect of the initial design
-### App Structure
+![List View](screenshots/listview.png)
+![Detail View](screenshots/detailview.png)
#### Restaurant List Page
@@ -96,88 +100,65 @@ Go to `Preferences > Languages & Frameworks > Flutter` and set the Flutter SDK p
- User image
- Review Text (These are just snippets of the full review, usually like 3-4 lines long)
-#### Misc.
+### Architechture
-- Clear documentation on the structure and architecture of your application.
-- Clear and logical commit messages.
- - We suggest following [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
+The project follows a Domain based architecture, a less rigid form of Clean Architecture. On the domain, sets of features may share same widgets, repositories and logic to acomplish the desired tasks. So far, the only domain is the Restaurant itself since it's a small app, therefore dependencies are declared right on top of widget tree, the top of the domain, and injected when needed through the usage of RepositoryProvider and BlocProvider.
-## Test Coverage
+- Restaurant domain
+ - data
+ - Restaurant models
+ - Yelp Repository
+ - logic
+ - FavouriteRestaurantsBloc
+ - RestaurantReviewsCubit
+ - RestaurantsBloczz
+ - presentation
+ - All Restaurants
+ - Favourite Restaurants
+ - Restaurant details
-To demonstrate your experience writing different types of tests in Flutter please do the following:
+This organization also aims to protect core models from change and make the view dependent on models and logic in a way that make the view reflect the desired business rules of the project.
-- Choose ONE portion of your state management and write a unit test.
-- Choose ONE widget and write a widget test.
+### Presentation
-Feel free to add more tests as you see fit but the above is the minimum requirement.
+The presentation widgets are splited into two different folders
+- common, where widgets used across the Restaurants domain are stored
+- views, where widgets representing bigger/complete screen vies are stored
-## Design
+Common and View widgets both may have widget folders within, where minor widgets are stored to enhance code splitting. This helps improve maintainance, code readability and testing.
-- See this [Figma File](https://www.figma.com/file/KsEhQUp66m9yeVkvQ0hSZm/Flutter-Test?node-id=0%3A1) for design information related to the overall look and feel of the application. We do not expect pixel-perfection but would like the application to visually be close to what is specified in the Figma file.
+Whenever view or common widgets get bigger, consider spliting into smaller parts within widgets folder.
-![List View](screenshots/listview.png)
-![Detail View](screenshots/detailview.png)
+### Data
+
+Data folder contains models and repositories that will dictate the domain main data types and how the application interacts with the source of data.
-## API
+#### Yelp API
-The [Yelp GraphQL API](https://www.yelp.com/developers/graphql/guides/intro) is used as the API for this Application. We have provided the boilerplate of the API requests and backing data models to save you some time. To successfully make a request to the Yelp GraphQL API, please follow these steps:
+The [Yelp GraphQL API](https://www.yelp.com/developers/graphql/guides/intro) is used as the API for this Application. To successfully make a request to the Yelp GraphQL API, please follow these steps:
1. Please go to https://www.yelp.com/signup and sign up for a developer account.
1. Once signed up, navigate to https://www.yelp.com/developers/v3/manage_app.
1. Create a new app by filling out the required information.
1. Once your app is created, scroll down and join the `Developer Beta`. This allows you to use the GraphQL API.
-1. Copy your API Key from your app page and paste it on `line 5` [yelp_repository.dart](app/lib/yelp_repository.dart) replacing the `` with your key.
-1. Run the app and tap the `Fetch Restaurants` button. If you see a log like `Fetched x restaurants` you are all set!
-
-## Technical Requirements
-
-### State Management
-
-Please restrict your usage of state management or dependency injection to the following options:
-
-1. [provider](https://pub.dev/packages/provider)
-2. [Riverpod](https://pub.dev/packages/riverpod)
-3. [bloc](https://pub.dev/packages/bloc)
-4. [get_it](https://pub.dev/packages/get_it)/[get_it_mixins](https://pub.dev/packages/get_it_mixin)
-5. [Mobx](https://pub.dev/packages/mobx)
-
-We ask this because this challenge values consistency and efficiency over ingenuity. Using commonly used libraries ensures that we can review your code in a timely manner and allows us to provide better feedback.
-
-## Coding Values
-
-At **Superformula** we strive to build applications that have
-
-- Consistent architecture
-- Extensible, clean code
-- Solid testing
-- Good security & performance best practices
-
-### Clear, consistent architecture
-
-Approach your submission as if it were a real world app. This includes Use any libraries that you would normally choose.
-
-_Please note: we're interested in your code & the way you solve the problem, not how well you can use a particular library or feature._
-
-### Easy to understand
-
-Writing boring code that is easy to follow is essential at **Superformula**.
-
-We're interested in your method and how you approach the problem just as much as we're interested in the end result.
+1. Copy your API Key from your app page and paste it on `line 5` [yelp_repository.dart](./lib/data/repositories/yelp_repository.dart) replacing the `` with your key.
-### Solid testing approach
+#### Yelp Repository
-While the purpose of this challenge is not to gauge whether you can achieve 100% test coverage, we do seek to evaluate whether you know how & what to test.
+This repository uses Dio and GraphQl notation to fetch data from Yelp GraphQL API. Response models from models folder are used to handle response parsing.
-## Q&A
+#### Favourite Restaurants
-> Where should I send back the result when I'm done?
+Due to the simplicity of data, local storage of favourite restaurants is being handled under the hood by HydratedBloc. No repository is necessary to handle this source of data.
-Please fork this repo and then send us a pull request to our repo when you think you are done. There is no deadline for this task unless otherwise noted to you directly.
+### State management / Logic
-> What if I have a question?
+The logic folder contains the BLoC files to handle the logic and state of the application. BLoC was chosen to better define the desired states and the transition between them, and also HydratedBloc provided a built-in way to handle local storage of states. There are 3 BLoCs, all of them making use of Yelp Repository:
-Just create a new issue in this repo and we will respond and get back to you quickly.
+- FavouriteRestaurantsBloc
+- RestaurantReviewsCubit
+- RestaurantsBloc
-## Review
+## Misc.
-The coding challenge is a take-home test upon which we'll be conducting a thorough code review once complete. The review will consist of meeting some more of our mobile engineers and giving a review of the solution you have designed. Please be prepared to share your screen and run/demo the application to the group. During this process, the engineers will be asking questions.
+- This project follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) guidelines
\ No newline at end of file
From b464662c8af7c5fe9922287e6e67390872987847 Mon Sep 17 00:00:00 2001
From: Jodamco
Date: Mon, 15 Apr 2024 10:46:39 -0300
Subject: [PATCH 19/19] fix(yelp repo): reset apiKey value
---
ios/Flutter/Debug.xcconfig | 1 +
ios/Flutter/Release.xcconfig | 1 +
ios/Podfile | 44 ++++++++++++++++++++++
lib/data/repositories/yelp_repository.dart | 3 +-
4 files changed, 47 insertions(+), 2 deletions(-)
create mode 100644 ios/Podfile
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index 592ceee8..ec97fc6f 100644
--- a/ios/Flutter/Debug.xcconfig
+++ b/ios/Flutter/Debug.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
index 592ceee8..c4855bfe 100644
--- a/ios/Flutter/Release.xcconfig
+++ b/ios/Flutter/Release.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
new file mode 100644
index 00000000..fdcc671e
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,44 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '11.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/lib/data/repositories/yelp_repository.dart b/lib/data/repositories/yelp_repository.dart
index 7c0a6fa9..4c8ead59 100644
--- a/lib/data/repositories/yelp_repository.dart
+++ b/lib/data/repositories/yelp_repository.dart
@@ -5,8 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:restaurantour/data/models/restaurant.dart';
-const _apiKey =
- 'X-Me22MBBw6lIYs9fV-ntllINQ_8rfQXuHojnDY8UxXUpuSR0zHY5TSaPmyKY8qwqirh2LWdew6yjgxPSk2eiPWA77TgJHXRC4En1JJ0Kn2ej0vNHBTsuDfLVKMZZnYx';
+const _apiKey = '';
class MockResponse {
final Map data;