diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json index d8abe1b9..b3db758e 100644 --- a/.fvm/fvm_config.json +++ b/.fvm/fvm_config.json @@ -1,4 +1,4 @@ { - "flutterSdkVersion": "3.13.9", + "flutterSdkVersion": "stable", "flavors": {} } \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..b6a7b5aa --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: StormShadonw +custom: ["https://paypal.me/developezdo"] diff --git a/.gitignore b/.gitignore index 1be2d875..afdbf594 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 + +#envied +.env +env.g.dart \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index f285aa4a..a39f95c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,9 @@ { - "dart.flutterSdkPath": ".fvm/flutter_sdk", + "dart.flutterSdkPath": ".fvm\\versions\\stable", "search.exclude": { - "**/.fvm": true + "**/.fvm": true }, "files.watcherExclude": { - "**/.fvm": true + "**/.fvm": true } } \ No newline at end of file diff --git a/README.md b/README.md index 6c2ea7c9..5f4a978b 100644 --- a/README.md +++ b/README.md @@ -181,3 +181,15 @@ Just create a new issue in this repo and we will respond and get back to you qui ## Review The coding challenge is a take-home test upon which we'll be conducting a thorough code review once complete. The review will consist of meeting some more of our mobile engineers and giving a review of the solution you have designed. Please be prepared to share your screen and run/demo the application to the group. During this process, the engineers will be asking questions. + +# Here some screenshots of the app working +![image](https://github.com/StormShadonw/flutter_test/assets/20448268/1d054b77-5f1d-404f-880b-da6987e6603f) +![image](https://github.com/StormShadonw/flutter_test/assets/20448268/1f085cdd-3090-4333-968b-05986a042b6a) +![image](https://github.com/StormShadonw/flutter_test/assets/20448268/4c5b7b01-72ca-417a-a3b6-db1c8867e7da) +![image](https://github.com/StormShadonw/flutter_test/assets/20448268/fa734fe1-3e4f-460d-8fe5-8bcb093ee2e5) +![image](https://github.com/StormShadonw/flutter_test/assets/20448268/155ac3c5-689e-4916-8387-6211f4b4f6ef) + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6cb1ae9c..cdec28a4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,7 @@ + + DataProvider()), + ChangeNotifierProvider(create: (context) => FavoritesProvider()), + ], + child: MaterialApp( + debugShowCheckedModeBanner: false, + title: 'RestauranTour', + theme: ThemeData( + visualDensity: VisualDensity.adaptivePlatformDensity, + colorScheme: const ColorScheme( + background: Colors.white, + brightness: Brightness.light, + primary: Colors.black87, + secondary: Colors.white, + onPrimary: Colors.white, + onSecondary: Colors.white, + error: Colors.redAccent, + onError: Colors.redAccent, + onBackground: Colors.black87, + surface: Colors.white, + onSurface: Colors.black87, + ), ), + home: const HomePage(), ), ); } diff --git a/lib/models/restaurant.dart b/lib/models/restaurant.dart index 87c7aab5..8058033c 100644 --- a/lib/models/restaurant.dart +++ b/lib/models/restaurant.dart @@ -54,11 +54,13 @@ class User { class Review { final String? id; final int? rating; + final String? text; final User? user; const Review({ this.id, this.rating, + this.text, this.user, }); diff --git a/lib/models/restaurant.g.dart b/lib/models/restaurant.g.dart index 3ed33f9a..abf54a73 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), @@ -47,6 +48,7 @@ Review _$ReviewFromJson(Map json) => Review( Map _$ReviewToJson(Review instance) => { 'id': instance.id, 'rating': instance.rating, + 'text': instance.text, 'user': instance.user, }; diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart new file mode 100644 index 00000000..4c84a926 --- /dev/null +++ b/lib/pages/home_page.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:restaurantour/widgets/restaurants_list_widget.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Scaffold( + appBar: AppBar( + bottom: const TabBar(tabs: [ + Tab( + text: "All Restaurants", + ), + Tab( + text: "My Favorites", + ), + ]), + title: const Text("RestauranTour"), + centerTitle: true, + titleTextStyle: Theme.of(context).textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.bold, + ), + ), + body: TabBarView( + children: [ + RestaurantsListWidget(), + RestaurantsListWidget( + favorites: true, + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/restaurant_details_page.dart b/lib/pages/restaurant_details_page.dart new file mode 100644 index 00000000..92d99f07 --- /dev/null +++ b/lib/pages/restaurant_details_page.dart @@ -0,0 +1,223 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; +import 'package:restaurantour/models/restaurant.dart'; +import 'package:restaurantour/providers/data_provider.dart'; +import 'package:restaurantour/providers/favorites_helper.dart'; +import 'package:restaurantour/widgets/category_widget%20copy.dart'; +import 'package:restaurantour/widgets/my_divider_widget.dart'; +import 'package:restaurantour/widgets/opennow_widget.dart'; +import 'package:restaurantour/widgets/review_widget.dart'; + +class RestaurantDetailsPage extends StatefulWidget { + Restaurant restaurant; + RestaurantDetailsPage({required this.restaurant}); + + @override + State createState() => _RestaurantDetailsPageState(); +} + +class _RestaurantDetailsPageState extends State { + late DataProvider dataProvider; + late FavoritesProvider favoritesProvider; + bool _isFavorite = false; + bool _isLoading = false; + + Future getInitData() async { + setState(() { + _isLoading = true; + }); + await favoritesProvider.getFavorites(); + print(favoritesProvider.favorites); + + setState(() { + _isFavorite = favoritesProvider.favorites + .any((element) => element == widget.restaurant.id); + _isLoading = false; + }); + } + + @override + void initState() { + dataProvider = Provider.of( + context, + listen: false, + ); + favoritesProvider = Provider.of( + context, + listen: false, + ); + getInitData(); + super.initState(); + } + + Future changeFavorite(bool value, String id) async { + print("Value: $value"); + !value + ? await favoritesProvider.addFavorite(id) + : await favoritesProvider.removeFavorite(id); + getInitData(); + } + + @override + Widget build(BuildContext context) { + var size = MediaQuery.of(context).size; + return Scaffold( + appBar: AppBar( + title: Text( + widget.restaurant.name ?? "", + style: Theme.of(context).textTheme.titleLarge!.copyWith( + fontWeight: FontWeight.bold, + ), + ), + actions: [ + _isLoading + ? const CircularProgressIndicator.adaptive() + : IconButton( + onPressed: () => + changeFavorite(_isFavorite, widget.restaurant.id ?? ""), + icon: Icon( + Icons.favorite, + color: _isFavorite ? Colors.pink : Colors.grey, + )) + ], + ), + body: SafeArea( + child: Container( + width: size.width, + height: size.height, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + margin: const EdgeInsets.only( + bottom: 10, + ), + height: size.height * 0.45, + width: size.width, + child: Hero( + tag: widget.restaurant.id ?? "", + child: Image.network( + widget.restaurant.heroImage, + fit: BoxFit.cover, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 15, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + CategoryWidget( + text: + "${widget.restaurant.price} ${widget.restaurant.displayCategory}"), + OpenNowWidget(isOpen: widget.restaurant.isOpen), + ], + ), + ), + const MyDivider(), + Container( + alignment: Alignment.centerLeft, + child: const Padding( + padding: EdgeInsets.symmetric( + vertical: 15, + horizontal: 15, + ), + child: Text("Address"), + ), + ), + Container( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only( + top: 5, + bottom: 10, + left: 15, + right: 15, + ), + child: Text( + widget.restaurant.location!.formattedAddress ?? "", + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const MyDivider(), + Container( + alignment: Alignment.centerLeft, + child: const Padding( + padding: EdgeInsets.symmetric( + vertical: 15, + horizontal: 15, + ), + child: Text("Overall Rating"), + ), + ), + Container( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only( + top: 5, + bottom: 10, + left: 15, + right: 15, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + widget.restaurant.rating.toString(), + style: + Theme.of(context).textTheme.displayMedium!.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const Padding( + padding: EdgeInsets.only( + bottom: 5, + ), + child: Icon( + Icons.star, + color: Colors.yellow, + ), + ), + ], + ), + ), + ), + const MyDivider(), + Container( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 15, + ), + child: Text("${widget.restaurant.reviews!.length} Reviews"), + ), + ), + Container( + child: Column( + mainAxisSize: MainAxisSize.min, + children: List.generate( + widget.restaurant.reviews!.length, + (index) => ReviewWidget( + review: widget.restaurant.reviews![index])), + ), + ), + ], + ), + ), + )), + ); + } +} diff --git a/lib/providers/data_provider.dart b/lib/providers/data_provider.dart new file mode 100644 index 00000000..6b247c30 --- /dev/null +++ b/lib/providers/data_provider.dart @@ -0,0 +1,25 @@ +import 'package:flutter/foundation.dart'; +import 'package:restaurantour/models/restaurant.dart'; +import 'package:restaurantour/repositories/yelp_repository.dart'; + +class DataProvider extends ChangeNotifier { + List restaurants = []; + + Future getRestaurants() async { + try { + var result = await YelpRepository().getRestaurants(); + print("Result: $result"); + if (result != null) { + restaurants.clear(); + for (var element in result.restaurants!) { + print("Element: ${element.name}"); + restaurants.add(element); + } + } + notifyListeners(); + return null; + } catch (error) { + return error.toString(); + } + } +} diff --git a/lib/providers/favorites_helper.dart b/lib/providers/favorites_helper.dart new file mode 100644 index 00000000..d89d8c32 --- /dev/null +++ b/lib/providers/favorites_helper.dart @@ -0,0 +1,55 @@ +import 'package:flutter/foundation.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class FavoritesProvider extends ChangeNotifier { + late SharedPreferences? prefs = null; + List favorites = []; + String prefsKey = "favorites"; + + Future addFavorite(String id) async { + try { + prefs ??= await SharedPreferences.getInstance(); + await getFavorites(); + if (!favorites.any((element) => element == id)) { + favorites.add(id); + } + await prefs!.setStringList(prefsKey, favorites); + print("id $id added to favorites!"); + await getFavorites(); + return null; + } catch (error) { + print("Error adding to favorite: $error"); + return error.toString(); + } + } + + Future removeFavorite(String id) async { + try { + prefs ??= await SharedPreferences.getInstance(); + await getFavorites(); + + favorites.removeWhere( + (element) => element == id, + ); + await prefs!.setStringList(prefsKey, favorites); + print("id $id added to favorites!"); + await getFavorites(); + return null; + } catch (error) { + print("Error adding to favorite: $error"); + return error.toString(); + } + } + + Future getFavorites() async { + try { + prefs ??= await SharedPreferences.getInstance(); + favorites = await prefs!.getStringList(prefsKey) ?? []; + print("Favorites: $favorites"); + notifyListeners(); + return null; + } catch (error) { + return error.toString(); + } + } +} diff --git a/lib/repositories/yelp_repository.dart b/lib/repositories/yelp_repository.dart index f251d7b4..6c777509 100644 --- a/lib/repositories/yelp_repository.dart +++ b/lib/repositories/yelp_repository.dart @@ -1,8 +1,9 @@ import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:restaurantour/models/restaurant.dart'; +import 'package:restaurantour/env.dart'; -const _apiKey = ''; +final String _apiKey = Env.YELP_KEY; class YelpRepository { late Dio dio; @@ -19,53 +20,17 @@ class YelpRepository { }, ), ); - - /// Returns a response in this shape - /// { - /// "data": { - /// "search": { - /// "total": 5056, - /// "business": [ - /// { - /// "id": "faPVqws-x-5k2CQKDNtHxw", - /// "name": "Yardbird Southern Table & Bar", - /// "price": "$$", - /// "rating": 4.5, - /// "photos": [ - /// "https:///s3-media4.fl.yelpcdn.com/bphoto/_zXRdYX4r1OBfF86xKMbDw/o.jpg" - /// ], - /// "reviews": [ - /// { - /// "id": "sjZoO8wcK1NeGJFDk5i82Q", - /// "rating": 5, - /// "user": { - /// "id": "BuBCkWFNT_O2dbSnBZvpoQ", - /// "image_url": "https:///s3-media2.fl.yelpcdn.com/photo/v8tbTjYaFvkzh1d7iE-pcQ/o.jpg", - /// "name": "Gina T." - /// } - /// }, - /// { - /// "id": "okpO9hfpxQXssbTZTKq9hA", - /// "rating": 5, - /// "user": { - /// "id": "0x9xu_b0Ct_6hG6jaxpztw", - /// "image_url": "https:///s3-media3.fl.yelpcdn.com/photo/gjz8X6tqE3e4praK4HfCiA/o.jpg", - /// "name": "Crystal L." - /// } - /// }, - /// ... - /// ] - /// } - /// } - /// Future getRestaurants({int offset = 0}) async { try { final response = await dio.post>( '/v3/graphql', data: _getQuery(offset), ); + print("Response: ${response}"); + return RestaurantQueryResult.fromJson(response.data!['data']['search']); } catch (e) { + print(e); return null; } } @@ -84,6 +49,7 @@ query getRestaurants { reviews { id rating + text user { id image_url diff --git a/lib/widgets/category_widget copy.dart b/lib/widgets/category_widget copy.dart new file mode 100644 index 00000000..b11f69d0 --- /dev/null +++ b/lib/widgets/category_widget copy.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class CategoryWidget extends StatelessWidget { + String text; + CategoryWidget({required this.text}); + + @override + Widget build(BuildContext context) { + return Text( + text, + style: Theme.of(context).textTheme.bodySmall, + ); + } +} diff --git a/lib/widgets/my_divider_widget.dart b/lib/widgets/my_divider_widget.dart new file mode 100644 index 00000000..32a9fd70 --- /dev/null +++ b/lib/widgets/my_divider_widget.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +class MyDivider extends StatelessWidget { + const MyDivider(); + + @override + Widget build(BuildContext context) { + return const Padding( + padding: EdgeInsets.symmetric( + vertical: 5, + horizontal: 15, + ), + child: Divider( + color: Colors.black12, + ), + ); + } +} diff --git a/lib/widgets/opennow_widget.dart b/lib/widgets/opennow_widget.dart new file mode 100644 index 00000000..d866eee3 --- /dev/null +++ b/lib/widgets/opennow_widget.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +class OpenNowWidget extends StatelessWidget { + bool isOpen; + OpenNowWidget({required this.isOpen}); + + @override + Widget build(BuildContext context) { + return Container( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + isOpen ? "Open now" : " Closed", + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontStyle: FontStyle.italic, + ), + ), + Container( + margin: const EdgeInsets.only( + left: 5, + ), + child: Icon( + Icons.circle, + color: isOpen ? Colors.greenAccent : Colors.redAccent, + size: 15, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/restaurants_list_widget.dart b/lib/widgets/restaurants_list_widget.dart new file mode 100644 index 00000000..7ffe192f --- /dev/null +++ b/lib/widgets/restaurants_list_widget.dart @@ -0,0 +1,263 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:restaurantour/pages/restaurant_details_page.dart'; +import 'package:restaurantour/providers/favorites_helper.dart'; +import 'package:restaurantour/models/restaurant.dart'; +import 'package:restaurantour/providers/data_provider.dart'; +import 'package:restaurantour/widgets/category_widget%20copy.dart'; +import 'package:restaurantour/widgets/opennow_widget.dart'; + +class RestaurantsListWidget extends StatefulWidget { + bool favorites; + RestaurantsListWidget({this.favorites = false}); + + @override + State createState() => _RestaurantsListWidgetState(); +} + +class _RestaurantsListWidgetState extends State { + DataProvider dataProvider = DataProvider(); + FavoritesProvider favoritesProvider = FavoritesProvider(); + bool _isLoading = false; + List restaurants = []; + List favorites = []; + + Future getInitData() async { + if (!widget.favorites) { + setState(() { + _isLoading = true; + }); + await favoritesProvider.getFavorites(); + var result = await dataProvider.getRestaurants(); + favorites = favoritesProvider.favorites; + if (result != null) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(result), + backgroundColor: Theme.of(context).colorScheme.error, + )); + } + + setState(() { + restaurants = !widget.favorites + ? dataProvider.restaurants + : dataProvider.restaurants + .where((restaurant) => favoritesProvider.favorites + .any((element) => element == restaurant.id!)) + .toList(); + }); + + setState(() { + _isLoading = false; + }); + } else { + setState(() { + restaurants = dataProvider.restaurants + .where((restaurant) => favoritesProvider.favorites + .any((element) => element == restaurant.id!)) + .toList(); + }); + } + } + + @override + void initState() { + dataProvider = Provider.of( + context, + listen: false, + ); + favoritesProvider = Provider.of( + context, + listen: false, + ); + getInitData(); + super.initState(); + } + + Future changeFavorite(bool value, String id) async { + print("Value: $value"); + !value + ? await favoritesProvider.addFavorite(id) + : await favoritesProvider.removeFavorite(id); + if (widget.favorites) { + getInitData(); + } + } + + @override + Widget build(BuildContext context) { + var size = MediaQuery.of(context).size; + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 10, + ), + child: Center( + child: _isLoading + ? const CircularProgressIndicator.adaptive() + : Container( + // color: Colors.red, + height: size.height, + width: size.width, + padding: const EdgeInsets.symmetric( + horizontal: 15, + ), + child: ListView.builder( + itemCount: restaurants.length, + itemBuilder: (context, index) => GestureDetector( + onTap: () { + Navigator.of(context) + .push(MaterialPageRoute( + builder: (context) => RestaurantDetailsPage( + restaurant: restaurants[index]), + )) + .then((value) { + if (widget.favorites) { + getInitData(); + } + }); + }, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + margin: const EdgeInsets.symmetric( + vertical: 5, + ), + elevation: 3, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.white, + ), + alignment: Alignment.centerLeft, + height: size.height * 0.135, + width: size.width * 0.95, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Hero( + tag: restaurants[index].id ?? "", + child: Image.network( + restaurants[index].heroImage, + fit: BoxFit.cover, + ), + ), + ), + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 10, + ), + width: size.width * 0.28, + height: size.height * 0.15, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 10, + ), + child: Stack( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Expanded( + // height: size.height * 0.05, + child: Text( + restaurants[index].name!, + maxLines: 2, + style: Theme.of(context) + .textTheme + .bodyLarge, + ), + ), + CategoryWidget( + text: + "${restaurants[index].price} ${restaurants[index].displayCategory}"), + Container( + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Row( + mainAxisAlignment: + MainAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisSize: + MainAxisSize.min, + children: List.generate( + restaurants[index] + .rating! + .toInt(), + (index) => Icon( + Icons.star, + color: + Colors.yellow, + size: 15, + )).toList(), + ), + ), + OpenNowWidget( + isOpen: restaurants[index] + .isOpen), + ], + ), + ), + ], + ), + Positioned( + right: 0, + child: Consumer( + builder: (context, value, child) { + return GestureDetector( + onTap: () => changeFavorite( + value.favorites.any((element) => + element == + restaurants[index].id), + restaurants[index].id ?? ""), + child: Builder( + builder: (context) { + var id = + restaurants[index].id ?? ""; + var _favorites = + value.favorites; + var isPinnk = + _favorites.indexOf(id) >= 0; + return Icon( + Icons.favorite, + color: isPinnk + ? Colors.pink + : Colors.grey, + ); + }, + ), + ); + }, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + )); + } +} diff --git a/lib/widgets/review_widget.dart b/lib/widgets/review_widget.dart new file mode 100644 index 00000000..3e7c8873 --- /dev/null +++ b/lib/widgets/review_widget.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:restaurantour/models/restaurant.dart'; + +class ReviewWidget extends StatelessWidget { + final Review review; + const ReviewWidget({required this.review}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric( + vertical: 5, + horizontal: 15, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + review.rating ?? 0, + (index) => const Icon( + Icons.star, + color: Colors.yellow, + size: 20, + ), + ), + ), + ), + Container( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Text(review.text ?? ""), + ), + Container( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CircleAvatar( + backgroundImage: NetworkImage(review.user!.imageUrl ?? ""), + ), + const SizedBox( + width: 5, + ), + Text(review.user!.name ?? ""), + ], + ), + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 0b052c68..8fe38d07 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,26 +5,26 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "67.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.4.1" args: dependency: transitive description: name: args - sha256: "0bd9a99b6eb96f07af141f0eb53eace8983e8e5aa5de59777aca31684680ef22" + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.5.0" async: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" build_config: dependency: transitive description: @@ -61,34 +61,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.4.8" + version: "2.4.9" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: f4d6244cc071ba842c296cb1c4ee1b31596b9f924300647ac7a1445493471a3f + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "7.2.3" + version: "7.3.0" built_collection: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: built_value - sha256: b6c9911b2d670376918d5b8779bc27e0e612a94ec3ff0343689e991d8d0a3b8a + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.1.4" + version: "8.9.2" characters: dependency: transitive description: @@ -113,22 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 - url: "https://pub.dev" - source: hosted - version: "1.3.1" checked_yaml: dependency: transitive description: name: checked_yaml - sha256: dd007e4fb8270916820a0d66e24f619266b60773cddd082c6439341645af2659 + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" clock: dependency: transitive description: @@ -157,18 +149,26 @@ packages: dependency: transitive description: name: convert - sha256: f08428ad63615f96a27e34221c65e1a451439b5f26030f78d790f461c686d65d + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + coverage: + dependency: transitive + description: + name: coverage + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "1.7.2" crypto: dependency: transitive description: name: crypto - sha256: cf75650c66c0316274e21d7c43d3dea246273af5955bd94e8184837cd577575c + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -181,18 +181,42 @@ packages: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.6" dio: dependency: "direct main" description: name: dio - sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + sha256: "0978e9a3e45305a80a7210dbeaf79d6ee8bee33f70c8e542dc654c952070217f" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.4.2+1" + envied: + dependency: "direct main" + description: + name: envied + sha256: bbff9c76120e4dc5e2e36a46690cf0a26feb65e7765633f4e8d916bcd173a450 + url: "https://pub.dev" + source: hosted + version: "0.5.4+1" + envied_generator: + dependency: "direct dev" + description: + name: envied_generator + sha256: "517b70de08d13dcd40e97b4e5347e216a0b1c75c99e704f3c85c0474a392d14a" + url: "https://pub.dev" + source: hosted + version: "0.5.4+1" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -201,22 +225,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" file: dependency: transitive description: name: file - sha256: b69516f2c26a5bcac4eee2e32512e1a5205ab312b3536c1c1227b2b942b5f9ad + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "7.0.0" fixnum: dependency: transitive description: name: fixnum - sha256: "6a2ef17156f4dc49684f9d99aaf4a93aba8ac49f5eac861755f5730ddf6e2e4e" + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -234,15 +266,20 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -255,10 +292,10 @@ packages: dependency: transitive description: name: glob - sha256: "8321dd2c0ab0683a91a51307fa844c6db4aa8e3981219b78961672aaab434658" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.2" graphs: dependency: transitive description: @@ -267,38 +304,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.1" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: bfb651625e251a88804ad6d596af01ea903544757906addcb2dcdf088b5ea185 + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.2.1" http_parser: dependency: transitive description: name: http_parser - sha256: e362d639ba3bc07d5a71faebb98cde68c05bfbcfbbb444b60b6f60bb67719185 + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.2" io: dependency: transitive description: name: io - sha256: "0d4c73c3653ab85bf696d51a9657604c900a370549196a91f33e4c39af760852" + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" js: dependency: transitive description: name: js - sha256: d9bdfd70d828eeb352390f81b18d6a354ef2044aa28ef25682079797fa7cd174 + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.6.7" json_annotation: dependency: "direct main" description: @@ -315,6 +360,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -327,58 +396,74 @@ packages: dependency: transitive description: name: logging - sha256: "293ae2d49fd79d4c04944c3a26dfd313382d5f52e821ec57119230ae16031ad4" + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.2.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: name: mime - sha256: fd5f81041e6a9fc9b9d7fa2cb8a01123f9f5d5d49136e06cb9dc7d33689529f4 + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.5" + 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: name: package_config - sha256: a4d5ede5ca9c3d88a2fef1147a078570c861714c806485c596b109819135bc12 + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -387,6 +472,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" petitparser: dependency: transitive description: @@ -395,46 +504,150 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" pool: dependency: transitive description: name: pool - sha256: "05955e3de2683e1746222efd14b775df7131139e07695dc8e24650f6b4204504" + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.5.1" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: b5a5fcc6425ea43704852ba4453ba94b08c2226c63418a260240c3a054579014 + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" pubspec_parse: dependency: transitive description: name: pubspec_parse - sha256: "3686efe4a4613a4449b1a4ae08670aadbd3376f2e78d93e3f8f0919db02a7256" + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.3" + recase: + dependency: transitive + description: + name: recase + sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" shelf: dependency: transitive description: name: shelf - sha256: c240984c924796e055e831a0a36db23be8cb04f170b26df572931ab36418421d + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" + 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: name: shelf_web_socket - sha256: fd84910bf7d58db109082edf7326b75322b8f186162028482f53dc892f00332d + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.4" sky_engine: dependency: transitive description: flutter @@ -456,6 +669,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: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + 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: @@ -484,10 +713,10 @@ packages: dependency: transitive description: name: stream_transform - sha256: ed464977cb26a1f41537e177e190c67223dbd9f4f683489b6ab2e5d211ec564e + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.0" string_scanner: dependency: transitive description: @@ -504,6 +733,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test: + dependency: "direct dev" + description: + name: test + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + url: "https://pub.dev" + source: hosted + version: "1.24.9" test_api: dependency: transitive description: @@ -512,46 +749,54 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + test_core: + dependency: transitive + description: + name: test_core + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + url: "https://pub.dev" + source: hosted + version: "0.5.9" timing: dependency: transitive description: name: timing - sha256: c386d07d7f5efc613479a7c4d9d64b03710b03cfaa7e8ad5f2bfb295a1f0dfad + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.2" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "18f6690295af52d081f6808f2f7c69f0eed6d7e23a71539d75f4aeb8f0062172" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.9+2" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "531d20465c10dfac7f5cd90b60bbe4dd9921f1ec4ca54c83ebb176dbacb7bb2d" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.9+2" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "03012b0a33775c5530576b70240308080e1d5050f0faf000118c20e6463bc0ad" + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.9+2" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -560,30 +805,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" watcher: dependency: transitive description: name: watcher - sha256: e42dfcc48f67618344da967b10f62de57e04bae01d9d3af4c2596f3712a88c99 + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" web: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "0c2ada1b1aeb2ad031ca81872add6be049b8cb479262c6ad3c4b0f9c24eaab2f" + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.5" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a" + url: "https://pub.dev" + source: hosted + version: "5.4.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" xml: dependency: transitive description: @@ -596,10 +873,10 @@ packages: dependency: transitive description: name: yaml - sha256: "3cee79b1715110341012d27756d9bae38e650588acd38d3f3c610822e1337ace" + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.7.0-0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index be3055e0..fab9106a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ version: 1.0.0+1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=3.0.0 <4.0.0" dependencies: flutter: @@ -16,13 +16,18 @@ dependencies: dio: ^5.4.0 json_annotation: ^4.8.1 flutter_svg: ^2.0.9 + envied: ^0.5.4+1 + provider: ^6.1.2 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^1.0.2 - build_runner: ^2.4.8 + build_runner: ^2.4.9 json_serializable: ^6.7.1 + envied_generator: ^0.5.4+1 + test: ^1.24.9 flutter: uses-material-design: true diff --git a/test/favorites_provider_test.dart b/test/favorites_provider_test.dart new file mode 100644 index 00000000..c3468430 --- /dev/null +++ b/test/favorites_provider_test.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:restaurantour/providers/favorites_helper.dart'; + +void main() { + // test('favorites should increment length when adding one', () async { + // WidgetsFlutterBinding.ensureInitialized(); + // final favoritesProvider = FavoritesProvider(); + // await favoritesProvider.addFavorite("120"); + // expect(0, 1); + // }); + + test('favorites should decrease length when removing one', () async { + WidgetsFlutterBinding.ensureInitialized(); + final favoritesProvider = FavoritesProvider(); + await favoritesProvider.addFavorite("120"); + await favoritesProvider.removeFavorite("120"); + expect(favoritesProvider.favorites.length, 0); + }); +} diff --git a/test/open_now_widget_test.dart b/test/open_now_widget_test.dart new file mode 100644 index 00000000..fcda5192 --- /dev/null +++ b/test/open_now_widget_test.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:restaurantour/widgets/my_divider_widget.dart'; +import 'package:restaurantour/widgets/opennow_widget.dart'; + +void main() { + testWidgets("Open now widget should always return a text", + (widgetTester) async { + await widgetTester.pumpWidget(OpenNowWidget( + isOpen: true, + )); + + final textFinder = find.byIcon(Icons.circle); + + expect(textFinder, findsWidgets); + }); +}