Compare commits
1 Commits
1449df78c2
...
renovate/c
Author | SHA1 | Date | |
---|---|---|---|
ac99ef3930 |
@@ -1,34 +0,0 @@
|
|||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- backend/**
|
|
||||||
|
|
||||||
name: Run linting on the backend code
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- uses: https://gitea.com/actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update && apt-get install -y python3 python3-pip
|
|
||||||
pip install pipenv
|
|
||||||
|
|
||||||
- name: Install packages
|
|
||||||
run: |
|
|
||||||
ls -la
|
|
||||||
# only install dev-packages
|
|
||||||
pipenv install --categories=dev-packages
|
|
||||||
pipenv run pip freeze
|
|
||||||
|
|
||||||
working-directory: backend
|
|
||||||
|
|
||||||
- name: Run linter
|
|
||||||
run: pipenv run pylint src
|
|
||||||
working-directory: backend
|
|
@@ -1,33 +0,0 @@
|
|||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- backend/**
|
|
||||||
|
|
||||||
name: Run testing on the backend code
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- uses: https://gitea.com/actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update && apt-get install -y python3 python3-pip
|
|
||||||
pip install pipenv
|
|
||||||
|
|
||||||
- name: Install packages
|
|
||||||
run: |
|
|
||||||
ls -la
|
|
||||||
# install all packages, including dev-packages
|
|
||||||
pipenv install --dev
|
|
||||||
pipenv run pip freeze
|
|
||||||
working-directory: backend
|
|
||||||
|
|
||||||
- name: Run Tests
|
|
||||||
run: pipenv run pytest src
|
|
||||||
working-directory: backend
|
|
@@ -43,10 +43,8 @@ jobs:
|
|||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
|
|
||||||
- name: Add required secrets
|
- name: Add required secrets
|
||||||
env:
|
|
||||||
ANDROID_SECRETS_PROPERTIES: ${{ secrets.ANDROID_SECRETS_PROPERTIES }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "$ANDROID_SECRETS_PROPERTIES" >> ./android/secrets.properties
|
echo ${{ secrets.ANDROID_SECRETS_PROPERTIES }} > ./android/secrets.properties
|
||||||
working-directory: ./frontend
|
working-directory: ./frontend
|
||||||
|
|
||||||
- name: Sanity check
|
- name: Sanity check
|
||||||
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -14,9 +14,9 @@
|
|||||||
"DEBUG": "true"
|
"DEBUG": "true"
|
||||||
},
|
},
|
||||||
"args": [
|
"args": [
|
||||||
// "--app-dir",
|
"--app-dir",
|
||||||
// "src",
|
"src",
|
||||||
"src.main:app",
|
"main:app",
|
||||||
"--reload",
|
"--reload",
|
||||||
],
|
],
|
||||||
"jinja": true,
|
"jinja": true,
|
||||||
|
6
backend/.gitignore
vendored
6
backend/.gitignore
vendored
@@ -1,10 +1,6 @@
|
|||||||
# osm-cache and wikidata cache
|
# osm-cache
|
||||||
cache/
|
cache/
|
||||||
apicache/
|
apicache/
|
||||||
|
|
||||||
# wikidata throttle
|
|
||||||
*.ctrl
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
[MAIN]
|
|
||||||
max-line-length=240
|
|
@@ -13,6 +13,5 @@ EXPOSE 8000
|
|||||||
# Set environment variables used by the deployment. These can be overridden by the user using this image.
|
# Set environment variables used by the deployment. These can be overridden by the user using this image.
|
||||||
ENV NUM_WORKERS=1
|
ENV NUM_WORKERS=1
|
||||||
ENV OSM_CACHE_DIR=/cache
|
ENV OSM_CACHE_DIR=/cache
|
||||||
ENV MEMCACHED_HOST_PATH=none
|
|
||||||
|
|
||||||
CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS
|
CMD fastapi run src/main.py --port 8000 --workers $NUM_WORKERS
|
||||||
|
@@ -4,8 +4,6 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
pylint = "*"
|
|
||||||
pytest = "*"
|
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
numpy = "*"
|
numpy = "*"
|
||||||
@@ -16,4 +14,3 @@ shapely = "*"
|
|||||||
scipy = "*"
|
scipy = "*"
|
||||||
osmpythontools = "*"
|
osmpythontools = "*"
|
||||||
pywikibot = "*"
|
pywikibot = "*"
|
||||||
pymemcache = "*"
|
|
||||||
|
952
backend/Pipfile.lock
generated
952
backend/Pipfile.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -15,21 +15,13 @@ OSM_CACHE_DIR = Path(cache_dir_string)
|
|||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
# if we are in a debug session, set verbose and rich logging
|
import yaml
|
||||||
|
|
||||||
|
LOGGING_CONFIG = LOCATION_PREFIX / 'log_config.yaml'
|
||||||
|
config = yaml.safe_load(LOGGING_CONFIG.read_text())
|
||||||
|
|
||||||
|
logging.config.dictConfig(config)
|
||||||
|
|
||||||
|
# if we are in a debug session, set the log level to debug
|
||||||
if os.getenv('DEBUG', False):
|
if os.getenv('DEBUG', False):
|
||||||
from rich.logging import RichHandler
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.DEBUG,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
handlers=[RichHandler()]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
MEMCACHED_HOST_PATH = os.getenv('MEMCACHED_HOST_PATH', None)
|
|
||||||
if MEMCACHED_HOST_PATH == "none":
|
|
||||||
MEMCACHED_HOST_PATH = None
|
|
||||||
|
34
backend/src/log_config.yaml
Normal file
34
backend/src/log_config.yaml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
version: 1
|
||||||
|
disable_existing_loggers: False
|
||||||
|
formatters:
|
||||||
|
simple:
|
||||||
|
format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
handlers:
|
||||||
|
console:
|
||||||
|
class: rich.logging.RichHandler
|
||||||
|
formatter: simple
|
||||||
|
# access:
|
||||||
|
# class: logging.FileHandler
|
||||||
|
# filename: logs/access.log
|
||||||
|
# level: INFO
|
||||||
|
# formatter: simple
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
loggers:
|
||||||
|
uvicorn.error:
|
||||||
|
level: INFO
|
||||||
|
handlers:
|
||||||
|
- console
|
||||||
|
propagate: no
|
||||||
|
# uvicorn.access:
|
||||||
|
# level: INFO
|
||||||
|
# handlers:
|
||||||
|
# - access
|
||||||
|
# propagate: no
|
||||||
|
root:
|
||||||
|
level: INFO
|
||||||
|
handlers:
|
||||||
|
- console
|
||||||
|
propagate: yes
|
@@ -1,14 +1,12 @@
|
|||||||
import logging
|
import logging
|
||||||
from fastapi import FastAPI, Query, Body, HTTPException
|
from fastapi import FastAPI, Query, Body
|
||||||
|
|
||||||
from .structs.landmark import Landmark
|
from structs.landmark import Landmark
|
||||||
from .structs.preferences import Preferences
|
from structs.preferences import Preferences
|
||||||
from .structs.linked_landmarks import LinkedLandmarks
|
from structs.linked_landmarks import LinkedLandmarks
|
||||||
from .structs.trip import Trip
|
from utils.landmarks_manager import LandmarkManager
|
||||||
from .utils.landmarks_manager import LandmarkManager
|
from utils.optimizer import Optimizer
|
||||||
from .utils.optimizer import Optimizer
|
from utils.refiner import Refiner
|
||||||
from .utils.refiner import Refiner
|
|
||||||
from .persistence import client as cache_client
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -19,8 +17,8 @@ optimizer = Optimizer()
|
|||||||
refiner = Refiner(optimizer=optimizer)
|
refiner = Refiner(optimizer=optimizer)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/trip/new")
|
@app.post("/route/new")
|
||||||
def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[float, float] | None = None) -> Trip:
|
def get_route(preferences: Preferences, start: tuple[float, float], end: tuple[float, float] | None = None) -> str:
|
||||||
'''
|
'''
|
||||||
Main function to call the optimizer.
|
Main function to call the optimizer.
|
||||||
:param preferences: the preferences specified by the user as the post body
|
:param preferences: the preferences specified by the user as the post body
|
||||||
@@ -29,17 +27,15 @@ def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[fl
|
|||||||
:return: the uuid of the first landmark in the optimized route
|
:return: the uuid of the first landmark in the optimized route
|
||||||
'''
|
'''
|
||||||
if preferences is None:
|
if preferences is None:
|
||||||
raise HTTPException(status_code=406, detail="Preferences not provided")
|
raise ValueError("Please provide preferences in the form of a 'Preference' BaseModel class.")
|
||||||
if preferences.shopping.score == 0 and preferences.sightseeing.score == 0 and preferences.nature.score == 0:
|
|
||||||
raise HTTPException(status_code=406, detail="All preferences are 0.")
|
|
||||||
if start is None:
|
if start is None:
|
||||||
raise HTTPException(status_code=406, detail="Start coordinates not provided")
|
raise ValueError("Please provide the starting coordinates as a tuple of floats.")
|
||||||
if end is None:
|
if end is None:
|
||||||
end = start
|
end = start
|
||||||
logger.info("No end coordinates provided. Using start=end.")
|
logger.info("No end coordinates provided. Using start=end.")
|
||||||
|
|
||||||
start_landmark = Landmark(name='start', type='start', location=(start[0], start[1]), osm_type='start', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
|
start_landmark = Landmark(name='start', type='start', location=(start[0], start[1]), osm_type='start', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
|
||||||
end_landmark = Landmark(name='finish', type='finish', location=(end[0], end[1]), osm_type='end', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
|
end_landmark = Landmark(name='end', type='finish', location=(end[0], end[1]), osm_type='end', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
|
||||||
|
|
||||||
# Generate the landmarks from the start location
|
# Generate the landmarks from the start location
|
||||||
landmarks, landmarks_short = manager.generate_landmarks_list(
|
landmarks, landmarks_short = manager.generate_landmarks_list(
|
||||||
@@ -51,37 +47,22 @@ def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[fl
|
|||||||
landmarks_short.insert(0, start_landmark)
|
landmarks_short.insert(0, start_landmark)
|
||||||
landmarks_short.append(end_landmark)
|
landmarks_short.append(end_landmark)
|
||||||
|
|
||||||
|
# TODO infer these parameters from the preferences
|
||||||
|
max_walking_time = 4 # hours
|
||||||
|
detour = 30 # minutes
|
||||||
|
|
||||||
# First stage optimization
|
# First stage optimization
|
||||||
try:
|
base_tour = optimizer.solve_optimization(max_walking_time*60, landmarks_short)
|
||||||
base_tour = optimizer.solve_optimization(preferences.max_time_minute, landmarks_short)
|
|
||||||
except ArithmeticError:
|
|
||||||
raise HTTPException(status_code=500, detail="No solution found")
|
|
||||||
except TimeoutError:
|
|
||||||
raise HTTPException(status_code=500, detail="Optimzation took too long")
|
|
||||||
|
|
||||||
# Second stage optimization
|
# Second stage optimization
|
||||||
refined_tour = refiner.refine_optimization(landmarks, base_tour, preferences.max_time_minute, preferences.detour_tolerance_minute)
|
refined_tour = refiner.refine_optimization(landmarks, base_tour, max_walking_time*60, detour)
|
||||||
|
|
||||||
linked_tour = LinkedLandmarks(refined_tour)
|
linked_tour = LinkedLandmarks(refined_tour)
|
||||||
# upon creation of the trip, persistence of both the trip and its landmarks is ensured. Ca
|
return linked_tour[0].uuid
|
||||||
trip = Trip.from_linked_landmarks(linked_tour, cache_client)
|
|
||||||
return trip
|
|
||||||
|
|
||||||
|
|
||||||
#### For already existing trips/landmarks
|
|
||||||
@app.get("/trip/{trip_uuid}")
|
|
||||||
def get_trip(trip_uuid: str) -> Trip:
|
|
||||||
try:
|
|
||||||
trip = cache_client.get(f"trip_{trip_uuid}")
|
|
||||||
return trip
|
|
||||||
except KeyError:
|
|
||||||
raise HTTPException(status_code=404, detail="Trip not found")
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/landmark/{landmark_uuid}")
|
@app.get("/landmark/{landmark_uuid}")
|
||||||
def get_landmark(landmark_uuid: str) -> Landmark:
|
def get_landmark(landmark_uuid: str) -> Landmark:
|
||||||
try:
|
#cherche dans linked_tour et retourne le landmark correspondant
|
||||||
landmark = cache_client.get(f"landmark_{landmark_uuid}")
|
pass
|
||||||
return landmark
|
|
||||||
except KeyError:
|
|
||||||
raise HTTPException(status_code=404, detail="Landmark not found")
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
city_bbox_side: 5000 #m
|
city_bbox_side: 5000 #m
|
||||||
radius_close_to: 50
|
radius_close_to: 50
|
||||||
church_coeff: 0.8
|
church_coeff: 0.8
|
||||||
park_coeff: 1.0
|
park_coeff: 1.2
|
||||||
tag_coeff: 10
|
tag_coeff: 10
|
||||||
N_important: 40
|
N_important: 40
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
from pymemcache.client.base import Client
|
|
||||||
|
|
||||||
from .constants import MEMCACHED_HOST_PATH
|
|
||||||
|
|
||||||
|
|
||||||
class DummyClient:
|
|
||||||
_data = {}
|
|
||||||
def set(self, key, value, **kwargs):
|
|
||||||
self._data[key] = value
|
|
||||||
|
|
||||||
def set_many(self, data, **kwargs):
|
|
||||||
self._data.update(data)
|
|
||||||
|
|
||||||
def get(self, key, **kwargs):
|
|
||||||
return self._data[key]
|
|
||||||
|
|
||||||
|
|
||||||
if MEMCACHED_HOST_PATH is None:
|
|
||||||
client = DummyClient()
|
|
||||||
else:
|
|
||||||
client = Client(
|
|
||||||
MEMCACHED_HOST_PATH,
|
|
||||||
timeout=1,
|
|
||||||
allow_unicode_keys=True,
|
|
||||||
encoding='utf-8'
|
|
||||||
)
|
|
@@ -1,5 +1,6 @@
|
|||||||
|
import uuid
|
||||||
from .landmark import Landmark
|
from .landmark import Landmark
|
||||||
from ..utils.get_time_separation import get_time
|
from utils.get_time_separation import get_time
|
||||||
|
|
||||||
class LinkedLandmarks:
|
class LinkedLandmarks:
|
||||||
"""
|
"""
|
||||||
@@ -8,7 +9,8 @@ class LinkedLandmarks:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
_landmarks = list[Landmark]
|
_landmarks = list[Landmark]
|
||||||
total_time: int = 0
|
total_time = int
|
||||||
|
uuid = str
|
||||||
|
|
||||||
def __init__(self, data: list[Landmark] = None) -> None:
|
def __init__(self, data: list[Landmark] = None) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -17,6 +19,7 @@ class LinkedLandmarks:
|
|||||||
Args:
|
Args:
|
||||||
data (list[Landmark], optional): The list of landmarks that are linked together. Defaults to None.
|
data (list[Landmark], optional): The list of landmarks that are linked together. Defaults to None.
|
||||||
"""
|
"""
|
||||||
|
self.uuid = uuid.uuid4()
|
||||||
self._landmarks = data if data else []
|
self._landmarks = data if data else []
|
||||||
self._link_landmarks()
|
self._link_landmarks()
|
||||||
|
|
||||||
@@ -25,6 +28,7 @@ class LinkedLandmarks:
|
|||||||
"""
|
"""
|
||||||
Create the links between the landmarks in the list by setting their .next_uuid and the .time_to_next attributes.
|
Create the links between the landmarks in the list by setting their .next_uuid and the .time_to_next attributes.
|
||||||
"""
|
"""
|
||||||
|
self.total_time = 0
|
||||||
for i, landmark in enumerate(self._landmarks[:-1]):
|
for i, landmark in enumerate(self._landmarks[:-1]):
|
||||||
landmark.next_uuid = self._landmarks[i + 1].uuid
|
landmark.next_uuid = self._landmarks[i + 1].uuid
|
||||||
time_to_next = get_time(landmark.location, self._landmarks[i + 1].location)
|
time_to_next = get_time(landmark.location, self._landmarks[i + 1].location)
|
||||||
@@ -40,4 +44,18 @@ class LinkedLandmarks:
|
|||||||
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"LinkedLandmarks [{' ->'.join([str(landmark) for landmark in self._landmarks])}]"
|
return f"LinkedLandmarks, total time: {self.total_time} minutes, {len(self._landmarks)} stops: [{','.join([str(landmark) for landmark in self._landmarks])}]"
|
||||||
|
|
||||||
|
|
||||||
|
def asdict(self) -> dict:
|
||||||
|
"""
|
||||||
|
Convert the linked landmarks to a json serializable dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary representation of the linked landmarks.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'uuid': self.uuid,
|
||||||
|
'total_time': self.total_time,
|
||||||
|
'landmarks': [landmark.dict() for landmark in self._landmarks]
|
||||||
|
}
|
||||||
|
@@ -2,6 +2,7 @@ from pydantic import BaseModel
|
|||||||
from typing import Optional, Literal
|
from typing import Optional, Literal
|
||||||
|
|
||||||
class Preference(BaseModel) :
|
class Preference(BaseModel) :
|
||||||
|
name: str
|
||||||
type: Literal['sightseeing', 'nature', 'shopping', 'start', 'finish']
|
type: Literal['sightseeing', 'nature', 'shopping', 'start', 'finish']
|
||||||
score: int # score could be from 1 to 5
|
score: int # score could be from 1 to 5
|
||||||
|
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
from pydantic import BaseModel, Field
|
|
||||||
from pymemcache.client.base import Client
|
|
||||||
|
|
||||||
from .linked_landmarks import LinkedLandmarks
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
class Trip(BaseModel):
|
|
||||||
uuid: str = Field(default_factory=uuid.uuid4)
|
|
||||||
total_time: int
|
|
||||||
first_landmark_uuid: str
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_linked_landmarks(self, landmarks: LinkedLandmarks, cache_client: Client) -> "Trip":
|
|
||||||
"""
|
|
||||||
Initialize a new Trip object and ensure it is stored in the cache.
|
|
||||||
"""
|
|
||||||
trip = Trip(
|
|
||||||
total_time = landmarks.total_time,
|
|
||||||
first_landmark_uuid = str(landmarks[0].uuid)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Store the trip in the cache
|
|
||||||
cache_client.set(f"trip_{trip.uuid}", trip)
|
|
||||||
cache_client.set_many({f"landmark_{landmark.uuid}": landmark for landmark in landmarks}, expire=3600)
|
|
||||||
# is equivalent to:
|
|
||||||
# for landmark in landmarks:
|
|
||||||
# cache_client.set(f"landmark_{landmark.uuid}", landmark, expire=3600)
|
|
||||||
|
|
||||||
return trip
|
|
@@ -20,9 +20,18 @@ def test(start_coords: tuple[float, float], finish_coords: tuple[float, float] =
|
|||||||
|
|
||||||
|
|
||||||
preferences = Preferences(
|
preferences = Preferences(
|
||||||
sightseeing=Preference(type='sightseeing', score = 5),
|
sightseeing=Preference(
|
||||||
nature=Preference(type='nature', score = 5),
|
name='sightseeing',
|
||||||
shopping=Preference(type='shopping', score = 5),
|
type='sightseeing',
|
||||||
|
score = 5),
|
||||||
|
nature=Preference(
|
||||||
|
name='nature',
|
||||||
|
type='nature',
|
||||||
|
score = 5),
|
||||||
|
shopping=Preference(
|
||||||
|
name='shopping',
|
||||||
|
type='shopping',
|
||||||
|
score = 5),
|
||||||
|
|
||||||
max_time_minute=180,
|
max_time_minute=180,
|
||||||
detour_tolerance_minute=30
|
detour_tolerance_minute=30
|
||||||
|
@@ -1,34 +0,0 @@
|
|||||||
from fastapi.testclient import TestClient
|
|
||||||
import pytest
|
|
||||||
from ..main import app
|
|
||||||
|
|
||||||
@pytest.fixture()
|
|
||||||
def client():
|
|
||||||
return TestClient(app)
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_trip_invalid_prefs(client):
|
|
||||||
response = client.post(
|
|
||||||
"/trip/new",
|
|
||||||
json={
|
|
||||||
"preferences": {},
|
|
||||||
"start": [48.8566, 2.3522]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
assert response.status_code == 422
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_trip_single_prefs(client):
|
|
||||||
response = client.post(
|
|
||||||
"/trip/new",
|
|
||||||
json={
|
|
||||||
"preferences": {"sightseeing": {"type": "sightseeing", "score": 1}, "nature": {"type": "nature", "score": 1}, "shopping": {"type": "shopping", "score": 1}, "max_time_minute": 360, "detour_tolerance_minute": 0},
|
|
||||||
"start": [48.8566, 2.3522]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
def test_new_trip_matches_prefs(client):
|
|
||||||
# todo
|
|
||||||
pass
|
|
@@ -1,9 +1,9 @@
|
|||||||
import yaml
|
import yaml
|
||||||
from geopy.distance import geodesic
|
from geopy.distance import geodesic
|
||||||
|
|
||||||
from ..constants import OPTIMIZER_PARAMETERS_PATH
|
import constants
|
||||||
|
|
||||||
with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
||||||
parameters = yaml.safe_load(f)
|
parameters = yaml.safe_load(f)
|
||||||
DETOUR_FACTOR = parameters['detour_factor']
|
DETOUR_FACTOR = parameters['detour_factor']
|
||||||
AVERAGE_WALKING_SPEED = parameters['average_walking_speed']
|
AVERAGE_WALKING_SPEED = parameters['average_walking_speed']
|
||||||
|
@@ -9,10 +9,15 @@ from pywikibot import config
|
|||||||
config.put_throttle = 0
|
config.put_throttle = 0
|
||||||
config.maxlag = 0
|
config.maxlag = 0
|
||||||
|
|
||||||
from ..structs.preferences import Preferences, Preference
|
from structs.preferences import Preferences, Preference
|
||||||
from ..structs.landmark import Landmark
|
from structs.landmark import Landmark
|
||||||
from .take_most_important import take_most_important
|
from .take_most_important import take_most_important
|
||||||
from ..constants import AMENITY_SELECTORS_PATH, LANDMARK_PARAMETERS_PATH, OPTIMIZER_PARAMETERS_PATH, OSM_CACHE_DIR
|
import constants
|
||||||
|
|
||||||
|
|
||||||
|
SIGHTSEEING = 'sightseeing'
|
||||||
|
NATURE = 'nature'
|
||||||
|
SHOPPING = 'shopping'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -20,6 +25,7 @@ class LandmarkManager:
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
city_bbox_side: int # bbox side in meters
|
||||||
radius_close_to: int # radius in meters
|
radius_close_to: int # radius in meters
|
||||||
church_coeff: float # coeff to adjsut score of churches
|
church_coeff: float # coeff to adjsut score of churches
|
||||||
park_coeff: float # coeff to adjust score of parks
|
park_coeff: float # coeff to adjust score of parks
|
||||||
@@ -29,25 +35,20 @@ class LandmarkManager:
|
|||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
|
||||||
with AMENITY_SELECTORS_PATH.open('r') as f:
|
with constants.AMENITY_SELECTORS_PATH.open('r') as f:
|
||||||
self.amenity_selectors = yaml.safe_load(f)
|
self.amenity_selectors = yaml.safe_load(f)
|
||||||
|
|
||||||
with LANDMARK_PARAMETERS_PATH.open('r') as f:
|
with constants.LANDMARK_PARAMETERS_PATH.open('r') as f:
|
||||||
parameters = yaml.safe_load(f)
|
parameters = yaml.safe_load(f)
|
||||||
self.max_bbox_side = parameters['city_bbox_side']
|
self.city_bbox_side = parameters['city_bbox_side']
|
||||||
self.radius_close_to = parameters['radius_close_to']
|
self.radius_close_to = parameters['radius_close_to']
|
||||||
self.church_coeff = parameters['church_coeff']
|
self.church_coeff = parameters['church_coeff']
|
||||||
self.park_coeff = parameters['park_coeff']
|
self.park_coeff = parameters['park_coeff']
|
||||||
self.tag_coeff = parameters['tag_coeff']
|
self.tag_coeff = parameters['tag_coeff']
|
||||||
self.N_important = parameters['N_important']
|
self.N_important = parameters['N_important']
|
||||||
|
|
||||||
with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
|
||||||
parameters = yaml.safe_load(f)
|
|
||||||
self.walking_speed = parameters['average_walking_speed']
|
|
||||||
self.detour_factor = parameters['detour_factor']
|
|
||||||
|
|
||||||
self.overpass = Overpass()
|
self.overpass = Overpass()
|
||||||
CachingStrategy.use(JSON, cacheDir=OSM_CACHE_DIR)
|
CachingStrategy.use(JSON, cacheDir=constants.OSM_CACHE_DIR)
|
||||||
|
|
||||||
|
|
||||||
def generate_landmarks_list(self, center_coordinates: tuple[float, float], preferences: Preferences) -> tuple[list[Landmark], list[Landmark]]:
|
def generate_landmarks_list(self, center_coordinates: tuple[float, float], preferences: Preferences) -> tuple[list[Landmark], list[Landmark]]:
|
||||||
@@ -68,33 +69,30 @@ class LandmarkManager:
|
|||||||
- A list of the most important landmarks based on the user's preferences.
|
- A list of the most important landmarks based on the user's preferences.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
max_walk_dist = (preferences.max_time_minute/2)/60*self.walking_speed*1000/self.detour_factor
|
|
||||||
reachable_bbox_side = min(max_walk_dist, self.max_bbox_side)
|
|
||||||
|
|
||||||
L = []
|
L = []
|
||||||
bbox = self.create_bbox(center_coordinates, reachable_bbox_side)
|
bbox = self.create_bbox(center_coordinates)
|
||||||
# list for sightseeing
|
# list for sightseeing
|
||||||
if preferences.sightseeing.score != 0:
|
if preferences.sightseeing.score != 0:
|
||||||
score_function = lambda loc, n_tags: int((((n_tags**1.2)*self.tag_coeff) )*self.church_coeff) # self.count_elements_close_to(loc) +
|
score_function = lambda loc, n_tags: int((self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff) )*self.church_coeff)
|
||||||
L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], preferences.sightseeing.type, score_function)
|
L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], SIGHTSEEING, score_function)
|
||||||
|
self.correct_score(L1, preferences.sightseeing)
|
||||||
L += L1
|
L += L1
|
||||||
|
|
||||||
# list for nature
|
# list for nature
|
||||||
if preferences.nature.score != 0:
|
if preferences.nature.score != 0:
|
||||||
score_function = lambda loc, n_tags: int((((n_tags**1.2)*self.tag_coeff) )*self.park_coeff) # self.count_elements_close_to(loc) +
|
score_function = lambda loc, n_tags: int((self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff) )*self.park_coeff)
|
||||||
L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], preferences.nature.type, score_function)
|
L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], NATURE, score_function)
|
||||||
|
self.correct_score(L2, preferences.nature)
|
||||||
L += L2
|
L += L2
|
||||||
|
|
||||||
# list for shopping
|
# list for shopping
|
||||||
if preferences.shopping.score != 0:
|
if preferences.shopping.score != 0:
|
||||||
score_function = lambda loc, n_tags: int(((n_tags**1.2)*self.tag_coeff)) # self.count_elements_close_to(loc) +
|
score_function = lambda loc, n_tags: int(self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff))
|
||||||
L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], preferences.shopping.type, score_function)
|
L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], SHOPPING, score_function)
|
||||||
|
self.correct_score(L3, preferences.shopping)
|
||||||
L += L3
|
L += L3
|
||||||
|
|
||||||
|
|
||||||
L = self.remove_duplicates(L)
|
L = self.remove_duplicates(L)
|
||||||
self.correct_score(L, preferences)
|
|
||||||
|
|
||||||
L_constrained = take_most_important(L, self.N_important)
|
L_constrained = take_most_important(L, self.N_important)
|
||||||
self.logger.info(f'Generated {len(L)} landmarks around {center_coordinates}, and constrained to {len(L_constrained)} most important ones.')
|
self.logger.info(f'Generated {len(L)} landmarks around {center_coordinates}, and constrained to {len(L_constrained)} most important ones.')
|
||||||
|
|
||||||
@@ -125,7 +123,7 @@ class LandmarkManager:
|
|||||||
return L_clean
|
return L_clean
|
||||||
|
|
||||||
|
|
||||||
def correct_score(self, landmarks: list[Landmark], preferences: Preferences) -> None:
|
def correct_score(self, landmarks: list[Landmark], preference: Preference):
|
||||||
"""
|
"""
|
||||||
Adjust the attractiveness score of each landmark in the list based on user preferences.
|
Adjust the attractiveness score of each landmark in the list based on user preferences.
|
||||||
|
|
||||||
@@ -134,16 +132,20 @@ class LandmarkManager:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
landmarks (list[Landmark]): A list of landmarks whose scores need to be corrected.
|
landmarks (list[Landmark]): A list of landmarks whose scores need to be corrected.
|
||||||
preferences (Preferences): The user's preference settings that influence the attractiveness score adjustment.
|
preference (Preference): The user's preference settings that influence the attractiveness score adjustment.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If the type of any landmark in the list does not match the expected type in the preference.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
score_dict = {
|
if len(landmarks) == 0:
|
||||||
preferences.sightseeing.type: preferences.sightseeing.score,
|
return
|
||||||
preferences.nature.type: preferences.nature.score,
|
|
||||||
preferences.shopping.type: preferences.shopping.score
|
if landmarks[0].type != preference.type:
|
||||||
}
|
raise TypeError(f"LandmarkType {preference.type} does not match the type of Landmark {landmarks[0].name}")
|
||||||
for landmark in landmarks:
|
|
||||||
landmark.attractiveness = int(landmark.attractiveness * score_dict[landmark.type] / 5)
|
for elem in landmarks:
|
||||||
|
elem.attractiveness = int(elem.attractiveness*preference.score/5) # arbitrary computation
|
||||||
|
|
||||||
|
|
||||||
def count_elements_close_to(self, coordinates: tuple[float, float]) -> int:
|
def count_elements_close_to(self, coordinates: tuple[float, float]) -> int:
|
||||||
@@ -189,13 +191,12 @@ class LandmarkManager:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def create_bbox(self, coordinates: tuple[float, float], reachable_bbox_side: int) -> tuple[float, float, float, float]:
|
def create_bbox(self, coordinates: tuple[float, float]) -> tuple[float, float, float, float]:
|
||||||
"""
|
"""
|
||||||
Create a bounding box around the given coordinates.
|
Create a bounding box around the given coordinates.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
coordinates (tuple[float, float]): The latitude and longitude of the center of the bounding box.
|
coordinates (tuple[float, float]): The latitude and longitude of the center of the bounding box.
|
||||||
reachable_bbox_side (int): The side length of the bounding box in meters.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple[float, float, float, float]: The minimum latitude, minimum longitude, maximum latitude, and maximum longitude
|
tuple[float, float, float, float]: The minimum latitude, minimum longitude, maximum latitude, and maximum longitude
|
||||||
@@ -206,7 +207,7 @@ class LandmarkManager:
|
|||||||
lon = coordinates[1]
|
lon = coordinates[1]
|
||||||
|
|
||||||
# Half the side length in km (since it's a square bbox)
|
# Half the side length in km (since it's a square bbox)
|
||||||
half_side_length_km = reachable_bbox_side / 2 / 1000
|
half_side_length_km = self.city_bbox_side / 2 / 1000
|
||||||
|
|
||||||
# Convert distance to degrees
|
# Convert distance to degrees
|
||||||
lat_diff = half_side_length_km / 111 # 1 degree latitude is approximately 111 km
|
lat_diff = half_side_length_km / 111 # 1 degree latitude is approximately 111 km
|
||||||
@@ -295,26 +296,21 @@ class LandmarkManager:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if "wikipedia" in tag:
|
if "wikipedia" in tag:
|
||||||
n_tags += 1 # wikipedia entries count more
|
n_tags += 3 # wikipedia entries count more
|
||||||
|
|
||||||
# if tag == "wikidata":
|
if tag == "wikidata":
|
||||||
# Q = elem.tag('wikidata')
|
Q = elem.tag('wikidata')
|
||||||
# site = Site("wikidata", "wikidata")
|
site = Site("wikidata", "wikidata")
|
||||||
# item = ItemPage(site, Q)
|
item = ItemPage(site, Q)
|
||||||
# item.get()
|
item.get()
|
||||||
# n_languages = len(item.labels)
|
n_languages = len(item.labels)
|
||||||
# n_tags += n_languages/10
|
n_tags += n_languages/10
|
||||||
if "viewpoint" in tag:
|
|
||||||
n_tags += 10
|
|
||||||
|
|
||||||
if elem_type != "nature":
|
if elem_type != "nature":
|
||||||
if "leisure" in tag and elem.tag('leisure') == "park":
|
if "leisure" in tag and elem.tag('leisure') == "park":
|
||||||
elem_type = "nature"
|
elem_type = "nature"
|
||||||
|
|
||||||
if elem_type == "nature":
|
if landmarktype != SHOPPING:
|
||||||
n_tags += 1
|
|
||||||
|
|
||||||
if landmarktype != "shopping":
|
|
||||||
if "shop" in tag:
|
if "shop" in tag:
|
||||||
skip = True
|
skip = True
|
||||||
break
|
break
|
||||||
@@ -322,6 +318,7 @@ class LandmarkManager:
|
|||||||
if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']:
|
if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']:
|
||||||
skip = True
|
skip = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if skip:
|
if skip:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@@ -5,9 +5,9 @@ from scipy.optimize import linprog
|
|||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
from geopy.distance import geodesic
|
from geopy.distance import geodesic
|
||||||
|
|
||||||
from ..structs.landmark import Landmark
|
from structs.landmark import Landmark
|
||||||
from .get_time_separation import get_time
|
from .get_time_separation import get_time
|
||||||
from ..constants import OPTIMIZER_PARAMETERS_PATH
|
import constants
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ class Optimizer:
|
|||||||
def __init__(self) :
|
def __init__(self) :
|
||||||
|
|
||||||
# load parameters from file
|
# load parameters from file
|
||||||
with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
||||||
parameters = yaml.safe_load(f)
|
parameters = yaml.safe_load(f)
|
||||||
self.detour_factor = parameters['detour_factor']
|
self.detour_factor = parameters['detour_factor']
|
||||||
self.average_walking_speed = parameters['average_walking_speed']
|
self.average_walking_speed = parameters['average_walking_speed']
|
||||||
|
@@ -3,10 +3,10 @@ import yaml, logging
|
|||||||
from shapely import buffer, LineString, Point, Polygon, MultiPoint, concave_hull
|
from shapely import buffer, LineString, Point, Polygon, MultiPoint, concave_hull
|
||||||
from math import pi
|
from math import pi
|
||||||
|
|
||||||
from ..structs.landmark import Landmark
|
from structs.landmark import Landmark
|
||||||
from . import take_most_important, get_time_separation
|
from . import take_most_important, get_time_separation
|
||||||
from .optimizer import Optimizer
|
from .optimizer import Optimizer
|
||||||
from ..constants import OPTIMIZER_PARAMETERS_PATH
|
import constants
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ class Refiner :
|
|||||||
self.optimizer = optimizer
|
self.optimizer = optimizer
|
||||||
|
|
||||||
# load parameters from file
|
# load parameters from file
|
||||||
with OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f:
|
||||||
parameters = yaml.safe_load(f)
|
parameters = yaml.safe_load(f)
|
||||||
self.detour_factor = parameters['detour_factor']
|
self.detour_factor = parameters['detour_factor']
|
||||||
self.detour_corridor_width = parameters['detour_corridor_width']
|
self.detour_corridor_width = parameters['detour_corridor_width']
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from ..structs.landmark import Landmark
|
from structs.landmark import Landmark
|
||||||
|
|
||||||
def take_most_important(landmarks: list[Landmark], N_important) -> list[Landmark] :
|
def take_most_important(landmarks: list[Landmark], N_important) -> list[Landmark] :
|
||||||
L = len(landmarks)
|
L = len(landmarks)
|
||||||
|
@@ -42,7 +42,7 @@ if (secretPropertiesFile.exists()) {
|
|||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace "com.anydev.anyway"
|
namespace "com.example.fast_network_navigation"
|
||||||
compileSdk flutter.compileSdkVersion
|
compileSdk flutter.compileSdkVersion
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId "com.anydev.anyway"
|
applicationId "com.example.fast_network_navigation"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
// Minimum Android version for Google Maps SDK
|
// Minimum Android version for Google Maps SDK
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="anyway"
|
android:label="fast_network_navigation"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package com.anydev.anyway
|
package com.example.fast_network_navigation
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>anyway</string>
|
<string>fast_network_navigation</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
const String APP_NAME = 'AnyWay';
|
|
||||||
|
|
||||||
String API_URL_BASE = 'https://anyway.kluster.moll.re';
|
|
||||||
|
|
@@ -1,29 +1,23 @@
|
|||||||
import 'dart:collection';
|
import 'package:fast_network_navigation/modules/trips_overview.dart';
|
||||||
|
import 'package:fast_network_navigation/pages/new_trip.dart';
|
||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:fast_network_navigation/pages/tutorial.dart';
|
||||||
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
|
import 'package:fast_network_navigation/utils/load_trips.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:anyway/constants.dart';
|
import 'package:fast_network_navigation/pages/overview.dart';
|
||||||
|
import 'package:fast_network_navigation/pages/profile.dart';
|
||||||
import 'package:anyway/structs/trip.dart';
|
|
||||||
import 'package:anyway/modules/trips_overview.dart';
|
|
||||||
import 'package:anyway/utils/load_trips.dart';
|
|
||||||
|
|
||||||
import 'package:anyway/pages/new_trip.dart';
|
|
||||||
import 'package:anyway/pages/tutorial.dart';
|
|
||||||
import 'package:anyway/pages/overview.dart';
|
|
||||||
import 'package:anyway/pages/profile.dart';
|
|
||||||
|
|
||||||
// BasePage is the scaffold that holds all other pages
|
// BasePage is the scaffold that holds all other pages
|
||||||
// A side drawer is used to switch between pages
|
// A side drawer is used to switch between pages
|
||||||
class BasePage extends StatefulWidget {
|
class BasePage extends StatefulWidget {
|
||||||
final String mainScreen;
|
final String mainScreen;
|
||||||
final Trip? trip;
|
final Future<Trip>? trip;
|
||||||
|
|
||||||
const BasePage({
|
const BasePage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.mainScreen,
|
required this.mainScreen,
|
||||||
this.trip,
|
this.trip
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -49,20 +43,20 @@ class _BasePageState extends State<BasePage> {
|
|||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: Text(APP_NAME)),
|
appBar: AppBar(title: Text("City Nav")),
|
||||||
body: Center(child: currentView),
|
body: Center(child: currentView),
|
||||||
drawer: Drawer(
|
drawer: Drawer(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
DrawerHeader(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(colors: [Colors.red, Colors.yellow])
|
gradient: LinearGradient(colors: [Colors.cyan, theme.primaryColor])
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
APP_NAME,
|
'City Nav',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.grey[800],
|
color: Colors.white,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
@@ -132,71 +126,9 @@ class _BasePageState extends State<BasePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is used to get the first trip from a list of trips
|
|
||||||
// TODO: Implement this function
|
|
||||||
Trip getFirstTrip(Future<List<Trip>> trips) {
|
Future<Trip> getFirstTrip (Future<List<Trip>> trips) async {
|
||||||
Trip t1 = Trip(uuid: '1', landmarks: LinkedList<Landmark>());
|
List<Trip> tripsf = await trips;
|
||||||
t1.landmarks.add(
|
return tripsf[0];
|
||||||
Landmark(
|
|
||||||
uuid: '0',
|
|
||||||
name: "Start",
|
|
||||||
location: [48.85, 2.32],
|
|
||||||
type: start,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
t1.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
uuid: '1',
|
|
||||||
name: "Eiffel Tower",
|
|
||||||
location: [48.859, 2.295],
|
|
||||||
type: sightseeing,
|
|
||||||
imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
t1.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
uuid: "2",
|
|
||||||
name: "Notre Dame Cathedral",
|
|
||||||
location: [48.8530, 2.3498],
|
|
||||||
type: sightseeing,
|
|
||||||
imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
t1.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
uuid: "3",
|
|
||||||
name: "Louvre palace",
|
|
||||||
location: [48.8606, 2.3376],
|
|
||||||
type: sightseeing,
|
|
||||||
imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
t1.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
uuid: "4",
|
|
||||||
name: "Pont-des-arts",
|
|
||||||
location: [48.8585, 2.3376],
|
|
||||||
type: sightseeing,
|
|
||||||
imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
t1.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
uuid: "5",
|
|
||||||
name: "Panthéon",
|
|
||||||
location: [48.847, 2.347],
|
|
||||||
type: sightseeing,
|
|
||||||
imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
t1.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
uuid: "6",
|
|
||||||
name: "Galeries Lafayette",
|
|
||||||
location: [48.87, 2.32],
|
|
||||||
type: shopping,
|
|
||||||
imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/GaleriesLafayetteNuit.jpg/220px-GaleriesLafayetteNuit.jpg"
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return t1;
|
|
||||||
}
|
}
|
@@ -1,19 +1,19 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:anyway/constants.dart';
|
import 'package:fast_network_navigation/layout.dart';
|
||||||
import 'package:anyway/layout.dart';
|
|
||||||
|
|
||||||
void main() => runApp(const App());
|
void main() => runApp(const App());
|
||||||
|
|
||||||
class App extends StatelessWidget {
|
class App extends StatelessWidget {
|
||||||
const App({super.key});
|
const App({super.key});
|
||||||
|
|
||||||
|
static const appTitle = 'City Nav';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: APP_NAME,
|
title: appTitle,
|
||||||
home: BasePage(mainScreen: "map"),
|
home: BasePage(mainScreen: "map"),
|
||||||
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.red[600]),
|
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.green),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
import 'dart:developer';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Greeter extends StatefulWidget {
|
class Greeter extends StatefulWidget {
|
||||||
final Trip trip;
|
final Future<Trip> trip;
|
||||||
|
final bool standalone;
|
||||||
|
|
||||||
Greeter({
|
Greeter({
|
||||||
required this.trip,
|
required this.standalone,
|
||||||
|
required this.trip
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -17,92 +16,44 @@ class Greeter extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class _GreeterState extends State<Greeter> {
|
class _GreeterState extends State<Greeter> {
|
||||||
|
Widget greeterBuild (BuildContext context, AsyncSnapshot<Trip> snapshot) {
|
||||||
Widget greeterBuilder (BuildContext context, Widget? child) {
|
|
||||||
ThemeData theme = Theme.of(context);
|
ThemeData theme = Theme.of(context);
|
||||||
TextStyle greeterStyle = TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24);
|
String cityName = "";
|
||||||
|
|
||||||
Widget topGreeter;
|
|
||||||
|
|
||||||
if (widget.trip.uuid != 'pending') {
|
|
||||||
topGreeter = FutureBuilder(
|
|
||||||
future: widget.trip.cityName,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return AutoSizeText(
|
cityName = snapshot.data?.cityName ?? '...';
|
||||||
maxLines: 1,
|
|
||||||
'Welcome to ${snapshot.data}!',
|
|
||||||
style: greeterStyle
|
|
||||||
);
|
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
log('Error while fetching city name');
|
cityName = "error";
|
||||||
return AutoSizeText(
|
} else { // still awaiting the cityname
|
||||||
maxLines: 1,
|
cityName = "...";
|
||||||
'Welcome to your trip!',
|
|
||||||
style: greeterStyle
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return AutoSizeText(
|
|
||||||
maxLines: 1,
|
|
||||||
'Welcome to ...',
|
|
||||||
style: greeterStyle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget topGreeter = Text(
|
||||||
|
'Welcome to $cityName!',
|
||||||
|
style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// still awaiting the trip
|
if (widget.standalone) {
|
||||||
// We can hopefully infer the city name from the cityName future
|
return Center(
|
||||||
// Show a linear loader at the bottom and an info message above
|
child: Padding(
|
||||||
topGreeter = Column(
|
padding: EdgeInsets.only(top: 24.0),
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
child: topGreeter,
|
||||||
children: [
|
|
||||||
FutureBuilder(
|
|
||||||
future: widget.trip.cityName,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
|
||||||
return AutoSizeText(
|
|
||||||
maxLines: 1,
|
|
||||||
'Generating your trip to ${snapshot.data}...',
|
|
||||||
style: greeterStyle
|
|
||||||
);
|
|
||||||
} else if (snapshot.hasError) {
|
|
||||||
// the exact error is shown in the central part of the trip overview. No need to show it here
|
|
||||||
return AutoSizeText(
|
|
||||||
maxLines: 1,
|
|
||||||
'Error while loading trip.',
|
|
||||||
style: greeterStyle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return AutoSizeText(
|
|
||||||
maxLines: 1,
|
|
||||||
'Generating your trip...',
|
|
||||||
style: greeterStyle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.all(5),
|
|
||||||
child: const LinearProgressIndicator()
|
|
||||||
)
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Padding(padding: EdgeInsets.only(top: 20)),
|
Padding(padding: EdgeInsets.only(top: 24.0)),
|
||||||
topGreeter,
|
topGreeter,
|
||||||
Padding(
|
bottomGreeter,
|
||||||
padding: EdgeInsets.all(20),
|
Padding(padding: EdgeInsets.only(bottom: 24.0)),
|
||||||
child: bottomGreeter
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget bottomGreeter = const Text(
|
Widget bottomGreeter = const Text(
|
||||||
"Busy day ahead? Here is how to make the most of it!",
|
"Busy day ahead? Here is how to make the most of it!",
|
||||||
@@ -114,9 +65,9 @@ class _GreeterState extends State<Greeter> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListenableBuilder(
|
return FutureBuilder(
|
||||||
listenable: widget.trip,
|
future: widget.trip,
|
||||||
builder: greeterBuilder,
|
builder: greeterBuild,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,4 @@
|
|||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -32,10 +31,9 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
|||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
// force a fixed width
|
// force a fixed width
|
||||||
width: 160,
|
width: 160,
|
||||||
child: CachedNetworkImage(
|
child: Image.network(
|
||||||
imageUrl: widget.landmark.imageURL ?? '',
|
widget.landmark.imageURL ?? '',
|
||||||
placeholder: (context, url) => CircularProgressIndicator(),
|
errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
|
||||||
errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
|
|
||||||
// TODO: make this a switch statement to load a placeholder if null
|
// TODO: make this a switch statement to load a placeholder if null
|
||||||
// cover the whole container meaning the image will be cropped
|
// cover the whole container meaning the image will be cropped
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
@@ -1,16 +1,17 @@
|
|||||||
import 'dart:developer';
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:fast_network_navigation/modules/landmark_card.dart';
|
||||||
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
|
|
||||||
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'package:anyway/modules/landmark_card.dart';
|
|
||||||
import 'package:anyway/structs/landmark.dart';
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LandmarksOverview extends StatefulWidget {
|
class LandmarksOverview extends StatefulWidget {
|
||||||
final Trip? trip;
|
final Future<Trip>? trip;
|
||||||
const LandmarksOverview({super.key, this.trip});
|
const LandmarksOverview({super.key, this.trip});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -18,37 +19,22 @@ class LandmarksOverview extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LandmarksOverviewState extends State<LandmarksOverview> {
|
class _LandmarksOverviewState extends State<LandmarksOverview> {
|
||||||
|
// final Future<List<Landmark>> _landmarks = fetchLandmarks();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListenableBuilder(
|
final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip);
|
||||||
listenable: widget.trip!,
|
return DefaultTextStyle(
|
||||||
builder: (BuildContext context, Widget? child) {
|
style: Theme.of(context).textTheme.displayMedium!,
|
||||||
Trip trip = widget.trip!;
|
textAlign: TextAlign.center,
|
||||||
log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks");
|
child: FutureBuilder<LinkedList<Landmark>>(
|
||||||
|
future: _landmarks,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) {
|
||||||
List<Widget> children;
|
List<Widget> children;
|
||||||
|
if (snapshot.hasData) {
|
||||||
if (trip.uuid != 'pending' && trip.uuid != 'error') {
|
children = [landmarksWithSteps(snapshot.data!), saveButton()];
|
||||||
log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks");
|
} else if (snapshot.hasError) {
|
||||||
if (trip.landmarks.length <= 1) {
|
children = <Widget>[
|
||||||
children = [
|
|
||||||
const Text("No landmarks in this trip"),
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
children = [
|
|
||||||
landmarksWithSteps(),
|
|
||||||
saveButton(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} else if(trip.uuid == 'pending') {
|
|
||||||
// the trip is still being fetched from the api
|
|
||||||
children = [Center(child: CircularProgressIndicator())];
|
|
||||||
} else {
|
|
||||||
// trip.uuid == 'error'
|
|
||||||
// show the error raised by the api
|
|
||||||
// String error =
|
|
||||||
children = [
|
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.error_outline,
|
Icons.error_outline,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
@@ -56,15 +42,20 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16),
|
padding: const EdgeInsets.only(top: 16),
|
||||||
child: Text('Error: ${trip.errorDescription}'),
|
child: Text('Error: ${snapshot.error}', style: TextStyle(fontSize: 12)),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
} else {
|
||||||
|
children = [Center(child: CircularProgressIndicator())];
|
||||||
}
|
}
|
||||||
|
return Center(
|
||||||
return Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: children,
|
children: children,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Widget saveButton() => ElevatedButton(
|
Widget saveButton() => ElevatedButton(
|
||||||
@@ -76,56 +67,29 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
|
|||||||
child: const Text('Save'),
|
child: const Text('Save'),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget landmarksWithSteps() {
|
}
|
||||||
return ListenableBuilder(
|
|
||||||
listenable: widget.trip!,
|
Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
|
||||||
builder: (BuildContext context, Widget? child) {
|
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
for (Landmark landmark in widget.trip!.landmarks) {
|
for (Landmark landmark in landmarks) {
|
||||||
children.add(
|
children.add(LandmarkCard(landmark));
|
||||||
Dismissible(
|
|
||||||
key: ValueKey<int>(landmark.hashCode),
|
|
||||||
child: LandmarkCard(landmark),
|
|
||||||
dismissThresholds: {DismissDirection.endToStart: 0.6},
|
|
||||||
onDismissed: (direction) {
|
|
||||||
// Remove the item from the data source.
|
|
||||||
log(landmark.name);
|
|
||||||
setState(() {
|
|
||||||
widget.trip!.removeLandmark(landmark);
|
|
||||||
});
|
|
||||||
// Then show a snackbar.
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
.showSnackBar(SnackBar(content: Text("We won't show ${landmark.name} again")));
|
|
||||||
},
|
|
||||||
background: Container(color: Colors.red),
|
|
||||||
secondaryBackground: Container(
|
|
||||||
color: Colors.red,
|
|
||||||
child: Icon(
|
|
||||||
Icons.delete,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.all(15),
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if (landmark.next != null) {
|
if (landmark.next != null) {
|
||||||
Widget step = stepBetweenLandmarks(landmark, landmark.next!);
|
Widget step = stepBetweenLandmarks(landmark, landmark.next!);
|
||||||
children.add(step);
|
children.add(step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: children
|
children: children
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Widget stepBetweenLandmarks(Landmark current, Landmark next) {
|
Widget stepBetweenLandmarks(Landmark before, Landmark after) {
|
||||||
int timeRounded = 5 * (current.tripTime?.inMinutes ?? 0) ~/ 5;
|
// This is a simple widget that draws a line between landmark-cards
|
||||||
// ~/ is integer division (rounding)
|
// It's a vertical dotted line
|
||||||
|
// Next to the line is the icon for the mode of transport (walking for now) and the estimated time
|
||||||
|
// There is also a button to open the navigation instructions as a new intent
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.all(10),
|
margin: EdgeInsets.all(10),
|
||||||
padding: EdgeInsets.all(10),
|
padding: EdgeInsets.all(10),
|
||||||
@@ -144,7 +108,7 @@ Widget stepBetweenLandmarks(Landmark current, Landmark next) {
|
|||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.directions_walk),
|
Icon(Icons.directions_walk),
|
||||||
Text("~$timeRounded min", style: TextStyle(fontSize: 10)),
|
Text("5 min", style: TextStyle(fontSize: 10)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
@@ -152,17 +116,15 @@ Widget stepBetweenLandmarks(Landmark current, Landmark next) {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Open navigation instructions
|
// Open navigation instructions
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Text("Navigate"),
|
||||||
children: [
|
|
||||||
Icon(Icons.directions),
|
|
||||||
Text("Directions"),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async {
|
||||||
|
Trip tripf = await trip!;
|
||||||
|
return tripf.landmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1,16 +1,13 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:anyway/structs/landmark.dart';
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class MapWidget extends StatefulWidget {
|
class MapWidget extends StatefulWidget {
|
||||||
|
|
||||||
final Trip? trip;
|
final Future<Trip>? trip;
|
||||||
|
|
||||||
MapWidget({
|
MapWidget({
|
||||||
this.trip
|
this.trip
|
||||||
@@ -22,130 +19,58 @@ class MapWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _MapWidgetState extends State<MapWidget> {
|
class _MapWidgetState extends State<MapWidget> {
|
||||||
late GoogleMapController mapController;
|
late GoogleMapController mapController;
|
||||||
|
// coordinates of Paris
|
||||||
CameraPosition _cameraPosition = CameraPosition(
|
CameraPosition _cameraPosition = CameraPosition(
|
||||||
target: LatLng(48.8566, 2.3522),
|
target: LatLng(48.8566, 2.3522),
|
||||||
zoom: 11.0,
|
zoom: 11.0,
|
||||||
);
|
);
|
||||||
Set<Marker> mapMarkers = <Marker>{};
|
Set<Marker> markers = <Marker>{};
|
||||||
|
|
||||||
|
|
||||||
void _onMapCreated(GoogleMapController controller) async {
|
void _onMapCreated(GoogleMapController controller) async {
|
||||||
mapController = controller;
|
mapController = controller;
|
||||||
List<double>? newLocation = widget.trip?.landmarks.firstOrNull?.location;
|
Trip? trip = await widget.trip;
|
||||||
|
List<double>? newLocation = trip?.landmarks.first.location;
|
||||||
if (newLocation != null) {
|
if (newLocation != null) {
|
||||||
CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
|
CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
|
||||||
controller.moveCamera(update);
|
controller.moveCamera(update);
|
||||||
}
|
}
|
||||||
setMapMarkers();
|
drawLandmarks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _onCameraIdle() {
|
void _onCameraIdle() {
|
||||||
// print(mapController.getLatLng(ScreenCoordinate(x: 0, y: 0)));
|
// print(mapController.getLatLng(ScreenCoordinate(x: 0, y: 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void setMapMarkers() async {
|
void drawLandmarks() async {
|
||||||
List<Landmark> landmarks = widget.trip?.landmarks.toList() ?? [];
|
// (re)draws landmarks on the map
|
||||||
Set<Marker> newMarkers = <Marker>{};
|
Trip? trip = await widget.trip;
|
||||||
for (int i = 0; i < landmarks.length; i++) {
|
LinkedList<Landmark>? landmarks = trip?.landmarks;
|
||||||
Landmark landmark = landmarks[i];
|
if (landmarks != null){
|
||||||
List<double> location = landmark.location;
|
|
||||||
Marker marker = Marker(
|
|
||||||
markerId: MarkerId(landmark.uuid),
|
|
||||||
position: LatLng(location[0], location[1]),
|
|
||||||
icon: await CustomMarker(landmark: landmark, position: i).toBitmapDescriptor(
|
|
||||||
logicalSize: const Size(150, 150),
|
|
||||||
imageSize: const Size(150, 150)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
newMarkers.add(marker);
|
|
||||||
}
|
|
||||||
setState(() {
|
setState(() {
|
||||||
mapMarkers = newMarkers;
|
for (Landmark landmark in landmarks) {
|
||||||
|
markers.add(Marker(
|
||||||
|
markerId: MarkerId(landmark.name),
|
||||||
|
position: LatLng(landmark.location[0], landmark.location[1]),
|
||||||
|
infoWindow: InfoWindow(title: landmark.name, snippet: landmark.type.name),
|
||||||
|
));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
widget.trip?.addListener(setMapMarkers);
|
|
||||||
return GoogleMap(
|
return GoogleMap(
|
||||||
onMapCreated: _onMapCreated,
|
onMapCreated: _onMapCreated,
|
||||||
initialCameraPosition: _cameraPosition,
|
initialCameraPosition: _cameraPosition,
|
||||||
onCameraIdle: _onCameraIdle,
|
onCameraIdle: _onCameraIdle,
|
||||||
// onLongPress: ,
|
// onLongPress: ,
|
||||||
markers: mapMarkers,
|
markers: markers,
|
||||||
cloudMapId: '41c21ac9b81dbfd8',
|
cloudMapId: '41c21ac9b81dbfd8',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CustomMarker extends StatelessWidget {
|
|
||||||
final Landmark landmark;
|
|
||||||
final int position;
|
|
||||||
|
|
||||||
CustomMarker({
|
|
||||||
super.key,
|
|
||||||
required this.landmark,
|
|
||||||
required this.position
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// This returns an outlined circle, with an icon corresponding to the landmark type
|
|
||||||
// As a small dot, the number of the landmark is displayed in the top right
|
|
||||||
Icon icon;
|
|
||||||
if (landmark.type == sightseeing) {
|
|
||||||
icon = Icon(Icons.church, color: Colors.black, size: 50);
|
|
||||||
} else if (landmark.type == nature) {
|
|
||||||
icon = Icon(Icons.park, color: Colors.black, size: 50);
|
|
||||||
} else if (landmark.type == shopping) {
|
|
||||||
icon = Icon(Icons.shopping_cart, color: Colors.black, size: 50);
|
|
||||||
} else if (landmark.type == start || landmark.type == finish) {
|
|
||||||
icon = Icon(Icons.flag, color: Colors.black, size: 50);
|
|
||||||
} else {
|
|
||||||
icon = Icon(Icons.location_on, color: Colors.black, size: 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget? positionIndicator;
|
|
||||||
if (landmark.type != start && landmark.type != finish) {
|
|
||||||
positionIndicator = Positioned(
|
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.all(5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Text('$position', style: TextStyle(color: Colors.white, fontSize: 20)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return RepaintBoundary(
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
// these are not the final sizes, since the final size is set in the toBitmapDescriptor method
|
|
||||||
// they are useful nevertheless to ensure the scale of the components are correct
|
|
||||||
width: 75,
|
|
||||||
height: 75,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [Colors.red, Colors.yellow]
|
|
||||||
),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(color: Colors.black, width: 5),
|
|
||||||
),
|
|
||||||
child: icon,
|
|
||||||
),
|
|
||||||
positionIndicator ?? Container(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:anyway/layout.dart';
|
import 'package:fast_network_navigation/layout.dart';
|
||||||
import 'package:anyway/structs/trip.dart';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
|
|
||||||
|
|
||||||
class TripsOverview extends StatefulWidget {
|
class TripsOverview extends StatefulWidget {
|
||||||
@@ -25,23 +25,12 @@ class _TripsOverviewState extends State<TripsOverview> {
|
|||||||
children = List<Widget>.generate(snapshot.data!.length, (index) {
|
children = List<Widget>.generate(snapshot.data!.length, (index) {
|
||||||
Trip trip = snapshot.data![index];
|
Trip trip = snapshot.data![index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: FutureBuilder(
|
title: Text("Trip to ${trip.cityName}"),
|
||||||
future: trip.cityName,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
|
||||||
return Text("Trip to ${snapshot.data}");
|
|
||||||
} else if (snapshot.hasError) {
|
|
||||||
return Text("Error: ${snapshot.error}");
|
|
||||||
} else {
|
|
||||||
return const Text("Trip to ...");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
leading: Icon(Icons.pin_drop),
|
leading: Icon(Icons.pin_drop),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => BasePage(mainScreen: "map", trip: trip)
|
builder: (context) => BasePage(mainScreen: "map", trip: Future.value(trip))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@@ -1,13 +1,5 @@
|
|||||||
import 'package:anyway/structs/landmark.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:geocoding/geocoding.dart';
|
|
||||||
|
|
||||||
import 'package:anyway/layout.dart';
|
|
||||||
import 'package:anyway/utils/fetch_trip.dart';
|
|
||||||
import 'package:anyway/structs/preferences.dart';
|
|
||||||
import "package:anyway/structs/trip.dart";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NewTripPage extends StatefulWidget {
|
class NewTripPage extends StatefulWidget {
|
||||||
const NewTripPage({Key? key}) : super(key: key);
|
const NewTripPage({Key? key}) : super(key: key);
|
||||||
@@ -17,77 +9,22 @@ class NewTripPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _NewTripPageState extends State<NewTripPage> {
|
class _NewTripPageState extends State<NewTripPage> {
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
||||||
final TextEditingController latController = TextEditingController();
|
|
||||||
final TextEditingController lonController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('New Trip'),
|
title: const Text('New Trip'),
|
||||||
),
|
),
|
||||||
body: Form(
|
body: Center(
|
||||||
key: _formKey,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(15.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
TextFormField(
|
const Text(
|
||||||
decoration: const InputDecoration(hintText: 'Lat'),
|
'Create a new trip',
|
||||||
controller: latController,
|
|
||||||
validator: (String? value) {
|
|
||||||
if (value == null || value.isEmpty || double.tryParse(value) == null){
|
|
||||||
return 'Please enter a floating point number';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextFormField(
|
|
||||||
decoration: const InputDecoration(hintText: 'Lon'),
|
|
||||||
controller: lonController,
|
|
||||||
|
|
||||||
validator: (String? value) {
|
|
||||||
if (value == null || value.isEmpty || double.tryParse(value) == null){
|
|
||||||
return 'Please enter a floating point number';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Divider(height: 15, color: Colors.transparent),
|
|
||||||
ElevatedButton(
|
|
||||||
child: const Text('Create trip'),
|
|
||||||
onPressed: () {
|
|
||||||
if (_formKey.currentState!.validate()) {
|
|
||||||
List<double> startPoint = [
|
|
||||||
double.parse(latController.text),
|
|
||||||
double.parse(lonController.text)
|
|
||||||
];
|
|
||||||
Future<UserPreferences> preferences = loadUserPreferences();
|
|
||||||
Trip trip = Trip();
|
|
||||||
trip.landmarks.add(
|
|
||||||
Landmark(
|
|
||||||
location: startPoint,
|
|
||||||
name: "Start",
|
|
||||||
type: start,
|
|
||||||
uuid: "pending"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
fetchTrip(trip, preferences);
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => BasePage(mainScreen: "map", trip: trip)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,19 +1,19 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
||||||
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
|
|
||||||
import 'package:anyway/modules/landmarks_overview.dart';
|
import 'package:fast_network_navigation/modules/landmarks_overview.dart';
|
||||||
import 'package:anyway/modules/map.dart';
|
import 'package:fast_network_navigation/modules/map.dart';
|
||||||
import 'package:anyway/modules/greeter.dart';
|
import 'package:fast_network_navigation/modules/greeter.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class NavigationOverview extends StatefulWidget {
|
class NavigationOverview extends StatefulWidget {
|
||||||
final Trip trip;
|
final Future<Trip> trip;
|
||||||
|
|
||||||
NavigationOverview({
|
NavigationOverview({
|
||||||
required this.trip,
|
required this.trip
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -27,56 +27,53 @@ class _NavigationOverviewState extends State<NavigationOverview> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SlidingUpPanel(
|
return SlidingUpPanel(
|
||||||
|
renderPanelSheet: false,
|
||||||
panel: _floatingPanel(),
|
panel: _floatingPanel(),
|
||||||
// collapsed: _floatingCollapsed(),
|
collapsed: _floatingCollapsed(),
|
||||||
body: MapWidget(trip: widget.trip),
|
body: MapWidget(trip: widget.trip)
|
||||||
// renderPanelSheet: false,
|
|
||||||
// backdropEnabled: true,
|
|
||||||
maxHeight: MediaQuery.of(context).size.height * 0.8,
|
|
||||||
padding: EdgeInsets.all(10),
|
|
||||||
// panelSnapping: false,
|
|
||||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
blurRadius: 20.0,
|
|
||||||
color: Colors.black,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _floatingCollapsed(){
|
Widget _floatingCollapsed(){
|
||||||
return Greeter(
|
final ThemeData theme = Theme.of(context);
|
||||||
trip: widget.trip
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: theme.canvasColor,
|
||||||
|
borderRadius: BorderRadius.only(topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)),
|
||||||
|
boxShadow: []
|
||||||
|
),
|
||||||
|
|
||||||
|
child: Greeter(standalone: true, trip: widget.trip)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _floatingPanel(){
|
Widget _floatingPanel(){
|
||||||
return Column(
|
final ThemeData theme = Theme.of(context);
|
||||||
children: [
|
return Container(
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(15),
|
|
||||||
child:
|
|
||||||
Center(
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 5,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey[300],
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12.0)),
|
borderRadius: BorderRadius.all(Radius.circular(24.0)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
blurRadius: 20.0,
|
||||||
|
color: theme.shadowColor,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: ListView(
|
|
||||||
children: [
|
|
||||||
Greeter(trip: widget.trip),
|
|
||||||
LandmarksOverview(trip: widget.trip)
|
|
||||||
]
|
]
|
||||||
)
|
),
|
||||||
)
|
child: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Greeter(standalone: false, trip: widget.trip),
|
||||||
|
LandmarksOverview(trip: widget.trip),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,7 @@
|
|||||||
import 'package:anyway/constants.dart';
|
import 'package:fast_network_navigation/structs/preferences.dart';
|
||||||
import 'package:anyway/structs/preferences.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
bool debugMode = false;
|
|
||||||
|
|
||||||
class ProfilePage extends StatefulWidget {
|
class ProfilePage extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@@ -11,56 +9,6 @@ class ProfilePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ProfilePageState extends State<ProfilePage> {
|
class _ProfilePageState extends State<ProfilePage> {
|
||||||
Future<UserPreferences> _prefs = loadUserPreferences();
|
|
||||||
|
|
||||||
|
|
||||||
Widget debugButton() {
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(top: 20),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text('Debug mode'),
|
|
||||||
Switch(
|
|
||||||
value: debugMode,
|
|
||||||
onChanged: (bool? newValue) {
|
|
||||||
setState(() {
|
|
||||||
debugMode = newValue!;
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text('Debug mode - custom API'),
|
|
||||||
content: TextField(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'http://localhost:8000'
|
|
||||||
),
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
API_URL_BASE = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
child: Text('OK'),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView(
|
return ListView(
|
||||||
@@ -76,82 +24,66 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||||||
child: Text('Curious traveler', style: TextStyle(fontSize: 24))
|
child: Text('Curious traveler', style: TextStyle(fontSize: 24))
|
||||||
),
|
),
|
||||||
|
|
||||||
Divider(indent: 25, endIndent: 25, height: 50),
|
Padding(padding: EdgeInsets.all(10)),
|
||||||
|
Divider(indent: 25, endIndent: 25),
|
||||||
|
Padding(padding: EdgeInsets.all(10)),
|
||||||
|
|
||||||
Center(
|
Padding(
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 10),
|
padding: EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 10),
|
||||||
child: Text('For a tailored experience, please rate your discovery preferences.', style: TextStyle(fontSize: 18))
|
child: Text('Please rate your personal preferences so that we can taylor your experience.', style: TextStyle(fontSize: 18))
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
FutureBuilder(future: _prefs, builder: futureSliders),
|
// Now the sliders
|
||||||
debugButton()
|
ImportanceSliders()
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget futureSliders(BuildContext context, AsyncSnapshot<UserPreferences> snapshot) {
|
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
|
||||||
UserPreferences prefs = snapshot.data!;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
PreferenceSliders(prefs: [prefs.maxTime, prefs.maxDetour]),
|
|
||||||
Divider(indent: 25, endIndent: 25, height: 50),
|
|
||||||
PreferenceSliders(prefs: [prefs.sightseeing, prefs.shopping, prefs.nature])
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return CircularProgressIndicator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PreferenceSliders extends StatefulWidget {
|
|
||||||
final List<SinglePreference> prefs;
|
|
||||||
|
|
||||||
PreferenceSliders({required this.prefs});
|
class ImportanceSliders extends StatefulWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PreferenceSliders> createState() => _PreferenceSlidersState();
|
State<ImportanceSliders> createState() => _ImportanceSlidersState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class _PreferenceSlidersState extends State<PreferenceSliders> {
|
class _ImportanceSlidersState extends State<ImportanceSliders> {
|
||||||
|
|
||||||
@override
|
UserPreferences _prefs = UserPreferences();
|
||||||
Widget build(BuildContext context) {
|
|
||||||
|
List<Card> _createSliders() {
|
||||||
List<Card> sliders = [];
|
List<Card> sliders = [];
|
||||||
for (SinglePreference pref in widget.prefs) {
|
for (SinglePreference pref in _prefs.preferences) {
|
||||||
sliders.add(
|
sliders.add(Card(
|
||||||
Card(
|
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: pref.icon,
|
leading: pref.icon,
|
||||||
title: Text(pref.name),
|
title: Text(pref.name),
|
||||||
subtitle: Slider(
|
subtitle: Slider(
|
||||||
value: pref.value.toDouble(),
|
value: pref.value.toDouble(),
|
||||||
min: pref.minVal.toDouble(),
|
min: 0,
|
||||||
max: pref.maxVal.toDouble(),
|
max: 10,
|
||||||
divisions: pref.maxVal - pref.minVal,
|
divisions: 10,
|
||||||
label: pref.value.toString(),
|
label: pref.value.toString(),
|
||||||
onChanged: (double newValue) {
|
onChanged: (double newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
pref.value = newValue.toInt();
|
pref.value = newValue.toInt();
|
||||||
pref.save();
|
_prefs.save();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 0),
|
margin: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 0),
|
||||||
shadowColor: Colors.grey,
|
shadowColor: Colors.grey,
|
||||||
)
|
));
|
||||||
);
|
}
|
||||||
|
return sliders;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
@override
|
||||||
children: sliders);
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return Column(children: _createSliders());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,15 +3,6 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
const LandmarkType sightseeing = LandmarkType(name: 'sightseeing');
|
|
||||||
const LandmarkType nature = LandmarkType(name: 'nature');
|
|
||||||
const LandmarkType shopping = LandmarkType(name: 'shopping');
|
|
||||||
// const LandmarkType museum = LandmarkType(name: 'Museum');
|
|
||||||
// const LandmarkType restaurant = LandmarkType(name: 'Restaurant');
|
|
||||||
const LandmarkType start = LandmarkType(name: 'start');
|
|
||||||
const LandmarkType finish = LandmarkType(name: 'finish');
|
|
||||||
|
|
||||||
|
|
||||||
final class Landmark extends LinkedListEntry<Landmark>{
|
final class Landmark extends LinkedListEntry<Landmark>{
|
||||||
// A linked node of a list of Landmarks
|
// A linked node of a list of Landmarks
|
||||||
final String uuid;
|
final String uuid;
|
||||||
@@ -56,7 +47,7 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
'location': List<dynamic> location,
|
'location': List<dynamic> location,
|
||||||
'type': String type,
|
'type': String type,
|
||||||
}) {
|
}) {
|
||||||
// refine the parsing on a few fields
|
// refine the parsing on a few
|
||||||
List<double> locationFixed = List<double>.from(location);
|
List<double> locationFixed = List<double>.from(location);
|
||||||
// parse the rest separately, they could be missing
|
// parse the rest separately, they could be missing
|
||||||
LandmarkType typeFixed = LandmarkType(name: type);
|
LandmarkType typeFixed = LandmarkType(name: type);
|
||||||
@@ -64,12 +55,11 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
final imageURL = json['image_url'] as String?;
|
final imageURL = json['image_url'] as String?;
|
||||||
final description = json['description'] as String?;
|
final description = json['description'] as String?;
|
||||||
var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
|
var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
|
||||||
// if (duration == const Duration()) {duration = null;};
|
if (duration == const Duration()) {duration = null;};
|
||||||
final visited = json['visited'] as bool?;
|
final visited = json['visited'] as bool?;
|
||||||
var tripTime = Duration(minutes: json['time_to_reach_next'] ?? 0) as Duration?;
|
|
||||||
|
|
||||||
return Landmark(
|
return Landmark(
|
||||||
uuid: uuid, name: name, location: locationFixed, type: typeFixed, isSecondary: isSecondary, imageURL: imageURL, description: description, duration: duration, visited: visited, tripTime: tripTime);
|
uuid: uuid, name: name, location: locationFixed, type: typeFixed, isSecondary: isSecondary, imageURL: imageURL, description: description, duration: duration, visited: visited);
|
||||||
} else {
|
} else {
|
||||||
throw FormatException('Invalid JSON: $json');
|
throw FormatException('Invalid JSON: $json');
|
||||||
}
|
}
|
||||||
@@ -91,8 +81,7 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
|||||||
'image_url': imageURL,
|
'image_url': imageURL,
|
||||||
'description': description,
|
'description': description,
|
||||||
'duration': duration?.inMinutes,
|
'duration': duration?.inMinutes,
|
||||||
'visited': visited,
|
'visited': visited
|
||||||
'trip_time': tripTime?.inMinutes,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,14 +96,6 @@ class LandmarkType {
|
|||||||
// required this.description,
|
// required this.description,
|
||||||
// required this.icon,
|
// required this.icon,
|
||||||
});
|
});
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if (other is LandmarkType) {
|
|
||||||
return name == other.name;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
46
frontend/lib/structs/linked_landmarks.dart
Normal file
46
frontend/lib/structs/linked_landmarks.dart
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// import "package:fast_network_navigation/structs/landmark.dart";
|
||||||
|
|
||||||
|
// class Linked<Landmark> {
|
||||||
|
// Landmark? head;
|
||||||
|
|
||||||
|
// Linked();
|
||||||
|
|
||||||
|
// // class methods
|
||||||
|
// bool get isEmpty => head == null;
|
||||||
|
|
||||||
|
// // Add a new node to the end of the list
|
||||||
|
// void add(Landmark value) {
|
||||||
|
// if (isEmpty) {
|
||||||
|
// // If the list is empty, set the new node as the head
|
||||||
|
// head = value;
|
||||||
|
// } else {
|
||||||
|
// Landmark? current = head;
|
||||||
|
// while (current!.next != null) {
|
||||||
|
// // Traverse the list to find the last node
|
||||||
|
// current = current.next;
|
||||||
|
// }
|
||||||
|
// current.next = value; // Set the new node as the next node of the last node
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Remove the first node with the given value
|
||||||
|
// void remove(Landmark value) {
|
||||||
|
// if (isEmpty) return;
|
||||||
|
|
||||||
|
// // If the value is in the head node, update the head to the next node
|
||||||
|
// if (head! == value) {
|
||||||
|
// head = head.next;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var current = head;
|
||||||
|
// while (current!.next != null) {
|
||||||
|
// if (current.next! == value) {
|
||||||
|
// // If the value is found in the next node, skip the next node
|
||||||
|
// current.next = current.next.next;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// current = current.next;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
@@ -3,100 +3,80 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
|
|
||||||
|
|
||||||
class SinglePreference {
|
class SinglePreference {
|
||||||
String slug;
|
|
||||||
String name;
|
String name;
|
||||||
String description;
|
String description;
|
||||||
int value;
|
int value;
|
||||||
int minVal;
|
|
||||||
int maxVal;
|
|
||||||
Icon icon;
|
Icon icon;
|
||||||
|
String key;
|
||||||
|
|
||||||
SinglePreference({
|
SinglePreference({
|
||||||
required this.slug,
|
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
this.minVal = 0,
|
required this.key,
|
||||||
this.maxVal = 5,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
void save() async {
|
|
||||||
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
|
|
||||||
sharedPrefs.setInt('pref_$slug', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void load() async {
|
|
||||||
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
|
|
||||||
value = sharedPrefs.getInt('pref_$slug') ?? minVal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UserPreferences {
|
class UserPreferences {
|
||||||
SinglePreference sightseeing = SinglePreference(
|
List<SinglePreference> preferences = [
|
||||||
|
SinglePreference(
|
||||||
name: "Sightseeing",
|
name: "Sightseeing",
|
||||||
slug: "sightseeing",
|
|
||||||
description: "How much do you like sightseeing?",
|
description: "How much do you like sightseeing?",
|
||||||
value: 0,
|
value: 0,
|
||||||
icon: Icon(Icons.church),
|
icon: Icon(Icons.church),
|
||||||
);
|
key: "sightseeing",
|
||||||
SinglePreference shopping = SinglePreference(
|
),
|
||||||
|
SinglePreference(
|
||||||
name: "Shopping",
|
name: "Shopping",
|
||||||
slug: "shopping",
|
|
||||||
description: "How much do you like shopping?",
|
description: "How much do you like shopping?",
|
||||||
value: 0,
|
value: 0,
|
||||||
icon: Icon(Icons.shopping_bag),
|
icon: Icon(Icons.shopping_bag),
|
||||||
);
|
key: "shopping",
|
||||||
SinglePreference nature = SinglePreference(
|
),
|
||||||
|
SinglePreference(
|
||||||
|
name: "Foods & Drinks",
|
||||||
|
description: "How much do you like eating?",
|
||||||
|
value: 0,
|
||||||
|
icon: Icon(Icons.restaurant),
|
||||||
|
key: "eating",
|
||||||
|
),
|
||||||
|
SinglePreference(
|
||||||
|
name: "Nightlife",
|
||||||
|
description: "How much do you like nightlife?",
|
||||||
|
value: 0,
|
||||||
|
icon: Icon(Icons.wine_bar),
|
||||||
|
key: "nightlife",
|
||||||
|
),
|
||||||
|
SinglePreference(
|
||||||
name: "Nature",
|
name: "Nature",
|
||||||
slug: "nature",
|
|
||||||
description: "How much do you like nature?",
|
description: "How much do you like nature?",
|
||||||
value: 0,
|
value: 0,
|
||||||
icon: Icon(Icons.landscape),
|
icon: Icon(Icons.landscape),
|
||||||
);
|
key: "nature",
|
||||||
|
),
|
||||||
SinglePreference maxTime = SinglePreference(
|
SinglePreference(
|
||||||
name: "Trip duration",
|
name: "Culture",
|
||||||
slug: "duration",
|
description: "How much do you like culture?",
|
||||||
description: "How long do you want your trip to be?",
|
|
||||||
value: 30,
|
|
||||||
minVal: 30,
|
|
||||||
maxVal: 720,
|
|
||||||
icon: Icon(Icons.timer),
|
|
||||||
);
|
|
||||||
SinglePreference maxDetour = SinglePreference(
|
|
||||||
name: "Trip detours",
|
|
||||||
slug: "detours",
|
|
||||||
description: "Are you okay with roaming even if makes the trip longer?",
|
|
||||||
value: 0,
|
value: 0,
|
||||||
maxVal: 30,
|
icon: Icon(Icons.palette),
|
||||||
icon: Icon(Icons.loupe_sharp),
|
key: "culture",
|
||||||
);
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
void save() async {
|
||||||
Future<void> load() async {
|
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
|
||||||
for (SinglePreference pref in [sightseeing, shopping, nature, maxTime, maxDetour]) {
|
for (SinglePreference pref in preferences) {
|
||||||
pref.load();
|
sharedPrefs.setInt(pref.key, pref.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
void load() async {
|
||||||
// This is "opinionated" JSON, corresponding to the backend's expectations
|
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
|
||||||
return {
|
for (SinglePreference pref in preferences) {
|
||||||
"sightseeing": {"type": "sightseeing", "score": sightseeing.value},
|
pref.value = sharedPrefs.getInt(pref.key) ?? 0;
|
||||||
"shopping": {"type": "shopping", "score": shopping.value},
|
}
|
||||||
"nature": {"type": "nature", "score": nature.value},
|
|
||||||
"max_time_minute": maxTime.value,
|
|
||||||
"detour_tolerance_minute": maxDetour.value
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<UserPreferences> loadUserPreferences() async {
|
|
||||||
UserPreferences prefs = UserPreferences();
|
|
||||||
await prefs.load();
|
|
||||||
return prefs;
|
|
||||||
}
|
|
14
frontend/lib/structs/route.dart
Normal file
14
frontend/lib/structs/route.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import "package:fast_network_navigation/structs/landmark.dart";
|
||||||
|
|
||||||
|
|
||||||
|
class Route {
|
||||||
|
final String name;
|
||||||
|
final Duration duration;
|
||||||
|
final List<Landmark> landmarks;
|
||||||
|
|
||||||
|
Route({
|
||||||
|
required this.name,
|
||||||
|
required this.duration,
|
||||||
|
required this.landmarks
|
||||||
|
});
|
||||||
|
}
|
@@ -4,72 +4,32 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:geocoding/geocoding.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class Trip with ChangeNotifier {
|
class Trip {
|
||||||
String uuid;
|
final String uuid;
|
||||||
int totalTime;
|
final String cityName;
|
||||||
LinkedList<Landmark> landmarks;
|
// TODO: cityName should be inferred from coordinates of the Landmarks
|
||||||
|
final LinkedList<Landmark> landmarks;
|
||||||
// could be empty as well
|
// could be empty as well
|
||||||
String? errorDescription;
|
|
||||||
|
|
||||||
Future<String> get cityName async {
|
|
||||||
List<double>? location = landmarks.firstOrNull?.location;
|
|
||||||
if (GeocodingPlatform.instance == null) {
|
|
||||||
return '$location';
|
|
||||||
} else if (location == null) {
|
|
||||||
return 'Unknown';
|
|
||||||
} else{
|
|
||||||
List<Placemark> placemarks = await placemarkFromCoordinates(location[0], location[1]);
|
|
||||||
return placemarks.first.locality ?? 'Unknown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Trip({
|
Trip({
|
||||||
this.uuid = 'pending',
|
required this.uuid,
|
||||||
this.totalTime = 0,
|
required this.cityName,
|
||||||
LinkedList<Landmark>? landmarks
|
required this.landmarks,
|
||||||
// a trip can be created with no landmarks, but the list should be initialized anyway
|
});
|
||||||
}) : landmarks = landmarks ?? LinkedList<Landmark>();
|
|
||||||
|
|
||||||
|
|
||||||
factory Trip.fromJson(Map<String, dynamic> json) {
|
factory Trip.fromJson(Map<String, dynamic> json) {
|
||||||
Trip trip = Trip(
|
return Trip(
|
||||||
uuid: json['uuid'],
|
uuid: json['uuid'],
|
||||||
totalTime: json['total_time'],
|
cityName: json['city_name'],
|
||||||
|
landmarks: LinkedList()
|
||||||
);
|
);
|
||||||
|
|
||||||
return trip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadFromJson(Map<String, dynamic> json) {
|
|
||||||
uuid = json['uuid'];
|
|
||||||
totalTime = json['total_time'];
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
void addLandmark(Landmark landmark) {
|
|
||||||
landmarks.add(landmark);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateUUID(String newUUID) {
|
|
||||||
uuid = newUUID;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeLandmark(Landmark landmark) {
|
|
||||||
landmarks.remove(landmark);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateError(String error) {
|
|
||||||
errorDescription = error;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
|
factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
|
||||||
String? content = prefs.getString('trip_$uuid');
|
String? content = prefs.getString('trip_$uuid');
|
||||||
@@ -83,8 +43,8 @@ class Trip with ChangeNotifier {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'uuid': uuid,
|
'uuid': uuid,
|
||||||
'total_time': totalTime,
|
'city_name': cityName,
|
||||||
'first_landmark_uuid': landmarks.first.uuid
|
'entry_uuid': landmarks.first?.uuid ?? ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
52
frontend/lib/utils/fetch_landmarks.dart
Normal file
52
frontend/lib/utils/fetch_landmarks.dart
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// import "package:fast_network_navigation/structs/landmark.dart";
|
||||||
|
// import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
|
||||||
|
// Future<List<Landmark>> fetchLandmarks() async {
|
||||||
|
// // final response = await http
|
||||||
|
// // .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1'));
|
||||||
|
|
||||||
|
// // if (response.statusCode == 200) {
|
||||||
|
// // If the server did return a 200 OK response,
|
||||||
|
// // then parse the JSON.
|
||||||
|
// List<Landmark> landmarks = [
|
||||||
|
// // 48°51′29.6″N 2°17′40.2″E
|
||||||
|
// Landmark(
|
||||||
|
// name: "Eiffel Tower",
|
||||||
|
// location: [48.51296, 2.17402],
|
||||||
|
// type: LandmarkType(name: "Tower"),
|
||||||
|
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg"
|
||||||
|
// ),
|
||||||
|
// Landmark(
|
||||||
|
// name: "Notre Dame Cathedral",
|
||||||
|
// location: [48.8530, 2.3498],
|
||||||
|
// type: LandmarkType(name: "Monument"),
|
||||||
|
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg"
|
||||||
|
// ),
|
||||||
|
// Landmark(
|
||||||
|
// name: "Louvre palace",
|
||||||
|
// location: [48.8606, 2.3376],
|
||||||
|
// type: LandmarkType(name: "Museum"),
|
||||||
|
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg"
|
||||||
|
// ),
|
||||||
|
// Landmark(
|
||||||
|
// name: "Pont-des-arts",
|
||||||
|
// location: [48.5130, 2.2015],
|
||||||
|
// type: LandmarkType(name: "Bridge"),
|
||||||
|
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"),
|
||||||
|
// Landmark(
|
||||||
|
// name: "Panthéon",
|
||||||
|
// location: [48.5046, 2.2046],
|
||||||
|
// type: LandmarkType(name: "Monument"),
|
||||||
|
// imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG"
|
||||||
|
// ),
|
||||||
|
// ];
|
||||||
|
// // sleep 10 seconds
|
||||||
|
// await Future.delayed(Duration(seconds: 5));
|
||||||
|
// return landmarks;
|
||||||
|
// // } else {
|
||||||
|
// // // If the server did not return a 200 OK response,
|
||||||
|
// // // then throw an exception.
|
||||||
|
// // throw Exception('Failed to load destination');
|
||||||
|
// // }
|
||||||
|
// }
|
@@ -1,93 +0,0 @@
|
|||||||
import "dart:convert";
|
|
||||||
import "dart:developer";
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
|
|
||||||
import 'package:anyway/constants.dart';
|
|
||||||
import "package:anyway/structs/landmark.dart";
|
|
||||||
import "package:anyway/structs/trip.dart";
|
|
||||||
import "package:anyway/structs/preferences.dart";
|
|
||||||
|
|
||||||
|
|
||||||
Dio dio = Dio(
|
|
||||||
BaseOptions(
|
|
||||||
baseUrl: API_URL_BASE,
|
|
||||||
connectTimeout: const Duration(seconds: 5),
|
|
||||||
receiveTimeout: const Duration(seconds: 120),
|
|
||||||
// also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors
|
|
||||||
validateStatus: (status) => status! <= 500,
|
|
||||||
receiveDataWhenStatusError: true,
|
|
||||||
// api is notoriously slow
|
|
||||||
// headers: {
|
|
||||||
// HttpHeaders.userAgentHeader: 'dio',
|
|
||||||
// 'api': '1.0.0',
|
|
||||||
// },
|
|
||||||
contentType: Headers.jsonContentType,
|
|
||||||
responseType: ResponseType.json,
|
|
||||||
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
fetchTrip(
|
|
||||||
Trip trip,
|
|
||||||
Future<UserPreferences> preferences,
|
|
||||||
) async {
|
|
||||||
UserPreferences prefs = await preferences;
|
|
||||||
Map<String, dynamic> data = {
|
|
||||||
"preferences": prefs.toJson(),
|
|
||||||
"start": trip.landmarks!.first.location,
|
|
||||||
};
|
|
||||||
String dataString = jsonEncode(data);
|
|
||||||
log(dataString);
|
|
||||||
|
|
||||||
final response = await dio.post(
|
|
||||||
"/trip/new",
|
|
||||||
data: data
|
|
||||||
);
|
|
||||||
|
|
||||||
// handle errors
|
|
||||||
if (response.statusCode != 200) {
|
|
||||||
trip.updateUUID("error");
|
|
||||||
if (response.data["detail"] != null) {
|
|
||||||
trip.updateError(response.data["detail"]);
|
|
||||||
log(response.data["detail"]);
|
|
||||||
// throw Exception(response.data["detail"]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Map<String, dynamic> json = response.data;
|
|
||||||
|
|
||||||
// only fill in the trip "meta" data for now
|
|
||||||
trip.loadFromJson(json);
|
|
||||||
|
|
||||||
// now fill the trip with landmarks
|
|
||||||
// we are going to recreate ALL the landmarks from the information given by the api
|
|
||||||
trip.landmarks.remove(trip.landmarks.first);
|
|
||||||
String? nextUUID = json["first_landmark_uuid"];
|
|
||||||
while (nextUUID != null) {
|
|
||||||
var (landmark, newUUID) = await fetchLandmark(nextUUID);
|
|
||||||
trip.addLandmark(landmark);
|
|
||||||
nextUUID = newUUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
log(response.data.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future<(Landmark, String?)> fetchLandmark(String uuid) async {
|
|
||||||
final response = await dio.get(
|
|
||||||
"/landmark/$uuid"
|
|
||||||
);
|
|
||||||
|
|
||||||
// handle errors
|
|
||||||
if (response.statusCode != 200) {
|
|
||||||
throw Exception('Failed to load landmark');
|
|
||||||
}
|
|
||||||
if (response.data["detail"] != null) {
|
|
||||||
throw Exception(response.data["detail"]);
|
|
||||||
}
|
|
||||||
log(response.data.toString());
|
|
||||||
Map<String, dynamic> json = response.data;
|
|
||||||
String? nextUUID = json["next_uuid"];
|
|
||||||
return (Landmark.fromJson(json), nextUUID);
|
|
||||||
}
|
|
@@ -1,7 +1,7 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
Future<List<Trip>> loadTrips() async {
|
Future<List<Trip>> loadTrips() async {
|
||||||
@@ -17,7 +17,7 @@ Future<List<Trip>> loadTrips() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (trips.isEmpty) {
|
if (trips.isEmpty) {
|
||||||
Trip t1 = Trip(uuid: '1', landmarks: LinkedList<Landmark>());
|
Trip t1 = Trip(uuid: '1', cityName: 'Paris', landmarks: LinkedList<Landmark>());
|
||||||
t1.landmarks.add(
|
t1.landmarks.add(
|
||||||
Landmark(
|
Landmark(
|
||||||
uuid: '1',
|
uuid: '1',
|
||||||
@@ -66,7 +66,7 @@ Future<List<Trip>> loadTrips() async {
|
|||||||
trips.add(t1);
|
trips.add(t1);
|
||||||
|
|
||||||
|
|
||||||
Trip t2 = Trip(uuid: '2', landmarks: LinkedList<Landmark>());
|
Trip t2 = Trip(uuid: '2', cityName: 'Vienna', landmarks: LinkedList<Landmark>());
|
||||||
|
|
||||||
t2.landmarks.add(
|
t2.landmarks.add(
|
||||||
Landmark(
|
Landmark(
|
||||||
|
@@ -4,10 +4,10 @@ project(runner LANGUAGES CXX)
|
|||||||
|
|
||||||
# The name of the executable created for the application. Change this to change
|
# The name of the executable created for the application. Change this to change
|
||||||
# the on-disk name of your application.
|
# the on-disk name of your application.
|
||||||
set(BINARY_NAME "anyway")
|
set(BINARY_NAME "fast_network_navigation")
|
||||||
# The unique GTK application identifier for this application. See:
|
# The unique GTK application identifier for this application. See:
|
||||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||||
set(APPLICATION_ID "com.example.anyway")
|
set(APPLICATION_ID "com.example.fast_network_navigation")
|
||||||
|
|
||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
# versions of CMake.
|
# versions of CMake.
|
||||||
|
@@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) {
|
|||||||
if (use_header_bar) {
|
if (use_header_bar) {
|
||||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||||
gtk_header_bar_set_title(header_bar, "anyway");
|
gtk_header_bar_set_title(header_bar, "fast_network_navigation");
|
||||||
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
||||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||||
} else {
|
} else {
|
||||||
gtk_window_set_title(window, "anyway");
|
gtk_window_set_title(window, "fast_network_navigation");
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_window_set_default_size(window, 1280, 720);
|
gtk_window_set_default_size(window, 1280, 720);
|
||||||
|
@@ -5,12 +5,8 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import path_provider_foundation
|
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
import sqflite
|
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
|
||||||
}
|
}
|
||||||
|
@@ -64,7 +64,7 @@
|
|||||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||||
33CC10ED2044A3C60003C045 /* anyway.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "anyway.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
33CC10ED2044A3C60003C045 /* fast_network_navigation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "fast_network_navigation.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
33CC10EE2044A3C60003C045 /* Products */ = {
|
33CC10EE2044A3C60003C045 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
33CC10ED2044A3C60003C045 /* anyway.app */,
|
33CC10ED2044A3C60003C045 /* fast_network_navigation.app */,
|
||||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
|
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
);
|
);
|
||||||
name = Runner;
|
name = Runner;
|
||||||
productName = Runner;
|
productName = Runner;
|
||||||
productReference = 33CC10ED2044A3C60003C045 /* anyway.app */;
|
productReference = 33CC10ED2044A3C60003C045 /* fast_network_navigation.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
@@ -388,7 +388,7 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/anyway.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/anyway";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fast_network_navigation.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fast_network_navigation";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -402,7 +402,7 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/anyway.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/anyway";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fast_network_navigation.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fast_network_navigation";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
@@ -416,7 +416,7 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation.RunnerTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/anyway.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/anyway";
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/fast_network_navigation.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/fast_network_navigation";
|
||||||
};
|
};
|
||||||
name = Profile;
|
name = Profile;
|
||||||
};
|
};
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "anyway.app"
|
BuildableName = "fast_network_navigation.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "anyway.app"
|
BuildableName = "fast_network_navigation.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "anyway.app"
|
BuildableName = "fast_network_navigation.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "anyway.app"
|
BuildableName = "fast_network_navigation.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
// 'flutter create' template.
|
// 'flutter create' template.
|
||||||
|
|
||||||
// The application's name. By default this is also the title of the Flutter window.
|
// The application's name. By default this is also the title of the Flutter window.
|
||||||
PRODUCT_NAME = anyway
|
PRODUCT_NAME = fast_network_navigation
|
||||||
|
|
||||||
// The application's bundle identifier
|
// The application's bundle identifier
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation
|
PRODUCT_BUNDLE_IDENTIFIER = com.example.fastNetworkNavigation
|
||||||
|
@@ -9,14 +9,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
version: "2.11.0"
|
||||||
auto_size_text:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: auto_size_text
|
|
||||||
sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -25,30 +17,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
cached_network_image:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: cached_network_image
|
|
||||||
sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.4.0"
|
|
||||||
cached_network_image_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: cached_network_image_platform_interface
|
|
||||||
sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.1.0"
|
|
||||||
cached_network_image_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: cached_network_image_web
|
|
||||||
sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -73,14 +41,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
crypto:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: crypto
|
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.3"
|
|
||||||
csslib:
|
csslib:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -97,22 +57,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
dio:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: dio
|
|
||||||
sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.5.0+1"
|
|
||||||
dio_web_adapter:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dio_web_adapter
|
|
||||||
sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -137,27 +81,11 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.0.0"
|
||||||
fixnum:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: fixnum
|
|
||||||
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0"
|
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_cache_manager:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: flutter_cache_manager
|
|
||||||
sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.4.0"
|
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -170,10 +98,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_plugin_android_lifecycle
|
name: flutter_plugin_android_lifecycle
|
||||||
sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
|
sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.21"
|
version: "2.0.19"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -184,86 +112,54 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
geocoding:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: geocoding
|
|
||||||
sha256: d580c801cba9386b4fac5047c4c785a4e19554f46be42f4f5e5b7deacd088a66
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
geocoding_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: geocoding_android
|
|
||||||
sha256: "1b13eca79b11c497c434678fed109c2be020b158cec7512c848c102bc7232603"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.3.1"
|
|
||||||
geocoding_ios:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: geocoding_ios
|
|
||||||
sha256: "94ddba60387501bd1c11e18dca7c5a9e8c645d6e3da9c38b9762434941870c24"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.1"
|
|
||||||
geocoding_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: geocoding_platform_interface
|
|
||||||
sha256: "8c2c8226e5c276594c2e18bfe88b19110ed770aeb7c1ab50ede570be8b92229b"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.2.0"
|
|
||||||
google_maps:
|
google_maps:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps
|
name: google_maps
|
||||||
sha256: "463b38e5a92a05cde41220a11fd5eef3847031fef3e8cf295ac76ec453246907"
|
sha256: "47eef3836b49bb030d5cb3afc60b8451408bf34cf753e571b645d6529eb4251a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.0"
|
version: "7.1.0"
|
||||||
google_maps_flutter:
|
google_maps_flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter
|
name: google_maps_flutter
|
||||||
sha256: acf0ec482d86b2ac55ade80597ce7f797a47971f5210ebfd030f0d58130e0a94
|
sha256: c1972cbad779bc5346c49045f26ae45550a0958b1cbca5b524dd3c8954995d28
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "2.6.1"
|
||||||
google_maps_flutter_android:
|
google_maps_flutter_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_android
|
name: google_maps_flutter_android
|
||||||
sha256: "5d444f4135559488d7ea325eae710ae3284e6951b1b61729a0ac026456fe1548"
|
sha256: "0bcadb80eba39afda77dede89a6caafd3b68f2786b90491eceea4a01c3db181c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.1"
|
version: "2.8.0"
|
||||||
google_maps_flutter_ios:
|
google_maps_flutter_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_ios
|
name: google_maps_flutter_ios
|
||||||
sha256: a6e3c6ecdda6c985053f944be13a0645ebb919da2ef0f5bc579c5e1670a5b2a8
|
sha256: e5132d17f051600d90d79d9f574b177c24231da702453a036db2490f9ced4646
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.0"
|
version: "2.6.0"
|
||||||
google_maps_flutter_platform_interface:
|
google_maps_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_platform_interface
|
name: google_maps_flutter_platform_interface
|
||||||
sha256: bd60ca330e3c7763b95b477054adec338a522d982af73ecc520b232474063ac5
|
sha256: "167af879da4d004cd58771f1469b91dcc3b9b0a2c5334cc6bf71fd41d4b35403"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.0"
|
version: "2.6.0"
|
||||||
google_maps_flutter_web:
|
google_maps_flutter_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_web
|
name: google_maps_flutter_web
|
||||||
sha256: "8d5d0f58bfc4afac0bbe3d399f2018fcea691e3ea3d35254b7aae56df5827659"
|
sha256: "0c0d5c723d94b295cf86dd1c45ff91d2ac1fff7c05ddca4f01bef9fa0a014690"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.9+1"
|
version: "0.5.7"
|
||||||
html:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -276,10 +172,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
|
sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -288,22 +184,38 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.2"
|
version: "4.0.2"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
|
js_wrapping:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js_wrapping
|
||||||
|
sha256: e385980f7c76a8c1c9a560dfb623b890975841542471eade630b2871d243851c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.4"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.5"
|
version: "10.0.4"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.3"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -332,34 +244,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.8.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.12.0"
|
||||||
nested:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: nested
|
|
||||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.0"
|
|
||||||
octo_image:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: octo_image
|
|
||||||
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -368,30 +264,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
path_provider:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider
|
|
||||||
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.4"
|
|
||||||
path_provider_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_android
|
|
||||||
sha256: "490539678396d4c3c0b06efdaab75ae60675c3e0c66f72bc04c2e2c1e0e2abeb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.9"
|
|
||||||
path_provider_foundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_foundation
|
|
||||||
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.0"
|
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -412,18 +284,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.2.1"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.5"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -432,22 +304,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
provider:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: provider
|
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "6.1.2"
|
|
||||||
rxdart:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: rxdart
|
|
||||||
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.28.0"
|
|
||||||
sanitize_html:
|
sanitize_html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -460,58 +316,58 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: c3f888ba2d659f3e75f4686112cc1e71f46177f74452d40d8307edc332296ead
|
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.2.3"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294"
|
sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.2.2"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
|
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0"
|
version: "2.4.0"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_linux
|
name: shared_preferences_linux
|
||||||
sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1"
|
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.3.2"
|
||||||
shared_preferences_platform_interface:
|
shared_preferences_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.3.2"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_web
|
name: shared_preferences_web
|
||||||
sha256: "3a293170d4d9403c3254ee05b84e62e8a9b3c5808ebd17de6a33fe9ea6457936"
|
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.3.0"
|
||||||
shared_preferences_windows:
|
shared_preferences_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_windows
|
name: shared_preferences_windows
|
||||||
sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2"
|
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.0"
|
version: "2.3.2"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -533,30 +389,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.10.0"
|
||||||
sprintf:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sprintf
|
|
||||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.0"
|
|
||||||
sqflite:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite
|
|
||||||
sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.3+1"
|
|
||||||
sqflite_common:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_common
|
|
||||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.5.4"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -589,14 +421,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
synchronized:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: synchronized
|
|
||||||
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0+1"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -609,10 +433,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -621,14 +445,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
uuid:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: uuid
|
|
||||||
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.4.2"
|
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -641,10 +457,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.4"
|
version: "14.2.1"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -653,14 +469,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.5.1"
|
||||||
widget_to_marker:
|
win32:
|
||||||
dependency: "direct main"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: widget_to_marker
|
name: win32
|
||||||
sha256: badc36f23c76f3ca9d43d7780058096be774adf0f661bdb6eb6f6b893f648ab9
|
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.6"
|
version: "5.5.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -671,4 +487,4 @@ packages:
|
|||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.4.0 <4.0.0"
|
dart: ">=3.4.0 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.19.0"
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
name: "anyway"
|
name: "fast_network_navigation"
|
||||||
description: "A customizable, agile city navigator for your trips."
|
description: "An interactive city navigator."
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
@@ -36,15 +36,9 @@ dependencies:
|
|||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.6
|
cupertino_icons: ^1.0.6
|
||||||
sliding_up_panel: ^2.0.0+1
|
sliding_up_panel: ^2.0.0+1
|
||||||
|
google_maps_flutter: ^2.6.1
|
||||||
http: ^1.2.1
|
http: ^1.2.1
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
dio: ^5.5.0+1
|
|
||||||
google_maps_flutter: ^2.7.0
|
|
||||||
cached_network_image: ^3.4.0
|
|
||||||
geocoding: ^3.0.0
|
|
||||||
widget_to_marker: ^1.0.6
|
|
||||||
provider: ^6.1.2
|
|
||||||
auto_size_text: ^3.0.0
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@@ -8,8 +8,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
// import 'package:anyway/main.dart';
|
// import 'package:fast_network_navigation/main.dart';
|
||||||
import 'package:anyway/layout.dart';
|
import 'package:fast_network_navigation/layout.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||||
|
@@ -24,13 +24,13 @@
|
|||||||
<!-- iOS meta tags & icons -->
|
<!-- iOS meta tags & icons -->
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="apple-mobile-web-app-title" content="anyway">
|
<meta name="apple-mobile-web-app-title" content="fast_network_navigation">
|
||||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
|
||||||
<title>anyway</title>
|
<title>fast_network_navigation</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "anyway",
|
"name": "fast_network_navigation",
|
||||||
"short_name": "anyway",
|
"short_name": "fast_network_navigation",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0175C2",
|
"background_color": "#0175C2",
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
# Project-level configuration.
|
# Project-level configuration.
|
||||||
cmake_minimum_required(VERSION 3.14)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
project(anyway LANGUAGES CXX)
|
project(fast_network_navigation LANGUAGES CXX)
|
||||||
|
|
||||||
# The name of the executable created for the application. Change this to change
|
# The name of the executable created for the application. Change this to change
|
||||||
# the on-disk name of your application.
|
# the on-disk name of your application.
|
||||||
set(BINARY_NAME "anyway")
|
set(BINARY_NAME "fast_network_navigation")
|
||||||
|
|
||||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
# versions of CMake.
|
# versions of CMake.
|
||||||
|
@@ -90,12 +90,12 @@ BEGIN
|
|||||||
BLOCK "040904e4"
|
BLOCK "040904e4"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "com.example" "\0"
|
VALUE "CompanyName", "com.example" "\0"
|
||||||
VALUE "FileDescription", "anyway" "\0"
|
VALUE "FileDescription", "fast_network_navigation" "\0"
|
||||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||||
VALUE "InternalName", "anyway" "\0"
|
VALUE "InternalName", "fast_network_navigation" "\0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0"
|
VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0"
|
||||||
VALUE "OriginalFilename", "anyway.exe" "\0"
|
VALUE "OriginalFilename", "fast_network_navigation.exe" "\0"
|
||||||
VALUE "ProductName", "anyway" "\0"
|
VALUE "ProductName", "fast_network_navigation" "\0"
|
||||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
@@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
|||||||
FlutterWindow window(project);
|
FlutterWindow window(project);
|
||||||
Win32Window::Point origin(10, 10);
|
Win32Window::Point origin(10, 10);
|
||||||
Win32Window::Size size(1280, 720);
|
Win32Window::Size size(1280, 720);
|
||||||
if (!window.Create(L"anyway", origin, size)) {
|
if (!window.Create(L"fast_network_navigation", origin, size)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
window.SetQuitOnClose(true);
|
window.SetQuitOnClose(true);
|
||||||
|
3
renovate.json
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
Reference in New Issue
Block a user