diff --git a/analysis_options.yaml b/analysis_options.yaml index 3bf8125..93ab340 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,6 +7,9 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + errors: + public_member_api_docs: ignore include: package:very_good_analysis/analysis_options.yaml linter: diff --git a/assets/food_delivery/img_cui_1.png b/assets/food_delivery/img_cui_1.png new file mode 100644 index 0000000..6962e8d Binary files /dev/null and b/assets/food_delivery/img_cui_1.png differ diff --git a/assets/food_delivery/img_cui_2.png b/assets/food_delivery/img_cui_2.png new file mode 100644 index 0000000..5f2305a Binary files /dev/null and b/assets/food_delivery/img_cui_2.png differ diff --git a/assets/food_delivery/img_cui_3.png b/assets/food_delivery/img_cui_3.png new file mode 100644 index 0000000..501d60b Binary files /dev/null and b/assets/food_delivery/img_cui_3.png differ diff --git a/assets/food_delivery/img_deals_1.png b/assets/food_delivery/img_deals_1.png new file mode 100644 index 0000000..2e5075e Binary files /dev/null and b/assets/food_delivery/img_deals_1.png differ diff --git a/assets/food_delivery/img_deals_2.png b/assets/food_delivery/img_deals_2.png new file mode 100644 index 0000000..f704607 Binary files /dev/null and b/assets/food_delivery/img_deals_2.png differ diff --git a/assets/food_delivery/img_deals_3.png b/assets/food_delivery/img_deals_3.png new file mode 100644 index 0000000..0f529a9 Binary files /dev/null and b/assets/food_delivery/img_deals_3.png differ diff --git a/assets/food_delivery/img_dine_in.png b/assets/food_delivery/img_dine_in.png new file mode 100644 index 0000000..84f5bb5 Binary files /dev/null and b/assets/food_delivery/img_dine_in.png differ diff --git a/assets/food_delivery/img_food_delivery.png b/assets/food_delivery/img_food_delivery.png new file mode 100644 index 0000000..3501a74 Binary files /dev/null and b/assets/food_delivery/img_food_delivery.png differ diff --git a/assets/food_delivery/img_pick_up.png b/assets/food_delivery/img_pick_up.png new file mode 100644 index 0000000..d5b9a2d Binary files /dev/null and b/assets/food_delivery/img_pick_up.png differ diff --git a/assets/food_delivery/img_shops.png b/assets/food_delivery/img_shops.png new file mode 100644 index 0000000..d064e5a Binary files /dev/null and b/assets/food_delivery/img_shops.png differ diff --git a/lib/feature/food_delivery/core/food_body_items.dart b/lib/feature/food_delivery/core/food_body_items.dart new file mode 100644 index 0000000..2030978 --- /dev/null +++ b/lib/feature/food_delivery/core/food_body_items.dart @@ -0,0 +1,17 @@ +import 'package:only_code/feature/food_delivery/core/food_delivery_images.dart'; + +final class FoodBodyItems { + FoodBodyItems._(); + + static final List dailyDeals = [ + FoodDeliveryImages.deals1.assetPath, + FoodDeliveryImages.deals2.assetPath, + FoodDeliveryImages.deals3.assetPath + ]; + + static final List cuisines = [ + FoodDeliveryImages.cuisine1.assetPath, + FoodDeliveryImages.cuisine2.assetPath, + FoodDeliveryImages.cuisine3.assetPath + ]; +} diff --git a/lib/feature/food_delivery/core/food_body_top_model.dart b/lib/feature/food_delivery/core/food_body_top_model.dart new file mode 100644 index 0000000..bc74a46 --- /dev/null +++ b/lib/feature/food_delivery/core/food_body_top_model.dart @@ -0,0 +1,40 @@ +import 'package:only_code/feature/food_delivery/core/food_delivery_images.dart'; + +final class _FoodBodyTopModel { + const _FoodBodyTopModel({ + required this.imagePath, + required this.title, + required this.subtitle, + }); + final String imagePath; + final String title; + final String subtitle; +} + +final class FoodBodyTopModelItems { + FoodBodyTopModelItems._(); + + static final foodDelivery = _FoodBodyTopModel( + imagePath: FoodDeliveryImages.topFoodDelivery.assetPath, + title: 'Food Delivery', + subtitle: 'Order from your favourite \n restaurants and home chefs', + ); + + static final foodShops = _FoodBodyTopModel( + imagePath: FoodDeliveryImages.topShops.assetPath, + title: 'Shops', + subtitle: 'everyday essentials', + ); + + static final foodPickUp = _FoodBodyTopModel( + imagePath: FoodDeliveryImages.topPickUp.assetPath, + title: 'Pick Up', + subtitle: 'Enjoy 50% off', + ); + + static final foodDineIn = _FoodBodyTopModel( + imagePath: FoodDeliveryImages.topDineIn.assetPath, + title: 'Dine In', + subtitle: 'Eat out and save 75% off', + ); +} diff --git a/lib/feature/food_delivery/core/food_delivery_constants.dart b/lib/feature/food_delivery/core/food_delivery_constants.dart new file mode 100644 index 0000000..6d9e945 --- /dev/null +++ b/lib/feature/food_delivery/core/food_delivery_constants.dart @@ -0,0 +1,14 @@ +final class FoodDeliveryConstants { + FoodDeliveryConstants._(); + + static _Keys keys = _Keys._(); +} + +final class _Keys { + _Keys._(); + + final university = 'COMSATS UNIVERSITY ISLAMABAD '; + final search = 'Search for shops & restaurants'; + final dailyDeals = 'Your daily deals'; + final cuisines = 'Cuisines'; +} diff --git a/lib/feature/food_delivery/core/food_delivery_images.dart b/lib/feature/food_delivery/core/food_delivery_images.dart new file mode 100644 index 0000000..cbdca72 --- /dev/null +++ b/lib/feature/food_delivery/core/food_delivery_images.dart @@ -0,0 +1,17 @@ +enum FoodDeliveryImages { + topFoodDelivery('img_food_delivery'), + topShops('img_shops'), + topPickUp('img_pick_up'), + topDineIn('img_dine_in'), + deals1('img_deals_1'), + deals2('img_deals_2'), + deals3('img_deals_3'), + cuisine1('img_cui_1'), + cuisine2('img_cui_2'), + cuisine3('img_cui_3'); + + final String _path; + const FoodDeliveryImages(String path) : _path = path; + + String get assetPath => 'assets/food_delivery/$_path.png'; +} diff --git a/lib/feature/food_delivery/core/food_delivery_view_style.dart b/lib/feature/food_delivery/core/food_delivery_view_style.dart new file mode 100644 index 0000000..cb4972a --- /dev/null +++ b/lib/feature/food_delivery/core/food_delivery_view_style.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +final class FoodDeliveryViewColors { + FoodDeliveryViewColors._(); + static const Color white = Colors.white; + static const Color customRed = Color(0xffD00764); + static const Color grey = Color(0xffBDBDBD); + static const Color black = Colors.black; + static const Color lightGrey = Color(0xffE8ECEF); +} + +final class FoodDeliveryViewPadding { + FoodDeliveryViewPadding._(); + static const EdgeInsets screenPadding = EdgeInsets.all(16); + static const EdgeInsets screenPaddingHalf = EdgeInsets.all(8); + static const EdgeInsets horizontalPadding = + EdgeInsets.symmetric(horizontal: 16); + static const EdgeInsets verticalPadding = EdgeInsets.symmetric(vertical: 16); + static const EdgeInsets topPadding = EdgeInsets.only(top: 16); + static const EdgeInsets bottomPadding = EdgeInsets.only(bottom: 16); + static const EdgeInsets leftPadding = EdgeInsets.only(left: 16); + static const EdgeInsets rightPadding = EdgeInsets.only(right: 16); +} + +final class FoodDeliveryViewTextStyle { + final BuildContext context; + + FoodDeliveryViewTextStyle(this.context); + + TextStyle? get titleLarge => + Theme.of(context).textTheme.headlineMedium?.copyWith( + color: FoodDeliveryViewColors.white, + ); + + TextStyle? get titleMedium => + Theme.of(context).textTheme.titleMedium?.copyWith( + color: FoodDeliveryViewColors.white, + ); + + TextStyle? get bodyLarge => Theme.of(context).textTheme.bodyLarge?.copyWith( + color: FoodDeliveryViewColors.white, + ); + + TextStyle? get bodyMedium => Theme.of(context).textTheme.bodyMedium?.copyWith( + color: FoodDeliveryViewColors.white, + ); + + TextStyle? get bodySmall => Theme.of(context).textTheme.bodySmall?.copyWith( + color: FoodDeliveryViewColors.white, + ); +} diff --git a/lib/feature/food_delivery/food_delivery_view.dart b/lib/feature/food_delivery/food_delivery_view.dart new file mode 100644 index 0000000..9c86bac --- /dev/null +++ b/lib/feature/food_delivery/food_delivery_view.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:only_code/feature/food_delivery/core/food_body_items.dart'; +import 'package:only_code/feature/food_delivery/core/food_body_top_model.dart'; +import 'package:only_code/feature/food_delivery/core/food_delivery_constants.dart'; +import 'package:only_code/feature/food_delivery/core/food_delivery_view_style.dart'; +import 'package:only_code/feature/food_delivery/widget/food_delivery_app_bar.dart'; + +part './widget/food_delivery_body_top.dart'; + +final class FoodDeliveryView extends StatefulWidget { + const FoodDeliveryView({Key? key}) : super(key: key); + @override + State createState() => _FoodDeliveryViewState(); +} + +class _FoodDeliveryViewState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: FoodDeliveryAppBar( + onSearch: (String value) {}, + ), + body: ListView( + children: [ + const _FoodDeliveryBodyTop(), + Padding( + padding: FoodDeliveryViewPadding.horizontalPadding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _Title( + context: context, + data: FoodDeliveryConstants.keys.dailyDeals), + const _DailyDeals(), + _Title( + context: context, + data: FoodDeliveryConstants.keys.cuisines), + const _Cuisines(), + ], + ), + ), + ], + ), + ); + } +} + +final class _DailyDeals extends StatelessWidget { + const _DailyDeals(); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 200, + child: ListView.builder( + itemCount: FoodBodyItems.dailyDeals.length, + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + return Image.asset(FoodBodyItems.dailyDeals[index]); + }, + ), + ); + } +} + +class _Cuisines extends StatelessWidget { + const _Cuisines({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 100, + child: ListView.builder( + itemCount: FoodBodyItems.cuisines.length, + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + return Image.asset(FoodBodyItems.cuisines[index]); + }, + ), + ); + } +} + +final class _Title extends Text { + _Title({required String data, required BuildContext context}) + : super( + data, + style: FoodDeliveryViewTextStyle(context).titleMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.bold, + ), + ); +} diff --git a/lib/feature/food_delivery/widget/food_delivery_app_bar.dart b/lib/feature/food_delivery/widget/food_delivery_app_bar.dart new file mode 100644 index 0000000..5d70e10 --- /dev/null +++ b/lib/feature/food_delivery/widget/food_delivery_app_bar.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:only_code/feature/food_delivery/core/food_delivery_constants.dart'; +import 'package:only_code/feature/food_delivery/core/food_delivery_view_style.dart'; + +class FoodDeliveryAppBar extends StatelessWidget + implements PreferredSizeWidget { + const FoodDeliveryAppBar({required this.onSearch, super.key}); + + /// Callback when user searches for a food item + final ValueChanged onSearch; + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: FoodDeliveryViewColors.customRed, + child: SafeArea( + child: Padding( + padding: FoodDeliveryViewPadding.horizontalPadding, + child: Column( + children: [const _Header(), _SearchField(onSearch: onSearch)], + ), + ), + ), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(100); +} + +final class _SearchField extends StatelessWidget { + const _SearchField({ + required this.onSearch, + }); + + final ValueChanged onSearch; + @override + Widget build(BuildContext context) { + return Padding( + padding: FoodDeliveryViewPadding.topPadding, + child: TextField( + onChanged: onSearch, + decoration: InputDecoration( + hintText: FoodDeliveryConstants.keys.search, + hintStyle: FoodDeliveryViewTextStyle(context).bodyMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.normal), + prefixIcon: + const Icon(Icons.search, color: FoodDeliveryViewColors.black), + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(24), + ), + filled: true, + contentPadding: EdgeInsets.zero, + fillColor: FoodDeliveryViewColors.white, + )), + ); + } +} + +final class _Header extends StatelessWidget { + const _Header(); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + const Icon(Icons.location_on, color: FoodDeliveryViewColors.white), + Expanded( + child: Text( + FoodDeliveryConstants.keys.university, + style: FoodDeliveryViewTextStyle(context).bodyMedium, + ), + ), + ], + ); + } +} diff --git a/lib/feature/food_delivery/widget/food_delivery_body_top.dart b/lib/feature/food_delivery/widget/food_delivery_body_top.dart new file mode 100644 index 0000000..3492803 --- /dev/null +++ b/lib/feature/food_delivery/widget/food_delivery_body_top.dart @@ -0,0 +1,208 @@ +part of '../food_delivery_view.dart'; + +class _FoodDeliveryBodyTop extends StatelessWidget { + const _FoodDeliveryBodyTop(); + + @override + Widget build(BuildContext context) { + return ColoredBox( + color: FoodDeliveryViewColors.lightGrey, + child: Padding( + padding: FoodDeliveryViewPadding.verticalPadding + + FoodDeliveryViewPadding.horizontalPadding, + child: Column( + children: [ + _FoodDeliveryCard( + child: Padding( + padding: FoodDeliveryViewPadding.screenPadding + + FoodDeliveryViewPadding.bottomPadding, + // ignore: prefer_const_constructors + child: _FoodDeliveryItem(), + ), + ), + const Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: _FoodShopsCard()), + Expanded( + child: Column( + children: [ + _PickUpCard(), + _DineInCard(), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +class _DineInCard extends StatelessWidget { + const _DineInCard(); + + @override + Widget build(BuildContext context) { + return Padding( + padding: FoodDeliveryViewPadding.topPadding / 2, + child: _FoodDeliveryCard( + child: Padding( + padding: FoodDeliveryViewPadding.screenPaddingHalf, + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + FoodBodyTopModelItems.foodDineIn.title, + style: FoodDeliveryViewTextStyle(context) + .titleMedium + ?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.bold, + ), + ), + Text(FoodBodyTopModelItems.foodDineIn.subtitle), + ], + ), + ), + Padding( + padding: FoodDeliveryViewPadding.leftPadding / 3, + child: Image.asset(FoodBodyTopModelItems.foodDineIn.imagePath), + ), + ], + ), + ), + ), + ); + } +} + +class _PickUpCard extends StatelessWidget { + const _PickUpCard(); + + @override + Widget build(BuildContext context) { + return _FoodDeliveryCard( + child: Padding( + padding: FoodDeliveryViewPadding.screenPaddingHalf, + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + FoodBodyTopModelItems.foodPickUp.title, + style: + FoodDeliveryViewTextStyle(context).titleMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + FoodBodyTopModelItems.foodPickUp.subtitle, + style: + FoodDeliveryViewTextStyle(context).bodyMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + Expanded( + child: Padding( + padding: FoodDeliveryViewPadding.leftPadding, + child: Image.asset(FoodBodyTopModelItems.foodPickUp.imagePath), + ), + ), + ], + ), + ), + ); + } +} + +class _FoodShopsCard extends StatelessWidget { + const _FoodShopsCard(); + + @override + Widget build(BuildContext context) { + return _FoodDeliveryCard( + child: Padding( + padding: FoodDeliveryViewPadding.screenPadding / 2 + + FoodDeliveryViewPadding.bottomPadding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + FoodBodyTopModelItems.foodShops.title, + style: FoodDeliveryViewTextStyle(context).titleMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + FoodBodyTopModelItems.foodShops.subtitle, + style: FoodDeliveryViewTextStyle(context).bodyMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.w500, + ), + ), + Align( + alignment: Alignment.bottomRight, + child: Image.asset(FoodBodyTopModelItems.foodShops.imagePath), + ), + ], + ), + ), + ); + } +} + +final class _FoodDeliveryCard extends Card { + const _FoodDeliveryCard({super.child}) + : super(color: FoodDeliveryViewColors.white); +} + +class _FoodDeliveryItem extends StatelessWidget { + const _FoodDeliveryItem(); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Padding( + padding: FoodDeliveryViewPadding.rightPadding / 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + FoodBodyTopModelItems.foodDelivery.title, + style: + FoodDeliveryViewTextStyle(context).titleLarge?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.bold, + ), + ), + Text( + FoodBodyTopModelItems.foodDelivery.subtitle, + style: + FoodDeliveryViewTextStyle(context).bodyMedium?.copyWith( + color: FoodDeliveryViewColors.black, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ), + ), + Image.asset(FoodBodyTopModelItems.foodDelivery.imagePath), + ], + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index bf8d526..e20447c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:only_code/feature/custom_sign_up/custom_sign_up.dart'; +import 'package:only_code/feature/food_delivery/food_delivery_view.dart'; void main() => runApp(const MyApp()); @@ -10,7 +11,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Material App', - home: CustomSignUpView(), + home: FoodDeliveryView(), ); } } diff --git a/pubspec.lock b/pubspec.lock index 40b2232..d554b2c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -79,26 +79,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" matcher: dependency: transitive description: @@ -119,10 +119,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -180,10 +180,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -204,9 +204,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" sdks: dart: ">=3.3.2 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index e7ef701..d289b1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,9 +33,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/food_delivery/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware