Restaurant card | Settings button | Swiping | Redesigned folder structure
This commit is contained in:
@@ -1,60 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class RestaurantCard extends StatelessWidget {
|
|
||||||
RestaurantCard(
|
|
||||||
{@required this.id, this.name, this.city, this.imgUrl, this.tags});
|
|
||||||
|
|
||||||
final id;
|
|
||||||
final name;
|
|
||||||
final city;
|
|
||||||
final imgUrl;
|
|
||||||
final tags;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Card(
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
|
||||||
children: <Widget>[
|
|
||||||
Container(
|
|
||||||
child: ClipRRect(
|
|
||||||
child: Image.asset(
|
|
||||||
"img/bg_tile.jpg",
|
|
||||||
width: 80,
|
|
||||||
height: 80,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.all(8),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
name,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.orange[600], fontSize: 16, height: 1.7),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
'Miasto: $city',
|
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 14),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Opis...',
|
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 14),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
color: Color.fromRGBO(50, 50, 50, 0.8),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
74
lib/components/restaurantCard.dart
Normal file
74
lib/components/restaurantCard.dart
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:menui_mobile/services.dart';
|
||||||
|
|
||||||
|
class RestaurantCard extends StatelessWidget {
|
||||||
|
RestaurantCard({@required this.restaurant});
|
||||||
|
|
||||||
|
final Restaurant restaurant;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {},
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: ClipRRect(
|
||||||
|
child: Image.network(
|
||||||
|
restaurant.imgUrl,
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(8),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
restaurant.name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.orange[600], fontSize: 16, height: 1.6),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
'Miasto: ${restaurant.city}',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: TextStyle(color: Colors.grey, fontSize: 14),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
restaurant.description,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
style: TextStyle(color: Colors.grey, fontSize: 14),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Container(
|
||||||
|
child: Icon(
|
||||||
|
Icons.arrow_right,
|
||||||
|
color: Colors.orange,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
color: Color.fromRGBO(50, 50, 50, 0.8),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
margin: EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:menui_mobile/main.dart';
|
||||||
import '../services.dart';
|
import '../services.dart';
|
||||||
|
|
||||||
class MenuiSearchBar extends StatefulWidget {
|
class MenuiSearchBar extends StatefulWidget {
|
||||||
@@ -18,32 +19,50 @@ class MenuiSearchBarState extends State<MenuiSearchBar> {
|
|||||||
var suggestions = <String>[];
|
var suggestions = <String>[];
|
||||||
bool suggestionsOpen = false;
|
bool suggestionsOpen = false;
|
||||||
|
|
||||||
Future<void> fetchAutocomplete(text) async {
|
@override
|
||||||
final List<String> results = await services.fetchAutocomplete(text);
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller.addListener(fetchAutocomplete);
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
Future<void> fetchAutocomplete() async {
|
||||||
suggestions = results;
|
if (_controller.text.isNotEmpty) {
|
||||||
});
|
final List<String> results =
|
||||||
|
await services.fetchAutocomplete(_controller.text);
|
||||||
|
|
||||||
if (!suggestionsOpen && results.isNotEmpty) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
suggestionsOpen = true;
|
suggestions = results;
|
||||||
});
|
});
|
||||||
_overlayEntry = _createOverlayEntry();
|
|
||||||
Overlay.of(context).insert(_overlayEntry);
|
if (!suggestionsOpen && results.isNotEmpty) {
|
||||||
|
setState(() {
|
||||||
|
suggestionsOpen = true;
|
||||||
|
});
|
||||||
|
_overlayEntry = _createOverlayEntry();
|
||||||
|
Overlay.of(context).insert(_overlayEntry);
|
||||||
|
} else if (results.isEmpty) {
|
||||||
|
hideSuggestions();
|
||||||
|
}
|
||||||
|
print(suggestions);
|
||||||
|
} else {
|
||||||
|
hideSuggestions();
|
||||||
}
|
}
|
||||||
print(suggestions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> searchRestaurantsByString() async {
|
Future<void> searchRestaurantsByString() async {
|
||||||
final List<Restaurant> results =
|
final List<Restaurant> results =
|
||||||
await services.fetchSearchByString(_controller.text);
|
await services.fetchSearchByString(_controller.text);
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SearchResults(restaurants: results)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void hideSuggestions() {
|
void hideSuggestions() {
|
||||||
if (suggestionsOpen) {
|
if (suggestionsOpen) {
|
||||||
_overlayEntry.remove();
|
_overlayEntry.remove();
|
||||||
setState(() {
|
setState(() {
|
||||||
|
suggestions = [];
|
||||||
suggestionsOpen = false;
|
suggestionsOpen = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -82,6 +101,7 @@ class MenuiSearchBarState extends State<MenuiSearchBar> {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_controller.text = suggestions[index];
|
_controller.text = suggestions[index];
|
||||||
|
searchRestaurantsByString();
|
||||||
},
|
},
|
||||||
title: Text(
|
title: Text(
|
||||||
suggestions[index],
|
suggestions[index],
|
||||||
@@ -115,7 +135,7 @@ class MenuiSearchBarState extends State<MenuiSearchBar> {
|
|||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
onChanged: (text) => fetchAutocomplete(text),
|
//onChanged: (text) => fetchAutocomplete(text),
|
||||||
style: TextStyle(color: Colors.orange),
|
style: TextStyle(color: Colors.orange),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintStyle: TextStyle(color: Colors.grey),
|
hintStyle: TextStyle(color: Colors.grey),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:menui_mobile/components/restaurantCard.dart';
|
||||||
import 'components/searchBar.dart';
|
import 'components/searchBar.dart';
|
||||||
|
import 'services.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(App());
|
runApp(App());
|
||||||
@@ -18,20 +20,22 @@ class App extends StatelessWidget {
|
|||||||
child: MaterialApp(
|
child: MaterialApp(
|
||||||
title: 'Menui',
|
title: 'Menui',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
|
platform: TargetPlatform.iOS,
|
||||||
primarySwatch: Colors.orange,
|
primarySwatch: Colors.orange,
|
||||||
primaryColor: Colors.orange,
|
primaryColor: Colors.orange,
|
||||||
accentColor: Colors.grey,
|
accentColor: Colors.grey,
|
||||||
backgroundColor: Colors.grey,
|
backgroundColor: Colors.grey,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
),
|
),
|
||||||
home: HomePage(title: 'Menui - food guide'),
|
home: HomePage(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ROUTE --- HOME
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
HomePage({Key key, this.title}) : super(key: key);
|
HomePage({Key key}) : super(key: key);
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_HomePageState createState() => _HomePageState();
|
_HomePageState createState() => _HomePageState();
|
||||||
@@ -58,6 +62,66 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: Icon(
|
||||||
|
Icons.settings,
|
||||||
|
color: Colors.orange,
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.grey[850],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ROUTE --- SEARCH RESULTS
|
||||||
|
|
||||||
|
class SearchResults extends StatelessWidget {
|
||||||
|
final List<Restaurant> restaurants;
|
||||||
|
|
||||||
|
SearchResults({@required this.restaurants});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
image: DecorationImage(
|
||||||
|
image: AssetImage("img/bg_tile.jpg"), fit: BoxFit.cover)),
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
Image.asset(
|
||||||
|
"img/logo_mint.png",
|
||||||
|
width: 60,
|
||||||
|
),
|
||||||
|
MenuiSearchBar(),
|
||||||
|
Row(children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(left: 12),
|
||||||
|
child: Text(
|
||||||
|
'Znaleziono: ${restaurants.length}',
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: restaurants.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return RestaurantCard(
|
||||||
|
restaurant: restaurants[index],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ class MenuiServices {
|
|||||||
final response =
|
final response =
|
||||||
await http.get('${backendURL}search/autocomplete?string=$text');
|
await http.get('${backendURL}search/autocomplete?string=$text');
|
||||||
if (response.statusCode == 200 || response.statusCode == 304) {
|
if (response.statusCode == 200 || response.statusCode == 304) {
|
||||||
final List<String> cities = jsonDecode(response.body)['cities'];
|
final List citiesDynamic = jsonDecode(response.body)['cities'];
|
||||||
final List<String> restaurants = jsonDecode(response.body)['restaurants'];
|
final List restaurantsDynamic = jsonDecode(response.body)['restaurants'];
|
||||||
|
final List<String> cities = citiesDynamic.cast<String>().toList();
|
||||||
|
final List<String> restaurants =
|
||||||
|
restaurantsDynamic.cast<String>().toList();
|
||||||
final List<String> result = [...cities, ...restaurants];
|
final List<String> result = [...cities, ...restaurants];
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -39,17 +42,55 @@ class MenuiServices {
|
|||||||
final response = await http.get('${backendURL}search?string=$text');
|
final response = await http.get('${backendURL}search?string=$text');
|
||||||
if (response.statusCode == 200 || response.statusCode == 304) {
|
if (response.statusCode == 200 || response.statusCode == 304) {
|
||||||
final List decoded = jsonDecode(response.body);
|
final List decoded = jsonDecode(response.body);
|
||||||
List<Restaurant> results;
|
List<Restaurant> results = [];
|
||||||
for (var restaurant in decoded) {
|
for (var restaurant in decoded) {
|
||||||
|
final workingHours = restaurant['workingHours'];
|
||||||
|
final tags = restaurant['tags'];
|
||||||
|
final links = restaurant['links'];
|
||||||
|
final List responseLunchMenu = restaurant['lunchMenu'];
|
||||||
|
List<MenuiLunchMenuSet> lunchMenu = [];
|
||||||
|
if (responseLunchMenu != null) {
|
||||||
|
for (var lunchSet in responseLunchMenu) {
|
||||||
|
final MenuiLunchMenuSet thisSet = new MenuiLunchMenuSet(
|
||||||
|
lunchSet['lunchSetName'],
|
||||||
|
lunchSet['lunchSetPrice'],
|
||||||
|
lunchSet['lunchSetDishes']);
|
||||||
|
lunchMenu.add(thisSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
final result = new Restaurant(
|
final result = new Restaurant(
|
||||||
id: restaurant['_id'],
|
id: restaurant['_id'],
|
||||||
name: restaurant['name'],
|
name: restaurant['name'],
|
||||||
city: restaurant['city'],
|
city: restaurant['city'],
|
||||||
adress: restaurant['adress'],
|
adress: restaurant['adress'],
|
||||||
coordinates: restaurant['coordinates'],
|
coordinates: restaurant['coordinates'],
|
||||||
imgUrl: restaurant['imgUrl']);
|
imgUrl: restaurant['imgUrl'],
|
||||||
|
placesId: restaurant['placesId'],
|
||||||
|
workingHours: new MenuiWorkingHours(
|
||||||
|
workingHours['pn'],
|
||||||
|
workingHours['wt'],
|
||||||
|
workingHours['sr'],
|
||||||
|
workingHours['cz'],
|
||||||
|
workingHours['pt'],
|
||||||
|
workingHours['sb'],
|
||||||
|
workingHours['nd']),
|
||||||
|
description: restaurant['description'],
|
||||||
|
tags: new MenuiTags(
|
||||||
|
tags['cardPayments'],
|
||||||
|
tags['petFriendly'],
|
||||||
|
tags['glutenFree'],
|
||||||
|
tags['vegan'],
|
||||||
|
tags['vegetarian'],
|
||||||
|
tags['alcohol'],
|
||||||
|
tags['delivery']),
|
||||||
|
links: new MenuiLinks(
|
||||||
|
links['facebook'], links['instagram'], links['www']),
|
||||||
|
phone: restaurant['phone'],
|
||||||
|
categories: restaurant['categories'],
|
||||||
|
lunchHours: restaurant['lunchHours'],
|
||||||
|
lunchMenu: lunchMenu,
|
||||||
|
dishes: restaurant['dishes']);
|
||||||
results.add(result);
|
results.add(result);
|
||||||
print(result.name);
|
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
} else {
|
} else {
|
||||||
@@ -75,7 +116,7 @@ class Restaurant {
|
|||||||
String phone;
|
String phone;
|
||||||
List categories;
|
List categories;
|
||||||
String lunchHours;
|
String lunchHours;
|
||||||
MenuiLunchMenu lunchMenu;
|
List<MenuiLunchMenuSet> lunchMenu;
|
||||||
List dishes;
|
List dishes;
|
||||||
|
|
||||||
Restaurant(
|
Restaurant(
|
||||||
@@ -143,12 +184,12 @@ class MenuiAllergens {
|
|||||||
this.peanuts, this.sesame);
|
this.peanuts, this.sesame);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MenuiLunchMenu {
|
class MenuiLunchMenuSet {
|
||||||
String lunchSetName;
|
String lunchSetName;
|
||||||
String lunchSetPrice;
|
String lunchSetPrice;
|
||||||
List lunchSetDishes;
|
List lunchSetDishes;
|
||||||
|
|
||||||
MenuiLunchMenu(this.lunchSetName, this.lunchSetPrice, this.lunchSetDishes);
|
MenuiLunchMenuSet(this.lunchSetName, this.lunchSetPrice, this.lunchSetDishes);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MenuiLinks {
|
class MenuiLinks {
|
||||||
|
|||||||
Reference in New Issue
Block a user