mirror of
https://github.com/nisrulz/flutter-examples.git
synced 2026-03-21 22:18:28 +00:00
New Example - Lunch App (#98)
This commit is contained in:
29
lunch_app/lib/main.dart
Normal file
29
lunch_app/lib/main.dart
Normal file
@@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunch_app/views/prototype/food_detail_view.dart';
|
||||
|
||||
import 'views/prototype/home.dart';
|
||||
|
||||
void main() => runApp(LunchApp());
|
||||
|
||||
class LunchApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
iconTheme: IconThemeData(
|
||||
color: Colors.white,
|
||||
),
|
||||
appBarTheme: AppBarTheme(
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
titleTextStyle: TextStyle(
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
home: Home(),
|
||||
// home: FoodDetailView(),
|
||||
);
|
||||
}
|
||||
}
|
||||
18
lunch_app/lib/model/prototype/category.dart
Normal file
18
lunch_app/lib/model/prototype/category.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class Category {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
|
||||
Category(this.title, this.icon);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is Category && other.title == title && other.icon == icon;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => title.hashCode ^ icon.hashCode;
|
||||
}
|
||||
54
lunch_app/lib/views/prototype/bottom_nav/bottom_nav.dart
Normal file
54
lunch_app/lib/views/prototype/bottom_nav/bottom_nav.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomNav extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(10),
|
||||
height: 60,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Icon(Icons.home),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 45,
|
||||
width: 45,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.link,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Icon(
|
||||
Icons.shopping_basket,
|
||||
color: Colors.white.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
96
lunch_app/lib/views/prototype/food_detail_view.dart
Normal file
96
lunch_app/lib/views/prototype/food_detail_view.dart
Normal file
@@ -0,0 +1,96 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunch_app/views/prototype/widgets/carousel_indicator.dart';
|
||||
|
||||
class FoodDetailView extends StatefulWidget {
|
||||
@override
|
||||
_FoodDetailViewState createState() => _FoodDetailViewState();
|
||||
}
|
||||
|
||||
class _FoodDetailViewState extends State<FoodDetailView> {
|
||||
int _currentPage;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentPage = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.blueGrey,
|
||||
appBar: AppBar(
|
||||
title: Text("Deals of the week"),
|
||||
),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
child: PageView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: 3,
|
||||
onPageChanged: (i) {
|
||||
setState(() {
|
||||
_currentPage = i;
|
||||
});
|
||||
},
|
||||
itemBuilder: (c, i) {
|
||||
return Image.asset(
|
||||
"assets/images/burger.png",
|
||||
fit: BoxFit.contain,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 25, right: 25, bottom: 25),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CarouselIndicator(length: 3, selected: _currentPage),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Delicious Burger",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 50,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
"Who doesn't love a big,\ntender, juicy burger?",
|
||||
style:
|
||||
TextStyle(color: Colors.grey.shade300, height: 1.5),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: MediaQuery.of(context).size.width / 1.5,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
child: Center(
|
||||
child: Text(
|
||||
"Get Started",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
48
lunch_app/lib/views/prototype/home.dart
Normal file
48
lunch_app/lib/views/prototype/home.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:lunch_app/views/prototype/widgets/category_selector.dart';
|
||||
import 'package:lunch_app/views/prototype/widgets/food_lister.dart';
|
||||
import 'package:lunch_app/views/prototype/widgets/options_selector.dart';
|
||||
|
||||
import 'bottom_nav/bottom_nav.dart';
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
@override
|
||||
_HomeState createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||
child: SvgPicture.asset(
|
||||
"assets/nav/drawer.svg",
|
||||
width: 25,
|
||||
color: Colors.black.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
OptionsSelector(
|
||||
onChange: (x) {},
|
||||
),
|
||||
CategorySelector(
|
||||
onChange: (cat) {},
|
||||
),
|
||||
FoodLister(),
|
||||
],
|
||||
),
|
||||
),
|
||||
BottomNav(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CarouselIndicator extends StatelessWidget {
|
||||
final int length;
|
||||
final int selected;
|
||||
|
||||
const CarouselIndicator(
|
||||
{Key key, @required this.length, @required this.selected})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(
|
||||
length,
|
||||
(i) {
|
||||
var active = i == selected;
|
||||
return Container(
|
||||
height: 8,
|
||||
width: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: active ? Colors.white : Colors.white.withOpacity(0.4),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
margin: const EdgeInsets.only(right: 5),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
62
lunch_app/lib/views/prototype/widgets/category_selector.dart
Normal file
62
lunch_app/lib/views/prototype/widgets/category_selector.dart
Normal file
@@ -0,0 +1,62 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunch_app/model/prototype/category.dart';
|
||||
import 'package:lunch_app/views/prototype/widgets/main_btn.dart';
|
||||
|
||||
class CategorySelector extends StatefulWidget {
|
||||
final Function(Category) onChange;
|
||||
|
||||
const CategorySelector({Key key, @required this.onChange}) : super(key: key);
|
||||
|
||||
@override
|
||||
_CategorySelectorState createState() => _CategorySelectorState();
|
||||
}
|
||||
|
||||
class _CategorySelectorState extends State<CategorySelector> {
|
||||
final List<Category> categories = [
|
||||
Category("All", null),
|
||||
Category("Foods", Icons.local_pizza),
|
||||
Category("Drinks", Icons.local_drink_sharp),
|
||||
];
|
||||
|
||||
int _current;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_current = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: List.generate(
|
||||
categories.length,
|
||||
(i) {
|
||||
var cur = categories[i];
|
||||
return Padding(
|
||||
padding: (i == 0)
|
||||
? const EdgeInsets.symmetric(horizontal: 15.0)
|
||||
: const EdgeInsets.only(right: 15),
|
||||
child: MainBtn(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_current = i;
|
||||
});
|
||||
widget.onChange(categories[i]);
|
||||
},
|
||||
title: cur.title,
|
||||
icon: cur.icon,
|
||||
active: i == _current,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
152
lunch_app/lib/views/prototype/widgets/food_lister.dart
Normal file
152
lunch_app/lib/views/prototype/widgets/food_lister.dart
Normal file
@@ -0,0 +1,152 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lunch_app/views/prototype/food_detail_view.dart';
|
||||
|
||||
class FoodLister extends StatefulWidget {
|
||||
@override
|
||||
_FoodListerState createState() => _FoodListerState();
|
||||
}
|
||||
|
||||
class _FoodListerState extends State<FoodLister> {
|
||||
final PageController controller =
|
||||
PageController(initialPage: 2, viewportFraction: 0.9);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.width * 0.8,
|
||||
child: PageView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: 10,
|
||||
controller: controller,
|
||||
itemBuilder: (c, i) {
|
||||
return _ItemWidget();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ItemWidget extends StatefulWidget {
|
||||
@override
|
||||
__ItemWidgetState createState() => __ItemWidgetState();
|
||||
}
|
||||
|
||||
class __ItemWidgetState extends State<_ItemWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context, MaterialPageRoute(builder: (c) => FoodDetailView()));
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 5),
|
||||
child: Stack(
|
||||
children: [
|
||||
_buildBackgroundCard(),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildImage(),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 15, vertical: 10),
|
||||
child: _buildDetails(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBackgroundCard() {
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.width * 0.8 * 0.2,
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
// margin: const EdgeInsets.only(top: 40.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImage() {
|
||||
return Center(
|
||||
child: Image.asset(
|
||||
"assets/images/burger.png",
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetails() {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
"Hello Burger",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 20,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
"An 100% beef party whipped in a soft bean and topped with onion, cheese and tomato",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Colors.grey.shade400,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"\$3.58",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
"\$5.58",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 20,
|
||||
color: Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
61
lunch_app/lib/views/prototype/widgets/main_btn.dart
Normal file
61
lunch_app/lib/views/prototype/widgets/main_btn.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MainBtn extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final bool active;
|
||||
|
||||
final Function onTap;
|
||||
|
||||
const MainBtn({
|
||||
Key key,
|
||||
this.icon,
|
||||
this.title = "All",
|
||||
this.active = true,
|
||||
this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: MediaQuery.of(context).size.width / 3,
|
||||
minHeight: 50,
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: active ? Colors.blueGrey : null,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: active ? null : Border.all(color: Colors.grey.shade400),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
(icon != null)
|
||||
? Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
color: active ? Colors.white : Colors.grey.shade400,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
],
|
||||
)
|
||||
: SizedBox(),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
color: active ? Colors.white : Colors.grey.shade400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
66
lunch_app/lib/views/prototype/widgets/options_selector.dart
Normal file
66
lunch_app/lib/views/prototype/widgets/options_selector.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OptionsSelector extends StatefulWidget {
|
||||
final Function(String) onChange;
|
||||
|
||||
const OptionsSelector({Key key, @required this.onChange}) : super(key: key);
|
||||
|
||||
@override
|
||||
_OptionsSelectorState createState() => _OptionsSelectorState();
|
||||
}
|
||||
|
||||
class _OptionsSelectorState extends State<OptionsSelector> {
|
||||
final List<String> options = ["Offers", "Foods", "Drinks"];
|
||||
|
||||
int _current;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_current = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(15, 10, 0, 10),
|
||||
child: Row(
|
||||
children: List.generate(
|
||||
options.length,
|
||||
(i) => Padding(
|
||||
padding: const EdgeInsets.only(right: 20),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_current = i;
|
||||
});
|
||||
widget.onChange(options[i]);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 5,
|
||||
color:
|
||||
(i == _current) ? Colors.amber : Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
options[i],
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user