Flutter Food Delivery App:Uber Eats, DoorDash, Delivery Hero or Lunching.pl – these food delivery apps are changing the way we order our takeaway food. Inspired by them, we decided to build a cross-platform app based on the Flutter framework (as our internal project).
Food ordering and delivery apps are perfect for Flutter because of the low level of complexity. Yet they need to have a beautiful design to present tasty food photos as well as a quick and user-friendly ordering flow (who wants to wait for food, right?).
What is SliverAppBar?
In Flutter, SliverAppBar is a successor to the AppBar widget, which allows you to create the floating app bar effect. The SliverAppBar expands the AppBar when the screen is scrolled up and collapse on scroll down.
You can also completely remove or hide the AppBar when the user is scrolling down a long list. SliverAppBar has got a lot of customization options so you can tailor it to your needs.
SliverAppBar(
leading: IconButton(
onPressed: () {},
icon: const Icon(
Icons.location_pin,
color: Colors.red,
)),
title: SizedBox(
height: 35,
width: 200,
child: TextField(
style: const TextStyle(
color: Colors.red,
fontSize: 20,
fontWeight: FontWeight.w400,
letterSpacing: 0.5,
),
//onChanged: _handleSearch,
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xfff1f1f1),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
hintText: "Search here",
hintStyle: const TextStyle(
color: Colors.red,
fontSize: 14,
fontWeight: FontWeight.w400,
letterSpacing: 0.5,
),
prefixIcon: const Icon(Icons.search),
prefixIconColor: Colors.black,
),
),
),
actions: [
IconButton(
onPressed: () {},
icon: const Icon(
Icons.menu,
color: Colors.black,
))
],
pinned: true,
floating: true,
bottom: const TabBar(
isScrollable: true,
tabs: [
Tab(child: Text('Nearest')),
Tab(child: Text('Popular')),
Tab(child: Text('Top Review')),
Tab(child: Text('Recommended')),
],
),
)
TabBar
To implement TabBar in your Flutter app, complete the following steps:
- Wrap theÂ
Scaffold
 widget inside theÂDefaultTabController
. Use for most simple use cases. If you want to control the tabs programmatically, you should use TabController and avoid this step - Place the
TabBar
widget as the bottom property ofAppBar
- ProvideÂ
TabBarView
 in the body of theÂAppBar
.ÂTabBarView
 is likeÂPageView
, which is use mostly with TabBar because it shows the widget based on the currently selected tab
DefaultTabController(
length: 5,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
title: Text('Tabs Demo'),
pinned: true,
floating: true,
bottom: TabBar(
isScrollable: true,
tabs: [
Tab(child: Text('Flight')),
Tab(child: Text('Train')),
Tab(child: Text('Car')),
Tab(child: Text('Cycle')),
Tab(child: Text('Boat')),
],
),
),
];
},
body: TabBarView(
children: <Widget>[
Icon(Icons.flight, size: 350),
Icon(Icons.directions_transit, size: 350),
Icon(Icons.directions_car, size: 350),
Icon(Icons.directions_bike, size: 350),
Icon(Icons.directions_boat, size: 350),
],
),
)),
);
GridView
A grid view is a graphical control element used to show items in the tabular form. In this section, we are going to learn how to render items in a grid view in the Flutter application.
GridView is a widget in Flutter that displays the items in a 2-D array (two-dimensional rows and columns). As the name suggests, it will be use when we want to show items in a Grid. We can select the desired item from the grid list by tapping on them. This widget can contain text, images, icons, etc. to display in a grid layout depending on the user requirement. It is also refer to as a scrollable 2-D array of widgets. Since it is scrollable, we can specify the direction only in which it scrolls.
The grid view can be implements in various ways, which are gives below:
GridView.builder()
This property is use when we want to display data dynamically or on-demand. In other words, if the user wants to create a grid with a large (infinite) number of children, then they can use the GridView.builder() constructor with either a SliverGridDelegateWithFixedCrossAxisCount or a SliverGridDelegateWithMaxCrossAxisExtent.
The common attributes of this widget are:
itemCount: It is used to define the amount of data to be display.
gridDelegate: It determines the grid or its divider. Its argument should not be null.
itemBuilder: It is used to create items that will be display on the grid view. It will be call only when the indices >= zero && indices < itemCount.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
List<String> images = [
"https://www.licious.in/blog/wp-content/uploads/2020/12/Hyderabadi-chicken-Biryani.jpg",
"https://norecipes.com/wp-content/uploads/2017/05/chicken-biryani-006-500x375.jpg",
"https://www.foodandwine.com/thmb/QRYKO8zoBeWhLCvkxuTgOo8zu0s=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/Chicken-Biryani-FT-RECIPE0823-9d51c6e665ec4c9daa45162841e2f034.jpg",
"https://www.authenticroyal.com/wp-content/uploads/2021/06/LTFA-FY21_Biryani_Web_1280x1600_MyUniversalKitchen-1-819x1024.jpg",
"https://www.simplyrecipes.com/thmb/Fn3xsvrChEJSP-UkWJO2A81PdKw=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/__opt__aboutcom__coeus__resources__content_migration__simply_recipes__uploads__2019__01__Chicken-Biryani-LEAD-4-d53c64cefd0142febe882911d7d88622.jpg",
];
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Flutter GridView Demo"),
backgroundColor: Colors.red,
),
body: Container(
padding: EdgeInsets.all(12.0),
child: GridView.builder(
itemCount: images.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0
),
itemBuilder: (BuildContext context, int index){
return Image.network(images[index]);
},
)),
),
);
}
}
Full Code:Flutter Food Delivery App
import 'package:flutter/material.dart';
class TabPage extends StatefulWidget {
TabPage({Key? key}) : super(key: key);
@override
State<TabPage> createState() => _TabPageState();
}
TextTheme _textTheme(context) => Theme.of(context).textTheme;
Widget _buildSectionHoteSaleIcon() {
return Container(
margin: const EdgeInsets.only(right: 4.0),
child: const Icon(
Icons.whatshot,
color: Colors.pink,
size: 20.0,
),
);
}
Widget _buildFoodHotSaleIcon() {
return Container(
padding: const EdgeInsets.all(4.0),
decoration: BoxDecoration(
color: Colors.pink.withOpacity(0.1),
borderRadius: BorderRadius.circular(16.0),
),
child: const Icon(Icons.whatshot, color: Colors.pink, size: 16.0),
);
}
class CateogryItem {
String title;
bool isHotSale;
String subtitle;
String price;
String comparePrice;
String imageUrl;
String image;
String name;
CateogryItem({
required this.title,
required this.isHotSale,
required this.subtitle,
required this.price,
required this.comparePrice,
required this.imageUrl,
required this.image,
required this.name,
});
}
class _TabPageState extends State<TabPage> {
final List<CateogryItem> _cateogry = [
CateogryItem(
title: "Popular hotpot",
subtitle:
"Comes with a side dish, choose one of the hot pot meats 'smoked pork or marinated beef'",
isHotSale: true,
name: "701. Superman Bubble Vegetable Pot",
price: "\$200",
comparePrice: "\$198",
image: 'assets/images/bri.jpeg',
imageUrl:
"https://www.licious.in/blog/wp-content/uploads/2020/12/Hyderabadi-chicken-Biryani.jpg",
),
CateogryItem(
title: "Vegetarian Hot Pot",
subtitle:
"Comes with a meal, which can be cooked as a side dish. Please let us know if you need it.",
isHotSale: false,
name: "728. Lianting Vegetarian Hot Pot",
price: "\$240",
comparePrice: "\$300",
image: 'assets/images/bri1.jpeg',
imageUrl:
"https://norecipes.com/wp-content/uploads/2017/05/chicken-biryani-006-500x375.jpg",
),
CateogryItem(
title: "Classic hot pot",
subtitle: "Comes with a side meal",
isHotSale: false,
name: "706. Mini original Vegetarian pot",
price: "\$258",
comparePrice: "\$289",
image: 'assets/images/bri2.jpeg',
imageUrl:
"https://www.foodandwine.com/thmb/QRYKO8zoBeWhLCvkxuTgOo8zu0s=/1500x0/filters:no_upscale():max_bytes(150000):strip_icc()/Chicken-Biryani-FT-RECIPE0823-9d51c6e665ec4c9daa45162841e2f034.jpg",
),
CateogryItem(
title: "Vegetarian Hot Pot",
subtitle:
"Comes with a meal, which can be cooked as a side dish. Please let us know if you need it.",
isHotSale: false,
name: "728. Lianting Vegetarian Hot Pot",
price: "\$240",
comparePrice: "\$300",
image: 'assets/images/bri3.jpeg',
imageUrl:
"https://www.authenticroyal.com/wp-content/uploads/2021/06/LTFA-FY21_Biryani_Web_1280x1600_MyUniversalKitchen-1-819x1024.jpg",
),
CateogryItem(
title: "Popular hotpot",
subtitle:
"Comes with a side dish, choose one of the hot pot meats 'smoked pork or marinated beef'",
isHotSale: true,
name: "701. Superman Bubble Vegetable Pot",
price: "\$200",
comparePrice: "\$198",
image: 'assets/images/bri.jpeg',
imageUrl:
"https://www.licious.in/blog/wp-content/uploads/2020/12/Hyderabadi-chicken-Biryani.jpg",
),
CateogryItem(
title: "Vegetarian Hot Pot",
subtitle:
"Comes with a meal, which can be cooked as a side dish. Please let us know if you need it.",
isHotSale: false,
name: "728. Lianting Vegetarian Hot Pot",
price: "\$240",
comparePrice: "\$300",
image: 'assets/images/bri1.jpeg',
imageUrl:
"https://norecipes.com/wp-content/uploads/2017/05/chicken-biryani-006-500x375.jpg",
),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
leading: IconButton(
onPressed: () {},
icon: const Icon(
Icons.location_pin,
color: Colors.red,
)),
title: SizedBox(
height: 35,
width: 200,
child: TextField(
style: const TextStyle(
color: Colors.red,
fontSize: 20,
fontWeight: FontWeight.w400,
letterSpacing: 0.5,
),
//onChanged: _handleSearch,
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xfff1f1f1),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none,
),
hintText: "Search here",
hintStyle: const TextStyle(
color: Colors.red,
fontSize: 14,
fontWeight: FontWeight.w400,
letterSpacing: 0.5,
),
prefixIcon: const Icon(Icons.search),
prefixIconColor: Colors.black,
),
),
),
actions: [
IconButton(
onPressed: () {},
icon: const Icon(
Icons.menu,
color: Colors.black,
))
],
pinned: true,
floating: true,
bottom: const TabBar(
isScrollable: true,
tabs: [
Tab(child: Text('Nearest')),
Tab(child: Text('Popular')),
Tab(child: Text('Top Review')),
Tab(child: Text('Recommended')),
],
),
),
];
},
body: TabBarView(children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: GridView.builder(
shrinkWrap: true,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisExtent: 250,
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10),
itemCount: _cateogry.length,
itemBuilder: (context, index) {
final category = _cateogry[index];
return SizedBox(
height: 300,
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
child: Image.asset(
category.image,
height: 130,
width: double.infinity,
fit: BoxFit.cover,
),
),
const SizedBox(
height: 10,
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
_cateogry[index].name,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
category.price,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
category.comparePrice,
style: _textTheme(context)
.caption
?.copyWith(
decoration: TextDecoration
.lineThrough),
),
if (category.isHotSale)
_buildFoodHotSaleIcon(),
],
),
const SizedBox(
height: 5,
),
Align(
alignment: Alignment.center,
child: Text(
_cateogry[index].title,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
)
],
),
),
);
}),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: GridView.builder(
shrinkWrap: true,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisExtent: 250,
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10),
itemCount: _cateogry.length,
itemBuilder: (context, index) {
final category = _cateogry[index];
return SizedBox(
height: 300,
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
child: Image.asset(
category.image,
height: 130,
width: double.infinity,
fit: BoxFit.cover,
),
),
const SizedBox(
height: 10,
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
_cateogry[index].name,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
category.price,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
category.comparePrice,
style: _textTheme(context)
.caption
?.copyWith(
decoration: TextDecoration
.lineThrough),
),
if (category.isHotSale)
_buildFoodHotSaleIcon(),
],
),
const SizedBox(
height: 5,
),
Align(
alignment: Alignment.center,
child: Text(
_cateogry[index].title,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
)
],
),
),
);
}),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: GridView.builder(
shrinkWrap: true,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisExtent: 250,
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10),
itemCount: _cateogry.length,
itemBuilder: (context, index) {
final category = _cateogry[index];
return SizedBox(
height: 300,
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
child: Image.asset(
category.image,
height: 130,
width: double.infinity,
fit: BoxFit.cover,
),
),
const SizedBox(
height: 10,
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
_cateogry[index].name,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
category.price,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
category.comparePrice,
style: _textTheme(context)
.caption
?.copyWith(
decoration: TextDecoration
.lineThrough),
),
if (category.isHotSale)
_buildFoodHotSaleIcon(),
],
),
const SizedBox(
height: 5,
),
Align(
alignment: Alignment.center,
child: Text(
_cateogry[index].title,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
)
],
),
),
);
}),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: GridView.builder(
shrinkWrap: true,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisExtent: 250,
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10),
itemCount: _cateogry.length,
itemBuilder: (context, index) {
final category = _cateogry[index];
return SizedBox(
height: 300,
child: Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
child: Image.asset(
category.image,
height: 130,
width: double.infinity,
fit: BoxFit.cover,
),
),
const SizedBox(
height: 10,
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
_cateogry[index].name,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceAround,
children: [
Text(
category.price,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
Text(
category.comparePrice,
style: _textTheme(context)
.caption
?.copyWith(
decoration: TextDecoration
.lineThrough),
),
if (category.isHotSale)
_buildFoodHotSaleIcon(),
],
),
const SizedBox(
height: 5,
),
Align(
alignment: Alignment.center,
child: Text(
_cateogry[index].title,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
)
],
),
),
);
}),
),
])),
));
}}
For More: To know about Social Media Screen in Flutter
Leave a Reply