diff --git a/.gitignore b/.gitignore index 1be2d87..64680be 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,8 @@ app.*.map.json /android/app/release # fvm -.fvm/flutter_sdk \ No newline at end of file +.fvm/flutter_sdk +.env +*.env +env.dart +coverage_report \ No newline at end of file diff --git a/assets/avatars/av1.png b/assets/avatars/av1.png new file mode 100644 index 0000000..ff6fb58 Binary files /dev/null and b/assets/avatars/av1.png differ diff --git a/assets/avatars/av2.png b/assets/avatars/av2.png new file mode 100644 index 0000000..d3b671b Binary files /dev/null and b/assets/avatars/av2.png differ diff --git a/assets/avatars/av3.png b/assets/avatars/av3.png new file mode 100644 index 0000000..1bec5f1 Binary files /dev/null and b/assets/avatars/av3.png differ diff --git a/assets/avatars/av4.png b/assets/avatars/av4.png new file mode 100644 index 0000000..af45697 Binary files /dev/null and b/assets/avatars/av4.png differ diff --git a/assets/avatars/av5.png b/assets/avatars/av5.png new file mode 100644 index 0000000..ab033c1 Binary files /dev/null and b/assets/avatars/av5.png differ diff --git a/assets/avatars/av6.png b/assets/avatars/av6.png new file mode 100644 index 0000000..c8ac8dd Binary files /dev/null and b/assets/avatars/av6.png differ diff --git a/assets/avatars/av7.png b/assets/avatars/av7.png new file mode 100644 index 0000000..63ce78f Binary files /dev/null and b/assets/avatars/av7.png differ diff --git a/assets/placeholder.png b/assets/placeholder.png new file mode 100644 index 0000000..71bc239 Binary files /dev/null and b/assets/placeholder.png differ diff --git a/coverage/lcov.info b/coverage/lcov.info new file mode 100644 index 0000000..a1cff15 --- /dev/null +++ b/coverage/lcov.info @@ -0,0 +1,513 @@ +SF:lib/cubit/restaurant_state.dart +DA:10,7 +DA:16,1 +DA:17,2 +DA:21,3 +DA:23,0 +DA:25,0 +DA:26,0 +DA:32,5 +DA:34,1 +DA:35,2 +DA:37,0 +DA:39,0 +DA:40,0 +DA:46,2 +DA:54,1 +DA:55,3 +DA:57,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:72,1 +DA:75,1 +DA:76,1 +DA:77,2 +DA:79,0 +DA:81,0 +DA:82,0 +DA:83,0 +DA:91,4 +DA:97,0 +DA:99,0 +DA:100,0 +DA:101,0 +LF:33 +LH:15 +end_of_record +SF:lib/cubit/restaurant_cubit.dart +DA:11,1 +DA:12,1 +DA:15,1 +DA:16,1 +DA:18,2 +DA:20,2 +DA:22,1 +DA:23,1 +DA:29,1 +DA:39,1 +DA:40,3 +DA:41,1 +DA:42,1 +DA:44,0 +DA:47,1 +DA:48,1 +DA:49,2 +DA:50,1 +LF:18 +LH:17 +end_of_record +SF:lib/models/restaurant.dart +DA:10,2 +DA:15,2 +DA:16,2 +DA:18,0 +DA:26,2 +DA:30,4 +DA:32,0 +DA:42,2 +DA:48,4 +DA:50,0 +DA:60,2 +DA:67,4 +DA:69,0 +DA:77,2 +DA:81,2 +DA:82,2 +DA:84,0 +DA:99,2 +DA:111,2 +DA:112,2 +DA:114,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:125,1 +DA:126,3 +DA:127,2 +DA:134,1 +DA:135,3 +DA:136,3 +DA:148,2 +DA:153,2 +DA:154,2 +DA:156,0 +LF:34 +LH:24 +end_of_record +SF:lib/models/restaurant.g.dart +DA:9,4 +DA:10,2 +DA:11,2 +DA:14,0 +DA:15,0 +DA:16,0 +DA:19,4 +DA:20,2 +DA:23,0 +DA:24,0 +DA:27,4 +DA:28,2 +DA:29,2 +DA:30,2 +DA:33,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:39,4 +DA:40,2 +DA:41,2 +DA:42,2 +DA:43,2 +DA:45,4 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:54,4 +DA:55,2 +DA:58,0 +DA:59,0 +DA:62,4 +DA:63,2 +DA:64,2 +DA:65,2 +DA:66,4 +DA:68,8 +DA:69,2 +DA:70,6 +DA:71,2 +DA:72,2 +DA:73,6 +DA:74,2 +DA:75,2 +DA:76,6 +DA:77,2 +DA:78,2 +DA:80,4 +DA:83,0 +DA:84,0 +DA:85,0 +DA:86,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:96,2 +DA:98,2 +DA:99,2 +DA:100,2 +DA:101,6 +DA:102,2 +DA:105,0 +DA:107,0 +DA:108,0 +DA:109,0 +LF:70 +LH:40 +end_of_record +SF:lib/usecases/fetch_restaurants.dart +DA:8,0 +DA:9,0 +DA:10,0 +DA:12,0 +DA:13,0 +DA:15,0 +DA:22,0 +DA:26,0 +DA:29,0 +LF:9 +LH:0 +end_of_record +SF:lib/datasources/yielp_datasource.dart +DA:10,2 +DA:12,1 +DA:14,1 +DA:15,2 +DA:16,2 +DA:18,1 +DA:24,1 +DA:25,0 +DA:30,1 +DA:64,1 +LF:10 +LH:9 +end_of_record +SF:lib/repositories/yelp_repository.dart +DA:9,2 +DA:11,1 +DA:13,1 +DA:14,3 +DA:16,0 +DA:20,1 +DA:22,2 +DA:24,0 +LF:8 +LH:6 +end_of_record +SF:lib/network/dio_http_client.dart +DA:8,2 +DA:9,1 +DA:15,2 +DA:18,1 +DA:20,0 +LF:5 +LH:4 +end_of_record +SF:lib/pages/favorites_list_page_view.dart +DA:8,1 +DA:10,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:29,0 +DA:31,0 +DA:32,0 +DA:33,0 +DA:43,0 +LF:21 +LH:1 +end_of_record +SF:lib/pages/restaurant_list_page.dart +DA:9,1 +DA:11,1 +DA:13,1 +DA:14,1 +DA:15,1 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,1 +DA:21,1 +DA:23,1 +DA:24,0 +DA:25,0 +DA:28,1 +DA:29,0 +DA:30,1 +DA:31,3 +DA:32,1 +DA:33,1 +DA:35,1 +DA:36,1 +DA:37,3 +DA:45,0 +DA:54,1 +DA:62,1 +DA:64,1 +DA:65,0 +DA:66,0 +DA:68,0 +DA:69,0 +DA:73,1 +DA:74,2 +DA:77,1 +DA:79,2 +DA:80,1 +DA:81,1 +DA:82,1 +DA:83,1 +DA:89,1 +DA:90,1 +DA:91,1 +DA:92,1 +DA:93,1 +DA:94,1 +DA:104,1 +DA:106,1 +DA:109,1 +DA:110,1 +DA:111,2 +DA:117,2 +DA:121,2 +LF:52 +LH:40 +end_of_record +SF:lib/pages/home_page.dart +DA:19,1 +DA:21,1 +DA:23,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:29,3 +DA:31,1 +DA:32,1 +DA:34,1 +DA:35,0 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:49,3 +DA:52,1 +DA:53,1 +DA:55,3 +LF:19 +LH:18 +end_of_record +SF:lib/pages/restaurant_detail_page.dart +DA:8,0 +DA:10,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:27,0 +DA:29,0 +DA:32,0 +DA:33,0 +DA:37,0 +DA:41,0 +DA:43,0 +DA:49,0 +DA:50,0 +DA:52,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:62,0 +DA:77,0 +DA:84,0 +DA:86,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:91,0 +DA:95,0 +DA:96,0 +LF:39 +LH:0 +end_of_record +SF:lib/widgets/appbar.dart +DA:10,0 +DA:12,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:24,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:37,0 +LF:15 +LH:0 +end_of_record +SF:lib/utils/get_random_avatar.dart +DA:4,0 +DA:6,0 +DA:17,0 +DA:20,0 +LF:4 +LH:0 +end_of_record +SF:lib/widgets/divider.dart +DA:3,0 +LF:1 +LH:0 +end_of_record +SF:lib/widgets/hero_image_widget.dart +DA:6,1 +DA:16,1 +DA:18,1 +DA:19,3 +DA:20,1 +DA:21,2 +DA:22,3 +DA:23,5 +DA:24,1 +DA:25,2 +DA:27,1 +DA:28,1 +DA:30,0 +DA:31,0 +DA:32,0 +DA:34,0 +LF:16 +LH:12 +end_of_record +SF:lib/widgets/rataurant_open_status.dart +DA:4,1 +DA:6,1 +DA:8,1 +DA:10,1 +DA:11,1 +DA:12,1 +DA:13,3 +DA:18,1 +DA:21,1 +LF:9 +LH:9 +end_of_record +SF:lib/widgets/rating_and_open_status_widget.dart +DA:8,1 +DA:15,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,3 +DA:23,1 +DA:24,2 +LF:8 +LH:8 +end_of_record +SF:lib/widgets/rating_widget.dart +DA:10,1 +DA:15,3 +DA:17,1 +DA:19,1 +DA:21,3 +DA:22,1 +DA:24,1 +DA:25,1 +LF:8 +LH:8 +end_of_record +SF:lib/widgets/restaurant_details.dart +DA:5,1 +DA:12,1 +DA:14,1 +DA:15,1 +DA:16,1 +DA:17,2 +DA:18,3 +DA:23,1 +DA:24,1 +DA:25,6 +DA:26,3 +LF:11 +LH:11 +end_of_record +SF:lib/widgets/restaurant_ratings_review_section.dart +DA:9,0 +DA:12,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:19,0 +DA:24,0 +DA:28,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:39,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:47,0 +DA:51,0 +DA:52,0 +DA:57,0 +DA:58,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:65,0 +DA:69,0 +DA:70,0 +DA:71,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:80,0 +DA:86,0 +DA:95,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +LF:39 +LH:0 +end_of_record diff --git a/coverage_report/amber.png b/coverage_report/amber.png new file mode 100644 index 0000000..2cab170 Binary files /dev/null and b/coverage_report/amber.png differ diff --git a/coverage_report/cmd_line b/coverage_report/cmd_line new file mode 100644 index 0000000..247ae83 --- /dev/null +++ b/coverage_report/cmd_line @@ -0,0 +1 @@ +genhtml coverage/lcov.info -o coverage_report diff --git a/coverage_report/cubit/index-sort-f.html b/coverage_report/cubit/index-sort-f.html new file mode 100644 index 0000000..2ff87b7 --- /dev/null +++ b/coverage_report/cubit/index-sort-f.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - lcov.info - cubit + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubitCoverageTotalHit
Test:lcov.infoLines:62.7 %5132
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant_cubit.dart +
94.4%94.4%
+
94.4 %1817-
restaurant_state.dart +
45.5%45.5%
+
45.5 %3315-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/index-sort-l.html b/coverage_report/cubit/index-sort-l.html new file mode 100644 index 0000000..f350ba9 --- /dev/null +++ b/coverage_report/cubit/index-sort-l.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - lcov.info - cubit + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubitCoverageTotalHit
Test:lcov.infoLines:62.7 %5132
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant_state.dart +
45.5%45.5%
+
45.5 %3315-
restaurant_cubit.dart +
94.4%94.4%
+
94.4 %1817-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/index.html b/coverage_report/cubit/index.html new file mode 100644 index 0000000..111a966 --- /dev/null +++ b/coverage_report/cubit/index.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - lcov.info - cubit + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubitCoverageTotalHit
Test:lcov.infoLines:62.7 %5132
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant_cubit.dart +
94.4%94.4%
+
94.4 %1817-
restaurant_state.dart +
45.5%45.5%
+
45.5 %3315-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/restaurant_cubit.dart.func-c.html b/coverage_report/cubit/restaurant_cubit.dart.func-c.html new file mode 100644 index 0000000..e758734 --- /dev/null +++ b/coverage_report/cubit/restaurant_cubit.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - cubit/restaurant_cubit.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubit - restaurant_cubit.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:94.4 %1817
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/restaurant_cubit.dart.func.html b/coverage_report/cubit/restaurant_cubit.dart.func.html new file mode 100644 index 0000000..c2fc654 --- /dev/null +++ b/coverage_report/cubit/restaurant_cubit.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - cubit/restaurant_cubit.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubit - restaurant_cubit.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:94.4 %1817
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/restaurant_cubit.dart.gcov.html b/coverage_report/cubit/restaurant_cubit.dart.gcov.html new file mode 100644 index 0000000..fc0b223 --- /dev/null +++ b/coverage_report/cubit/restaurant_cubit.dart.gcov.html @@ -0,0 +1,130 @@ + + + + + + + LCOV - lcov.info - cubit/restaurant_cubit.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubit - restaurant_cubit.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:94.4 %1817
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:bloc/bloc.dart';
+       2              : import 'package:equatable/equatable.dart';
+       3              : import 'package:flutter/material.dart';
+       4              : import 'package:restaurant_tour/models/restaurant.dart';
+       5              : 
+       6              : import '../usecases/fetch_restaurants.dart';
+       7              : 
+       8              : part 'restaurant_state.dart';
+       9              : 
+      10              : class RestaurantCubit extends Cubit<RestaurantState> {
+      11            1 :   RestaurantCubit(this.fetchRestaurantsUseCase)
+      12            1 :       : super(const RestaurantInitial(favoriteRestaurants: []));
+      13              :   final FetchRestaurants fetchRestaurantsUseCase;
+      14              : 
+      15            1 :   Future<void> fetchRestaurants() async {
+      16            1 :     emit(const LoadingRestaurantsState(favoriteRestaurants: []));
+      17              :     RestaurantQueryResult? result =
+      18            2 :         await fetchRestaurantsUseCase.getRestaurants();
+      19              :     List<String> favoriteRestaurants =
+      20            2 :         await fetchRestaurantsUseCase.getFavoriteRestaurants();
+      21              :     if (result != null) {
+      22            1 :       emit(
+      23            1 :         RestaurantsLoadedState(
+      24              :           result: result,
+      25              :           favoriteRestaurants: favoriteRestaurants,
+      26              :         ),
+      27              :       );
+      28              :     } else {
+      29            1 :       emit(
+      30              :         const ErrorState(
+      31              :           message:
+      32              :               'The server encountered a problem and we couldn\'t load the list.',
+      33              :           favoriteRestaurants: [],
+      34              :         ),
+      35              :       );
+      36              :     }
+      37              :   }
+      38              : 
+      39            1 :   Future<void> favoriteAResturant(String id) async {
+      40            3 :     final favoriteRestaurants = List<String>.from(state.favoriteRestaurants);
+      41            1 :     if (favoriteRestaurants.contains(id)) {
+      42            1 :       favoriteRestaurants.remove(id);
+      43              :     } else {
+      44            0 :       favoriteRestaurants.add(id);
+      45              :     }
+      46              :     // await fetchRestaurantsUseCase.saveFavoriteRestaurants(favoriteRestaurants);
+      47            1 :     emit(
+      48            1 :       FavoriteRestaurantState(
+      49            2 :         result: state.result!,
+      50            1 :         favoriteRestaurants: List.of(favoriteRestaurants),
+      51              :       ),
+      52              :     );
+      53              :   }
+      54              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/restaurant_state.dart.func-c.html b/coverage_report/cubit/restaurant_state.dart.func-c.html new file mode 100644 index 0000000..314155a --- /dev/null +++ b/coverage_report/cubit/restaurant_state.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - cubit/restaurant_state.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubit - restaurant_state.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:45.5 %3315
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/restaurant_state.dart.func.html b/coverage_report/cubit/restaurant_state.dart.func.html new file mode 100644 index 0000000..cfa8a32 --- /dev/null +++ b/coverage_report/cubit/restaurant_state.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - cubit/restaurant_state.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubit - restaurant_state.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:45.5 %3315
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/cubit/restaurant_state.dart.gcov.html b/coverage_report/cubit/restaurant_state.dart.gcov.html new file mode 100644 index 0000000..f80c546 --- /dev/null +++ b/coverage_report/cubit/restaurant_state.dart.gcov.html @@ -0,0 +1,180 @@ + + + + + + + LCOV - lcov.info - cubit/restaurant_state.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - cubit - restaurant_state.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:45.5 %3315
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : // ignore_for_file: overridden_fields
+       2              : 
+       3              : part of 'restaurant_cubit.dart';
+       4              : 
+       5              : @immutable
+       6              : abstract class RestaurantState extends Equatable {
+       7              :   final List<String> favoriteRestaurants;
+       8              :   final RestaurantQueryResult? result;
+       9              : 
+      10            7 :   const RestaurantState({
+      11              :     required this.favoriteRestaurants,
+      12              :     this.result,
+      13              :   });
+      14              : 
+      15              :   RestaurantState copyWith({List<String>? favoriteRestaurants});
+      16            1 :   @override
+      17            2 :   List<Object?> get props => [favoriteRestaurants];
+      18              : }
+      19              : 
+      20              : class RestaurantInitial extends RestaurantState {
+      21            3 :   const RestaurantInitial({required super.favoriteRestaurants});
+      22              : 
+      23            0 :   @override
+      24              :   RestaurantState copyWith({List<String>? favoriteRestaurants}) {
+      25            0 :     return RestaurantInitial(
+      26            0 :       favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants,
+      27              :     );
+      28              :   }
+      29              : }
+      30              : 
+      31              : class LoadingRestaurantsState extends RestaurantState {
+      32            5 :   const LoadingRestaurantsState({required super.favoriteRestaurants});
+      33              : 
+      34            1 :   @override
+      35            2 :   List<Object?> get props => [favoriteRestaurants];
+      36              : 
+      37            0 :   @override
+      38              :   RestaurantState copyWith({List<String>? favoriteRestaurants}) {
+      39            0 :     return LoadingRestaurantsState(
+      40            0 :       favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants,
+      41              :     );
+      42              :   }
+      43              : }
+      44              : 
+      45              : class RestaurantsLoadedState extends RestaurantState {
+      46            2 :   const RestaurantsLoadedState({
+      47              :     required this.result,
+      48              :     required super.favoriteRestaurants,
+      49              :   });
+      50              : 
+      51              :   @override
+      52              :   final RestaurantQueryResult result;
+      53              : 
+      54            1 :   @override
+      55            3 :   List<Object?> get props => [result, favoriteRestaurants];
+      56              : 
+      57            0 :   @override
+      58              :   RestaurantsLoadedState copyWith({List<String>? favoriteRestaurants}) {
+      59            0 :     return RestaurantsLoadedState(
+      60            0 :       favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants,
+      61            0 :       result: result,
+      62              :     );
+      63              :   }
+      64              : }
+      65              : 
+      66              : class FavoriteRestaurantState extends RestaurantState {
+      67              :   @override
+      68              :   final RestaurantQueryResult result;
+      69              :   @override
+      70              :   final List<String> favoriteRestaurants;
+      71              : 
+      72            1 :   const FavoriteRestaurantState({
+      73              :     required this.result,
+      74              :     required this.favoriteRestaurants,
+      75            1 :   }) : super(favoriteRestaurants: favoriteRestaurants, result: result);
+      76            1 :   @override
+      77            2 :   List<Object?> get props => [favoriteRestaurants];
+      78              : 
+      79            0 :   @override
+      80              :   FavoriteRestaurantState copyWith({List<String>? favoriteRestaurants}) {
+      81            0 :     return FavoriteRestaurantState(
+      82            0 :       favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants,
+      83            0 :       result: result,
+      84              :     );
+      85              :   }
+      86              : }
+      87              : 
+      88              : class ErrorState extends RestaurantState {
+      89              :   final String message;
+      90              : 
+      91            4 :   const ErrorState({
+      92              :     required super.favoriteRestaurants,
+      93              :     super.result,
+      94              :     required this.message,
+      95              :   });
+      96              : 
+      97            0 :   @override
+      98              :   RestaurantState copyWith({List<String>? favoriteRestaurants}) {
+      99            0 :     return ErrorState(
+     100            0 :       favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants,
+     101            0 :       message: message,
+     102              :     );
+     103              :   }
+     104              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/emerald.png b/coverage_report/emerald.png new file mode 100644 index 0000000..38ad4f4 Binary files /dev/null and b/coverage_report/emerald.png differ diff --git a/coverage_report/gcov.css b/coverage_report/gcov.css new file mode 100644 index 0000000..6c23ba9 --- /dev/null +++ b/coverage_report/gcov.css @@ -0,0 +1,1101 @@ +/* All views: initial background and text color */ +body +{ + color: #000000; + background-color: #ffffff; +} + +/* All views: standard link format*/ +a:link +{ + color: #284fa8; + text-decoration: underline; +} + +/* All views: standard link - visited format */ +a:visited +{ + color: #00cb40; + text-decoration: underline; +} + +/* All views: standard link - activated format */ +a:active +{ + color: #ff0040; + text-decoration: underline; +} + +/* All views: main title format */ +td.title +{ + text-align: center; + padding-bottom: 10px; + font-family: sans-serif; + font-size: 20pt; + font-style: italic; + font-weight: bold; +} +/* "Line coverage date bins" leader */ +td.subTableHeader +{ + text-align: center; + padding-bottom: 6px; + font-family: sans-serif; + font-weight: bold; + vertical-align: center; +} + +/* All views: header item format */ +td.headerItem +{ + text-align: right; + padding-right: 6px; + font-family: sans-serif; + font-weight: bold; + vertical-align: top; + white-space: nowrap; +} + +/* All views: header item value format */ +td.headerValue +{ + text-align: left; + color: #284fa8; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; +} + +/* All views: header item coverage table heading */ +td.headerCovTableHead +{ + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; +} + +/* All views: header item coverage table entry */ +td.headerCovTableEntry +{ + text-align: right; + color: #284fa8; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #dae7fe; +} + +/* All views: header item coverage table entry for high coverage rate */ +td.headerCovTableEntryHi +{ + text-align: right; + color: #000000; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #a7fc9d; +} + +/* All views: header item coverage table entry for medium coverage rate */ +td.headerCovTableEntryMed +{ + text-align: right; + color: #000000; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #ffea20; +} + +/* All views: header item coverage table entry for ow coverage rate */ +td.headerCovTableEntryLo +{ + text-align: right; + color: #000000; + font-family: sans-serif; + font-weight: bold; + white-space: nowrap; + padding-left: 12px; + padding-right: 4px; + background-color: #ff0000; +} + +/* All views: header legend value for legend entry */ +td.headerValueLeg +{ + text-align: left; + color: #000000; + font-family: sans-serif; + font-size: 80%; + white-space: nowrap; + padding-top: 4px; +} + +/* All views: color of horizontal ruler */ +td.ruler +{ + background-color: #6688d4; +} + +/* All views: version string format */ +td.versionInfo +{ + text-align: center; + padding-top: 2px; + font-family: sans-serif; + font-style: italic; +} + +/* Directory view/File view (all)/Test case descriptions: + table headline format */ +td.tableHead +{ + text-align: center; + color: #ffffff; + background-color: #6688d4; + font-family: sans-serif; + font-size: 120%; + font-weight: bold; + white-space: nowrap; + padding-left: 4px; + padding-right: 4px; +} + +span.tableHeadSort +{ + padding-right: 4px; +} + +/* Directory view/File view (all): filename entry format */ +td.coverFile +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + background-color: #dae7fe; + font-family: monospace; +} + +/* Directory view/File view (all): directory name entry format */ +td.coverDirectory +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + background-color: #b8d0ff; + font-family: monospace; +} + +/* Directory view/File view (all): filename entry format */ +td.overallOwner +{ + text-align: center; + font-weight: bold; + font-family: sans-serif; + background-color: #dae7fe; + padding-right: 10px; + padding-left: 10px; +} + +/* Directory view/File view (all): filename entry format */ +td.ownerName +{ + text-align: right; + font-style: italic; + font-family: sans-serif; + background-color: #E5DBDB; + padding-right: 10px; + padding-left: 20px; +} + +/* Directory view/File view (all): bar-graph entry format*/ +td.coverBar +{ + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; +} + +/* Directory view/File view (all): bar-graph entry format*/ +td.owner_coverBar +{ + padding-left: 10px; + padding-right: 10px; + background-color: #E5DBDB; +} + +/* Directory view/File view (all): bar-graph outline color */ +td.coverBarOutline +{ + background-color: #000000; +} + +/* Directory view/File view (all): percentage entry for files with + high coverage rate */ +td.coverPerHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #a7fc9d; + font-weight: bold; + font-family: sans-serif; +} + +/* 'owner' entry: slightly lighter color than 'coverPerHi' */ +td.owner_coverPerHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #82E0AA; + font-weight: bold; + font-family: sans-serif; +} + +/* Directory view/File view (all): line count entry */ +td.coverNumDflt +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + white-space: nowrap; + font-family: sans-serif; +} + +/* td background color and font for the 'owner' section of the table */ +td.ownerTla +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #E5DBDB; + white-space: nowrap; + font-family: sans-serif; + font-style: italic; +} + +/* Directory view/File view (all): line count entry for files with + high coverage rate */ +td.coverNumHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #a7fc9d; + white-space: nowrap; + font-family: sans-serif; +} + +td.owner_coverNumHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #82E0AA; + white-space: nowrap; + font-family: sans-serif; +} + +/* Directory view/File view (all): percentage entry for files with + medium coverage rate */ +td.coverPerMed +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ffea20; + font-weight: bold; + font-family: sans-serif; +} + +td.owner_coverPerMed +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #F9E79F; + font-weight: bold; + font-family: sans-serif; +} + +/* Directory view/File view (all): line count entry for files with + medium coverage rate */ +td.coverNumMed +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ffea20; + white-space: nowrap; + font-family: sans-serif; +} + +td.owner_coverNumMed +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #F9E79F; + white-space: nowrap; + font-family: sans-serif; +} + +/* Directory view/File view (all): percentage entry for files with + low coverage rate */ +td.coverPerLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ff0000; + font-weight: bold; + font-family: sans-serif; +} + +td.owner_coverPerLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #EC7063; + font-weight: bold; + font-family: sans-serif; +} + +/* Directory view/File view (all): line count entry for files with + low coverage rate */ +td.coverNumLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ff0000; + white-space: nowrap; + font-family: sans-serif; +} + +td.owner_coverNumLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #EC7063; + white-space: nowrap; + font-family: sans-serif; +} + +/* File view (all): "show/hide details" link format */ +a.detail:link +{ + color: #b8d0ff; + font-size:80%; +} + +/* File view (all): "show/hide details" link - visited format */ +a.detail:visited +{ + color: #b8d0ff; + font-size:80%; +} + +/* File view (all): "show/hide details" link - activated format */ +a.detail:active +{ + color: #ffffff; + font-size:80%; +} + +/* File view (detail): test name entry */ +td.testName +{ + text-align: right; + padding-right: 10px; + background-color: #dae7fe; + font-family: sans-serif; +} + +/* File view (detail): test percentage entry */ +td.testPer +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-family: sans-serif; +} + +/* File view (detail): test lines count entry */ +td.testNum +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-family: sans-serif; +} + +/* Test case descriptions: test name format*/ +dt +{ + font-family: sans-serif; + font-weight: bold; +} + +/* Test case descriptions: description table body */ +td.testDescription +{ + padding-top: 10px; + padding-left: 30px; + padding-bottom: 10px; + padding-right: 30px; + background-color: #dae7fe; +} + +/* Source code view: function entry */ +td.coverFn +{ + text-align: left; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + background-color: #dae7fe; + font-family: monospace; +} + +/* Source code view: function entry zero count*/ +td.coverFnLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #ff0000; + font-weight: bold; + font-family: sans-serif; +} + +/* Source code view: function entry nonzero count*/ +td.coverFnHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-weight: bold; + font-family: sans-serif; +} + +td.coverFnAlias +{ + text-align: right; + padding-left: 10px; + padding-right: 20px; + color: #284fa8; + /* make this a slightly different color than the leader - otherwise, + otherwise the alias is hard to distinguish in the table */ + background-color: #E5DBDB; /* very light pale grey/blue */ + font-family: monospace; +} + +/* Source code view: function entry zero count*/ +td.coverFnAliasLo +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #EC7063; /* lighter red */ + font-family: sans-serif; +} + +/* Source code view: function entry nonzero count*/ +td.coverFnAliasHi +{ + text-align: right; + padding-left: 10px; + padding-right: 10px; + background-color: #dae7fe; + font-weight: bold; + font-family: sans-serif; +} + +/* Source code view: source code format */ +pre.source +{ + font-family: monospace; + white-space: pre; + margin-top: 2px; +} + +/* elided/removed code */ +span.elidedSource +{ + font-family: sans-serif; + /*font-size: 8pt; */ + font-style: italic; + background-color: lightgrey; +} + +/* Source code view: line number format */ +span.lineNum +{ + background-color: #efe383; +} + +/* Source code view: line number format when there are deleted + lines in the corresponding location */ +span.lineNumWithDelete +{ + foreground-color: #efe383; + background-color: lightgrey; +} + +/* Source code view: format for Cov legend */ +span.coverLegendCov +{ + padding-left: 10px; + padding-right: 10px; + padding-bottom: 2px; + background-color: #cad7fe; +} + +/* Source code view: format for NoCov legend */ +span.coverLegendNoCov +{ + padding-left: 10px; + padding-right: 10px; + padding-bottom: 2px; + background-color: #ff6230; +} + +/* Source code view: format for the source code heading line */ +pre.sourceHeading +{ + white-space: pre; + font-family: monospace; + font-weight: bold; + margin: 0px; +} + +/* All views: header legend value for low rate */ +td.headerValueLegL +{ + font-family: sans-serif; + text-align: center; + white-space: nowrap; + padding-left: 4px; + padding-right: 2px; + background-color: #ff0000; + font-size: 80%; +} + +/* All views: header legend value for med rate */ +td.headerValueLegM +{ + font-family: sans-serif; + text-align: center; + white-space: nowrap; + padding-left: 2px; + padding-right: 2px; + background-color: #ffea20; + font-size: 80%; +} + +/* All views: header legend value for hi rate */ +td.headerValueLegH +{ + font-family: sans-serif; + text-align: center; + white-space: nowrap; + padding-left: 2px; + padding-right: 4px; + background-color: #a7fc9d; + font-size: 80%; +} + +/* All views except source code view: legend format for low coverage */ +span.coverLegendCovLo +{ + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + background-color: #ff0000; +} + +/* All views except source code view: legend format for med coverage */ +span.coverLegendCovMed +{ + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + background-color: #ffea20; +} + +/* All views except source code view: legend format for hi coverage */ +span.coverLegendCovHi +{ + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + background-color: #a7fc9d; +} + +a.branchTla:link +{ + color: #000000; +} + +a.branchTla:visited +{ + color: #000000; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered New Code (+ => 0): +Newly added code is not tested" */ +td.tlaUNC +{ + text-align: right; + background-color: #FF6230; +} +td.tlaBgUNC { + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered New Code (+ => 0): +Newly added code is not tested" */ +span.tlaUNC +{ + text-align: left; + background-color: #FF6230; +} +span.tlaBgUNC { + background-color: #FF6230; +} +a.tlaBgUNC { + background-color: #FF6230; + color: #000000; +} + +td.headerCovTableHeadUNC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Lost Baseline Coverage (1 => 0): +Unchanged code is no longer tested" */ +td.tlaLBC +{ + text-align: right; + background-color: #FF6230; +} +td.tlaBgLBC { + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Lost Baseline Coverage (1 => 0): +Unchanged code is no longer tested" */ +span.tlaLBC +{ + text-align: left; + background-color: #FF6230; +} +span.tlaBgLBC { + background-color: #FF6230; +} +a.tlaBgLBC { + background-color: #FF6230; + color: #000000; +} + +td.headerCovTableHeadLBC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered Included Code (# => 0): +Previously unused code is untested" */ +td.tlaUIC +{ + text-align: right; + background-color: #FF6230; +} +td.tlaBgUIC { + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered Included Code (# => 0): +Previously unused code is untested" */ +span.tlaUIC +{ + text-align: left; + background-color: #FF6230; +} +span.tlaBgUIC { + background-color: #FF6230; +} +a.tlaBgUIC { + background-color: #FF6230; + color: #000000; +} + +td.headerCovTableHeadUIC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered Baseline Code (0 => 0): +Unchanged code was untested before, is untested now" */ +td.tlaUBC +{ + text-align: right; + background-color: #FF6230; +} +td.tlaBgUBC { + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Uncovered Baseline Code (0 => 0): +Unchanged code was untested before, is untested now" */ +span.tlaUBC +{ + text-align: left; + background-color: #FF6230; +} +span.tlaBgUBC { + background-color: #FF6230; +} +a.tlaBgUBC { + background-color: #FF6230; + color: #000000; +} + +td.headerCovTableHeadUBC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FF6230; +} + +/* Source code view/table entry background: format for lines classified as "Gained Baseline Coverage (0 => 1): +Unchanged code is tested now" */ +td.tlaGBC +{ + text-align: right; + background-color: #CAD7FE; +} +td.tlaBgGBC { + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Gained Baseline Coverage (0 => 1): +Unchanged code is tested now" */ +span.tlaGBC +{ + text-align: left; + background-color: #CAD7FE; +} +span.tlaBgGBC { + background-color: #CAD7FE; +} +a.tlaBgGBC { + background-color: #CAD7FE; + color: #000000; +} + +td.headerCovTableHeadGBC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Gained Included Coverage (# => 1): +Previously unused code is tested now" */ +td.tlaGIC +{ + text-align: right; + background-color: #CAD7FE; +} +td.tlaBgGIC { + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Gained Included Coverage (# => 1): +Previously unused code is tested now" */ +span.tlaGIC +{ + text-align: left; + background-color: #CAD7FE; +} +span.tlaBgGIC { + background-color: #CAD7FE; +} +a.tlaBgGIC { + background-color: #CAD7FE; + color: #000000; +} + +td.headerCovTableHeadGIC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Gained New Coverage (+ => 1): +Newly added code is tested" */ +td.tlaGNC +{ + text-align: right; + background-color: #CAD7FE; +} +td.tlaBgGNC { + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Gained New Coverage (+ => 1): +Newly added code is tested" */ +span.tlaGNC +{ + text-align: left; + background-color: #CAD7FE; +} +span.tlaBgGNC { + background-color: #CAD7FE; +} +a.tlaBgGNC { + background-color: #CAD7FE; + color: #000000; +} + +td.headerCovTableHeadGNC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Covered Baseline Code (1 => 1): +Unchanged code was tested before and is still tested" */ +td.tlaCBC +{ + text-align: right; + background-color: #CAD7FE; +} +td.tlaBgCBC { + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Covered Baseline Code (1 => 1): +Unchanged code was tested before and is still tested" */ +span.tlaCBC +{ + text-align: left; + background-color: #CAD7FE; +} +span.tlaBgCBC { + background-color: #CAD7FE; +} +a.tlaBgCBC { + background-color: #CAD7FE; + color: #000000; +} + +td.headerCovTableHeadCBC { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #CAD7FE; +} + +/* Source code view/table entry background: format for lines classified as "Excluded Uncovered Baseline (0 => #): +Previously untested code is unused now" */ +td.tlaEUB +{ + text-align: right; + background-color: #FFFFFF; +} +td.tlaBgEUB { + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Excluded Uncovered Baseline (0 => #): +Previously untested code is unused now" */ +span.tlaEUB +{ + text-align: left; + background-color: #FFFFFF; +} +span.tlaBgEUB { + background-color: #FFFFFF; +} +a.tlaBgEUB { + background-color: #FFFFFF; + color: #000000; +} + +td.headerCovTableHeadEUB { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Excluded Covered Baseline (1 => #): +Previously tested code is unused now" */ +td.tlaECB +{ + text-align: right; + background-color: #FFFFFF; +} +td.tlaBgECB { + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Excluded Covered Baseline (1 => #): +Previously tested code is unused now" */ +span.tlaECB +{ + text-align: left; + background-color: #FFFFFF; +} +span.tlaBgECB { + background-color: #FFFFFF; +} +a.tlaBgECB { + background-color: #FFFFFF; + color: #000000; +} + +td.headerCovTableHeadECB { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Deleted Uncovered Baseline (0 => -): +Previously untested code has been deleted" */ +td.tlaDUB +{ + text-align: right; + background-color: #FFFFFF; +} +td.tlaBgDUB { + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Deleted Uncovered Baseline (0 => -): +Previously untested code has been deleted" */ +span.tlaDUB +{ + text-align: left; + background-color: #FFFFFF; +} +span.tlaBgDUB { + background-color: #FFFFFF; +} +a.tlaBgDUB { + background-color: #FFFFFF; + color: #000000; +} + +td.headerCovTableHeadDUB { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Deleted Covered Baseline (1 => -): +Previously tested code has been deleted" */ +td.tlaDCB +{ + text-align: right; + background-color: #FFFFFF; +} +td.tlaBgDCB { + background-color: #FFFFFF; +} + +/* Source code view/table entry background: format for lines classified as "Deleted Covered Baseline (1 => -): +Previously tested code has been deleted" */ +span.tlaDCB +{ + text-align: left; + background-color: #FFFFFF; +} +span.tlaBgDCB { + background-color: #FFFFFF; +} +a.tlaBgDCB { + background-color: #FFFFFF; + color: #000000; +} + +td.headerCovTableHeadDCB { + text-align: center; + padding-right: 6px; + padding-left: 6px; + padding-bottom: 0px; + font-family: sans-serif; + white-space: nowrap; + background-color: #FFFFFF; +} + +/* Source code view: format for date/owner bin that is not hit */ +span.missBins +{ + background-color: #ff0000 /* red */ +} diff --git a/coverage_report/glass.png b/coverage_report/glass.png new file mode 100644 index 0000000..e1abc00 Binary files /dev/null and b/coverage_report/glass.png differ diff --git a/coverage_report/index-sort-f.html b/coverage_report/index-sort-f.html new file mode 100644 index 0000000..1aab820 --- /dev/null +++ b/coverage_report/index-sort-f.html @@ -0,0 +1,201 @@ + + + + + + + LCOV - lcov.info + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelCoverageTotalHit
Test:lcov.infoLines:51.7 %429222
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
cubit/ +
62.7%62.7%
+
62.7 %5132-
datasources/ +
90.0%90.0%
+
90.0 %109-
models/ +
61.5%61.5%
+
61.5 %10464-
network/ +
80.0%80.0%
+
80.0 %54-
pages/ +
45.0%45.0%
+
45.0 %13159-
repositories/ +
75.0%75.0%
+
75.0 %86-
usecases/ +
0.0%
+
0.0 %9-
utils/ +
0.0%
+
0.0 %4-
widgets/ +
44.9%44.9%
+
44.9 %10748-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/index-sort-l.html b/coverage_report/index-sort-l.html new file mode 100644 index 0000000..c8e439e --- /dev/null +++ b/coverage_report/index-sort-l.html @@ -0,0 +1,201 @@ + + + + + + + LCOV - lcov.info + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelCoverageTotalHit
Test:lcov.infoLines:51.7 %429222
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
utils/ +
0.0%
+
0.0 %4-
usecases/ +
0.0%
+
0.0 %9-
widgets/ +
44.9%44.9%
+
44.9 %10748-
pages/ +
45.0%45.0%
+
45.0 %13159-
models/ +
61.5%61.5%
+
61.5 %10464-
cubit/ +
62.7%62.7%
+
62.7 %5132-
repositories/ +
75.0%75.0%
+
75.0 %86-
network/ +
80.0%80.0%
+
80.0 %54-
datasources/ +
90.0%90.0%
+
90.0 %109-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/index.html b/coverage_report/index.html new file mode 100644 index 0000000..5a9109e --- /dev/null +++ b/coverage_report/index.html @@ -0,0 +1,201 @@ + + + + + + + LCOV - lcov.info + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top levelCoverageTotalHit
Test:lcov.infoLines:51.7 %429222
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directory Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
cubit/ +
62.7%62.7%
+
62.7 %5132-
datasources/ +
90.0%90.0%
+
90.0 %109-
models/ +
61.5%61.5%
+
61.5 %10464-
network/ +
80.0%80.0%
+
80.0 %54-
pages/ +
45.0%45.0%
+
45.0 %13159-
repositories/ +
75.0%75.0%
+
75.0 %86-
usecases/ +
0.0%
+
0.0 %9-
utils/ +
0.0%
+
0.0 %4-
widgets/ +
44.9%44.9%
+
44.9 %10748-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/index-sort-f.html b/coverage_report/models/index-sort-f.html new file mode 100644 index 0000000..1ecbf9e --- /dev/null +++ b/coverage_report/models/index-sort-f.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - lcov.info - models + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - modelsCoverageTotalHit
Test:lcov.infoLines:61.5 %10464
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant.dart +
70.6%70.6%
+
70.6 %3424-
restaurant.g.dart +
57.1%57.1%
+
57.1 %7040-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/index-sort-l.html b/coverage_report/models/index-sort-l.html new file mode 100644 index 0000000..545c4a7 --- /dev/null +++ b/coverage_report/models/index-sort-l.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - lcov.info - models + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - modelsCoverageTotalHit
Test:lcov.infoLines:61.5 %10464
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant.g.dart +
57.1%57.1%
+
57.1 %7040-
restaurant.dart +
70.6%70.6%
+
70.6 %3424-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/index.html b/coverage_report/models/index.html new file mode 100644 index 0000000..b4e6223 --- /dev/null +++ b/coverage_report/models/index.html @@ -0,0 +1,117 @@ + + + + + + + LCOV - lcov.info - models + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - modelsCoverageTotalHit
Test:lcov.infoLines:61.5 %10464
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant.dart +
70.6%70.6%
+
70.6 %3424-
restaurant.g.dart +
57.1%57.1%
+
57.1 %7040-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/restaurant.dart.func-c.html b/coverage_report/models/restaurant.dart.func-c.html new file mode 100644 index 0000000..c4982fb --- /dev/null +++ b/coverage_report/models/restaurant.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - models/restaurant.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - models - restaurant.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:70.6 %3424
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/restaurant.dart.func.html b/coverage_report/models/restaurant.dart.func.html new file mode 100644 index 0000000..0fb3e27 --- /dev/null +++ b/coverage_report/models/restaurant.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - models/restaurant.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - models - restaurant.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:70.6 %3424
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/restaurant.dart.gcov.html b/coverage_report/models/restaurant.dart.gcov.html new file mode 100644 index 0000000..165dc05 --- /dev/null +++ b/coverage_report/models/restaurant.dart.gcov.html @@ -0,0 +1,233 @@ + + + + + + + LCOV - lcov.info - models/restaurant.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - models - restaurant.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:70.6 %3424
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:json_annotation/json_annotation.dart';
+       2              : 
+       3              : part 'restaurant.g.dart';
+       4              : 
+       5              : @JsonSerializable()
+       6              : class Category {
+       7              :   final String? alias;
+       8              :   final String? title;
+       9              : 
+      10            2 :   Category({
+      11              :     this.alias,
+      12              :     this.title,
+      13              :   });
+      14              : 
+      15            2 :   factory Category.fromJson(Map<String, dynamic> json) =>
+      16            2 :       _$CategoryFromJson(json);
+      17              : 
+      18            0 :   Map<String, dynamic> toJson() => _$CategoryToJson(this);
+      19              : }
+      20              : 
+      21              : @JsonSerializable()
+      22              : class Hours {
+      23              :   @JsonKey(name: 'is_open_now')
+      24              :   final bool? isOpenNow;
+      25              : 
+      26            2 :   const Hours({
+      27              :     this.isOpenNow,
+      28              :   });
+      29              : 
+      30            4 :   factory Hours.fromJson(Map<String, dynamic> json) => _$HoursFromJson(json);
+      31              : 
+      32            0 :   Map<String, dynamic> toJson() => _$HoursToJson(this);
+      33              : }
+      34              : 
+      35              : @JsonSerializable()
+      36              : class User {
+      37              :   final String? id;
+      38              :   @JsonKey(name: 'image_url')
+      39              :   final String? imageUrl;
+      40              :   final String? name;
+      41              : 
+      42            2 :   const User({
+      43              :     this.id,
+      44              :     this.imageUrl,
+      45              :     this.name,
+      46              :   });
+      47              : 
+      48            4 :   factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
+      49              : 
+      50            0 :   Map<String, dynamic> toJson() => _$UserToJson(this);
+      51              : }
+      52              : 
+      53              : @JsonSerializable()
+      54              : class Review {
+      55              :   final String? id;
+      56              :   final int? rating;
+      57              :   final String? text;
+      58              :   final User? user;
+      59              : 
+      60            2 :   const Review({
+      61              :     this.id,
+      62              :     this.rating,
+      63              :     this.user,
+      64              :     this.text,
+      65              :   });
+      66              : 
+      67            4 :   factory Review.fromJson(Map<String, dynamic> json) => _$ReviewFromJson(json);
+      68              : 
+      69            0 :   Map<String, dynamic> toJson() => _$ReviewToJson(this);
+      70              : }
+      71              : 
+      72              : @JsonSerializable()
+      73              : class Location {
+      74              :   @JsonKey(name: 'formatted_address')
+      75              :   final String? formattedAddress;
+      76              : 
+      77            2 :   Location({
+      78              :     this.formattedAddress,
+      79              :   });
+      80              : 
+      81            2 :   factory Location.fromJson(Map<String, dynamic> json) =>
+      82            2 :       _$LocationFromJson(json);
+      83              : 
+      84            0 :   Map<String, dynamic> toJson() => _$LocationToJson(this);
+      85              : }
+      86              : 
+      87              : @JsonSerializable()
+      88              : class Restaurant {
+      89              :   final String? id;
+      90              :   final String? name;
+      91              :   final String? price;
+      92              :   final double? rating;
+      93              :   final List<String>? photos;
+      94              :   final List<Category>? categories;
+      95              :   final List<Hours>? hours;
+      96              :   final List<Review>? reviews;
+      97              :   final Location? location;
+      98              : 
+      99            2 :   const Restaurant({
+     100              :     this.id,
+     101              :     this.name,
+     102              :     this.price,
+     103              :     this.rating,
+     104              :     this.photos,
+     105              :     this.categories,
+     106              :     this.hours,
+     107              :     this.reviews,
+     108              :     this.location,
+     109              :   });
+     110              : 
+     111            2 :   factory Restaurant.fromJson(Map<String, dynamic> json) =>
+     112            2 :       _$RestaurantFromJson(json);
+     113              : 
+     114            0 :   Map<String, dynamic> toJson() => _$RestaurantToJson(this);
+     115              : 
+     116              :   /// Use the first category for the category shown to the user
+     117            0 :   String get displayCategory {
+     118            0 :     if (categories != null && categories!.isNotEmpty) {
+     119            0 :       return categories!.first.title ?? '';
+     120              :     }
+     121              :     return '';
+     122              :   }
+     123              : 
+     124              :   /// Use the first image as the image shown to the user
+     125            1 :   String get heroImage {
+     126            3 :     if (photos != null && photos!.isNotEmpty) {
+     127            2 :       return photos!.first;
+     128              :     }
+     129              :     return '';
+     130              :   }
+     131              : 
+     132              :   /// This logic is probably not correct in all cases but it is ok
+     133              :   /// for this application
+     134            1 :   bool get isOpen {
+     135            3 :     if (hours != null && hours!.isNotEmpty) {
+     136            3 :       return hours!.first.isOpenNow ?? false;
+     137              :     }
+     138              :     return false;
+     139              :   }
+     140              : }
+     141              : 
+     142              : @JsonSerializable()
+     143              : class RestaurantQueryResult {
+     144              :   final int? total;
+     145              :   @JsonKey(name: 'business')
+     146              :   final List<Restaurant>? restaurants;
+     147              : 
+     148            2 :   const RestaurantQueryResult({
+     149              :     this.total,
+     150              :     this.restaurants,
+     151              :   });
+     152              : 
+     153            2 :   factory RestaurantQueryResult.fromJson(Map<String, dynamic> json) =>
+     154            2 :       _$RestaurantQueryResultFromJson(json);
+     155              : 
+     156            0 :   Map<String, dynamic> toJson() => _$RestaurantQueryResultToJson(this);
+     157              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/restaurant.g.dart.func-c.html b/coverage_report/models/restaurant.g.dart.func-c.html new file mode 100644 index 0000000..ba92530 --- /dev/null +++ b/coverage_report/models/restaurant.g.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - models/restaurant.g.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - models - restaurant.g.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:57.1 %7040
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/restaurant.g.dart.func.html b/coverage_report/models/restaurant.g.dart.func.html new file mode 100644 index 0000000..a90fe0b --- /dev/null +++ b/coverage_report/models/restaurant.g.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - models/restaurant.g.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - models - restaurant.g.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:57.1 %7040
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/models/restaurant.g.dart.gcov.html b/coverage_report/models/restaurant.g.dart.gcov.html new file mode 100644 index 0000000..acaf7b1 --- /dev/null +++ b/coverage_report/models/restaurant.g.dart.gcov.html @@ -0,0 +1,186 @@ + + + + + + + LCOV - lcov.info - models/restaurant.g.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - models - restaurant.g.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:57.1 %7040
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : // GENERATED CODE - DO NOT MODIFY BY HAND
+       2              : 
+       3              : part of 'restaurant.dart';
+       4              : 
+       5              : // **************************************************************************
+       6              : // JsonSerializableGenerator
+       7              : // **************************************************************************
+       8              : 
+       9            4 : Category _$CategoryFromJson(Map<String, dynamic> json) => Category(
+      10            2 :       alias: json['alias'] as String?,
+      11            2 :       title: json['title'] as String?,
+      12              :     );
+      13              : 
+      14            0 : Map<String, dynamic> _$CategoryToJson(Category instance) => <String, dynamic>{
+      15            0 :       'alias': instance.alias,
+      16            0 :       'title': instance.title,
+      17              :     };
+      18              : 
+      19            4 : Hours _$HoursFromJson(Map<String, dynamic> json) => Hours(
+      20            2 :       isOpenNow: json['is_open_now'] as bool?,
+      21              :     );
+      22              : 
+      23            0 : Map<String, dynamic> _$HoursToJson(Hours instance) => <String, dynamic>{
+      24            0 :       'is_open_now': instance.isOpenNow,
+      25              :     };
+      26              : 
+      27            4 : User _$UserFromJson(Map<String, dynamic> json) => User(
+      28            2 :       id: json['id'] as String?,
+      29            2 :       imageUrl: json['image_url'] as String?,
+      30            2 :       name: json['name'] as String?,
+      31              :     );
+      32              : 
+      33            0 : Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
+      34            0 :       'id': instance.id,
+      35            0 :       'image_url': instance.imageUrl,
+      36            0 :       'name': instance.name,
+      37              :     };
+      38              : 
+      39            4 : Review _$ReviewFromJson(Map<String, dynamic> json) => Review(
+      40            2 :       id: json['id'] as String?,
+      41            2 :       rating: json['rating'] as int?,
+      42            2 :       text: json['text'] as String?,
+      43            2 :       user: json['user'] == null
+      44              :           ? null
+      45            4 :           : User.fromJson(json['user'] as Map<String, dynamic>),
+      46              :     );
+      47              : 
+      48            0 : Map<String, dynamic> _$ReviewToJson(Review instance) => <String, dynamic>{
+      49            0 :       'id': instance.id,
+      50            0 :       'rating': instance.rating,
+      51            0 :       'user': instance.user,
+      52              :     };
+      53              : 
+      54            4 : Location _$LocationFromJson(Map<String, dynamic> json) => Location(
+      55            2 :       formattedAddress: json['formatted_address'] as String?,
+      56              :     );
+      57              : 
+      58            0 : Map<String, dynamic> _$LocationToJson(Location instance) => <String, dynamic>{
+      59            0 :       'formatted_address': instance.formattedAddress,
+      60              :     };
+      61              : 
+      62            4 : Restaurant _$RestaurantFromJson(Map<String, dynamic> json) => Restaurant(
+      63            2 :       id: json['id'] as String?,
+      64            2 :       name: json['name'] as String?,
+      65            2 :       price: json['price'] as String?,
+      66            4 :       rating: (json['rating'] as num?)?.toDouble(),
+      67              :       photos:
+      68            8 :           (json['photos'] as List<dynamic>?)?.map((e) => e as String).toList(),
+      69            2 :       categories: (json['categories'] as List<dynamic>?)
+      70            6 :           ?.map((e) => Category.fromJson(e as Map<String, dynamic>))
+      71            2 :           .toList(),
+      72            2 :       hours: (json['hours'] as List<dynamic>?)
+      73            6 :           ?.map((e) => Hours.fromJson(e as Map<String, dynamic>))
+      74            2 :           .toList(),
+      75            2 :       reviews: (json['reviews'] as List<dynamic>?)
+      76            6 :           ?.map((e) => Review.fromJson(e as Map<String, dynamic>))
+      77            2 :           .toList(),
+      78            2 :       location: json['location'] == null
+      79              :           ? null
+      80            4 :           : Location.fromJson(json['location'] as Map<String, dynamic>),
+      81              :     );
+      82              : 
+      83            0 : Map<String, dynamic> _$RestaurantToJson(Restaurant instance) =>
+      84            0 :     <String, dynamic>{
+      85            0 :       'id': instance.id,
+      86            0 :       'name': instance.name,
+      87            0 :       'price': instance.price,
+      88            0 :       'rating': instance.rating,
+      89            0 :       'photos': instance.photos,
+      90            0 :       'categories': instance.categories,
+      91            0 :       'hours': instance.hours,
+      92            0 :       'reviews': instance.reviews,
+      93            0 :       'location': instance.location,
+      94              :     };
+      95              : 
+      96            2 : RestaurantQueryResult _$RestaurantQueryResultFromJson(
+      97              :         Map<String, dynamic> json) =>
+      98            2 :     RestaurantQueryResult(
+      99            2 :       total: json['total'] as int?,
+     100            2 :       restaurants: (json['business'] as List<dynamic>?)
+     101            6 :           ?.map((e) => Restaurant.fromJson(e as Map<String, dynamic>))
+     102            2 :           .toList(),
+     103              :     );
+     104              : 
+     105            0 : Map<String, dynamic> _$RestaurantQueryResultToJson(
+     106              :         RestaurantQueryResult instance) =>
+     107            0 :     <String, dynamic>{
+     108            0 :       'total': instance.total,
+     109            0 :       'business': instance.restaurants,
+     110              :     };
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/home_page.dart.func-c.html b/coverage_report/pages/home_page.dart.func-c.html new file mode 100644 index 0000000..14dfed4 --- /dev/null +++ b/coverage_report/pages/home_page.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - pages/home_page.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - home_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:94.7 %1918
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/home_page.dart.func.html b/coverage_report/pages/home_page.dart.func.html new file mode 100644 index 0000000..3423b0d --- /dev/null +++ b/coverage_report/pages/home_page.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - pages/home_page.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - home_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:94.7 %1918
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/home_page.dart.gcov.html b/coverage_report/pages/home_page.dart.gcov.html new file mode 100644 index 0000000..83da089 --- /dev/null +++ b/coverage_report/pages/home_page.dart.gcov.html @@ -0,0 +1,149 @@ + + + + + + + LCOV - lcov.info - pages/home_page.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - home_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:94.7 %1918
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : import 'package:restaurant_tour/pages/favorites_list_page_view.dart';
+       3              : import 'package:restaurant_tour/pages/restaurant_list_page.dart';
+       4              : 
+       5              : // class HomePageBlocProvider extends StatelessWidget {
+       6              : //   const HomePageBlocProvider({super.key});
+       7              : 
+       8              : //   @override
+       9              : //   Widget build(BuildContext context) {
+      10              : //     return BlocProvider(
+      11              : //       create: (context) =>
+      12              : //           GetIt.instance<RestaurantCubit>()..fetchRestaurants(),
+      13              : //       child: const HomePage(),
+      14              : //     );
+      15              : //   }
+      16              : // }
+      17              : 
+      18              : class HomePage extends StatelessWidget {
+      19            1 :   const HomePage({super.key});
+      20              : 
+      21            1 :   @override
+      22              :   Widget build(BuildContext context) {
+      23            1 :     return DefaultTabController(
+      24              :       length: 2,
+      25            1 :       child: Scaffold(
+      26            1 :         appBar: AppBar(
+      27            1 :           title: Text(
+      28              :             "RestauranTour",
+      29            3 :             style: Theme.of(context).textTheme.headlineLarge,
+      30              :           ),
+      31            1 :           actions: [
+      32            1 :             Padding(
+      33              :               padding: const EdgeInsets.only(right: 16),
+      34            1 :               child: TextButton(
+      35            0 :                 onPressed: () {},
+      36              :                 child: const CircleAvatar(
+      37              :                   backgroundImage: NetworkImage(
+      38              :                     'https://mdbcdn.b-cdn.net/img/new/avatars/2.webp',
+      39              :                   ),
+      40              :                 ),
+      41              :               ),
+      42              :             ),
+      43              :           ],
+      44            1 :           bottom: TabBar(
+      45            1 :             tabs: [
+      46            1 :               Tab(
+      47            1 :                 child: Text(
+      48              :                   'All restaurants',
+      49            3 :                   style: Theme.of(context).textTheme.displayLarge,
+      50              :                 ),
+      51              :               ),
+      52            1 :               Tab(
+      53            1 :                 child: Text(
+      54              :                   'My favorites',
+      55            3 :                   style: Theme.of(context).textTheme.displayLarge,
+      56              :                 ),
+      57              :               ),
+      58              :             ],
+      59              :           ),
+      60              :         ),
+      61              :         body: const SafeArea(
+      62              :           minimum: EdgeInsets.all(16),
+      63              :           child: TabBarView(
+      64              :             children: [
+      65              :               RestaurantListPageView(),
+      66              :               FavoritesListPageView(),
+      67              :             ],
+      68              :           ),
+      69              :         ),
+      70              :       ),
+      71              :     );
+      72              :   }
+      73              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/index-sort-f.html b/coverage_report/pages/index-sort-f.html new file mode 100644 index 0000000..8afb46b --- /dev/null +++ b/coverage_report/pages/index-sort-f.html @@ -0,0 +1,141 @@ + + + + + + + LCOV - lcov.info - pages + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pagesCoverageTotalHit
Test:lcov.infoLines:45.0 %13159
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
favorites_list_page_view.dart +
4.8%4.8%
+
4.8 %211-
home_page.dart +
94.7%94.7%
+
94.7 %1918-
restaurant_detail_page.dart +
0.0%
+
0.0 %39-
restaurant_list_page.dart +
76.9%76.9%
+
76.9 %5240-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/index-sort-l.html b/coverage_report/pages/index-sort-l.html new file mode 100644 index 0000000..97ccc27 --- /dev/null +++ b/coverage_report/pages/index-sort-l.html @@ -0,0 +1,141 @@ + + + + + + + LCOV - lcov.info - pages + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pagesCoverageTotalHit
Test:lcov.infoLines:45.0 %13159
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
restaurant_detail_page.dart +
0.0%
+
0.0 %39-
favorites_list_page_view.dart +
4.8%4.8%
+
4.8 %211-
restaurant_list_page.dart +
76.9%76.9%
+
76.9 %5240-
home_page.dart +
94.7%94.7%
+
94.7 %1918-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/index.html b/coverage_report/pages/index.html new file mode 100644 index 0000000..98e3690 --- /dev/null +++ b/coverage_report/pages/index.html @@ -0,0 +1,141 @@ + + + + + + + LCOV - lcov.info - pages + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pagesCoverageTotalHit
Test:lcov.infoLines:45.0 %13159
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
favorites_list_page_view.dart +
4.8%4.8%
+
4.8 %211-
home_page.dart +
94.7%94.7%
+
94.7 %1918-
restaurant_detail_page.dart +
0.0%
+
0.0 %39-
restaurant_list_page.dart +
76.9%76.9%
+
76.9 %5240-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/restaurant_detail_page.dart.func-c.html b/coverage_report/pages/restaurant_detail_page.dart.func-c.html new file mode 100644 index 0000000..06a464a --- /dev/null +++ b/coverage_report/pages/restaurant_detail_page.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - pages/restaurant_detail_page.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - restaurant_detail_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %390
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/restaurant_detail_page.dart.func.html b/coverage_report/pages/restaurant_detail_page.dart.func.html new file mode 100644 index 0000000..ea89c17 --- /dev/null +++ b/coverage_report/pages/restaurant_detail_page.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - pages/restaurant_detail_page.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - restaurant_detail_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %390
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/restaurant_detail_page.dart.gcov.html b/coverage_report/pages/restaurant_detail_page.dart.gcov.html new file mode 100644 index 0000000..5ed64c1 --- /dev/null +++ b/coverage_report/pages/restaurant_detail_page.dart.gcov.html @@ -0,0 +1,177 @@ + + + + + + + LCOV - lcov.info - pages/restaurant_detail_page.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - restaurant_detail_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %390
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : import 'package:restaurant_tour/models/restaurant.dart';
+       3              : import 'package:restaurant_tour/widgets/widgets.dart';
+       4              : import '../theme/text.dart';
+       5              : import '../widgets/appbar.dart';
+       6              : 
+       7              : class RestaurantDetailPage extends StatelessWidget {
+       8            0 :   const RestaurantDetailPage({super.key, required this.restaurant});
+       9              :   final Restaurant restaurant;
+      10            0 :   @override
+      11              :   Widget build(BuildContext context) {
+      12            0 :     return Scaffold(
+      13            0 :       appBar: RestaurantDetailAppBar(
+      14            0 :         restaurant: restaurant,
+      15              :       ),
+      16            0 :       body: SafeArea(
+      17            0 :         child: LayoutBuilder(
+      18            0 :           builder: (BuildContext context, BoxConstraints constraints) {
+      19            0 :             return SingleChildScrollView(
+      20            0 :               child: Column(
+      21            0 :                 children: [
+      22            0 :                   HeroImageWidget(
+      23            0 :                     restaurant: restaurant,
+      24            0 :                     width: constraints.maxWidth,
+      25            0 :                     height: constraints.maxHeight * 0.4,
+      26              :                   ),
+      27            0 :                   Padding(
+      28              :                     padding: const EdgeInsets.all(24),
+      29            0 :                     child: Column(
+      30              :                       mainAxisAlignment: MainAxisAlignment.start,
+      31              :                       crossAxisAlignment: CrossAxisAlignment.start,
+      32            0 :                       children: [
+      33            0 :                         BuildCategoryStatusRow(restaurant: restaurant),
+      34              :                         const SizedBox(
+      35              :                           height: spacingXL,
+      36              :                         ),
+      37            0 :                         buildDivider(),
+      38              :                         const SizedBox(
+      39              :                           height: spacingXL,
+      40              :                         ),
+      41            0 :                         Text(
+      42              :                           'Address',
+      43            0 :                           style: Theme.of(context).textTheme.bodySmall,
+      44              :                           textAlign: TextAlign.start,
+      45              :                         ),
+      46              :                         const SizedBox(
+      47              :                           height: spacingXL,
+      48              :                         ),
+      49            0 :                         Text(
+      50            0 :                           restaurant.location?.formattedAddress ??
+      51              :                               'No Address Informed',
+      52            0 :                           style: Theme.of(context).textTheme.displayLarge,
+      53              :                         ),
+      54              :                         const SizedBox(
+      55              :                           height: spacingXL,
+      56              :                         ),
+      57            0 :                         buildDivider(),
+      58            0 :                         restaurant.rating != null
+      59            0 :                             ? BuildRatingSection(
+      60            0 :                                 restaurant: restaurant,
+      61              :                               )
+      62            0 :                             : Container(),
+      63              :                       ],
+      64              :                     ),
+      65              :                   ),
+      66              :                 ],
+      67              :               ),
+      68              :             );
+      69              :           },
+      70              :         ),
+      71              :       ),
+      72              :     );
+      73              :   }
+      74              : }
+      75              : 
+      76              : class BuildCategoryStatusRow extends StatelessWidget {
+      77            0 :   const BuildCategoryStatusRow({
+      78              :     super.key,
+      79              :     required this.restaurant,
+      80              :   });
+      81              : 
+      82              :   final Restaurant restaurant;
+      83              : 
+      84            0 :   @override
+      85              :   Widget build(BuildContext context) {
+      86            0 :     return Row(
+      87              :       mainAxisSize: MainAxisSize.max,
+      88            0 :       children: [
+      89            0 :         Expanded(
+      90            0 :           child: PriceAndClassificationRowWidget(
+      91            0 :             restaurant: restaurant,
+      92              :           ),
+      93              :         ),
+      94              :         const Spacer(),
+      95            0 :         OpenStatusWidget(
+      96            0 :           isOpen: restaurant.isOpen,
+      97              :         ),
+      98              :       ],
+      99              :     );
+     100              :   }
+     101              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/restaurant_list_page.dart.func-c.html b/coverage_report/pages/restaurant_list_page.dart.func-c.html new file mode 100644 index 0000000..6aa2d37 --- /dev/null +++ b/coverage_report/pages/restaurant_list_page.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - pages/restaurant_list_page.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - restaurant_list_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:76.9 %5240
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/restaurant_list_page.dart.func.html b/coverage_report/pages/restaurant_list_page.dart.func.html new file mode 100644 index 0000000..4b3d8de --- /dev/null +++ b/coverage_report/pages/restaurant_list_page.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - pages/restaurant_list_page.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - restaurant_list_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:76.9 %5240
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/pages/restaurant_list_page.dart.gcov.html b/coverage_report/pages/restaurant_list_page.dart.gcov.html new file mode 100644 index 0000000..2b08e61 --- /dev/null +++ b/coverage_report/pages/restaurant_list_page.dart.gcov.html @@ -0,0 +1,206 @@ + + + + + + + LCOV - lcov.info - pages/restaurant_list_page.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - pages - restaurant_list_page.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:76.9 %5240
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : import 'package:flutter_bloc/flutter_bloc.dart';
+       3              : import 'package:restaurant_tour/cubit/cubit.dart';
+       4              : import 'package:restaurant_tour/models/models.dart';
+       5              : import '../widgets/widgets.dart';
+       6              : import 'restaurant_detail_page.dart';
+       7              : 
+       8              : class RestaurantListPageView extends StatelessWidget {
+       9            1 :   const RestaurantListPageView({super.key});
+      10              : 
+      11            1 :   @override
+      12              :   Widget build(BuildContext context) {
+      13            1 :     return LayoutBuilder(
+      14            1 :       builder: (context, constraints) {
+      15            1 :         return BlocBuilder<RestaurantCubit, RestaurantState>(
+      16            0 :           buildWhen: (previous, current) =>
+      17            0 :               current is LoadingRestaurantsState ||
+      18            0 :               current is RestaurantsLoadedState ||
+      19            0 :               current is ErrorState,
+      20            1 :           builder: (context, state) {
+      21            1 :             if (state is LoadingRestaurantsState) {
+      22              :               return const Center(child: CircularProgressIndicator());
+      23            1 :             } else if (state is ErrorState) {
+      24            0 :               return Center(
+      25            0 :                 child: Text(state.message),
+      26              :               );
+      27              :             }
+      28            1 :             if (state is RestaurantsLoadedState ||
+      29            0 :                 state is FavoriteRestaurantState) {
+      30            1 :               return ListView.builder(
+      31            3 :                 itemCount: state.result!.restaurants?.length,
+      32            1 :                 itemBuilder: (BuildContext context, int index) {
+      33            1 :                   return Row(
+      34              :                     mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+      35            1 :                     children: [
+      36            1 :                       RestaurantListTile(
+      37            3 :                         restaurant: state.result!.restaurants!.elementAt(index),
+      38              :                         constraints: constraints,
+      39              :                       ),
+      40              :                     ],
+      41              :                   );
+      42              :                 },
+      43              :               );
+      44              :             }
+      45            0 :             return Container();
+      46              :           },
+      47              :         );
+      48              :       },
+      49              :     );
+      50              :   }
+      51              : }
+      52              : 
+      53              : class RestaurantListTile extends StatelessWidget {
+      54            1 :   const RestaurantListTile({
+      55              :     super.key,
+      56              :     required this.restaurant,
+      57              :     required this.constraints,
+      58              :   });
+      59              :   final Restaurant restaurant;
+      60              :   final BoxConstraints constraints;
+      61              : 
+      62            1 :   @override
+      63              :   Widget build(BuildContext context) {
+      64            1 :     return GestureDetector(
+      65            0 :       onTap: () {
+      66            0 :         Navigator.push(
+      67              :           context,
+      68            0 :           MaterialPageRoute(
+      69            0 :             builder: (context) => RestaurantDetailPage(restaurant: restaurant),
+      70              :           ),
+      71              :         );
+      72              :       },
+      73            1 :       child: Container(
+      74            2 :         width: constraints.maxWidth,
+      75              :         margin: const EdgeInsets.only(bottom: 8, top: 8),
+      76              :         padding: const EdgeInsets.all(16),
+      77            1 :         decoration: BoxDecoration(
+      78              :           color: Colors.white,
+      79            2 :           border: Border.all(color: Colors.grey.shade300),
+      80            1 :           borderRadius: BorderRadius.circular(9),
+      81            1 :           boxShadow: [
+      82            1 :             BoxShadow(
+      83            1 :               color: Colors.grey.shade200,
+      84              :               blurRadius: 3,
+      85              :               offset: const Offset(0, 2),
+      86              :             ),
+      87              :           ],
+      88              :         ),
+      89            1 :         child: Row(
+      90            1 :           children: [
+      91            1 :             Column(
+      92            1 :               children: [
+      93            1 :                 HeroImageWidget(
+      94            1 :                   restaurant: restaurant,
+      95              :                   width: 90,
+      96              :                   height: 90,
+      97              :                   borderRadius: 9.0,
+      98              :                 ),
+      99              :               ],
+     100              :             ),
+     101              :             const Spacer(
+     102              :               flex: 1,
+     103              :             ),
+     104            1 :             Flexible(
+     105              :               flex: 10,
+     106            1 :               child: Column(
+     107              :                 mainAxisAlignment: MainAxisAlignment.start,
+     108              :                 crossAxisAlignment: CrossAxisAlignment.start,
+     109            1 :                 children: [
+     110            1 :                   Text(
+     111            2 :                     restaurant.name ?? '',
+     112              :                     maxLines: 2,
+     113              :                   ),
+     114              :                   const SizedBox(
+     115              :                     height: 10,
+     116              :                   ),
+     117            2 :                   PriceAndClassificationRowWidget(restaurant: restaurant),
+     118              :                   const SizedBox(
+     119              :                     height: 10,
+     120              :                   ),
+     121            2 :                   RatingAndOpenStatusWidget(restaurant: restaurant)
+     122              :                 ],
+     123              :               ),
+     124              :             ),
+     125              :           ],
+     126              :         ),
+     127              :       ),
+     128              :     );
+     129              :   }
+     130              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/repositories/index-sort-f.html b/coverage_report/repositories/index-sort-f.html new file mode 100644 index 0000000..1667bde --- /dev/null +++ b/coverage_report/repositories/index-sort-f.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - repositories + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - repositoriesCoverageTotalHit
Test:lcov.infoLines:75.0 %86
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
yelp_repository.dart +
75.0%75.0%
+
75.0 %86-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/repositories/index-sort-l.html b/coverage_report/repositories/index-sort-l.html new file mode 100644 index 0000000..e064664 --- /dev/null +++ b/coverage_report/repositories/index-sort-l.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - repositories + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - repositoriesCoverageTotalHit
Test:lcov.infoLines:75.0 %86
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
yelp_repository.dart +
75.0%75.0%
+
75.0 %86-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/repositories/index.html b/coverage_report/repositories/index.html new file mode 100644 index 0000000..c5e84b7 --- /dev/null +++ b/coverage_report/repositories/index.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - repositories + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - repositoriesCoverageTotalHit
Test:lcov.infoLines:75.0 %86
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
yelp_repository.dart +
75.0%75.0%
+
75.0 %86-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/repositories/yelp_repository.dart.func-c.html b/coverage_report/repositories/yelp_repository.dart.func-c.html new file mode 100644 index 0000000..aaefd16 --- /dev/null +++ b/coverage_report/repositories/yelp_repository.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - repositories/yelp_repository.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - repositories - yelp_repository.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:75.0 %86
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/repositories/yelp_repository.dart.func.html b/coverage_report/repositories/yelp_repository.dart.func.html new file mode 100644 index 0000000..d854f87 --- /dev/null +++ b/coverage_report/repositories/yelp_repository.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - repositories/yelp_repository.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - repositories - yelp_repository.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:75.0 %86
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/repositories/yelp_repository.dart.gcov.html b/coverage_report/repositories/yelp_repository.dart.gcov.html new file mode 100644 index 0000000..e4c2139 --- /dev/null +++ b/coverage_report/repositories/yelp_repository.dart.gcov.html @@ -0,0 +1,108 @@ + + + + + + + LCOV - lcov.info - repositories/yelp_repository.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - repositories - yelp_repository.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:75.0 %86
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'dart:developer';
+       2              : import 'package:restaurant_tour/datasources/yielp_datasource.dart';
+       3              : import 'package:restaurant_tour/models/restaurant.dart';
+       4              : import 'package:restaurant_tour/utils/sample_json.dart';
+       5              : 
+       6              : class YelpRepository {
+       7              :   final YielpDatasource datasource;
+       8              : 
+       9            2 :   YelpRepository({required this.datasource});
+      10              : 
+      11            1 :   RestaurantQueryResult getRestaurantsFromCache({int offset = 0}) {
+      12              :     try {
+      13            1 :       log('fetching from cache');
+      14            3 :       return RestaurantQueryResult.fromJson(sample['data']['search']);
+      15              :     } catch (e) {
+      16            0 :       throw Exception();
+      17              :     }
+      18              :   }
+      19              : 
+      20            1 :   Future<RestaurantQueryResult?> getRestaurants({int offset = 0}) async {
+      21              :     try {
+      22            2 :       final data = await datasource.fetchRestaurants(offset);
+      23              :       if (data != null) {
+      24            0 :         return RestaurantQueryResult.fromJson(data['data']['search']);
+      25              :       } else {
+      26              :         return null;
+      27              :       }
+      28              :     } catch (e) {
+      29              :       return null;
+      30              :     }
+      31              :   }
+      32              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/ruby.png b/coverage_report/ruby.png new file mode 100644 index 0000000..991b6d4 Binary files /dev/null and b/coverage_report/ruby.png differ diff --git a/coverage_report/snow.png b/coverage_report/snow.png new file mode 100644 index 0000000..2cdae10 Binary files /dev/null and b/coverage_report/snow.png differ diff --git a/coverage_report/updown.png b/coverage_report/updown.png new file mode 100644 index 0000000..aa56a23 Binary files /dev/null and b/coverage_report/updown.png differ diff --git a/coverage_report/usecases/fetch_restaurants.dart.func-c.html b/coverage_report/usecases/fetch_restaurants.dart.func-c.html new file mode 100644 index 0000000..01f8565 --- /dev/null +++ b/coverage_report/usecases/fetch_restaurants.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - usecases/fetch_restaurants.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - usecases - fetch_restaurants.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %90
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/usecases/fetch_restaurants.dart.func.html b/coverage_report/usecases/fetch_restaurants.dart.func.html new file mode 100644 index 0000000..e1902a5 --- /dev/null +++ b/coverage_report/usecases/fetch_restaurants.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - usecases/fetch_restaurants.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - usecases - fetch_restaurants.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %90
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/usecases/fetch_restaurants.dart.gcov.html b/coverage_report/usecases/fetch_restaurants.dart.gcov.html new file mode 100644 index 0000000..1336ee9 --- /dev/null +++ b/coverage_report/usecases/fetch_restaurants.dart.gcov.html @@ -0,0 +1,118 @@ + + + + + + + LCOV - lcov.info - usecases/fetch_restaurants.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - usecases - fetch_restaurants.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %90
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:restaurant_tour/models/models.dart';
+       2              : import 'package:restaurant_tour/repositories/yelp_repository.dart';
+       3              : // import 'package:shared_preferences/shared_preferences.dart';
+       4              : 
+       5              : class FetchRestaurants {
+       6              :   final YelpRepository repository;
+       7              : 
+       8            0 :   FetchRestaurants({required this.repository});
+       9            0 :   Future<RestaurantQueryResult?> getRestaurants() async => await repository
+      10            0 :       .getRestaurantsFromCache(); //CHANGE THIS LATER TO FETCH FROM THE REAL API
+      11              : 
+      12            0 :   Future<RestaurantQueryResult?> getRestaurantsFromCache() async =>
+      13            0 :       await repository.getRestaurantsFromCache();
+      14              : 
+      15            0 :   Future<List<String>> getFavoriteRestaurants() async {
+      16              :     // final prefs = await SharedPreferences.getInstance();
+      17              : 
+      18              :     // Retrieve the list from SharedPreferences
+      19              :     // List<String>? favoriteRestaurants = prefs.getStringList('fav');
+      20              : 
+      21              :     // Simulate delay
+      22            0 :     await Future.delayed(const Duration(milliseconds: 500));
+      23              : 
+      24              :     // Return the list or a default value if it doesn't exist
+      25              :     // return favoriteRestaurants ?? [];
+      26            0 :     return ['vHz2RLtfUMVRPFmd7VBEHA'];
+      27              :   }
+      28              : 
+      29            0 :   Future<void> saveFavoriteRestaurants(List<String> favoriteRestaurants) async {
+      30              :     // final prefs = await SharedPreferences.getInstance();
+      31              : 
+      32              :     // Save the list to SharedPreferences
+      33              :     // await prefs.setStringList('fav', favoriteRestaurants);
+      34              :   }
+      35              : }
+      36              :   // Future<List<String>> getFavoriteRestaurants() async {
+      37              :   //   //this could be fetched from anywhere later... a shared prefs or another database
+      38              :   //   await Future.delayed(const Duration(milliseconds: 500));
+      39              : 
+      40              :   //   return ['vHz2RLtfUMVRPFmd7VBEHA'];
+      41              :   // }
+      42              : 
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/usecases/index-sort-f.html b/coverage_report/usecases/index-sort-f.html new file mode 100644 index 0000000..5c0407b --- /dev/null +++ b/coverage_report/usecases/index-sort-f.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - usecases + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - usecasesCoverageTotalHit
Test:lcov.infoLines:0.0 %90
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
fetch_restaurants.dart +
0.0%
+
0.0 %9-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/usecases/index-sort-l.html b/coverage_report/usecases/index-sort-l.html new file mode 100644 index 0000000..e9b7f69 --- /dev/null +++ b/coverage_report/usecases/index-sort-l.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - usecases + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - usecasesCoverageTotalHit
Test:lcov.infoLines:0.0 %90
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
fetch_restaurants.dart +
0.0%
+
0.0 %9-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/usecases/index.html b/coverage_report/usecases/index.html new file mode 100644 index 0000000..46682c5 --- /dev/null +++ b/coverage_report/usecases/index.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - usecases + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - usecasesCoverageTotalHit
Test:lcov.infoLines:0.0 %90
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
fetch_restaurants.dart +
0.0%
+
0.0 %9-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/utils/get_random_avatar.dart.func-c.html b/coverage_report/utils/get_random_avatar.dart.func-c.html new file mode 100644 index 0000000..ff5de2b --- /dev/null +++ b/coverage_report/utils/get_random_avatar.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - utils/get_random_avatar.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - utils - get_random_avatar.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %40
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/utils/get_random_avatar.dart.func.html b/coverage_report/utils/get_random_avatar.dart.func.html new file mode 100644 index 0000000..b11e9da --- /dev/null +++ b/coverage_report/utils/get_random_avatar.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - utils/get_random_avatar.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - utils - get_random_avatar.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %40
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/utils/get_random_avatar.dart.gcov.html b/coverage_report/utils/get_random_avatar.dart.gcov.html new file mode 100644 index 0000000..6cb5863 --- /dev/null +++ b/coverage_report/utils/get_random_avatar.dart.gcov.html @@ -0,0 +1,97 @@ + + + + + + + LCOV - lcov.info - utils/get_random_avatar.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - utils - get_random_avatar.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %40
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'dart:math';
+       2              : import 'package:flutter/material.dart';
+       3              : 
+       4            0 : ImageProvider getRandomAvatar() {
+       5              :   // Lista de avatares
+       6            0 :   final List<String> avatars = [
+       7              :     'assets/avatars/av1.png',
+       8              :     'assets/avatars/av2.png',
+       9              :     'assets/avatars/av3.png',
+      10              :     'assets/avatars/av4.png',
+      11              :     'assets/avatars/av5.png',
+      12              :     'assets/avatars/av6.png',
+      13              :     'assets/avatars/av7.png',
+      14              :   ];
+      15              : 
+      16              :   // Seleciona um índice aleatório
+      17            0 :   final randomIndex = Random().nextInt(avatars.length);
+      18              : 
+      19              :   // Retorna a imagem correspondente
+      20            0 :   return AssetImage(avatars[randomIndex]);
+      21              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/utils/index-sort-f.html b/coverage_report/utils/index-sort-f.html new file mode 100644 index 0000000..44c858c --- /dev/null +++ b/coverage_report/utils/index-sort-f.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - utils + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - utilsCoverageTotalHit
Test:lcov.infoLines:0.0 %40
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
get_random_avatar.dart +
0.0%
+
0.0 %4-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/utils/index-sort-l.html b/coverage_report/utils/index-sort-l.html new file mode 100644 index 0000000..c771aa1 --- /dev/null +++ b/coverage_report/utils/index-sort-l.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - utils + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - utilsCoverageTotalHit
Test:lcov.infoLines:0.0 %40
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
get_random_avatar.dart +
0.0%
+
0.0 %4-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/utils/index.html b/coverage_report/utils/index.html new file mode 100644 index 0000000..8448817 --- /dev/null +++ b/coverage_report/utils/index.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - utils + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - utilsCoverageTotalHit
Test:lcov.infoLines:0.0 %40
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
get_random_avatar.dart +
0.0%
+
0.0 %4-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/appbar.dart.func-c.html b/coverage_report/widgets/appbar.dart.func-c.html new file mode 100644 index 0000000..3a2faab --- /dev/null +++ b/coverage_report/widgets/appbar.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/appbar.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - appbar.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %150
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/appbar.dart.func.html b/coverage_report/widgets/appbar.dart.func.html new file mode 100644 index 0000000..7e9b876 --- /dev/null +++ b/coverage_report/widgets/appbar.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/appbar.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - appbar.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %150
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/appbar.dart.gcov.html b/coverage_report/widgets/appbar.dart.gcov.html new file mode 100644 index 0000000..91c3d47 --- /dev/null +++ b/coverage_report/widgets/appbar.dart.gcov.html @@ -0,0 +1,115 @@ + + + + + + + LCOV - lcov.info - widgets/appbar.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - appbar.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %150
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : import 'package:flutter_bloc/flutter_bloc.dart';
+       3              : import 'package:restaurant_tour/cubit/restaurant_cubit.dart';
+       4              : import 'package:restaurant_tour/models/restaurant.dart';
+       5              : 
+       6              : class RestaurantDetailAppBar extends StatelessWidget
+       7              :     implements PreferredSizeWidget {
+       8              :   final Restaurant restaurant;
+       9              : 
+      10            0 :   const RestaurantDetailAppBar({super.key, required this.restaurant});
+      11              : 
+      12            0 :   @override
+      13              :   Widget build(BuildContext context) {
+      14              :     final isFavorite = context
+      15            0 :         .watch<RestaurantCubit>()
+      16            0 :         .state
+      17            0 :         .favoriteRestaurants
+      18            0 :         .contains(restaurant.id);
+      19            0 :     return AppBar(
+      20            0 :       title: Text(restaurant.name ?? ''),
+      21            0 :       actions: [
+      22            0 :         Padding(
+      23              :           padding: const EdgeInsets.only(right: 13.0),
+      24            0 :           child: IconButton(
+      25              :             icon: isFavorite
+      26              :                 ? const Icon(Icons.favorite)
+      27              :                 : const Icon(Icons.favorite_border_outlined),
+      28            0 :             onPressed: () => context
+      29            0 :                 .read<RestaurantCubit>()
+      30            0 :                 .favoriteAResturant(restaurant.id!),
+      31              :           ),
+      32              :         ),
+      33              :       ],
+      34              :     );
+      35              :   }
+      36              : 
+      37            0 :   @override
+      38              :   Size get preferredSize => const Size.fromHeight(kToolbarHeight);
+      39              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/divider.dart.func-c.html b/coverage_report/widgets/divider.dart.func-c.html new file mode 100644 index 0000000..ec3eb75 --- /dev/null +++ b/coverage_report/widgets/divider.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/divider.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - divider.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %10
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/divider.dart.func.html b/coverage_report/widgets/divider.dart.func.html new file mode 100644 index 0000000..791682b --- /dev/null +++ b/coverage_report/widgets/divider.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/divider.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - divider.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %10
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/divider.dart.gcov.html b/coverage_report/widgets/divider.dart.gcov.html new file mode 100644 index 0000000..fc2a9b4 --- /dev/null +++ b/coverage_report/widgets/divider.dart.gcov.html @@ -0,0 +1,83 @@ + + + + + + + LCOV - lcov.info - widgets/divider.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - divider.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %10
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : 
+       3            0 : Divider buildDivider() {
+       4              :   return const Divider(
+       5              :     thickness: 0.3,
+       6              :   );
+       7              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/hero_image_widget.dart.func-c.html b/coverage_report/widgets/hero_image_widget.dart.func-c.html new file mode 100644 index 0000000..42f3d09 --- /dev/null +++ b/coverage_report/widgets/hero_image_widget.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/hero_image_widget.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - hero_image_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:75.0 %1612
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/hero_image_widget.dart.func.html b/coverage_report/widgets/hero_image_widget.dart.func.html new file mode 100644 index 0000000..d85ad43 --- /dev/null +++ b/coverage_report/widgets/hero_image_widget.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/hero_image_widget.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - hero_image_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:75.0 %1612
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/hero_image_widget.dart.gcov.html b/coverage_report/widgets/hero_image_widget.dart.gcov.html new file mode 100644 index 0000000..c86672c --- /dev/null +++ b/coverage_report/widgets/hero_image_widget.dart.gcov.html @@ -0,0 +1,118 @@ + + + + + + + LCOV - lcov.info - widgets/hero_image_widget.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - hero_image_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:75.0 %1612
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : 
+       3              : import '../models/restaurant.dart';
+       4              : 
+       5              : class HeroImageWidget extends StatelessWidget {
+       6            1 :   const HeroImageWidget(
+       7              :       {super.key,
+       8              :       required this.restaurant,
+       9              :       this.borderRadius = 0,
+      10              :       required this.width,
+      11              :       required this.height});
+      12              :   final Restaurant restaurant;
+      13              :   final double borderRadius;
+      14              :   final double width;
+      15              :   final double height;
+      16            1 :   @override
+      17              :   Widget build(BuildContext context) {
+      18            1 :     return Hero(
+      19            3 :       tag: restaurant.id.toString(),
+      20            1 :       child: ClipRRect(
+      21            2 :         borderRadius: BorderRadius.circular(borderRadius),
+      22            3 :         child: restaurant.heroImage.isNotEmpty &&
+      23            5 :                 Uri.tryParse(restaurant.heroImage)?.isAbsolute == true
+      24            1 :             ? Image.network(
+      25            2 :                 restaurant.heroImage,
+      26              :                 fit: BoxFit.cover,
+      27            1 :                 width: width,
+      28            1 :                 height: height,
+      29              :               )
+      30            0 :             : Container(
+      31            0 :                 width: width,
+      32            0 :                 height: height,
+      33              :                 color: const Color.fromARGB(255, 197, 233, 199),
+      34            0 :                 child: Image.asset(
+      35              :                   'assets/placeholder.png',
+      36              :                   fit: BoxFit.fitHeight,
+      37              :                 ),
+      38              :               ),
+      39              :       ),
+      40              :     );
+      41              :   }
+      42              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/index-sort-f.html b/coverage_report/widgets/index-sort-f.html new file mode 100644 index 0000000..0f172c1 --- /dev/null +++ b/coverage_report/widgets/index-sort-f.html @@ -0,0 +1,189 @@ + + + + + + + LCOV - lcov.info - widgets + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgetsCoverageTotalHit
Test:lcov.infoLines:44.9 %10748
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
appbar.dart +
0.0%
+
0.0 %15-
divider.dart +
0.0%
+
0.0 %1-
hero_image_widget.dart +
75.0%75.0%
+
75.0 %1612-
rataurant_open_status.dart +
100.0%
+
100.0 %99-
rating_and_open_status_widget.dart +
100.0%
+
100.0 %88-
rating_widget.dart +
100.0%
+
100.0 %88-
restaurant_details.dart +
100.0%
+
100.0 %1111-
restaurant_ratings_review_section.dart +
0.0%
+
0.0 %39-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/index-sort-l.html b/coverage_report/widgets/index-sort-l.html new file mode 100644 index 0000000..d9d81e1 --- /dev/null +++ b/coverage_report/widgets/index-sort-l.html @@ -0,0 +1,189 @@ + + + + + + + LCOV - lcov.info - widgets + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgetsCoverageTotalHit
Test:lcov.infoLines:44.9 %10748
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
divider.dart +
0.0%
+
0.0 %1-
appbar.dart +
0.0%
+
0.0 %15-
restaurant_ratings_review_section.dart +
0.0%
+
0.0 %39-
hero_image_widget.dart +
75.0%75.0%
+
75.0 %1612-
rating_and_open_status_widget.dart +
100.0%
+
100.0 %88-
rating_widget.dart +
100.0%
+
100.0 %88-
rataurant_open_status.dart +
100.0%
+
100.0 %99-
restaurant_details.dart +
100.0%
+
100.0 %1111-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/index.html b/coverage_report/widgets/index.html new file mode 100644 index 0000000..470ddaf --- /dev/null +++ b/coverage_report/widgets/index.html @@ -0,0 +1,189 @@ + + + + + + + LCOV - lcov.info - widgets + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgetsCoverageTotalHit
Test:lcov.infoLines:44.9 %10748
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

File Sort by file nameLine Coverage Sort by line coverageFunction Coverage Sort by function coverage
Rate Total Hit Rate Total Hit
appbar.dart +
0.0%
+
0.0 %15-
divider.dart +
0.0%
+
0.0 %1-
hero_image_widget.dart +
75.0%75.0%
+
75.0 %1612-
rataurant_open_status.dart +
100.0%
+
100.0 %99-
rating_and_open_status_widget.dart +
100.0%
+
100.0 %88-
rating_widget.dart +
100.0%
+
100.0 %88-
restaurant_details.dart +
100.0%
+
100.0 %1111-
restaurant_ratings_review_section.dart +
0.0%
+
0.0 %39-
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rataurant_open_status.dart.func-c.html b/coverage_report/widgets/rataurant_open_status.dart.func-c.html new file mode 100644 index 0000000..00a934d --- /dev/null +++ b/coverage_report/widgets/rataurant_open_status.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/rataurant_open_status.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rataurant_open_status.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %99
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rataurant_open_status.dart.func.html b/coverage_report/widgets/rataurant_open_status.dart.func.html new file mode 100644 index 0000000..3c5ae88 --- /dev/null +++ b/coverage_report/widgets/rataurant_open_status.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/rataurant_open_status.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rataurant_open_status.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %99
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rataurant_open_status.dart.gcov.html b/coverage_report/widgets/rataurant_open_status.dart.gcov.html new file mode 100644 index 0000000..543cc8b --- /dev/null +++ b/coverage_report/widgets/rataurant_open_status.dart.gcov.html @@ -0,0 +1,102 @@ + + + + + + + LCOV - lcov.info - widgets/rataurant_open_status.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rataurant_open_status.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %99
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : 
+       3              : class OpenStatusWidget extends StatelessWidget {
+       4            1 :   const OpenStatusWidget({super.key, required this.isOpen});
+       5              :   final bool isOpen;
+       6            1 :   @override
+       7              :   Widget build(BuildContext context) {
+       8            1 :     return Row(
+       9              :       mainAxisSize: MainAxisSize.min,
+      10            1 :       children: [
+      11            1 :         Text(
+      12            1 :           isOpen ? 'Open Now' : 'Closed Now',
+      13            3 :           style: Theme.of(context).textTheme.displaySmall,
+      14              :         ),
+      15              :         const SizedBox(
+      16              :           width: 8,
+      17              :         ),
+      18            1 :         Icon(
+      19              :           Icons.circle,
+      20              :           size: 8.0,
+      21            1 :           color: isOpen ? Colors.green : Colors.red,
+      22              :         )
+      23              :       ],
+      24              :     );
+      25              :   }
+      26              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rating_and_open_status_widget.dart.func-c.html b/coverage_report/widgets/rating_and_open_status_widget.dart.func-c.html new file mode 100644 index 0000000..f160466 --- /dev/null +++ b/coverage_report/widgets/rating_and_open_status_widget.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/rating_and_open_status_widget.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rating_and_open_status_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %88
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rating_and_open_status_widget.dart.func.html b/coverage_report/widgets/rating_and_open_status_widget.dart.func.html new file mode 100644 index 0000000..aa9a287 --- /dev/null +++ b/coverage_report/widgets/rating_and_open_status_widget.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/rating_and_open_status_widget.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rating_and_open_status_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %88
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rating_and_open_status_widget.dart.gcov.html b/coverage_report/widgets/rating_and_open_status_widget.dart.gcov.html new file mode 100644 index 0000000..3bd747b --- /dev/null +++ b/coverage_report/widgets/rating_and_open_status_widget.dart.gcov.html @@ -0,0 +1,105 @@ + + + + + + + LCOV - lcov.info - widgets/rating_and_open_status_widget.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rating_and_open_status_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %88
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/widgets.dart';
+       2              : 
+       3              : import '../models/restaurant.dart';
+       4              : import 'rating_widget.dart';
+       5              : import 'widgets.dart';
+       6              : 
+       7              : class RatingAndOpenStatusWidget extends StatelessWidget {
+       8            1 :   const RatingAndOpenStatusWidget({
+       9              :     super.key,
+      10              :     required this.restaurant,
+      11              :   });
+      12              : 
+      13              :   final Restaurant restaurant;
+      14              : 
+      15            1 :   @override
+      16              :   Widget build(BuildContext context) {
+      17            1 :     return Row(
+      18            1 :       children: [
+      19            1 :         StarRating(
+      20            3 :           rating: restaurant.rating?.round() ?? 0,
+      21              :         ),
+      22              :         const Spacer(),
+      23            1 :         OpenStatusWidget(
+      24            2 :           isOpen: restaurant.isOpen,
+      25              :         )
+      26              :       ],
+      27              :     );
+      28              :   }
+      29              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rating_widget.dart.func-c.html b/coverage_report/widgets/rating_widget.dart.func-c.html new file mode 100644 index 0000000..eb096e6 --- /dev/null +++ b/coverage_report/widgets/rating_widget.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/rating_widget.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rating_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %88
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rating_widget.dart.func.html b/coverage_report/widgets/rating_widget.dart.func.html new file mode 100644 index 0000000..cdf239e --- /dev/null +++ b/coverage_report/widgets/rating_widget.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/rating_widget.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rating_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %88
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/rating_widget.dart.gcov.html b/coverage_report/widgets/rating_widget.dart.gcov.html new file mode 100644 index 0000000..3f30547 --- /dev/null +++ b/coverage_report/widgets/rating_widget.dart.gcov.html @@ -0,0 +1,106 @@ + + + + + + + LCOV - lcov.info - widgets/rating_widget.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - rating_widget.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %88
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : 
+       3              : import '../theme/colors.dart';
+       4              : 
+       5              : class StarRating extends StatelessWidget {
+       6              :   final int rating;
+       7              :   final double starSize;
+       8              :   final Color starColor;
+       9              : 
+      10            1 :   const StarRating({
+      11              :     super.key,
+      12              :     required this.rating, // Rating should be between 0 to 5
+      13              :     this.starSize = 12.0,
+      14              :     this.starColor = ThemeColors.starColor,
+      15            3 :   }) : assert(rating >= 0 && rating <= 5);
+      16              : 
+      17            1 :   @override
+      18              :   Widget build(BuildContext context) {
+      19            1 :     return Row(
+      20              :       mainAxisSize: MainAxisSize.min,
+      21            3 :       children: List.generate(rating, (index) {
+      22            1 :         return Icon(
+      23              :           Icons.star,
+      24            1 :           size: starSize,
+      25            1 :           color: starColor,
+      26              :         );
+      27              :       }),
+      28              :     );
+      29              :   }
+      30              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/restaurant_details.dart.func-c.html b/coverage_report/widgets/restaurant_details.dart.func-c.html new file mode 100644 index 0000000..fe73f6e --- /dev/null +++ b/coverage_report/widgets/restaurant_details.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/restaurant_details.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - restaurant_details.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %1111
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/restaurant_details.dart.func.html b/coverage_report/widgets/restaurant_details.dart.func.html new file mode 100644 index 0000000..644b718 --- /dev/null +++ b/coverage_report/widgets/restaurant_details.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/restaurant_details.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - restaurant_details.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %1111
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/restaurant_details.dart.gcov.html b/coverage_report/widgets/restaurant_details.dart.gcov.html new file mode 100644 index 0000000..a41b462 --- /dev/null +++ b/coverage_report/widgets/restaurant_details.dart.gcov.html @@ -0,0 +1,110 @@ + + + + + + + LCOV - lcov.info - widgets/restaurant_details.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - restaurant_details.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:100.0 %1111
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : import 'package:restaurant_tour/models/restaurant.dart';
+       3              : 
+       4              : class PriceAndClassificationRowWidget extends StatelessWidget {
+       5            1 :   const PriceAndClassificationRowWidget({
+       6              :     super.key,
+       7              :     required this.restaurant,
+       8              :   });
+       9              : 
+      10              :   final Restaurant restaurant;
+      11              : 
+      12            1 :   @override
+      13              :   Widget build(BuildContext context) {
+      14            1 :     return Row(
+      15            1 :       children: [
+      16            1 :         Text(
+      17            2 :           restaurant.price ?? '-',
+      18            3 :           style: Theme.of(context).textTheme.bodySmall,
+      19              :         ),
+      20              :         const SizedBox(
+      21              :           width: 6,
+      22              :         ),
+      23            1 :         Flexible(
+      24            1 :           child: Text(
+      25            6 :             restaurant.categories?.map((element) => element.title).first ?? '',
+      26            3 :             style: Theme.of(context).textTheme.bodySmall,
+      27              :             maxLines: 1,
+      28              :             softWrap: true,
+      29              :           ),
+      30              :         ),
+      31              :       ],
+      32              :     );
+      33              :   }
+      34              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/restaurant_ratings_review_section.dart.func-c.html b/coverage_report/widgets/restaurant_ratings_review_section.dart.func-c.html new file mode 100644 index 0000000..e79140a --- /dev/null +++ b/coverage_report/widgets/restaurant_ratings_review_section.dart.func-c.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/restaurant_ratings_review_section.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - restaurant_ratings_review_section.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %390
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/restaurant_ratings_review_section.dart.func.html b/coverage_report/widgets/restaurant_ratings_review_section.dart.func.html new file mode 100644 index 0000000..8e7d4d3 --- /dev/null +++ b/coverage_report/widgets/restaurant_ratings_review_section.dart.func.html @@ -0,0 +1,75 @@ + + + + + + + LCOV - lcov.info - widgets/restaurant_ratings_review_section.dart - functions + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - restaurant_ratings_review_section.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %390
Test Date:2024-08-26 14:48:33Functions:-00
+
+ +
+ + + + + + + + + +

Function Name Sort by function nameHit count Sort by function hit count
+
+
+ + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/coverage_report/widgets/restaurant_ratings_review_section.dart.gcov.html b/coverage_report/widgets/restaurant_ratings_review_section.dart.gcov.html new file mode 100644 index 0000000..517eef7 --- /dev/null +++ b/coverage_report/widgets/restaurant_ratings_review_section.dart.gcov.html @@ -0,0 +1,188 @@ + + + + + + + LCOV - lcov.info - widgets/restaurant_ratings_review_section.dart + + + + + + + + + + + + + + +
LCOV - code coverage report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Current view:top level - widgets - restaurant_ratings_review_section.dart (source / functions)CoverageTotalHit
Test:lcov.infoLines:0.0 %390
Test Date:2024-08-26 14:48:33Functions:-00
+
+ + + + + + + + +

+
            Line data    Source code
+
+       1              : import 'package:flutter/material.dart';
+       2              : import 'package:restaurant_tour/models/models.dart';
+       3              : import 'package:restaurant_tour/theme/text.dart';
+       4              : import 'package:restaurant_tour/utils/get_random_avatar.dart';
+       5              : import 'package:restaurant_tour/widgets/widgets.dart';
+       6              : import '../theme/colors.dart';
+       7              : 
+       8              : class BuildRatingSection extends StatelessWidget {
+       9            0 :   const BuildRatingSection({super.key, required this.restaurant});
+      10              :   final Restaurant restaurant;
+      11              : 
+      12            0 :   @override
+      13              :   Widget build(BuildContext context) {
+      14            0 :     return Column(
+      15              :       crossAxisAlignment: CrossAxisAlignment.start,
+      16            0 :       children: [
+      17            0 :         Text(
+      18              :           'Overall Rating',
+      19            0 :           style: Theme.of(context).textTheme.bodySmall,
+      20              :         ),
+      21              :         const SizedBox(
+      22              :           height: 16,
+      23              :         ),
+      24            0 :         overallClassificationRow(context),
+      25              :         const SizedBox(
+      26              :           height: spacingXL,
+      27              :         ),
+      28            0 :         buildDivider(),
+      29              :         const SizedBox(
+      30              :           height: spacingXL,
+      31              :         ),
+      32            0 :         Text(
+      33            0 :           '${restaurant.reviews?.length} reviews',
+      34            0 :           style: Theme.of(context).textTheme.bodySmall,
+      35              :         ),
+      36              :         // const SizedBox(
+      37              :         //   height: spacingXL,
+      38              :         // ),
+      39            0 :         ListView.builder(
+      40              :           shrinkWrap: true,
+      41              :           physics: const NeverScrollableScrollPhysics(),
+      42            0 :           itemCount: restaurant.reviews?.length,
+      43            0 :           itemBuilder: (context, index) {
+      44            0 :             var review = restaurant.reviews?[index];
+      45            0 :             return Column(
+      46              :               crossAxisAlignment: CrossAxisAlignment.start,
+      47            0 :               children: [
+      48              :                 const SizedBox(
+      49              :                   height: spacingM,
+      50              :                 ),
+      51            0 :                 StarRating(
+      52            0 :                   rating: review?.rating ?? 0,
+      53              :                 ),
+      54              :                 const SizedBox(
+      55              :                   height: spacingSM,
+      56              :                 ),
+      57            0 :                 review?.text != null
+      58            0 :                     ? Padding(
+      59              :                         padding: const EdgeInsets.only(top: 8.0),
+      60            0 :                         child: Text(
+      61            0 :                           review!.text!,
+      62            0 :                           style: Theme.of(context).textTheme.bodyLarge,
+      63              :                         ),
+      64              :                       )
+      65            0 :                     : Container(),
+      66              :                 const SizedBox(
+      67              :                   height: 8,
+      68              :                 ),
+      69            0 :                 Row(
+      70            0 :                   children: [
+      71            0 :                     CircleAvatar(
+      72              :                       radius: 35,
+      73            0 :                       backgroundImage: review?.user?.imageUrl != null
+      74            0 :                           ? NetworkImage(review!.user!.imageUrl!)
+      75            0 :                           : getRandomAvatar(),
+      76              :                     ),
+      77              :                     const SizedBox(
+      78              :                       width: 8,
+      79              :                     ),
+      80            0 :                     Text(review?.user?.name ?? 'Not identified'),
+      81              :                   ],
+      82              :                 ),
+      83              :                 const SizedBox(
+      84              :                   height: spacingM,
+      85              :                 ),
+      86            0 :                 buildDivider()
+      87              :               ],
+      88              :             );
+      89              :           },
+      90              :         ),
+      91              :       ],
+      92              :     );
+      93              :   }
+      94              : 
+      95            0 :   Row overallClassificationRow(BuildContext context) {
+      96            0 :     return Row(
+      97            0 :       children: [
+      98            0 :         Text(
+      99            0 :           restaurant.rating.toString(),
+     100            0 :           style: Theme.of(context).textTheme.headlineMedium,
+     101              :         ),
+     102              :         const SizedBox(
+     103              :           width: 6,
+     104              :         ),
+     105              :         const Icon(
+     106              :           Icons.star,
+     107              :           color: ThemeColors.starColor,
+     108              :         ),
+     109              :       ],
+     110              :     );
+     111              :   }
+     112              : }
+        
+
+
+ + + + +
Generated by: LCOV version 2.1-1
+
+ + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 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 592ceee..c4855bf 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 0000000..d97f17e --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,44 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '12.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/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..9d67035 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,23 @@ +PODS: + - Flutter (1.0.0) + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - Flutter (from `Flutter`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + +SPEC CHECKSUMS: + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 + +COCOAPODS: 1.14.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 182fb57..0efa999 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 5B9EB8175B8C5B859CCEC015 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F7A10419AE997EB6DE7E5D6A /* Pods_RunnerTests.framework */; }; + 6321356C5CCA15700174FB76 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A9A3C3DE807B1C6583136EC /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -44,9 +46,13 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 339EEEBAD5DCF72F3BEFB8C0 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 4202DE8A265392A0073DDC58 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 6A9A3C3DE807B1C6583136EC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 754CE2B68064AF3945817F8B /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -55,13 +61,26 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A74BE093EA688B08B5BBECD4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F4AC0A48403EA673C8AADA67 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + F7A10419AE997EB6DE7E5D6A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F964336D93D08154B23C613A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 3FFD19965B4C9D566984E5F5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B9EB8175B8C5B859CCEC015 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6321356C5CCA15700174FB76 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,6 +95,20 @@ path = RunnerTests; sourceTree = ""; }; + 8C577AD6DF1DDC3C789A7B26 /* Pods */ = { + isa = PBXGroup; + children = ( + F4AC0A48403EA673C8AADA67 /* Pods-Runner.debug.xcconfig */, + A74BE093EA688B08B5BBECD4 /* Pods-Runner.release.xcconfig */, + F964336D93D08154B23C613A /* Pods-Runner.profile.xcconfig */, + 339EEEBAD5DCF72F3BEFB8C0 /* Pods-RunnerTests.debug.xcconfig */, + 4202DE8A265392A0073DDC58 /* Pods-RunnerTests.release.xcconfig */, + 754CE2B68064AF3945817F8B /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -94,6 +127,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + 8C577AD6DF1DDC3C789A7B26 /* Pods */, + F800066FBB912D75920385E0 /* Frameworks */, ); sourceTree = ""; }; @@ -121,6 +156,15 @@ path = Runner; sourceTree = ""; }; + F800066FBB912D75920385E0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6A9A3C3DE807B1C6583136EC /* Pods_Runner.framework */, + F7A10419AE997EB6DE7E5D6A /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,8 +172,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 5BFF67856F74324DED4DA1A8 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + 3FFD19965B4C9D566984E5F5 /* Frameworks */, ); buildRules = ( ); @@ -145,12 +191,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + FF18833D2F062EF8455E3171 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 33BB0A415525F547416C12A9 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -222,6 +270,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 33BB0A415525F547416C12A9 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -238,6 +303,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 5BFF67856F74324DED4DA1A8 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -253,6 +340,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + FF18833D2F062EF8455E3171 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -379,6 +488,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 339EEEBAD5DCF72F3BEFB8C0 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -396,6 +506,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 4202DE8A265392A0073DDC58 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -411,6 +522,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 754CE2B68064AF3945817F8B /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/lib/cubit/cubit.dart b/lib/cubit/cubit.dart new file mode 100644 index 0000000..868fb90 --- /dev/null +++ b/lib/cubit/cubit.dart @@ -0,0 +1 @@ +export 'restaurant_cubit.dart'; diff --git a/lib/cubit/restaurant_cubit.dart b/lib/cubit/restaurant_cubit.dart new file mode 100644 index 0000000..ddde3a1 --- /dev/null +++ b/lib/cubit/restaurant_cubit.dart @@ -0,0 +1,54 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; + +import '../usecases/fetch_restaurants.dart'; + +part 'restaurant_state.dart'; + +class RestaurantCubit extends Cubit { + RestaurantCubit(this.fetchRestaurantsUseCase) + : super(const RestaurantInitial(favoriteRestaurants: [])); + final FetchRestaurants fetchRestaurantsUseCase; + + Future fetchRestaurants() async { + emit(const LoadingRestaurantsState(favoriteRestaurants: [])); + RestaurantQueryResult? result = + await fetchRestaurantsUseCase.getRestaurants(); + List favoriteRestaurants = + await fetchRestaurantsUseCase.getFavoriteRestaurants(); + if (result != null) { + emit( + RestaurantsLoadedState( + result: result, + favoriteRestaurants: favoriteRestaurants, + ), + ); + } else { + emit( + const ErrorState( + message: + 'The server encountered a problem and we couldn\'t load the list.', + favoriteRestaurants: [], + ), + ); + } + } + + Future favoriteAResturant(String id) async { + final favoriteRestaurants = List.from(state.favoriteRestaurants); + if (favoriteRestaurants.contains(id)) { + favoriteRestaurants.remove(id); + } else { + favoriteRestaurants.add(id); + } + // await fetchRestaurantsUseCase.saveFavoriteRestaurants(favoriteRestaurants); + emit( + FavoriteRestaurantState( + result: state.result!, + favoriteRestaurants: List.of(favoriteRestaurants), + ), + ); + } +} diff --git a/lib/cubit/restaurant_state.dart b/lib/cubit/restaurant_state.dart new file mode 100644 index 0000000..c3b75ac --- /dev/null +++ b/lib/cubit/restaurant_state.dart @@ -0,0 +1,104 @@ +// ignore_for_file: overridden_fields + +part of 'restaurant_cubit.dart'; + +@immutable +abstract class RestaurantState extends Equatable { + final List favoriteRestaurants; + final RestaurantQueryResult? result; + + const RestaurantState({ + required this.favoriteRestaurants, + this.result, + }); + + RestaurantState copyWith({List? favoriteRestaurants}); + @override + List get props => [favoriteRestaurants]; +} + +class RestaurantInitial extends RestaurantState { + const RestaurantInitial({required super.favoriteRestaurants}); + + @override + RestaurantState copyWith({List? favoriteRestaurants}) { + return RestaurantInitial( + favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants, + ); + } +} + +class LoadingRestaurantsState extends RestaurantState { + const LoadingRestaurantsState({required super.favoriteRestaurants}); + + @override + List get props => [favoriteRestaurants]; + + @override + RestaurantState copyWith({List? favoriteRestaurants}) { + return LoadingRestaurantsState( + favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants, + ); + } +} + +class RestaurantsLoadedState extends RestaurantState { + const RestaurantsLoadedState({ + required this.result, + required super.favoriteRestaurants, + }); + + @override + final RestaurantQueryResult result; + + @override + List get props => [result, favoriteRestaurants]; + + @override + RestaurantsLoadedState copyWith({List? favoriteRestaurants}) { + return RestaurantsLoadedState( + favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants, + result: result, + ); + } +} + +class FavoriteRestaurantState extends RestaurantState { + @override + final RestaurantQueryResult result; + @override + final List favoriteRestaurants; + + const FavoriteRestaurantState({ + required this.result, + required this.favoriteRestaurants, + }) : super(favoriteRestaurants: favoriteRestaurants, result: result); + @override + List get props => [favoriteRestaurants]; + + @override + FavoriteRestaurantState copyWith({List? favoriteRestaurants}) { + return FavoriteRestaurantState( + favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants, + result: result, + ); + } +} + +class ErrorState extends RestaurantState { + final String message; + + const ErrorState({ + required super.favoriteRestaurants, + super.result, + required this.message, + }); + + @override + RestaurantState copyWith({List? favoriteRestaurants}) { + return ErrorState( + favoriteRestaurants: favoriteRestaurants ?? this.favoriteRestaurants, + message: message, + ); + } +} diff --git a/lib/datasources/datasources.dart b/lib/datasources/datasources.dart new file mode 100644 index 0000000..4f6b57e --- /dev/null +++ b/lib/datasources/datasources.dart @@ -0,0 +1 @@ +export './datasources.dart'; diff --git a/lib/datasources/yielp_datasource.dart b/lib/datasources/yielp_datasource.dart new file mode 100644 index 0000000..5be2739 --- /dev/null +++ b/lib/datasources/yielp_datasource.dart @@ -0,0 +1,66 @@ +import 'dart:developer'; + +import 'package:restaurant_tour/env.dart'; +import 'package:restaurant_tour/network/network_client.dart'; + +class YielpDatasource { + final INetworkClient networkClient; + final String baseUrl; + + YielpDatasource({required this.networkClient, required this.baseUrl}); + + Future?> fetchRestaurants(int offset) async { + try { + final String query = _getQuery(offset); + final response = await networkClient.post( + '$baseUrl/v3/graphql', + data: query, + headers: { + 'Authorization': 'Bearer $API_KEY', + 'Content-Type': 'application/graphql', + }, + ); + return response; + } on Exception catch (e) { + log(e.toString()); + return null; + } + } + + String _getQuery(int offset) { + return ''' +query getRestaurants { + search(location: "Las Vegas", limit: 20, offset: $offset) { + total + business { + id + name + price + rating + photos + reviews { + id + rating + text + user { + id + image_url + name + } + } + categories { + title + alias + } + hours { + is_open_now + } + location { + formatted_address + } + } + } +} +'''; + } +} diff --git a/lib/main.dart b/lib/main.dart index 3a4af7d..e7f9483 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,51 +1,73 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:restaurant_tour/repositories/yelp_repository.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get_it/get_it.dart'; +import 'package:restaurant_tour/cubit/cubit.dart'; +import 'package:restaurant_tour/datasources/yielp_datasource.dart'; +import 'package:restaurant_tour/env.dart'; +import 'package:restaurant_tour/network/dio_http_client.dart'; +import 'package:restaurant_tour/repositories/repositories.dart'; +import 'package:restaurant_tour/theme/text.dart'; +import 'pages/home_page.dart'; +import 'usecases/fetch_restaurants.dart'; + +Future main() async { + setup(); + WidgetsFlutterBinding.ensureInitialized(); -void main() { runApp(const RestaurantTour()); } -class RestaurantTour extends StatelessWidget { - const RestaurantTour({Key? key}) : super(key: key); +void setup() { + final getIt = GetIt.instance; + final dio = Dio( + BaseOptions( + baseUrl: 'https://api.yelp.com', + headers: { + 'Authorization': 'Bearer $API_KEY', + 'Content-Type': 'application/graphql', + }, + ), + ); + getIt.registerSingleton(DioHttpClient(dio: dio)); - @override - Widget build(BuildContext context) { - return const MaterialApp( - title: 'Restaurant Tour', - home: HomePage(), - ); - } + getIt.registerSingleton( + YielpDatasource( + networkClient: getIt(), + baseUrl: 'https://api.yelp.com', + ), + ); + + getIt.registerSingleton( + YelpRepository(datasource: getIt()), + ); + + getIt.registerSingleton( + FetchRestaurants(repository: getIt()), + ); + getIt.registerSingleton( + RestaurantCubit(getIt()), + ); } -class HomePage extends StatelessWidget { - const HomePage({Key? key}) : super(key: key); +class RestaurantTour extends StatelessWidget { + const RestaurantTour({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Restaurant Tour'), - ElevatedButton( - child: const Text('Fetch Restaurants'), - onPressed: () async { - final yelpRepo = YelpRepository(); - - try { - final result = await yelpRepo.getRestaurants(); - if (result != null) { - print('Fetched ${result.restaurants!.length} restaurants'); - } else { - print('No restaurants fetched'); - } - } catch (e) { - print('Failed to fetch restaurants: $e'); - } - }, - ), - ], + return BlocProvider.value( + value: GetIt.instance()..fetchRestaurants(), + child: MaterialApp( + title: 'Restaurant Tour', + debugShowCheckedModeBanner: false, + home: const HomePage(), + theme: ThemeData( + fontFamily: 'Lora', + scaffoldBackgroundColor: const Color.fromARGB(255, 255, 255, 255), + appBarTheme: const AppBarTheme( + backgroundColor: Color.fromARGB(255, 255, 255, 255), + ), + textTheme: ThemeText.textTheming, ), ), ); diff --git a/lib/models/models.dart b/lib/models/models.dart new file mode 100644 index 0000000..d5a0fcf --- /dev/null +++ b/lib/models/models.dart @@ -0,0 +1 @@ +export 'restaurant.dart'; diff --git a/lib/models/restaurant.g.dart b/lib/models/restaurant.g.dart index 3ed33f9..0693ae5 100644 --- a/lib/models/restaurant.g.dart +++ b/lib/models/restaurant.g.dart @@ -39,6 +39,7 @@ Map _$UserToJson(User instance) => { Review _$ReviewFromJson(Map json) => Review( id: json['id'] as String?, rating: json['rating'] as int?, + text: json['text'] as String?, user: json['user'] == null ? null : User.fromJson(json['user'] as Map), diff --git a/lib/network/dio_http_client.dart b/lib/network/dio_http_client.dart new file mode 100644 index 0000000..0c56f51 --- /dev/null +++ b/lib/network/dio_http_client.dart @@ -0,0 +1,22 @@ +import 'package:dio/dio.dart'; + +import 'network_client.dart'; + +class DioHttpClient implements INetworkClient { + final Dio dio; + + DioHttpClient({required this.dio}); + @override + Future> post( + String url, { + required data, + Map? headers, + }) async { + final response = await dio.post>( + url, + data: data, + options: headers != null ? Options(headers: headers) : null, + ); + return response.data!; + } +} diff --git a/lib/network/network_client.dart b/lib/network/network_client.dart new file mode 100644 index 0000000..f36dc83 --- /dev/null +++ b/lib/network/network_client.dart @@ -0,0 +1,7 @@ +abstract class INetworkClient { + Future> post( + String url, { + required data, + Map? headers, + }); +} diff --git a/lib/pages/favorites_list_page_view.dart b/lib/pages/favorites_list_page_view.dart new file mode 100644 index 0000000..9d24606 --- /dev/null +++ b/lib/pages/favorites_list_page_view.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:restaurant_tour/cubit/cubit.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; +import 'package:restaurant_tour/pages/restaurant_list_page.dart'; + +class FavoritesListPageView extends StatelessWidget { + const FavoritesListPageView({super.key}); + // final List restaurants; + @override + Widget build(BuildContext context) { + return Scaffold(body: BlocBuilder( + builder: (context, state) { + if (state is LoadingRestaurantsState) { + return const Center(child: CircularProgressIndicator()); + } + if (state is RestaurantsLoadedState || + state is FavoriteRestaurantState) { + return LayoutBuilder( + builder: (context, constraints) { + List favoritesList = state.result!.restaurants! + .where( + (element) => state.favoriteRestaurants.contains(element.id), + ) + .toList(); + return ListView.builder( + itemCount: favoritesList.length, + itemBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + RestaurantListTile( + restaurant: favoritesList.elementAt(index), + constraints: constraints, + ), + ], + ); + }, + ); + }, + ); + } + return Container(); + }, + )); + } +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart new file mode 100644 index 0000000..7a3133c --- /dev/null +++ b/lib/pages/home_page.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:restaurant_tour/pages/favorites_list_page_view.dart'; +import 'package:restaurant_tour/pages/restaurant_list_page.dart'; + +// class HomePageBlocProvider extends StatelessWidget { +// const HomePageBlocProvider({super.key}); + +// @override +// Widget build(BuildContext context) { +// return BlocProvider( +// create: (context) => +// GetIt.instance()..fetchRestaurants(), +// child: const HomePage(), +// ); +// } +// } + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Scaffold( + appBar: AppBar( + title: Text( + "RestauranTour", + style: Theme.of(context).textTheme.headlineLarge, + ), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 16), + child: TextButton( + onPressed: () {}, + child: const CircleAvatar( + backgroundImage: NetworkImage( + 'https://mdbcdn.b-cdn.net/img/new/avatars/2.webp', + ), + ), + ), + ), + ], + bottom: TabBar( + tabs: [ + Tab( + child: Text( + 'All Restaurants', + style: Theme.of(context).textTheme.displayLarge, + ), + ), + Tab( + child: Text( + 'My Favorites', + style: Theme.of(context).textTheme.displayLarge, + ), + ), + ], + ), + ), + body: const SafeArea( + minimum: EdgeInsets.all(16), + child: TabBarView( + children: [ + RestaurantListPageView(), + FavoritesListPageView(), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/pages.dart b/lib/pages/pages.dart new file mode 100644 index 0000000..9d6aaab --- /dev/null +++ b/lib/pages/pages.dart @@ -0,0 +1,2 @@ +export 'home_page.dart'; +export 'restaurant_list_page.dart'; diff --git a/lib/pages/restaurant_detail_page.dart b/lib/pages/restaurant_detail_page.dart new file mode 100644 index 0000000..08181bd --- /dev/null +++ b/lib/pages/restaurant_detail_page.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; +import 'package:restaurant_tour/widgets/widgets.dart'; +import '../theme/text.dart'; +import '../widgets/appbar.dart'; + +class RestaurantDetailPage extends StatelessWidget { + const RestaurantDetailPage({super.key, required this.restaurant}); + final Restaurant restaurant; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: RestaurantDetailAppBar( + restaurant: restaurant, + ), + body: SafeArea( + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return SingleChildScrollView( + child: Column( + children: [ + HeroImageWidget( + restaurant: restaurant, + width: constraints.maxWidth, + height: constraints.maxHeight * 0.4, + ), + Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BuildCategoryStatusRow(restaurant: restaurant), + const SizedBox( + height: spacingXL, + ), + buildDivider(), + const SizedBox( + height: spacingXL, + ), + Text( + 'Address', + style: Theme.of(context).textTheme.bodySmall, + textAlign: TextAlign.start, + ), + const SizedBox( + height: spacingXL, + ), + Text( + restaurant.location?.formattedAddress ?? + 'No Address Informed', + style: Theme.of(context).textTheme.displayLarge, + ), + const SizedBox( + height: spacingXL, + ), + buildDivider(), + restaurant.rating != null + ? BuildRatingSection( + restaurant: restaurant, + ) + : Container(), + ], + ), + ), + ], + ), + ); + }, + ), + ), + ); + } +} + +class BuildCategoryStatusRow extends StatelessWidget { + const BuildCategoryStatusRow({ + super.key, + required this.restaurant, + }); + + final Restaurant restaurant; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: PriceAndClassificationRowWidget( + restaurant: restaurant, + ), + ), + const Spacer(), + OpenStatusWidget( + isOpen: restaurant.isOpen, + ), + ], + ); + } +} diff --git a/lib/pages/restaurant_list_page.dart b/lib/pages/restaurant_list_page.dart new file mode 100644 index 0000000..6a0f6c9 --- /dev/null +++ b/lib/pages/restaurant_list_page.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:restaurant_tour/cubit/cubit.dart'; +import 'package:restaurant_tour/models/models.dart'; +import '../widgets/widgets.dart'; +import 'restaurant_detail_page.dart'; + +class RestaurantListPageView extends StatelessWidget { + const RestaurantListPageView({super.key}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return BlocBuilder( + buildWhen: (previous, current) => + current is LoadingRestaurantsState || + current is RestaurantsLoadedState || + current is ErrorState, + builder: (context, state) { + if (state is LoadingRestaurantsState) { + return const Center(child: CircularProgressIndicator()); + } else if (state is ErrorState) { + return Center( + child: Text(state.message), + ); + } + if (state is RestaurantsLoadedState || + state is FavoriteRestaurantState) { + return ListView.builder( + itemCount: state.result!.restaurants?.length, + itemBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + RestaurantListTile( + restaurant: state.result!.restaurants!.elementAt(index), + constraints: constraints, + ), + ], + ); + }, + ); + } + return Container(); + }, + ); + }, + ); + } +} + +class RestaurantListTile extends StatelessWidget { + const RestaurantListTile({ + super.key, + required this.restaurant, + required this.constraints, + }); + final Restaurant restaurant; + final BoxConstraints constraints; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RestaurantDetailPage(restaurant: restaurant), + ), + ); + }, + child: Container( + width: constraints.maxWidth, + margin: const EdgeInsets.only(bottom: 8, top: 8), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(9), + boxShadow: [ + BoxShadow( + color: Colors.grey.shade200, + blurRadius: 3, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + children: [ + Column( + children: [ + HeroImageWidget( + restaurant: restaurant, + width: 90, + height: 90, + borderRadius: 9.0, + ), + ], + ), + const Spacer( + flex: 1, + ), + Flexible( + flex: 10, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + restaurant.name ?? '', + maxLines: 2, + ), + const SizedBox( + height: 10, + ), + PriceAndClassificationRowWidget(restaurant: restaurant), + const SizedBox( + height: 10, + ), + RatingAndOpenStatusWidget(restaurant: restaurant) + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/repositories/info.txt b/lib/repositories/info.txt new file mode 100644 index 0000000..aa0ed85 --- /dev/null +++ b/lib/repositories/info.txt @@ -0,0 +1,41 @@ + /// 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.", + /// "text": "I love this place! The food is amazing and the service is great." + /// } + /// }, + /// { + /// "id": "okpO9hfpxQXssbTZTKq9hA", + /// "rating": 5, + /// "user": { + /// "id": "0x9xu_b0Ct_6hG6jaxpztw", + /// "image_url": "https:///s3-media3.fl.yelpcdn.com/photo/gjz8X6tqE3e4praK4HfCiA/o.jpg", + /// "name": "Crystal L.", + /// "text": "Greate place to eat" + /// } + /// }, + /// ... + /// ] + /// } + /// } + /// + /// \ No newline at end of file diff --git a/lib/repositories/repositories.dart b/lib/repositories/repositories.dart new file mode 100644 index 0000000..644e268 --- /dev/null +++ b/lib/repositories/repositories.dart @@ -0,0 +1 @@ +export 'yelp_repository.dart'; diff --git a/lib/repositories/yelp_repository.dart b/lib/repositories/yelp_repository.dart index 9eab02a..c49add0 100644 --- a/lib/repositories/yelp_repository.dart +++ b/lib/repositories/yelp_repository.dart @@ -1,111 +1,32 @@ -import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; +import 'dart:developer'; +import 'package:restaurant_tour/datasources/yielp_datasource.dart'; import 'package:restaurant_tour/models/restaurant.dart'; - -const _apiKey = ''; +import 'package:restaurant_tour/utils/sample_json.dart'; class YelpRepository { - late Dio dio; + final YielpDatasource datasource; - YelpRepository({ - @visibleForTesting Dio? dio, - }) : dio = dio ?? - Dio( - BaseOptions( - baseUrl: 'https://api.yelp.com', - headers: { - 'Authorization': 'Bearer $_apiKey', - 'Content-Type': 'application/graphql', - }, - ), - ); + YelpRepository({required this.datasource}); - /// 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.", - /// "text": "I love this place! The food is amazing and the service is great." - /// } - /// }, - /// { - /// "id": "okpO9hfpxQXssbTZTKq9hA", - /// "rating": 5, - /// "user": { - /// "id": "0x9xu_b0Ct_6hG6jaxpztw", - /// "image_url": "https:///s3-media3.fl.yelpcdn.com/photo/gjz8X6tqE3e4praK4HfCiA/o.jpg", - /// "name": "Crystal L.", - /// "text": "Greate place to eat" - /// } - /// }, - /// ... - /// ] - /// } - /// } - /// - Future getRestaurants({int offset = 0}) async { + RestaurantQueryResult getRestaurantsFromCache({int offset = 0}) { try { - final response = await dio.post>( - '/v3/graphql', - data: _getQuery(offset), - ); - return RestaurantQueryResult.fromJson(response.data!['data']['search']); + log('fetching from cache'); + return RestaurantQueryResult.fromJson(sample['data']['search']); } catch (e) { - return null; + throw Exception(); } } - String _getQuery(int offset) { - return ''' -query getRestaurants { - search(location: "Las Vegas", limit: 20, offset: $offset) { - total - business { - id - name - price - rating - photos - reviews { - id - rating - text - user { - id - image_url - name - } - } - categories { - title - alias - } - hours { - is_open_now - } - location { - formatted_address + Future getRestaurants({int offset = 0}) async { + try { + final data = await datasource.fetchRestaurants(offset); + if (data != null) { + return RestaurantQueryResult.fromJson(data['data']['search']); + } else { + return null; } + } catch (e) { + return null; } } } -'''; - } -} diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart new file mode 100644 index 0000000..586527e --- /dev/null +++ b/lib/theme/colors.dart @@ -0,0 +1,5 @@ +import 'package:flutter/material.dart'; + +class ThemeColors { + static const starColor = Color(0xFFFFB800); +} diff --git a/lib/theme/text.dart b/lib/theme/text.dart new file mode 100644 index 0000000..9fe8350 --- /dev/null +++ b/lib/theme/text.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class ThemeText { + static const textTheming = TextTheme( + displayLarge: TextStyle( + fontSize: 14, + fontFamily: 'Open Sans', + fontWeight: FontWeight.w600, + height: 1.5), + displayMedium: TextStyle(fontSize: 14, fontFamily: 'Open Sans'), + displaySmall: TextStyle( + fontSize: 12, + fontFamily: 'Open Sans', + fontStyle: FontStyle.italic, + ), + bodyMedium: TextStyle(fontSize: 16, height: 1.5), + bodySmall: TextStyle( + fontSize: 12, + fontFamily: 'Open Sans', + fontWeight: FontWeight.w400, + ), + bodyLarge: TextStyle( + fontSize: 16, + fontFamily: 'Open Sans', + fontWeight: FontWeight.w400, + ), + headlineLarge: TextStyle(fontSize: 24, fontWeight: FontWeight.w700), + headlineMedium: TextStyle(fontSize: 28, fontWeight: FontWeight.w700), + ); +} + +const spacingXL = 24.0; +const spacingM = 16.0; +const spacingSM = 8.0; diff --git a/lib/usecases/fetch_restaurants.dart b/lib/usecases/fetch_restaurants.dart new file mode 100644 index 0000000..ccf16d2 --- /dev/null +++ b/lib/usecases/fetch_restaurants.dart @@ -0,0 +1,20 @@ +import 'package:restaurant_tour/models/models.dart'; +import 'package:restaurant_tour/repositories/yelp_repository.dart'; + +class FetchRestaurants { + final YelpRepository repository; + + FetchRestaurants({required this.repository}); + Future getRestaurants() async => await repository + .getRestaurantsFromCache(); //CHANGE THIS LATER TO FETCH FROM THE REAL API + + Future getRestaurantsFromCache() async => + await repository.getRestaurantsFromCache(); + + Future> getFavoriteRestaurants() async { + // Simulate delay + await Future.delayed(const Duration(milliseconds: 500)); + + return ['vHz2RLtfUMVRPFmd7VBEHA']; + } +} diff --git a/lib/usecases/usecases.dart b/lib/usecases/usecases.dart new file mode 100644 index 0000000..31d21ed --- /dev/null +++ b/lib/usecases/usecases.dart @@ -0,0 +1 @@ +export './fetch_restaurants.dart'; diff --git a/lib/utils/get_random_avatar.dart b/lib/utils/get_random_avatar.dart new file mode 100644 index 0000000..1ab3e36 --- /dev/null +++ b/lib/utils/get_random_avatar.dart @@ -0,0 +1,21 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; + +ImageProvider getRandomAvatar() { + // Lista de avatares + final List avatars = [ + 'assets/avatars/av1.png', + 'assets/avatars/av2.png', + 'assets/avatars/av3.png', + 'assets/avatars/av4.png', + 'assets/avatars/av5.png', + 'assets/avatars/av6.png', + 'assets/avatars/av7.png', + ]; + + // Seleciona um índice aleatório + final randomIndex = Random().nextInt(avatars.length); + + // Retorna a imagem correspondente + return AssetImage(avatars[randomIndex]); +} diff --git a/lib/utils/sample_json.dart b/lib/utils/sample_json.dart new file mode 100644 index 0000000..620b5fa --- /dev/null +++ b/lib/utils/sample_json.dart @@ -0,0 +1,1148 @@ +const Map sample = { + "data": { + "search": { + "total": 7517, + "business": [ + { + "id": "vHz2RLtfUMVRPFmd7VBEHA", + "name": "Gordon Ramsay Hell's Kitchen", + "price": "\$\$\$", + "rating": 4.4, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/q771KjLzI5y638leJsnJnQ/o.jpg" + ], + "reviews": [ + { + "id": "VJCoQlkk4Fjac0OPoRP8HQ", + "rating": 5, + "text": + "Me and my husband came to celebrate my birthday here and it was a 10/10 experience. Firstly, I booked the wrong area which was the Gordon Ramsay pub and...", + "user": { + "id": "0bQNLf0POLTW4VhQZqOZoQ", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/i_0K5RUOQnoIw1c4QzHmTg/o.jpg", + "name": "Glydel L." + } + }, + { + "id": "EeCKH7eUVDsZv0Ii9wcPiQ", + "rating": 5, + "text": + "phenomenal! Bridgette made our experience as superb as the food coming to the table! would definitely come here again and try everything else on the menu,...", + "user": { + "id": "gL7AGuKBW4ne93_mR168pQ", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/iU1sA7y3dEEc4iRL9LnWQQ/o.jpg", + "name": "Sydney O." + } + }, + { + "id": "F88H5ow44AmiwisbrbswPw", + "rating": 5, + "text": + "This entire experience is always so amazing. Every single dish is cooked to perfection. Every beef dish was so tender. The desserts were absolutely...", + "user": { + "id": "y742Fi1jF_JAqq5sRUlLEw", + "image_url": + "https://s3-media2.fl.yelpcdn.com/photo/rEWek1sYL0F35KZ0zRt3sw/o.jpg", + "name": "Ashley L." + } + } + ], + "categories": [ + {"title": "New American", "alias": "newamerican"}, + {"title": "Seafood", "alias": "seafood"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "3570 Las Vegas Blvd S\nLas Vegas, NV 89109" + } + }, + { + "id": "faPVqws-x-5k2CQKDNtHxw", + "name": "Yardbird", + "price": "\$\$", + "photos": [ + "https://s3-media1.fl.yelpcdn.com/bphoto/xYJaanpF3Dl1OovhmpqAYw/o.jpg" + ], + "reviews": [ + { + "id": "CN9oD1ncHKZtsGN7U1EMnA", + "rating": 5, + "text": + "The food was delicious and the host and waitress were very nice, my husband and I really loved all the food, their cocktails are also amazing.", + "user": { + "id": "HArOfrshTW9s1HhN8oz8rg", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/4sDrkYRIZxsXKCYdo9d1bQ/o.jpg", + "name": "Snow7 C." + } + }, + { + "id": "cqMrOWT9kRQOt3VUqOUbHg", + "rating": 5, + "text": + "Our last meal in Vegas was amazing at Yardbird. We have been to the Yardbird in Chicago so we thought we knew what to expect; however, we were blown away by...", + "user": { + "id": "10oig4nwHnOAnAApdYvNrg", + "image_url": null, + "name": "Ellie K." + } + }, + { + "id": "XpTuzInVGZ2Dmx5BX21aqA", + "rating": 4, + "text": + "The food was great and the atmosphere was fun! Not over the top or too fancy. Perfect for lunch or dinner.", + "user": { + "id": "kp40ga5HSczQfP5TDlzjPQ", + "image_url": null, + "name": "Tacie A." + } + } + ], + "categories": [ + {"title": "Southern", "alias": "southern"}, + {"title": "New American", "alias": "newamerican"}, + {"title": "Cocktail Bars", "alias": "cocktailbars"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "3355 Las Vegas Blvd S\nLas Vegas, NV 89109" + } + }, + { + "id": "syhA1ugJpyNLaB0MiP19VA", + "name": "888 Japanese BBQ", + "price": "\$\$\$", + "rating": 4.8, + "photos": [ + "https://s3-media1.fl.yelpcdn.com/bphoto/V_zmwCUG1o_vR29xfkb-ng/o.jpg" + ], + "reviews": [ + { + "id": "mb9gfnkSopq00f4LBZVPig", + "rating": 5, + "text": + "Food service and Ambiance are so high quality.povw and always come back every other week .", + "user": { + "id": "AKEHRiPmlrwKHxiiJlLGEQ", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/GdoKcKDBW0fWQ4To-X_clA/o.jpg", + "name": "Mellon D." + } + }, + { + "id": "e06Cw1RLUsjZw5tgNm-J8Q", + "rating": 5, + "text": + "Best bbq off the strip service was amazing and staff were friendly! Will definitely come back for more good eats!", + "user": { + "id": "tTocbjs4Whp_cUpvBceWWQ", + "image_url": null, + "name": "Banny C." + } + }, + { + "id": "DJRN1M6nsBL19HiX2Tkyug", + "rating": 5, + "text": + "Amazing experience. Would recommend to anyone visiting Lad Vegas. Wow! The service was super and very friendly. Definitely worth a try!", + "user": { + "id": "pMxm5enXAlT77jbyElGMjQ", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/Z43vPbN4hxV33IpMQClX0A/o.jpg", + "name": "Angela W." + } + } + ], + "categories": [ + {"title": "Barbeque", "alias": "bbq"}, + {"title": "Japanese", "alias": "japanese"} + ], + "hours": [ + {"is_open_now": false} + ], + "location": { + "formatted_address": "3550 S Decatur Blvd\nLas Vegas, NV 89103" + } + }, + { + "id": "QXV3L_QFGj8r6nWX2kS2hA", + "name": "Nacho Daddy", + "price": "\$\$", + "rating": 4.4, + "photos": [ + "https://s3-media4.fl.yelpcdn.com/bphoto/pu9doqMplB5x5SEs8ikW6w/o.jpg" + ], + "reviews": [ + { + "id": "JU_T9FlCGKVBulGEI-4OHg", + "rating": 5, + "text": + "Alyssa was amazing! The food and atmosphere were great! Definitely will be coming back!", + "user": { + "id": "CgVBZnioGBPgNLxq3z1E8Q", + "image_url": null, + "name": "Jazmin G." + } + }, + { + "id": "CifTHQgZ8L5IJc-dB_bizQ", + "rating": 5, + "text": + "Damn good! Xina was wonderful. 5 stars. Got some great nachos and drinks. Excellent vegan menu.", + "user": { + "id": "zT0QrkMBUGj4DqSye8LnCQ", + "image_url": null, + "name": "Mark T." + } + }, + { + "id": "-MAq_q1q6Vgk1_RQjNCdjA", + "rating": 5, + "text": + "This is my first time at nacho daddy's and My server Shannon was amazing. She came to our table fast and i love how on top of it she was to get our drinks...", + "user": { + "id": "MWZcvuYNlFq_UPJX-NyWLw", + "image_url": null, + "name": "Desiree L." + } + } + ], + "categories": [ + {"title": "New American", "alias": "newamerican"}, + {"title": "Mexican", "alias": "mexican"}, + {"title": "Breakfast & Brunch", "alias": "breakfast_brunch"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "3663 Las Vegas Blvd\nSte 595\nLas Vegas, NV 89109" + } + }, + { + "id": "rdE9gg0WB7Z8kRytIMSapg", + "name": "Lazy Dog Restaurant & Bar", + "price": "\$\$", + "rating": 4.5, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/_Wz-fNXawmbBinSf9Ev15g/o.jpg" + ], + "reviews": [ + { + "id": "SlDFYj2sdxeeAIibIPJLFg", + "rating": 5, + "text": + "Great food! Service was excellent. Had the Alfredo chicken and it hit the spot. The manager stopped by and made literally conversation. Her name was...", + "user": { + "id": "XcNwCjkxQ-A-ErsAxm2D9g", + "image_url": null, + "name": "Natalie C." + } + }, + { + "id": "WCQ3d28zbUGg4n2D2auUbw", + "rating": 5, + "text": + "What a great experience from our waiter Anthony F.! He saw our daughter was icing her knee after a grueling tournament and was the first to ask if we needed...", + "user": { + "id": "0MKASbKsZ4imiq_ugtt8NQ", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/rCz78mQ16LBbmADoOE9A3Q/o.jpg", + "name": "Barbara S." + } + }, + { + "id": "Gfj013WbLQo3sADlG1mXUQ", + "rating": 5, + "text": + "By far the BEST restaurant that we have been to while visiting Vegas. Food, atmosphere and customer service was phenomenal! Tina was our server, absolutely...", + "user": { + "id": "1TUNMdokzFDEa1SmZ-F3Dw", + "image_url": null, + "name": "Tasha R." + } + } + ], + "categories": [ + {"title": "New American", "alias": "newamerican"}, + {"title": "Comfort Food", "alias": "comfortfood"}, + {"title": "Burgers", "alias": "burgers"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "6509 S Las Vegas Blvd\nLas Vegas, NV 89119" + } + }, + { + "id": "3kdSl5mo9dWC4clrQjEDGg", + "name": "Egg & I", + "price": "\$\$", + "rating": 4.5, + "photos": [ + "https://s3-media1.fl.yelpcdn.com/bphoto/z4rdxoc6xaM4dmdPovPBDg/o.jpg" + ], + "reviews": [ + { + "id": "P6yj97cmXiHzXJV3B0L7xA", + "rating": 5, + "text": + "The food was delicious and just how i ordered it. I had the chicken sandwich with fries. Our waitress was Crystal, even though it was only her fourth day...", + "user": { + "id": "SxuhAYL2h6441S51H9tlmQ", + "image_url": null, + "name": "Bhlasian G." + } + }, + { + "id": "YLY6BcCx-APSr67Xfd6aJg", + "rating": 5, + "text": + "I came here after reading the reviews. When l checked in l received the banana muffin on the house, it was delicious. I ordered the original burrito and my...", + "user": { + "id": "3RCz-zMjeNEqsrQE43-vIA", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/lG-jJexCutZZi2hazBgH3Q/o.jpg", + "name": "Vicky K." + } + }, + { + "id": "7pOCQU3F1US1T-FpcmTXtA", + "rating": 4, + "text": + "Born and raised in Vegas and for some reason I've never been here. Finally decided to get some \"to go\" sandwiches for breakfast. \n\nOver \$40 for an egg...", + "user": { + "id": "keBv05MsMFBd0Hu98vXThQ", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/IKQxIIRJWxecyHIkv1xw4g/o.jpg", + "name": "Jesse L." + } + } + ], + "categories": [ + {"title": "Breakfast & Brunch", "alias": "breakfast_brunch"}, + {"title": "Burgers", "alias": "burgers"}, + {"title": "American", "alias": "tradamerican"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "4533 W Sahara Ave\nSte 5\nLas Vegas, NV 89102" + } + }, + { + "id": "gOOfBSBZlffCkQ7dr7cpdw", + "name": "CHICA", + "price": "\$\$", + "rating": 4.3, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/FxmtjuzPDiL7vx5KyceWuQ/o.jpg" + ], + "reviews": [ + { + "id": "xXQzEfd0czYwW_PW_QW1RQ", + "rating": 5, + "text": + "Came here with a group of 8 for brunch and we all had a wonderful experience. Our waitress, Karena, was amazing! She was super attentive and such a good...", + "user": { + "id": "A8wuelxCSNiuS6IFY6WKbw", + "image_url": null, + "name": "Joanna M." + } + }, + { + "id": "k0mR3x34X9bXMZfyTsO8nQ", + "rating": 5, + "text": + "The food was amazing. I had the Latin breakfast. Our table shared the donuts...delicious. We had drinks and they were made with fresh ingredients. They...", + "user": { + "id": "47SO7vTL6Louu9Gbkq8UeA", + "image_url": null, + "name": "Brandi T." + } + }, + { + "id": "pLdKzOPNlHmonm08eRP6Fw", + "rating": 5, + "text": + "Every dish was amazing. The parrillada is a must have to share. The sides are also great. I love the corn too. And the famous flaming skull dessert is both...", + "user": { + "id": "IE8XGvC-2fzE0lTltk-46Q", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/vOMr4EWJNoDbKR-lrDSfbg/o.jpg", + "name": "Fabian L." + } + } + ], + "categories": [ + {"title": "Latin American", "alias": "latin"}, + {"title": "Breakfast & Brunch", "alias": "breakfast_brunch"}, + {"title": "Cocktail Bars", "alias": "cocktailbars"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "3355 South Las Vegas Blvd\nSte 106\nLas Vegas, NV 89109" + } + }, + { + "id": "JPfi__QJAaRzmfh5aOyFEw", + "name": "Shang Artisan Noodle - Flamingo Road", + "price": "\$\$", + "rating": 4.6, + "photos": [ + "https://s3-media3.fl.yelpcdn.com/bphoto/TqV2TDWH-7Wje5B9Oh1EZw/o.jpg" + ], + "reviews": [ + { + "id": "GcGUAH0FPeyfw7rw7eu2Sg", + "rating": 5, + "text": + "Best beef noodle soup I've ever had. Portion sizes huge. Family of 5 could have shared 3 bowls with some appetizers. Spicy wonton and beef dumplings were...", + "user": { + "id": "4H2AFePQc7B4LGWhGkAb2g", + "image_url": null, + "name": "AA K." + } + }, + { + "id": "JZPALhqqab576i9xk80tgQ", + "rating": 5, + "text": + "Great restaurant with authentic flavors and everything is made from scratch! . Great service and very popular with the Asian community", + "user": { + "id": "MmOJaZ2cPwguz6bPTYdfWQ", + "image_url": null, + "name": "Squadron F." + } + }, + { + "id": "oJGyfL7kGuV43Z5kIceYZg", + "rating": 5, + "text": + "Every time I plan a Vegas trip, this is one of my spots I NEVER MISS. \n\nEach bowl from Shang has so much flavor, and the noodles are so chewy. They have a...", + "user": { + "id": "DJiwsCAKTvybSyOUvm_Avw", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/STrtsOu9xOs5ypKYPFMChQ/o.jpg", + "name": "Annie T." + } + } + ], + "categories": [ + {"title": "Noodles", "alias": "noodles"}, + {"title": "Chinese", "alias": "chinese"}, + {"title": "Soup", "alias": "soup"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "4983 W Flamingo Rd\nSte B\nLas Vegas, NV 89103" + } + }, + { + "id": "2iTsRqUsPGRH1li1WVRvKQ", + "name": "Carson Kitchen", + "price": "\$\$", + "rating": 4.5, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/LhaPvLHIrsHu8ZMLgV04OQ/o.jpg" + ], + "reviews": [ + { + "id": "PzKQYLK6skSfAUP73P8YXQ", + "rating": 5, + "text": + "Our son gave his mother a birthday gift of a meal at Carson Kitchen. He's the kind of guy that does thorough reviews on everything he's interested in...", + "user": { + "id": "Cvlm-uNVOY2i5zPWQdLupA", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/ZT4s2popID75p_yJbo1xjg/o.jpg", + "name": "Bill H." + } + }, + { + "id": "pq6VEb97OpbB-KwvsJVyfw", + "rating": 4, + "text": + "Came here during my most recent Vegas trip and was intrigued by the menu options! There's a parking lot close by (pay by the booth) but since I came on a...", + "user": { + "id": "TMeT1a_1MJLOYobdY6Bs-A", + "image_url": + "https://s3-media2.fl.yelpcdn.com/photo/CxCo55gIOATctXc5wLa5CQ/o.jpg", + "name": "Amy E." + } + }, + { + "id": "5LF6EKorAR01mWStVYmYBw", + "rating": 4, + "text": + "The service and the atmosphere were amazing! Our server was very knowledgeable about the menu and helped guide our selections. We tired five different...", + "user": { + "id": "a71YY9h3GRv7F-4_OGGiRQ", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/3EDvhfkljrLyodxSrn8Fqg/o.jpg", + "name": "May G." + } + } + ], + "categories": [ + {"title": "New American", "alias": "newamerican"}, + {"title": "Desserts", "alias": "desserts"}, + {"title": "Cocktail Bars", "alias": "cocktailbars"} + ], + "hours": [ + {"is_open_now": false} + ], + "location": { + "formatted_address": "124 S 6th St\nSte 100\nLas Vegas, NV 89101" + } + }, + { + "id": "igHYkXZMLAc9UdV5VnR_AA", + "name": "Echo & Rig", + "price": "\$\$\$", + "rating": 4.4, + "photos": [ + "https://s3-media1.fl.yelpcdn.com/bphoto/Q9swks1BO-w-hVskIHrCVg/o.jpg" + ], + "reviews": [ + { + "id": "S8KsRL4i-Qa4OQDpvTYy8Q", + "rating": 5, + "text": + "I had a delightful meal at Echo & Rig, enjoying the filet mignon with a salad and a side of fries. The steak was perfectly cooked and incredibly tender,...", + "user": { + "id": "VZdaG5_PQq6W_5TTbLPSog", + "image_url": null, + "name": "S R." + } + }, + { + "id": "HoUKWihuu-DVt77nA6QI5w", + "rating": 5, + "text": + "I've been here several times. Today I was meeting up with a friend and decided to sit at the bar downstairs to wait for him. I was pleasantly greeted by the...", + "user": { + "id": "cMZVJdi_fKG7rp1no_2ZvA", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/4NcP4S6RklA6sT8Zp2JCfw/o.jpg", + "name": "Joshua S." + } + }, + { + "id": "JId3KfBAwJTv3OqEoS-V9A", + "rating": 5, + "text": + "We had reservations very early at 5.30pm\nThis places is so large however super busy and popular.\nThe food and service are excellent. The only things that...", + "user": { + "id": "1v2UxV8i_Ae64yxT17rRRg", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/DxCDFZrPLOdc6o5CddaAoQ/o.jpg", + "name": "Apple S." + } + } + ], + "categories": [ + {"title": "Steakhouses", "alias": "steak"}, + {"title": "Butcher", "alias": "butcher"}, + {"title": "Tapas/Small Plates", "alias": "tapasmallplates"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "440 S Rampart Blvd\nLas Vegas, NV 89145" + } + }, + { + "id": "SVGApDPNdpFlEjwRQThCxA", + "name": "Juan's Flaming Fajitas & Cantina - Tropicana", + "price": "\$\$", + "rating": 4.6, + "photos": [ + "https://s3-media3.fl.yelpcdn.com/bphoto/a8L9bQZ2XW8etXLomKKdDw/o.jpg" + ], + "reviews": [ + { + "id": "QrqQBcKENE83z4sWBdQyrg", + "rating": 5, + "text": + "My FAVORITE Mexican food in Vegas!! From the service to the food I'd give 5 stars all around!!! Everyone is always friendly and welcoming!!! \n\nI can't ever...", + "user": { + "id": "YvEyOqT0PUyFwZ9NUj_A0A", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/kra2v9Z6sd7W9S1enUDJrA/o.jpg", + "name": "Christy G." + } + }, + { + "id": "apS-lxKwHo2yJBnk0a8Phw", + "rating": 5, + "text": + "One of my favorite places in town. It's always crowded, so it's best to use the Yelp reservation system. Parking can be a toughie too. It's located in the...", + "user": { + "id": "yPVnXy6Zf3LefjcbjOABDQ", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/6sCVZwFwdpUhElhUKTbJ6g/o.jpg", + "name": "Cecille L." + } + }, + { + "id": "4nTGQhV8vMDNS-V3nF99VA", + "rating": 5, + "text": + "I've been here a couple times and each time so far it has been amazing! From food to service - everything was great. \n\nOn my last visit we ordered the...", + "user": { + "id": "ayOlP5JtyF8RwtB0opJzrw", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/qrPJ560WVlLPJ8FFbrJlWg/o.jpg", + "name": "Tia G." + } + } + ], + "categories": [ + {"title": "Mexican", "alias": "mexican"}, + {"title": "Breakfast & Brunch", "alias": "breakfast_brunch"}, + {"title": "Cocktail Bars", "alias": "cocktailbars"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "9640 W Tropicana\nSte 101\nLas Vegas, NV 89147" + } + }, + { + "id": "4JNXUYY8wbaaDmk3BPzlWw", + "name": "Mon Ami Gabi", + "price": "\$\$\$", + "rating": 4.2, + "photos": [ + "https://s3-media3.fl.yelpcdn.com/bphoto/FFhN_E1rV0txRVa6elzcZw/o.jpg" + ], + "reviews": [ + { + "id": "baBnM1ontpOLgoeu2xv6Wg", + "rating": 5, + "text": + "the breakfast was amazing, possibly the best french toast i've ever eaten. i'd love to try more items in the future, super appetizing. ate an entire french...", + "user": { + "id": "xSvgz_-dtVa_GINcR85wzA", + "image_url": null, + "name": "Lilly H." + } + }, + { + "id": "Lg_j2vG2CTR8A5NGM7Zqhw", + "rating": 5, + "text": + "We recently ate at this French restaurant for the first time, and it was an amazing experience. Initially, we were eager to sit outside to enjoy the view of...", + "user": { + "id": "pgvFEonlrCa1BCmDg_dofQ", + "image_url": + "https://s3-media2.fl.yelpcdn.com/photo/xMn5z_xxJt_Qq3_PvTZ__g/o.jpg", + "name": "Chul L." + } + }, + { + "id": "Kiv-lCuE-VBUMR1V_fM6HA", + "rating": 5, + "text": + "Although I had a mixed experience at the Paris hotel, Mon Ami Gabi was a pleasant surprise. Given how overpriced some of the items were in the hotel shops I...", + "user": { + "id": "YnSxh5AC2EtmOyg2XqcqPQ", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/u9kYpEYZlmkg3sWWIqDtyA/o.jpg", + "name": "Jon L." + } + } + ], + "categories": [ + {"title": "French", "alias": "french"}, + {"title": "Steakhouses", "alias": "steak"}, + {"title": "Breakfast & Brunch", "alias": "breakfast_brunch"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "3655 Las Vegas Blvd S\nLas Vegas, NV 89109" + } + }, + { + "id": "UidEFF1WpnU4duev4fjPlQ", + "name": "Therapy ", + "price": "\$\$", + "rating": 4.3, + "photos": [ + "https://s3-media3.fl.yelpcdn.com/bphoto/otaMuPtauoEb6qZzmHlAlQ/o.jpg" + ], + "reviews": [ + { + "id": "a3UISKdTa1aMxok4mmzNsQ", + "rating": 5, + "text": + "Step into Therapy and take a sit, Chris the bartender is pretty chill. Talking to him is like talking to a long time friend, the type you don't see for a...", + "user": { + "id": "SbMQm6pAPRwg04y44S5zLA", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/3ZuAxm31p7iwQ_zV2lgWOA/o.jpg", + "name": "Vittor V." + } + }, + { + "id": "hfZ-9d6Xxztb9o-cEJmR7Q", + "rating": 5, + "text": + "The food and drinks great! Try the loaded crab fries ~ got seated and served quick- Dallas was the best!", + "user": { + "id": "7_uRkPfh8fvewEHDnhx6mg", + "image_url": null, + "name": "Patricia L." + } + }, + { + "id": "oSb7UzeaA-r_H8WgJYWH3A", + "rating": 5, + "text": + "I come here once in a while. I had a great time spent here dancing on tables! I came for happy hour during the day and had so much fun! They have a great...", + "user": { + "id": "zPRZtDcBqN0VjXDlkYK3cQ", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/4ZS87l3TNsOLSF6-Piqi_A/o.jpg", + "name": "Brenda B." + } + } + ], + "categories": [ + {"title": "Bars", "alias": "bars"}, + {"title": "New American", "alias": "newamerican"}, + {"title": "Dance Clubs", "alias": "danceclubs"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "518 Fremont St\nLas Vegas, NV 89101" + } + }, + { + "id": "QCCVxVRt1amqv0AaEWSKkg", + "name": "Esther's Kitchen", + "price": "\$\$", + "rating": 4.5, + "photos": [ + "https://s3-media3.fl.yelpcdn.com/bphoto/uk6-4u8H6BpxaJAKDEzFOA/o.jpg" + ], + "reviews": [ + { + "id": "VjmUIlp_Y0_0ISEjqZvKAw", + "rating": 5, + "text": + "Our server Josh was AMAZING! He was so attentive and sweet I've been to their on location and the new one does not disappoint. I tried something new...", + "user": { + "id": "59qcS7L8sHAaxziIg4_i5A", + "image_url": null, + "name": "Caitlin S." + } + }, + { + "id": "54vX-IPr0HmraBhjhNJh2g", + "rating": 5, + "text": + "Esther's Kitchen is a wonderful find, especially for locals who want a variety of good freshly made food at an affordable price. Some dishes/pottery they...", + "user": { + "id": "Uw9yxT40cGDCWI0AffnzdA", + "image_url": null, + "name": "Gigi O." + } + }, + { + "id": "SFLBVnLVHLI5c7Vdb2l5yg", + "rating": 5, + "text": + "Certainly! Here is a sample review for Esther's Kitchen:\n\n---\n\n**Esther's Kitchen - A Hidden Gem in the Heart of the City**\n\nEsther's Kitchen is a...", + "user": { + "id": "A59MiJ0LjgQIWWN1HdeI7A", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/f-xW9DAGYO8jXFKqW3NmFA/o.jpg", + "name": "Alfonso A." + } + } + ], + "categories": [ + {"title": "Italian", "alias": "italian"}, + {"title": "Pizza", "alias": "pizza"}, + {"title": "Cocktail Bars", "alias": "cocktailbars"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "1131 S Main St\nLas Vegas, NV 89104" + } + }, + { + "id": "mU3vlAVzTxgmZUu6F4XixA", + "name": "Momofuku", + "price": "\$\$", + "rating": 4.1, + "photos": [ + "https://s3-media1.fl.yelpcdn.com/bphoto/mB1g53Nqa62Q04u4oNuCSw/o.jpg" + ], + "reviews": [ + { + "id": "mAEPxxFflcYD6ZtzvnxzKg", + "rating": 3, + "text": + "Service subpar. Lamb was average. Pork belly for kids bad. Overall not worth the prices.", + "user": { + "id": "s4qyTcSQtHzlW8O4nm867A", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/lbb5PhyDftjXRuTV8mdBsA/o.jpg", + "name": "Jon L." + } + }, + { + "id": "40BE2te-wIXkc3xevcp4Ew", + "rating": 3, + "text": + "Service is pretty good.\n\nFor food, ordered corn rib, and it was fantastic. The ramen was just so so: mushroom ramen was too salty. kid ordered the other...", + "user": { + "id": "Dk68URVdrfDzQJvghTs9nA", + "image_url": null, + "name": "Peng Z." + } + }, + { + "id": "2OEMhHcFTfJ2rL_e-61-5w", + "rating": 5, + "text": + "Food was just perfect. We tried few different buns and noodles as well as what you see in the pics. My favorite was temaki and iberico pork. Temaki comes...", + "user": { + "id": "qwH-93rwVUExvTyK8QLxjQ", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/rwXdDb1vh2sK61MzYB-fyQ/o.jpg", + "name": "Lin K." + } + } + ], + "categories": [ + {"title": "New American", "alias": "newamerican"}, + {"title": "Asian Fusion", "alias": "asianfusion"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "3708 Las Vegas Blvd S\nLevel 2\nBoulevard Tower\nLas Vegas, NV 89109" + } + }, + { + "id": "fL-b760btOaGa85OJ9ut3w", + "name": "Rollin Smoke Barbeque", + "price": "\$\$", + "rating": 4.4, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/j6pMPJziv3-_Jzl1bRaMSw/o.jpg" + ], + "reviews": [ + { + "id": "bfnaYmZN0RPzutYSO9tPfg", + "rating": 5, + "text": + "This food was so good it made me write my first ever review. Absolutely delicious. My wife said it was the best mac and cheese she's ever had, and my wife...", + "user": { + "id": "nLXyYA1LoLEtARCBiB6xYA", + "image_url": null, + "name": "Nicholas M." + } + }, + { + "id": "eWVrb6J2OuQ0y8g2-Mm1_g", + "rating": 5, + "text": + "Rollin Smoke BBQ is that spot, for real! This place got the Arkansas Razorbacks vibe on lock, and the food? Straight fire! Let's break it down:\n\n**Ribs:**...", + "user": { + "id": "ZYe4DVeqMNMjJZVLil8yMw", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/0THv8LUjv9SkKLsZWxCqcA/o.jpg", + "name": "Andre W." + } + }, + { + "id": "nuyFJ8XQse6g5pGC5dC7tg", + "rating": 5, + "text": + "5 of 5\nWe celebrate a friend b day from Santa ana\nFamily style barbecue \nMy friend brought the mariachi too\nBbq was delicious \nHappy bday mr Peter's", + "user": { + "id": "uB61MiqCg_VV9wzDtCK7Gg", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/Ux0PYdEc31Y19vpo1rUk_w/o.jpg", + "name": "Araceli C." + } + } + ], + "categories": [ + {"title": "Barbeque", "alias": "bbq"}, + {"title": "Southern", "alias": "southern"}, + {"title": "Sandwiches", "alias": "sandwiches"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "3185 S Highland Dr\nSte 2\nLas Vegas, NV 89109" + } + }, + { + "id": "So132GP_uy3XbGs0KNyzyw", + "name": "Casa Di Amore", + "price": "\$\$", + "rating": 4.4, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/7Yu5-1ZOYYUgZaXcdz0K9w/o.jpg" + ], + "reviews": [ + { + "id": "k8JeqKM1ehBLiiZda8fcZw", + "rating": 5, + "text": + "The service & food was great!! I recommend you try it!! Or just have a drink at the BAR!! lol", + "user": { + "id": "nXxoKg7AMpiaZIDNeMcgwA", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/K-7O4xXLqHi6TGT-DWzr_g/o.jpg", + "name": "Lina A." + } + }, + { + "id": "_U9jbY372Ml8MPay9-OuGA", + "rating": 5, + "text": + "The service! The food! Both so amazing, on top of that we have a live performance of the piano such a vibe, would definitely recommend this place if your in...", + "user": { + "id": "iaGEMG7rXGp6AYM-GAjF_Q", + "image_url": null, + "name": "Amy C." + } + }, + { + "id": "hg0Q990LcQTzAF2aNmDK5w", + "rating": 5, + "text": + "The food and service were great! What a fun place. Randy Thomas the piano play...what a talent. He made our night out exceptional.", + "user": { + "id": "Z4Xjsime8D-qkFU12PmdaA", + "image_url": null, + "name": "Heidi M." + } + } + ], + "categories": [ + {"title": "Italian", "alias": "italian"}, + {"title": "Seafood", "alias": "seafood"}, + {"title": "Pizza", "alias": "pizza"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": "2850 E Tropicana Ave\nLas Vegas, NV 89121" + } + }, + { + "id": "nUpz0YiBsOK7ff9k3vUJ3A", + "name": "Buddy V's Ristorante", + "price": "\$\$", + "rating": 4.2, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/cQxDwddn5H6c8ZGBQnjwnQ/o.jpg" + ], + "reviews": [ + { + "id": "v9pnz6eqN7f2STYctnBBlQ", + "rating": 3, + "text": + "The dining experience left much to be desired. The food was subpar, with a notable texture issue in the carbonara; the pieces of crunchy crumbled pancetta...", + "user": { + "id": "Ux0VVNN8ICTmJbuWlW2djg", + "image_url": null, + "name": "Nathan T." + } + }, + { + "id": "JGb9E8nERjsNFM2F7SqCNA", + "rating": 5, + "text": + "Great food and great service.\nNice location.. they have outdoor and indoor seating.\nMeatballs are highly recommended!", + "user": { + "id": "loDGoLca5JC6dARvBQCUmg", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/It7kRVx2aq3EPC9amExlPA/o.jpg", + "name": "Daniel V." + } + }, + { + "id": "37kIixegf3pTb3jb6i1Y5g", + "rating": 3, + "text": + "Overall, the restaurant was average. The calamari was the redeeming aspect since it was one of the best I had, so make sure to get that (Hoboken style, as...", + "user": { + "id": "IAOAGReoxWaxhZm5-EpmOg", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/YI-5O4mLRjh3-o0keMuzbA/o.jpg", + "name": "Juliet M." + } + } + ], + "categories": [ + {"title": "Italian", "alias": "italian"}, + {"title": "American", "alias": "tradamerican"}, + {"title": "Wine Bars", "alias": "wine_bars"} + ], + "hours": [ + {"is_open_now": false} + ], + "location": { + "formatted_address": "3327 S Las Vegas Blvd\nLas Vegas, NV 89109" + } + }, + { + "id": "FNe5PPA9pyj8FjcDefCBpg", + "name": "Weera Thai Restaurant - Sahara", + "price": "\$\$", + "rating": 4.4, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/TOPFVZGJtaLJI_-Vyq078A/o.jpg" + ], + "reviews": [ + { + "id": "ETb_y4-gKokiY186fQ1dvw", + "rating": 5, + "text": + "We always come here when visiting Las Vegas. The food is excellent, pork Skewers are well seasoning!! \nBest pad thai and Thai Fried Rice \n\nMango sticky rice...", + "user": { + "id": "UMW1Q7If-YnfitQf271S-Q", + "image_url": + "https://s3-media3.fl.yelpcdn.com/photo/INM0Uxdt1pYMKuiryDWM_g/o.jpg", + "name": "Jesse P." + } + }, + { + "id": "cE_Ah4sxgfBquOSPTuqirQ", + "rating": 5, + "text": + "If you're a fan of Thai food, you absolutely need to check out this place. The pork belly appetizer is an absolute must-have--lightly breaded and bursting...", + "user": { + "id": "zLDkdRzxLJgFEHFFV52fbQ", + "image_url": + "https://s3-media2.fl.yelpcdn.com/photo/jDM2-0g29Ms_CoypGa78XA/o.jpg", + "name": "Kai T." + } + }, + { + "id": "VxDE--0BUyLY0daIiwZfJg", + "rating": 5, + "text": + "Of all our trips to Las Vegas, I am most delighted to have had discovered this hidden gem this trip and why did I not discover this location earlier! All of...", + "user": { + "id": "Kuc7KAZBnrW5O_C-Dn-r_Q", + "image_url": + "https://s3-media2.fl.yelpcdn.com/photo/UIQLPO2XlV4A2WbaWVIKmA/o.jpg", + "name": "Celia Z." + } + } + ], + "categories": [ + {"title": "Thai", "alias": "thai"}, + {"title": "Bars", "alias": "bars"} + ], + "hours": [ + {"is_open_now": true} + ], + "location": { + "formatted_address": + "3839 W Sahara Ave\nSte 7-9\nLas Vegas, NV 89102" + } + }, + { + "id": "-1m9o3vGRA8IBPNvNqKLmA", + "name": "Bavette's Steakhouse & Bar", + "price": "\$\$\$\$", + "rating": 4.5, + "photos": [ + "https://s3-media2.fl.yelpcdn.com/bphoto/pgcnYRHtbw_x_-OG8K4xVg/o.jpg" + ], + "reviews": [ + { + "id": "PbKZJlLCWVcnHLUV0AK45g", + "rating": 5, + "text": + "For a great dining experience look no further!\n\nBavette's has it all; delicious food, fantastic cocktails, and a service staff above them all.\n\nWe were a...", + "user": { + "id": "IJxjNg4fMDar8WTcY_s1NQ", + "image_url": + "https://s3-media1.fl.yelpcdn.com/photo/DN4xv1FYk_5yvPBhydRZGg/o.jpg", + "name": "Lisha K." + } + }, + { + "id": "Bk8AQJD8APVBWR6Y_Opvpw", + "rating": 5, + "text": + "First time at Bavettes and not sure what took us so long. Upon entry you feel whisked into a whole other atmosphere from the casino. The dark woods and...", + "user": { + "id": "c1sHJlr0MizIANx49BTXWQ", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/y9JnzleHF9G9Lx6EHIu8SA/o.jpg", + "name": "Alyssa Y." + } + }, + { + "id": "wmraj3HVLF7IO5FLdPD6oQ", + "rating": 5, + "text": + "10/10 service and hospitality. Ambiance is perfect for a late night dinner with a partner or even family. We definitely enjoyed ourselves here and enjoyed...", + "user": { + "id": "MZJmPApqOQ-St4II1lDnLg", + "image_url": + "https://s3-media4.fl.yelpcdn.com/photo/CTnyY9AwmIc5Rcb-r8BRaQ/o.jpg", + "name": "Kalia A." + } + } + ], + "categories": [ + {"title": "Steakhouses", "alias": "steak"}, + {"title": "Bars", "alias": "bars"}, + {"title": "New American", "alias": "newamerican"} + ], + "hours": [ + {"is_open_now": false} + ], + "location": { + "formatted_address": "3770 Las Vegas Blvd S\nLas Vegas, NV 89109" + } + } + ] + } + } +}; diff --git a/lib/widgets/appbar.dart b/lib/widgets/appbar.dart new file mode 100644 index 0000000..f32b220 --- /dev/null +++ b/lib/widgets/appbar.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:restaurant_tour/cubit/restaurant_cubit.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; + +class RestaurantDetailAppBar extends StatelessWidget + implements PreferredSizeWidget { + final Restaurant restaurant; + + const RestaurantDetailAppBar({super.key, required this.restaurant}); + + @override + Widget build(BuildContext context) { + final isFavorite = context + .watch() + .state + .favoriteRestaurants + .contains(restaurant.id); + return AppBar( + title: Text(restaurant.name ?? ''), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 13.0), + child: IconButton( + icon: isFavorite + ? const Icon(Icons.favorite) + : const Icon(Icons.favorite_border_outlined), + onPressed: () => context + .read() + .favoriteAResturant(restaurant.id!), + ), + ), + ], + ); + } + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} diff --git a/lib/widgets/divider.dart b/lib/widgets/divider.dart new file mode 100644 index 0000000..17c603e --- /dev/null +++ b/lib/widgets/divider.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +Divider buildDivider() { + return const Divider( + thickness: 0.3, + ); +} diff --git a/lib/widgets/hero_image_widget.dart b/lib/widgets/hero_image_widget.dart new file mode 100644 index 0000000..3d604b2 --- /dev/null +++ b/lib/widgets/hero_image_widget.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +import '../models/restaurant.dart'; + +class HeroImageWidget extends StatelessWidget { + const HeroImageWidget( + {super.key, + required this.restaurant, + this.borderRadius = 0, + required this.width, + required this.height}); + final Restaurant restaurant; + final double borderRadius; + final double width; + final double height; + @override + Widget build(BuildContext context) { + return Hero( + tag: restaurant.id.toString(), + child: ClipRRect( + borderRadius: BorderRadius.circular(borderRadius), + child: restaurant.heroImage.isNotEmpty && + Uri.tryParse(restaurant.heroImage)?.isAbsolute == true + ? Image.network( + restaurant.heroImage, + fit: BoxFit.cover, + width: width, + height: height, + ) + : Container( + width: width, + height: height, + color: const Color.fromARGB(255, 197, 233, 199), + child: Image.asset( + 'assets/placeholder.png', + fit: BoxFit.fitHeight, + ), + ), + ), + ); + } +} diff --git a/lib/widgets/rataurant_open_status.dart b/lib/widgets/rataurant_open_status.dart new file mode 100644 index 0000000..aa2ac36 --- /dev/null +++ b/lib/widgets/rataurant_open_status.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; + +class OpenStatusWidget extends StatelessWidget { + const OpenStatusWidget({super.key, required this.isOpen}); + final bool isOpen; + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + isOpen ? 'Open Now' : 'Closed Now', + style: Theme.of(context).textTheme.displaySmall, + ), + const SizedBox( + width: 8, + ), + Icon( + Icons.circle, + size: 8.0, + color: isOpen ? Colors.green : Colors.red, + ) + ], + ); + } +} diff --git a/lib/widgets/rating_and_open_status_widget.dart b/lib/widgets/rating_and_open_status_widget.dart new file mode 100644 index 0000000..b9957d0 --- /dev/null +++ b/lib/widgets/rating_and_open_status_widget.dart @@ -0,0 +1,29 @@ +import 'package:flutter/widgets.dart'; + +import '../models/restaurant.dart'; +import 'rating_widget.dart'; +import 'widgets.dart'; + +class RatingAndOpenStatusWidget extends StatelessWidget { + const RatingAndOpenStatusWidget({ + super.key, + required this.restaurant, + }); + + final Restaurant restaurant; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + StarRating( + rating: restaurant.rating?.round() ?? 0, + ), + const Spacer(), + OpenStatusWidget( + isOpen: restaurant.isOpen, + ) + ], + ); + } +} diff --git a/lib/widgets/rating_widget.dart b/lib/widgets/rating_widget.dart new file mode 100644 index 0000000..d1cf130 --- /dev/null +++ b/lib/widgets/rating_widget.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +import '../theme/colors.dart'; + +class StarRating extends StatelessWidget { + final int rating; + final double starSize; + final Color starColor; + + const StarRating({ + super.key, + required this.rating, // Rating should be between 0 to 5 + this.starSize = 12.0, + this.starColor = ThemeColors.starColor, + }) : assert(rating >= 0 && rating <= 5); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: List.generate(rating, (index) { + return Icon( + Icons.star, + size: starSize, + color: starColor, + ); + }), + ); + } +} diff --git a/lib/widgets/restaurant_details.dart b/lib/widgets/restaurant_details.dart new file mode 100644 index 0000000..e14ebcb --- /dev/null +++ b/lib/widgets/restaurant_details.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; + +class PriceAndClassificationRowWidget extends StatelessWidget { + const PriceAndClassificationRowWidget({ + super.key, + required this.restaurant, + }); + + final Restaurant restaurant; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Text( + restaurant.price ?? '-', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox( + width: 6, + ), + Flexible( + child: Text( + restaurant.categories?.map((element) => element.title).first ?? '', + style: Theme.of(context).textTheme.bodySmall, + maxLines: 1, + softWrap: true, + ), + ), + ], + ); + } +} diff --git a/lib/widgets/restaurant_ratings_review_section.dart b/lib/widgets/restaurant_ratings_review_section.dart new file mode 100644 index 0000000..895752c --- /dev/null +++ b/lib/widgets/restaurant_ratings_review_section.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:restaurant_tour/models/models.dart'; +import 'package:restaurant_tour/theme/text.dart'; +import 'package:restaurant_tour/utils/get_random_avatar.dart'; +import 'package:restaurant_tour/widgets/widgets.dart'; +import '../theme/colors.dart'; + +class BuildRatingSection extends StatelessWidget { + const BuildRatingSection({super.key, required this.restaurant}); + final Restaurant restaurant; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Overall Rating', + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox( + height: 16, + ), + overallClassificationRow(context), + const SizedBox( + height: spacingXL, + ), + buildDivider(), + const SizedBox( + height: spacingXL, + ), + Text( + '${restaurant.reviews?.length} reviews', + style: Theme.of(context).textTheme.bodySmall, + ), + // const SizedBox( + // height: spacingXL, + // ), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: restaurant.reviews?.length, + itemBuilder: (context, index) { + var review = restaurant.reviews?[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox( + height: spacingM, + ), + StarRating( + rating: review?.rating ?? 0, + ), + const SizedBox( + height: spacingSM, + ), + review?.text != null + ? Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + review!.text!, + style: Theme.of(context).textTheme.bodyLarge, + ), + ) + : Container(), + const SizedBox( + height: 8, + ), + Row( + children: [ + CircleAvatar( + radius: 35, + backgroundImage: review?.user?.imageUrl != null + ? NetworkImage(review!.user!.imageUrl!) + : getRandomAvatar(), + ), + const SizedBox( + width: 8, + ), + Text(review?.user?.name ?? 'Not identified'), + ], + ), + const SizedBox( + height: spacingM, + ), + buildDivider() + ], + ); + }, + ), + ], + ); + } + + Row overallClassificationRow(BuildContext context) { + return Row( + children: [ + Text( + restaurant.rating.toString(), + style: Theme.of(context).textTheme.headlineMedium, + ), + const SizedBox( + width: 6, + ), + const Icon( + Icons.star, + color: ThemeColors.starColor, + ), + ], + ); + } +} diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart new file mode 100644 index 0000000..6f30fb9 --- /dev/null +++ b/lib/widgets/widgets.dart @@ -0,0 +1,7 @@ +export 'hero_image_widget.dart'; +export 'rataurant_open_status.dart'; +export './rating_widget.dart'; +export './restaurant_details.dart'; +export './rating_and_open_status_widget.dart'; +export './restaurant_ratings_review_section.dart'; +export './divider.dart'; diff --git a/pubspec.lock b/pubspec.lock index 27b6e40..5033c47 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + bloc: + dependency: "direct main" + description: + name: bloc + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" + url: "https://pub.dev" + source: hosted + version: "8.1.4" + bloc_test: + dependency: "direct dev" + description: + name: bloc_test + sha256: "165a6ec950d9252ebe36dc5335f2e6eb13055f33d56db0eeb7642768849b43d2" + url: "https://pub.dev" + source: hosted + version: "9.1.7" boolean_selector: dependency: transitive description: @@ -161,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" + url: "https://pub.dev" + source: hosted + version: "1.8.0" crypto: dependency: transitive description: @@ -177,6 +201,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.2" + diff_match_patch: + dependency: transitive + description: + name: diff_match_patch + sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4" + url: "https://pub.dev" + source: hosted + version: "0.4.1" dio: dependency: "direct main" description: @@ -193,6 +225,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -222,6 +262,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a + url: "https://pub.dev" + source: hosted + version: "8.1.6" flutter_lints: dependency: "direct dev" description: @@ -251,6 +299,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" glob: dependency: transitive description: @@ -268,7 +324,7 @@ packages: source: hosted version: "2.3.1" http: - dependency: transitive + dependency: "direct dev" description: name: http sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 @@ -303,10 +359,10 @@ packages: dependency: transitive description: name: js - sha256: d9bdfd70d828eeb352390f81b18d6a354ef2044aa28ef25682079797fa7cd174 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.7.1" json_annotation: dependency: "direct main" description: @@ -395,6 +451,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + mocktail: + dependency: "direct dev" + description: + name: mocktail + sha256: "890df3f9688106f25755f26b1c60589a92b3ab91a22b8b224947ad041bf172d8" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" package_config: dependency: transitive description: @@ -435,6 +515,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + provider: + dependency: transitive + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" pub_semver: dependency: transitive description: @@ -459,6 +547,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" shelf_web_socket: dependency: transitive description: @@ -488,6 +592,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.4" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: @@ -536,6 +656,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test: + dependency: transitive + description: + name: test + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" + url: "https://pub.dev" + source: hosted + version: "1.25.2" test_api: dependency: transitive description: @@ -544,6 +672,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + test_core: + dependency: transitive + description: + name: test_core + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" + url: "https://pub.dev" + source: hosted + version: "0.6.0" timing: dependency: transitive description: @@ -624,6 +760,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" xml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4018593..be72877 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: restaurant_tour description: Flutter developer coding challenge starter project. -publish_to: 'none' +# publish_to: 'none' version: 1.0.0+1 @@ -16,6 +16,11 @@ dependencies: dio: ^5.6.0 json_annotation: ^4.9.0 flutter_svg: ^2.0.10 + bloc: ^8.1.4 + flutter_bloc: ^8.1.6 + get_it: ^7.7.0 + equatable: ^2.0.5 + dev_dependencies: flutter_test: @@ -23,10 +28,17 @@ dev_dependencies: flutter_lints: ^4.0.0 build_runner: ^2.4.10 json_serializable: ^6.8.0 + mocktail: ^1.0.4 + bloc_test: ^9.1.7 + http: ^1.2.2 flutter: generate: true uses-material-design: true + assets: + - assets/avatars/ + - assets/.env + - assets/ fonts: - family: Lora fonts: diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..22acfd5 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Set the colors for the output +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to run tests +run_tests() { + echo -e "${YELLOW}Running all tests...${NC}" + + # Run the tests and save the output + flutter test --coverage > test_output.txt 2>&1 + + # Check if tests passed or failed + if grep -q 'All tests passed!' test_output.txt; then + echo -e "${GREEN}All tests passed!${NC}" + else + echo -e "${RED}Some tests failed.${NC}" + echo -e "${YELLOW}Here are the details:${NC}" + grep -A 10 'Some tests failed' test_output.txt + fi + + # Generate coverage report (if coverage is enabled) + if [ -f "coverage/lcov.info" ]; then + echo -e "${YELLOW}Generating coverage report...${NC}" + genhtml coverage/lcov.info -o coverage_report + echo -e "${GREEN}Coverage report generated in the coverage_report directory.${NC}" + fi +} + +# Run the tests +run_tests + +# Cleanup +rm -f test_output.txt diff --git a/test/cubit/cubit_test.dart b/test/cubit/cubit_test.dart new file mode 100644 index 0000000..2ab9c2f --- /dev/null +++ b/test/cubit/cubit_test.dart @@ -0,0 +1,92 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:restaurant_tour/cubit/restaurant_cubit.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; +import 'package:restaurant_tour/usecases/fetch_restaurants.dart'; + +class MockFetchRestaurantsUseCase extends Mock implements FetchRestaurants {} + +class MockRestaurantQueryResult extends Mock implements RestaurantQueryResult {} + +void main() { + late RestaurantCubit restaurantCubit; + late MockFetchRestaurantsUseCase mockFetchRestaurantsUseCase; + + setUp(() { + mockFetchRestaurantsUseCase = MockFetchRestaurantsUseCase(); + restaurantCubit = RestaurantCubit(mockFetchRestaurantsUseCase); + }); + + tearDown(() { + restaurantCubit.close(); + }); + + blocTest( + 'Emits Loading and Loaded states when fetchRestaurants is successful', + build: () { + final mockResult = MockRestaurantQueryResult(); + when(() => mockFetchRestaurantsUseCase.getRestaurants()) + .thenAnswer((_) async => mockResult); + + when(() => mockFetchRestaurantsUseCase.getFavoriteRestaurants()) + .thenAnswer((_) async => ['abc', 'def']); + return restaurantCubit; + }, + act: (cubit) => cubit.fetchRestaurants(), + expect: () => [ + const LoadingRestaurantsState(favoriteRestaurants: []), + isA(), + ], + verify: (cubit) { + verify(() => mockFetchRestaurantsUseCase.getRestaurants()).called(1); + verify(() => mockFetchRestaurantsUseCase.getFavoriteRestaurants()) + .called(1); + }, + ); + + blocTest( + 'emits [LoadingRestaurantsState, ErrorState] when fetchRestaurants fails', + build: () { + when(() => mockFetchRestaurantsUseCase.getRestaurants()) + .thenAnswer((_) async => null); + when(() => mockFetchRestaurantsUseCase.getFavoriteRestaurants()) + .thenAnswer((_) async => []); + return restaurantCubit; + }, + act: (cubit) => cubit.fetchRestaurants(), + expect: () => [ + const LoadingRestaurantsState(favoriteRestaurants: []), + const ErrorState( + message: + 'The server encountered a problem and we couldn\'t load the list.', + favoriteRestaurants: [], + ), + ], + verify: (cubit) { + verify(() => mockFetchRestaurantsUseCase.getRestaurants()).called(1); + verify(() => mockFetchRestaurantsUseCase.getFavoriteRestaurants()) + .called(1); + }, + ); + + blocTest( + 'emits [FavoriteRestaurantState] when someone favorites or un-favorites a restaurant', + build: () => restaurantCubit, + setUp: () { + restaurantCubit.emit( + RestaurantsLoadedState( + result: MockRestaurantQueryResult(), + favoriteRestaurants: const ['1', '2'], + ), + ); + }, + act: (cubit) => cubit.favoriteAResturant('1'), + expect: () => [ + FavoriteRestaurantState( + result: MockRestaurantQueryResult(), + favoriteRestaurants: List.of(['2']), + ), + ], + ); +} diff --git a/test/interface/home_test.dart b/test/interface/home_test.dart new file mode 100644 index 0000000..90d067e --- /dev/null +++ b/test/interface/home_test.dart @@ -0,0 +1,102 @@ +import 'dart:io'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:restaurant_tour/cubit/cubit.dart'; +import 'package:restaurant_tour/datasources/yielp_datasource.dart'; +import 'package:restaurant_tour/network/dio_http_client.dart'; +import 'package:restaurant_tour/pages/pages.dart'; +import 'package:restaurant_tour/repositories/repositories.dart'; +import 'package:restaurant_tour/usecases/usecases.dart'; +import 'package:restaurant_tour/widgets/widgets.dart'; + +// Mock classes +class MockRestaurantCubit extends MockCubit + implements RestaurantCubit {} + +class MockRestaurantState extends Fake implements RestaurantState {} + +class MockFetchRestaurantsUseCase extends Mock implements FetchRestaurants {} + +class MockDio extends Mock implements Dio {} + +class MyHttpOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context) + ..badCertificateCallback = + (X509Certificate cert, String host, int port) => true; + } +} + +void main() { + late MockRestaurantCubit restaurantCubit; + + group('Home Page Widget Testing', () { + setUp(() { + HttpOverrides.global = MyHttpOverrides(); + + registerFallbackValue(MockRestaurantState()); + + restaurantCubit = MockRestaurantCubit(); + GetIt.I.registerSingleton(restaurantCubit); + }); + + tearDown(() { + GetIt.I.reset(); + }); + + testWidgets( + 'Expects to display a loading State with CircularProgressIndicator', + (tester) async { + when(() => restaurantCubit.state) + .thenReturn(const LoadingRestaurantsState(favoriteRestaurants: [])); + + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: BlocProvider.value( + value: GetIt.I(), + child: const HomePage(), + ), + ), + ); + + await tester.pump(Duration(seconds: 1)); + }); + + expect(find.byType(CircularProgressIndicator), findsOneWidget); + }); + + testWidgets( + 'When restaurants are loaded, display a listview with a HeroImageWidget', + (tester) async { + var fakeData = YelpRepository( + datasource: YielpDatasource( + baseUrl: 'http://example.com/get', + networkClient: DioHttpClient(dio: MockDio()), + ), + ).getRestaurantsFromCache(); + when(() => restaurantCubit.state).thenReturn( + RestaurantsLoadedState(favoriteRestaurants: [], result: fakeData), + ); + await tester.runAsync(() async { + await tester.pumpWidget( + MaterialApp( + home: BlocProvider.value( + value: GetIt.I(), + child: const HomePage(), + ), + ), + ); + await tester.pumpAndSettle(); + }); + + expect(find.byType(HeroImageWidget), findsAny); + }); + }); +} diff --git a/test/repository/yelp_repository_test.dart b/test/repository/yelp_repository_test.dart new file mode 100644 index 0000000..3612270 --- /dev/null +++ b/test/repository/yelp_repository_test.dart @@ -0,0 +1,103 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:restaurant_tour/datasources/yielp_datasource.dart'; +import 'package:restaurant_tour/models/restaurant.dart'; +import 'package:restaurant_tour/network/dio_http_client.dart'; +import 'package:restaurant_tour/repositories/repositories.dart'; +import 'package:restaurant_tour/utils/sample_json.dart'; + +class MockDio extends Mock implements Dio {} + +class MockResponse extends Mock implements Response {} + +String fakequery = ''' +query getRestaurants { + search(location: "Las Vegas", limit: 20, offset: 0) { + total + business { + id + name + price + rating + photos + reviews { + id + rating + text + user { + id + image_url + name + } + } + categories { + title + alias + } + hours { + is_open_now + } + location { + formatted_address + } + } + } +} +'''; +void main() { + late YelpRepository repository; + late MockDio mockDio; + setUp(() { + mockDio = MockDio(); + repository = YelpRepository( + datasource: YielpDatasource( + networkClient: DioHttpClient(dio: mockDio), + baseUrl: 'https://example.com', + ), + ); + }); + + // test('Get Restaurants should return a RestaurantQueryResult', () async { + // final response = Response( + // requestOptions: RequestOptions(path: 'https://example.com'), + // data: sample, + // statusCode: 200, + // ); + // final requestData = {'key': 'value'}; + + // // final mockResponse = MockResponse(); + // // when(() => mockResponse.data).thenReturn(aaaaa); + // // when(() => mockResponse.statusCode).thenReturn(200); + // when(() => mockDio.post( + // any(), + // data: fakequery, + // )).thenAnswer((_) async => response); + + // final result = await repository.getRestaurants(); + + // expect(result, isA()); + + // expect(result, isNotNull); + // }); + test('fromJson should parse the sample data correctly', () { + final result = RestaurantQueryResult.fromJson(sample['data']['search']); + expect(result, isNotNull); + expect(result, isA()); + }); + test('returns null on error', () async { + // Arrange + when(() => mockDio.post>( + any(), + data: any(named: 'data'), + )).thenThrow(DioError( + requestOptions: RequestOptions(path: '/v3/graphql'), + )); + + // Act + final result = await repository.getRestaurants(); + + // Assert + expect(result, isNull); + }); +} diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index b729d48..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:restaurant_tour/main.dart'; - -void main() { - testWidgets('Page loads', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const RestaurantTour()); - - // Verify that tests will run - expect(find.text('Fetch Restaurants'), findsOneWidget); - }); -}