Compare commits
10 commits
1.0.0-alph
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
42effc2908 | ||
|
48a9bea2f7 | ||
|
f5607abf0a | ||
|
76ec1883d8 | ||
|
5bc1cfb70b | ||
|
55bd0e56c4 | ||
|
74b795f00b | ||
|
56bd869cca | ||
|
33839b6b72 | ||
|
f91ef2e68f |
2
.flutter
|
@ -1 +1 @@
|
|||
Subproject commit 2ad6cd72c040113b47ee9055e722606a490ef0da
|
||||
Subproject commit 796c8ef79279f9c774545b3771238c3098dbefab
|
17
.gitignore
vendored
|
@ -42,3 +42,20 @@ app.*.map.json
|
|||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
# fastlane specific
|
||||
**/fastlane/report.xml
|
||||
|
||||
# deliver temporary files
|
||||
**/fastlane/Preview.html
|
||||
|
||||
# snapshot generated screenshots
|
||||
**/fastlane/screenshots
|
||||
|
||||
# scan temporary files
|
||||
**/fastlane/test_output
|
||||
|
||||
# Fastlane.swift runner binary
|
||||
**/fastlane/FastlaneRunner
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/fastlane
|
||||
|
|
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"conventionalCommits.scopes": [
|
||||
"pagerenderer"
|
||||
]
|
||||
}
|
17
CHANGELOG.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 1.0.0-alpha.2
|
||||
- Added l10n support
|
||||
- Added support for page downloads
|
||||
- Added interactive maps through flutter_map
|
||||
|
||||
## 1.0.0-alpha.1
|
||||
- First version
|
14
README.md
|
@ -1,13 +1,17 @@
|
|||
# Voyage Handbook
|
||||
Access [WikiVoyage](https://en.wikivoyage.org) conveniently from your phone!
|
||||
|
||||
## Install
|
||||
- [Google Play](https://play.google.com/store/apps/details?id=cafe.caras.voyagehandbook)
|
||||
- F-Droid (soon)
|
||||
|
||||
## Roadmap
|
||||
*(In no particular order)*
|
||||
|
||||
- [ ] Render articles *completely* using native widgets
|
||||
- [ ] Navigation in articles by heading
|
||||
- [ ] Settings for reader
|
||||
- [ ] Translate UI
|
||||
- [X] Translate UI
|
||||
- [ ] Support different WikiVoyage languages
|
||||
- [ ] Bookmark articles
|
||||
- [ ] Download articles
|
||||
|
@ -35,7 +39,13 @@ cd voyagehandbook
|
|||
./flutterw doctor
|
||||
```
|
||||
|
||||
3. Run on your connected device
|
||||
3. Generate locales
|
||||
|
||||
```sh
|
||||
./flutterw gen-l10n
|
||||
```
|
||||
|
||||
4. Run on your connected device
|
||||
|
||||
```sh
|
||||
./flutterw run
|
||||
|
|
3
android/Gemfile
Normal file
|
@ -0,0 +1,3 @@
|
|||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
218
android/Gemfile.lock
Normal file
|
@ -0,0 +1,218 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.6)
|
||||
rexml
|
||||
addressable (2.8.4)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
artifactory (3.0.15)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.782.0)
|
||||
aws-sdk-core (3.176.1)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.651.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.68.0)
|
||||
aws-sdk-core (~> 3, >= 3.176.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.127.0)
|
||||
aws-sdk-core (~> 3, >= 3.176.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.6)
|
||||
aws-sigv4 (1.6.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.6.4)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.100.0)
|
||||
faraday (1.10.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.4)
|
||||
multipart-post (~> 2)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.0)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.2.7)
|
||||
fastlane (2.213.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (~> 0.1.1)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.45.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.0)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
webrick
|
||||
google-apis-iamcredentials_v1 (0.17.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.13.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.19.0)
|
||||
google-apis-core (>= 0.9.0, < 2.a)
|
||||
google-cloud-core (1.6.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.1)
|
||||
google-cloud-storage (1.44.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.19.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.5)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
jmespath (1.6.2)
|
||||
json (2.6.3)
|
||||
jwt (2.7.1)
|
||||
memoist (0.16.2)
|
||||
mini_magick (4.12.0)
|
||||
mini_mime (1.1.2)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.3.0)
|
||||
nanaimo (0.3.0)
|
||||
naturally (2.2.1)
|
||||
optparse (0.1.1)
|
||||
os (1.1.4)
|
||||
plist (3.7.0)
|
||||
public_suffix (5.0.1)
|
||||
rake (13.0.6)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rouge (2.0.7)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
security (0.1.3)
|
||||
signet (0.17.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.1)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.8.1)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.22.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.3.0)
|
||||
rexml (~> 3.2.4)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.13
|
|
@ -26,6 +26,6 @@ subprojects {
|
|||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
|
2
android/fastlane/Appfile
Normal file
|
@ -0,0 +1,2 @@
|
|||
json_key_file("/home/mates/fastlane.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
||||
package_name("cafe.caras.voyagehandbook") # e.g. com.krausefx.app
|
28
android/fastlane/Fastfile
Normal file
|
@ -0,0 +1,28 @@
|
|||
# This file contains the fastlane.tools configuration
|
||||
# You can find the documentation at https://docs.fastlane.tools
|
||||
#
|
||||
# For a list of all available actions, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/actions
|
||||
#
|
||||
# For a list of all available plugins, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/plugins/available-plugins
|
||||
#
|
||||
|
||||
# Uncomment the line if you want fastlane to automatically update itself
|
||||
# update_fastlane
|
||||
|
||||
default_platform(:android)
|
||||
|
||||
platform :android do
|
||||
# desc "Runs all the tests"
|
||||
# lane :test do
|
||||
# gradle(task: "test")
|
||||
# end
|
||||
|
||||
desc "Deploy a new version to the Google Play"
|
||||
lane :deploy do
|
||||
upload_to_play_store(track: "beta", aab: "../build/app/outputs/bundle/release/app-release.aab",changes_not_sent_for_review:true)
|
||||
end
|
||||
end
|
32
android/fastlane/README.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
fastlane documentation
|
||||
----
|
||||
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||
|
||||
# Available Actions
|
||||
|
||||
## Android
|
||||
|
||||
### android deploy
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android deploy
|
||||
```
|
||||
|
||||
Deploy a new version to the Google Play
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||
|
||||
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||
|
||||
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
10
android/fastlane/metadata/android/en-US/full_description.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
An unofficial app to browse WikiVoyage articles.
|
||||
|
||||
Currently you can:
|
||||
* Search for articles
|
||||
* Read articles natively in the app
|
||||
* Navigate between pages
|
||||
|
||||
As the app is currently in development, features will be added as time goes by.
|
||||
|
||||
The app is released under GPL, anyone is open to contribute. It does not contain any trackers and all data is sent only directly to WikiMedia API. By using the app you are subject to the WikiMedia Terms of Service and Privacy Policy.
|
After Width: | Height: | Size: 53 KiB |
BIN
android/fastlane/metadata/android/en-US/images/icon.png
Normal file
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 251 KiB |
After Width: | Height: | Size: 917 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 731 KiB |
|
@ -0,0 +1 @@
|
|||
Read WikiVoyage articles comfortably from your phone
|
1
android/fastlane/metadata/android/en-US/title.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Voyage Handbook
|
0
android/fastlane/metadata/android/en-US/video.txt
Normal file
3
l10n.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
arb-dir: lib/l10n
|
||||
template-arb-file: app_en.arb
|
||||
output-localization-file: app_localizations.dart
|
|
@ -25,8 +25,10 @@ class SearchResponse {
|
|||
final String title;
|
||||
final String excerpt;
|
||||
final String? description;
|
||||
const SearchResponse(
|
||||
this.id, this.key, this.title, this.excerpt, this.description);
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
bool downloaded;
|
||||
SearchResponse(this.id, this.key, this.title, this.excerpt, this.description,
|
||||
{this.downloaded = false});
|
||||
|
||||
/// Connect the generated function to the `fromJson`
|
||||
/// factory.
|
||||
|
@ -56,11 +58,10 @@ class RawPage {
|
|||
final String title;
|
||||
@JsonKey(fromJson: _editedFromJson, toJson: _editedToJson, name: "latest")
|
||||
final String edited;
|
||||
final String html;
|
||||
String html;
|
||||
final LicenseAttribution license;
|
||||
|
||||
const RawPage(
|
||||
this.id, this.key, this.title, this.edited, this.html, this.license);
|
||||
RawPage(this.id, this.key, this.title, this.edited, this.html, this.license);
|
||||
|
||||
factory RawPage.fromJson(Map<String, dynamic> json) =>
|
||||
_$RawPageFromJson(json);
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:voyagehandbook/api/classes.dart';
|
||||
import 'package:voyagehandbook/util/storage.dart';
|
||||
|
||||
/*
|
||||
Voyage Handbook - The open-source WikiVoyage reader
|
||||
|
@ -39,8 +40,14 @@ class WikiApi {
|
|||
var r = await _getRequest("search/page?q=$q&limit=$limit");
|
||||
if (r.statusCode! > 399) return Future.error("API error ${r.statusCode}");
|
||||
var json = jsonDecode(r.data)["pages"];
|
||||
return List<SearchResponse>.generate(
|
||||
var list = List<SearchResponse>.generate(
|
||||
json.length, (index) => SearchResponse.fromJson(json[index]));
|
||||
for (var item in list) {
|
||||
if (await StorageAccess.isDownloaded(item.key)) {
|
||||
list[list.indexOf(item)].downloaded = true;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static Future<RawPage> getRawPage(String key) async {
|
||||
|
|
26
lib/l10n/app_cs.arb
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"home":"Domů",
|
||||
"noRecents":"Nemáte žádné nedávno otevřené články, potáhněte doprava a začněte hledat.",
|
||||
"recentPages":"Nedávné články",
|
||||
"searchAppBarTitle":"Prohledat WikiVoyage",
|
||||
"search":"Hledat",
|
||||
"offlineTitle":"Offline stažení",
|
||||
"offlineDialog":"Chcete stáhnout článek '{article}' pro zobrazení offline? Bude dostupný v sekci 'Stažené'.",
|
||||
"renderError":"Chyba při renderování:",
|
||||
"attrFrom":"Z",
|
||||
"attrUnder":"pod licencí",
|
||||
"downloadsTitle":"Stažené",
|
||||
"noDownloads":"Nemáte žádné stažené články. Vyhledejte nějaké, a poté je dlouhým podržením stáhněte.",
|
||||
"about":"O Aplikcai",
|
||||
"sourceCode":"Zdrojový kód",
|
||||
"creditsCreatedBy":"Vytvořil Matyáš Caras",
|
||||
"creditsAffiliation":"Není nijak spojeno s WikiVoyage",
|
||||
"yes":"Ano",
|
||||
"no":"Ne",
|
||||
"ok":"Ok",
|
||||
"downloading":"Stahuji, vyčkejte prosím...",
|
||||
"downloadComplete":"Stahování dokončeno, najdete ho v sekci 'Stažené'.",
|
||||
"error":"Nastala chyba",
|
||||
"offlineError":"Vypadá to, že jste offline a tento článek nemáte stažený.",
|
||||
"imageError":"Chyba při stahování obrázku, zkontrolujte připojení."
|
||||
}
|
72
lib/l10n/app_en.arb
Normal file
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"home":"Home",
|
||||
"@home":{
|
||||
"description":"As seen on the home page"
|
||||
},
|
||||
"noRecents":"You haven't opened anything recently, swipe right and start searching.",
|
||||
"@noRecents":{
|
||||
"description":"Shown when there are no recent pages"
|
||||
},
|
||||
"recentPages":"Recent articles",
|
||||
"@recentPages":{
|
||||
"description":"Title of the home page"
|
||||
},
|
||||
"searchAppBarTitle":"Search WikiVoyage",
|
||||
"@searchAppBarTitle":{
|
||||
"description":"Appbar title"
|
||||
},
|
||||
"search":"Search",
|
||||
"offlineTitle":"Offline download",
|
||||
"@offlineTitle":{
|
||||
"description":"Title of the dialog that appears when downloading an article offline"
|
||||
},
|
||||
"offlineDialog":"Would you like to download the article '{article}' for offline viewing? It will be available in the 'Downloads' section.",
|
||||
"@offlineDialog":{
|
||||
"description":"Offline download dialog content text",
|
||||
"placeholders":{
|
||||
"article":{
|
||||
"type":"String",
|
||||
"example":"Rail travel in Japan",
|
||||
"description":"WikiVoyage article name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"renderError":"Error while rendering:",
|
||||
"@renderError":{
|
||||
"description":"Displayed when rendering of a page throws an error"
|
||||
},
|
||||
"attrFrom":"From",
|
||||
"@pageFrom":{
|
||||
"description":"The *From* part of 'From WikiVoyage'"
|
||||
},
|
||||
"attrUnder":"under",
|
||||
"@attrUnder":{
|
||||
"description":"The *under* part of license attribution, e.g. 'under CC BY-SA 3.0'"
|
||||
},
|
||||
"downloadsTitle":"Downloads",
|
||||
"@downloadsTitle":{
|
||||
"description":"The appbar title for the Downloads page"
|
||||
},
|
||||
"noDownloads":"You don't have any articles downloaded. Search for some and then long-press them to download them offline.",
|
||||
"about":"About",
|
||||
"sourceCode":"Source code",
|
||||
"creditsCreatedBy":"Created by Matyáš Caras",
|
||||
"creditsAffiliation":"Not affiliated with WikiVoyage",
|
||||
"yes":"Yes",
|
||||
"no":"No",
|
||||
"ok":"Ok",
|
||||
"downloading":"Downloading, please wait...",
|
||||
"@downloading":{
|
||||
"description":"Shown in a dialog when downloading something"
|
||||
},
|
||||
"downloadComplete":"Download complete, you will find it in the 'Downloads' section.",
|
||||
"error":"An error occured",
|
||||
"@error":{
|
||||
"description":"Generic error dialog title"
|
||||
},
|
||||
"offlineError":"You seem to be offline and the curren article is not in your downloads.",
|
||||
"@offlineError":{
|
||||
"description":"Shown when trying to render a page offline"
|
||||
},
|
||||
"imageError":"Error downloading image, check network connection."
|
||||
}
|
|
@ -2,7 +2,7 @@ import 'package:dynamic_color/dynamic_color.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:voyagehandbook/util/color_schemes.g.dart';
|
||||
import 'package:voyagehandbook/views/home.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
/*
|
||||
Voyage Handbook - The open-source WikiVoyage reader
|
||||
Copyright (C) 2023 Matyáš Caras
|
||||
|
@ -33,6 +33,8 @@ class MyApp extends StatelessWidget {
|
|||
return DynamicColorBuilder(
|
||||
builder: (lightDynamic, darkDynamic) => MaterialApp(
|
||||
title: 'Voyage Handbook',
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
useMaterial3: true, colorScheme: lightDynamic ?? lightColorScheme),
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:voyagehandbook/views/downloads.dart';
|
||||
import 'package:voyagehandbook/views/home.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../views/search.dart';
|
||||
|
||||
/*
|
||||
|
@ -28,19 +31,19 @@ Drawer genDrawer(int page, BuildContext context) => Drawer(
|
|||
children: [
|
||||
DrawerHeader(
|
||||
child: Column(
|
||||
children: const [
|
||||
Text(
|
||||
children: [
|
||||
const Text(
|
||||
"Voyage Handbook",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text("Created by Matyáš Caras"),
|
||||
Text("Not affiliated with WikiVoyage")
|
||||
Text(AppLocalizations.of(context)!.creditsCreatedBy),
|
||||
Text(AppLocalizations.of(context)!.creditsAffiliation)
|
||||
],
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
selected: page == 1,
|
||||
title: const Text("Home"),
|
||||
title: Text(AppLocalizations.of(context)!.home),
|
||||
leading: const Icon(Icons.home),
|
||||
onTap: () => page == 1
|
||||
? Navigator.of(context).pop()
|
||||
|
@ -52,7 +55,7 @@ Drawer genDrawer(int page, BuildContext context) => Drawer(
|
|||
),
|
||||
ListTile(
|
||||
selected: page == 2,
|
||||
title: const Text("Search"),
|
||||
title: Text(AppLocalizations.of(context)!.search),
|
||||
leading: const Icon(Icons.search),
|
||||
onTap: () => page == 2
|
||||
? Navigator.of(context).pop()
|
||||
|
@ -64,9 +67,21 @@ Drawer genDrawer(int page, BuildContext context) => Drawer(
|
|||
),
|
||||
ListTile(
|
||||
selected: page == 3,
|
||||
title: const Text("About"),
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: Text(AppLocalizations.of(context)!.downloadsTitle),
|
||||
leading: const Icon(Icons.download),
|
||||
onTap: () => page == 3
|
||||
? Navigator.of(context).pop()
|
||||
: Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => const DownloadsView(),
|
||||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
selected: page == 99,
|
||||
title: Text(AppLocalizations.of(context)!.about),
|
||||
leading: const Icon(Icons.info_outline),
|
||||
onTap: () => page == 99
|
||||
? Navigator.of(context).pop()
|
||||
: Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
|
@ -78,6 +93,15 @@ Drawer genDrawer(int page, BuildContext context) => Drawer(
|
|||
),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
selected: page == 99,
|
||||
title: Text(AppLocalizations.of(context)!.sourceCode),
|
||||
leading: const Icon(Icons.code),
|
||||
onTap: () => page == 99
|
||||
? Navigator.of(context).pop()
|
||||
: launchUrlString("https://git.mnau.xyz/hernik/voyagehandbook",
|
||||
mode: LaunchMode.externalApplication),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -2,16 +2,21 @@ import 'dart:math';
|
|||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:voyagehandbook/api/classes.dart';
|
||||
import 'package:voyagehandbook/util/storage.dart';
|
||||
import 'package:voyagehandbook/util/styles.dart';
|
||||
import 'package:voyagehandbook/util/widgets/warning.dart';
|
||||
import 'package:html_unescape/html_unescape_small.dart';
|
||||
import 'package:voyagehandbook/views/pageview.dart';
|
||||
import 'package:widget_zoom/widget_zoom.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
/*
|
||||
Voyage Handbook - The open-source WikiVoyage reader
|
||||
|
@ -30,17 +35,27 @@ import 'package:widget_zoom/widget_zoom.dart';
|
|||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
final _ignoredTags = ["style", "script"];
|
||||
final logger = Logger(printer: PrettyPrinter());
|
||||
|
||||
class PageRenderer {
|
||||
final ColorScheme scheme;
|
||||
final double height;
|
||||
final double width;
|
||||
final BuildContext context;
|
||||
final AppLocalizations loc;
|
||||
|
||||
PageRenderer(this.scheme, this.height, this.width, this.context);
|
||||
/// For offline downloads; don't bother rendering the widget tree
|
||||
final bool offline;
|
||||
|
||||
/// HTML for offline download / caching
|
||||
String outHtml = "";
|
||||
String? document;
|
||||
|
||||
PageRenderer(this.scheme, this.height, this.width, this.context, this.loc,
|
||||
{this.offline = false});
|
||||
|
||||
/// Used to create Widgets from raw HTML from WM API
|
||||
ListView renderFromPageHTML(RawPage page) {
|
||||
Future<ListView> renderFromPageHTML(RawPage page) async {
|
||||
var out = <Widget>[
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
|
@ -56,7 +71,7 @@ class PageRenderer {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SelectableText("From"),
|
||||
SelectableText(loc.attrFrom),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
|
@ -74,26 +89,32 @@ class PageRenderer {
|
|||
),
|
||||
Row(
|
||||
children: [
|
||||
const SelectableText("under"),
|
||||
SelectableText(loc.attrUnder),
|
||||
const SizedBox(
|
||||
width: 5,
|
||||
width: 2,
|
||||
),
|
||||
SelectableText(
|
||||
Flexible(
|
||||
child: SelectableText(
|
||||
page.license.title,
|
||||
textAlign: TextAlign.center,
|
||||
onTap: () => launchUrl(Uri.parse(page.license.url),
|
||||
mode: LaunchMode.externalApplication),
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
];
|
||||
outHtml = "<html><head></head><body>";
|
||||
var document = parse(page.html);
|
||||
var sections = document.body!.getElementsByTagName("section");
|
||||
this.document = page.html;
|
||||
for (var sec in sections) {
|
||||
if (sec.localName == "section") {
|
||||
out.addAll(_renderSection(sec));
|
||||
if (!offline) out.addAll(await _renderSection(sec));
|
||||
outHtml += sec.outerHtml;
|
||||
}
|
||||
}
|
||||
var l = ListView.builder(
|
||||
|
@ -104,7 +125,7 @@ class PageRenderer {
|
|||
return l;
|
||||
}
|
||||
|
||||
List<Widget> _renderSection(dom.Element sec) {
|
||||
Future<List<Widget>> _renderSection(dom.Element sec) async {
|
||||
var out = <Widget>[];
|
||||
// Get Section Title
|
||||
var headings = sec.children.where(
|
||||
|
@ -202,7 +223,7 @@ class PageRenderer {
|
|||
); // space paragraphs
|
||||
break;
|
||||
case "figure":
|
||||
out.addAll(_renderImageFigure(element));
|
||||
out.addAll(await _renderImageFigure(element));
|
||||
out.add(
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
|
@ -210,7 +231,7 @@ class PageRenderer {
|
|||
);
|
||||
break;
|
||||
case "section":
|
||||
out.addAll(_renderSection(element));
|
||||
out.addAll(await _renderSection(element));
|
||||
break;
|
||||
case "dl":
|
||||
var dd = element.getElementsByTagName("dd").first;
|
||||
|
@ -274,7 +295,7 @@ class PageRenderer {
|
|||
for (var e in inner.body!.children) {
|
||||
if (e.localName == "figure") {
|
||||
// render image
|
||||
out.addAll(_renderImageFigure(e));
|
||||
out.addAll(await _renderImageFigure(e));
|
||||
out.add(
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
|
@ -329,25 +350,130 @@ class PageRenderer {
|
|||
),
|
||||
);
|
||||
} else if (element.classes.contains("mw-kartographer-container")) {
|
||||
logger.i("Found map container");
|
||||
var imgs = element
|
||||
.getElementsByTagName("div")
|
||||
.first
|
||||
.getElementsByTagName("a")
|
||||
.first
|
||||
.getElementsByTagName("img");
|
||||
if (imgs.isEmpty) break; // load maps that have a static image
|
||||
var img = imgs[0];
|
||||
var cap = element.getElementsByClassName("thumbcaption")[0];
|
||||
if (imgs.isEmpty ||
|
||||
(imgs.first.attributes["src"]
|
||||
?.startsWith("https://maps.wikimedia.org") ??
|
||||
false)) {
|
||||
logger.i("Rendering with FlutterMap");
|
||||
// render map using FlutterMap
|
||||
var dataElement = element
|
||||
.getElementsByClassName("thumbinner")
|
||||
.first
|
||||
.getElementsByClassName("mw-kartographer-map")
|
||||
.first;
|
||||
|
||||
var pointsRaw = RegExp(
|
||||
r"""<span class="noprint listing-coordinates".+?><span class="geo"><abbr class="latitude">(.+?)<\/abbr><abbr class="longitude">(.+?)<\/abbr>.+?}'>(\d+)<""")
|
||||
.allMatches(document!); // find markers
|
||||
logger.i("Found ${pointsRaw.length} markers");
|
||||
if (pointsRaw.isEmpty) break;
|
||||
var points = <Marker>[];
|
||||
for (var point in pointsRaw) {
|
||||
// convert to FlutterMap markers
|
||||
if (point.groups([1, 2]).any((element) => element == null)) {
|
||||
logger.w("No lat/lon found in pointer");
|
||||
continue;
|
||||
}
|
||||
// assign marker color
|
||||
var colorMatch = RegExp(r'background: #(.+?);')
|
||||
.firstMatch(point.group(0)!)
|
||||
?.group(1);
|
||||
Color bubbleColor = (colorMatch == null)
|
||||
? scheme.secondary
|
||||
: Color(int.parse("0xff$colorMatch"));
|
||||
|
||||
points.add(
|
||||
Marker(
|
||||
builder: (c) => Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: bubbleColor),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(point.group(3) ?? "X"),
|
||||
),
|
||||
),
|
||||
point: LatLng(
|
||||
double.parse(point.group(1)!),
|
||||
double.parse(point.group(2)!),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
out.add(const SizedBox(
|
||||
height: 10,
|
||||
));
|
||||
out.add(
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.8,
|
||||
height: (MediaQuery.of(context).size.width * 0.8 / 4) * 3,
|
||||
child: FlutterMap(
|
||||
options: MapOptions(
|
||||
center: LatLng(
|
||||
double.parse(
|
||||
dataElement.attributes["data-lat"] ?? "00.0"),
|
||||
double.parse(
|
||||
dataElement.attributes["data-lon"] ?? "00.0"),
|
||||
),
|
||||
zoom: double.parse(
|
||||
dataElement.attributes["data-zoom"] ?? "1"),
|
||||
),
|
||||
nonRotatedChildren: [
|
||||
RichAttributionWidget(
|
||||
attributions: [
|
||||
TextSourceAttribution(
|
||||
"OpenStreetMap contributors",
|
||||
onTap: () => launchUrlString(
|
||||
"https://openstreetmap.org/copyright"),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate:
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
userAgentPackageName: "cafe.caras.voyagehandbook",
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: points,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
var img = imgs[0];
|
||||
var cap = element.getElementsByClassName("thumbcaption")[0];
|
||||
out.add(const SizedBox(
|
||||
height: 10,
|
||||
));
|
||||
var offlineImage = await StorageAccess.getOfflineImage(img
|
||||
.attributes["src"]!
|
||||
.split('/')
|
||||
.last
|
||||
.replaceAll(RegExp(r"(?!\..+?)\?.+"), ""));
|
||||
out.add(
|
||||
SizedBox(
|
||||
width: width * 0.8,
|
||||
height: height * 0.3,
|
||||
child: WidgetZoom(
|
||||
zoomWidget:
|
||||
CachedNetworkImage(imageUrl: img.attributes["src"]!),
|
||||
zoomWidget: CachedNetworkImage(
|
||||
imageUrl: img.attributes["src"]!,
|
||||
errorWidget: (context, url, error) =>
|
||||
(offlineImage != null)
|
||||
? Image.file(offlineImage)
|
||||
: Flexible(
|
||||
child: Text(loc.imageError),
|
||||
),
|
||||
),
|
||||
heroAnimationTag: 'tag',
|
||||
),
|
||||
),
|
||||
|
@ -369,10 +495,12 @@ class PageRenderer {
|
|||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
element.remove();
|
||||
}
|
||||
|
||||
|
@ -384,7 +512,7 @@ class PageRenderer {
|
|||
return out;
|
||||
}
|
||||
|
||||
List<Widget> _renderImageFigure(dom.Element element) {
|
||||
Future<List<Widget>> _renderImageFigure(dom.Element element) async {
|
||||
var out = <Widget>[];
|
||||
|
||||
/// Image figure
|
||||
|
@ -397,6 +525,12 @@ class PageRenderer {
|
|||
if (figcap.isNotEmpty) {
|
||||
caption = figcap.first.text; // TODO: handle links
|
||||
}
|
||||
var offlineImage = await StorageAccess.getOfflineImage(img
|
||||
.attributes["src"]!
|
||||
.split('/')
|
||||
.last
|
||||
.replaceAll(RegExp(r"(?!\..+?)\?.+"), ""));
|
||||
|
||||
out.add(const SizedBox(
|
||||
height: 10,
|
||||
));
|
||||
|
@ -411,9 +545,10 @@ class PageRenderer {
|
|||
imageUrl: img.attributes["src"]!.replaceAll("//", "https://"),
|
||||
progressIndicatorBuilder: (context, url, downloadProgress) =>
|
||||
LinearProgressIndicator(value: downloadProgress.progress),
|
||||
errorWidget: (context, url, error) => Icon(
|
||||
Icons.error,
|
||||
color: scheme.error,
|
||||
errorWidget: (context, url, error) => (offlineImage != null)
|
||||
? Image.file(offlineImage)
|
||||
: Flexible(
|
||||
child: Text(loc.imageError),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -489,7 +624,9 @@ class PageRenderer {
|
|||
content.add(
|
||||
WidgetSpan(
|
||||
child: SelectableText(
|
||||
match.group(2)!,
|
||||
match.group(2)!.replaceAll(
|
||||
RegExp(r'<(?:(?:\/b)|(?:\/i)|(?:\/a)|(?:b)|(?:i)|(?:a )).*?>'),
|
||||
""),
|
||||
onTap: () {
|
||||
if (match.group(0)!.contains('rel="mw:ExtLink')) {
|
||||
// handle as an external link
|
||||
|
@ -535,8 +672,9 @@ class PageRenderer {
|
|||
.toList();
|
||||
for (var s in needToFormat) {
|
||||
content.add(TextSpan(
|
||||
text:
|
||||
noFormatting[needToFormat.indexOf(s)])); // add text before styled
|
||||
text: noFormatting[needToFormat.indexOf(s)].replaceAll(
|
||||
RegExp(r'<(?:(?:\/b)|(?:\/i)|(?:\/a)|(?:b)|(?:i)|(?:a )).*?>'),
|
||||
""))); // add text before styled
|
||||
var raw = s.group(0)!;
|
||||
content.add(
|
||||
TextSpan(
|
||||
|
@ -561,16 +699,6 @@ class PageRenderer {
|
|||
return content;
|
||||
}
|
||||
|
||||
final _extraColorsLight = {
|
||||
"blue": const Color.fromARGB(255, 34, 34, 157),
|
||||
"red": const Color.fromARGB(255, 152, 33, 33)
|
||||
};
|
||||
|
||||
final _extraColorsDark = {
|
||||
"blue": const Color.fromARGB(255, 84, 95, 247),
|
||||
"red": const Color.fromARGB(255, 242, 69, 69)
|
||||
};
|
||||
|
||||
SingleChildScrollView _renderList(dom.Element element) {
|
||||
var out = <Widget>[];
|
||||
var i = 0;
|
||||
|
@ -580,24 +708,19 @@ class PageRenderer {
|
|||
? item.getElementsByClassName("listing-name")[0].text
|
||||
: null;
|
||||
|
||||
Color bubbleColor;
|
||||
if (item.innerHtml.contains("background: #0000FF")) {
|
||||
bubbleColor =
|
||||
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
||||
? _extraColorsDark["blue"]!
|
||||
: _extraColorsLight["blue"]!;
|
||||
} else if (item.innerHtml.contains("background: #800000")) {
|
||||
bubbleColor =
|
||||
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
||||
? _extraColorsDark["red"]!
|
||||
: _extraColorsLight["red"]!;
|
||||
} else {
|
||||
bubbleColor = scheme.secondary;
|
||||
var colorMatch =
|
||||
RegExp(r'background: #(.+?);').firstMatch(item.innerHtml)?.group(1);
|
||||
Color bubbleColor = (colorMatch == null)
|
||||
? scheme.secondary
|
||||
: Color(int.parse("0xff$colorMatch"));
|
||||
if (element.getElementsByClassName("geo").isNotEmpty) {
|
||||
element.getElementsByClassName("geo").first.remove();
|
||||
}
|
||||
var rest =
|
||||
((title != null) ? item.text.replaceAll(title, "") : item.text);
|
||||
if (rest.startsWith("1")) {
|
||||
rest = rest.substring(1); // TODO: figure out how to remove it better?
|
||||
}
|
||||
|
||||
var rest = (title != null)
|
||||
? item.text.replaceAll(item.getElementsByTagName("span")[0].text, "")
|
||||
: item.text;
|
||||
out.add(const SizedBox(
|
||||
height: 5,
|
||||
));
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:html/parser.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:voyagehandbook/api/classes.dart';
|
||||
import 'package:voyagehandbook/api/wikimedia.dart';
|
||||
|
||||
/*
|
||||
Voyage Handbook - The open-source WikiVoyage reader
|
||||
|
@ -35,6 +40,19 @@ class StorageAccess {
|
|||
.toList();
|
||||
}
|
||||
|
||||
/// Get files in `offline` folder, which contains recently opened pages
|
||||
static Future<List<Map<String, dynamic>>> get offline async {
|
||||
var files =
|
||||
Directory("${(await getApplicationDocumentsDirectory()).path}/offline");
|
||||
if (!files.existsSync()) files.createSync();
|
||||
return files
|
||||
.listSync()
|
||||
.whereType<File>()
|
||||
.toList()
|
||||
.map<Map<String, dynamic>>((e) => jsonDecode(e.readAsStringSync()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
static void addToRecents(String pageName, String pageKey) async {
|
||||
var files =
|
||||
Directory("${(await getApplicationDocumentsDirectory()).path}/recent");
|
||||
|
@ -70,4 +88,91 @@ class StorageAccess {
|
|||
recent.writeAsStringSync(jsonEncode(recentContent));
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> isDownloaded(String pageKey) async {
|
||||
var files =
|
||||
Directory("${(await getApplicationDocumentsDirectory()).path}/offline");
|
||||
if (!files.existsSync()) files.createSync();
|
||||
var offlinePage = File("${files.path}/$pageKey");
|
||||
return offlinePage.existsSync();
|
||||
}
|
||||
|
||||
static Future<void> downloadArticle(String pageKey, String pageTitle) async {
|
||||
try {
|
||||
var files = Directory(
|
||||
"${(await getApplicationDocumentsDirectory()).path}/offline");
|
||||
if (!files.existsSync()) files.createSync();
|
||||
var offlinePage = File("${files.path}/$pageKey");
|
||||
var raw = await WikiApi.getRawPage(pageKey);
|
||||
var page = parse(raw.html);
|
||||
var out = "<html><head></head><body>";
|
||||
var sections = page.body!.children
|
||||
.where((element) => element.localName == "section");
|
||||
for (var el in sections) {
|
||||
out += el.outerHtml;
|
||||
}
|
||||
out += "</body></html>";
|
||||
var imgMatch = RegExp(r'<img.+?src="(.+?)".+?>')
|
||||
.allMatches(page.body!.innerHtml); // TODO: ask to overwrite
|
||||
if (imgMatch.isNotEmpty) {
|
||||
// download images offline
|
||||
for (var match in imgMatch) {
|
||||
var src = match.group(1)!;
|
||||
if (!src.startsWith("https://")) {
|
||||
src = src.replaceAll("//", "https://");
|
||||
}
|
||||
var r = await Dio().get(
|
||||
src,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
followRedirects: false,
|
||||
validateStatus: (status) {
|
||||
return (status ?? 200) < 500;
|
||||
},
|
||||
),
|
||||
);
|
||||
var assetDir = Directory("${files.path}/assets");
|
||||
if (!assetDir.existsSync()) assetDir.createSync();
|
||||
var img = File(
|
||||
"${assetDir.path}/${src.split('/').last.replaceAll(RegExp(r"(?!\..+?)\?.+"), "")}");
|
||||
print(img.path);
|
||||
var openImg = img.openSync(mode: FileMode.write);
|
||||
openImg.writeFromSync(r.data);
|
||||
}
|
||||
}
|
||||
raw.html = out;
|
||||
if (sections.isEmpty) {
|
||||
return Future.error("No sections to save");
|
||||
}
|
||||
offlinePage.writeAsStringSync(jsonEncode(raw.toJson()),
|
||||
mode: FileMode.writeOnly);
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return Future.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<RawPage?> getOfflinePage(String pageKey) async {
|
||||
var files =
|
||||
Directory("${(await getApplicationDocumentsDirectory()).path}/offline");
|
||||
if (!files.existsSync()) return null;
|
||||
var offlinePage = File("${files.path}/$pageKey");
|
||||
if (!offlinePage.existsSync()) return null;
|
||||
try {
|
||||
return RawPage.fromJson(jsonDecode(offlinePage.readAsStringSync()));
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<File?> getOfflineImage(String key) async {
|
||||
var files = Directory(
|
||||
"${(await getApplicationDocumentsDirectory()).path}/offline/assets");
|
||||
return File("${files.path}/$key");
|
||||
}
|
||||
}
|
||||
|
|
85
lib/views/downloads.dart
Normal file
|
@ -0,0 +1,85 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:voyagehandbook/util/drawer.dart';
|
||||
import 'package:voyagehandbook/util/storage.dart';
|
||||
import 'package:voyagehandbook/util/styles.dart';
|
||||
import 'package:voyagehandbook/views/pageview.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class DownloadsView extends StatefulWidget {
|
||||
const DownloadsView({super.key});
|
||||
|
||||
@override
|
||||
State<DownloadsView> createState() => _DownloadsViewState();
|
||||
}
|
||||
|
||||
class _DownloadsViewState extends State<DownloadsView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadDownloads();
|
||||
}
|
||||
|
||||
var _content = <Widget>[];
|
||||
var _isLoading = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.downloadsTitle),
|
||||
),
|
||||
drawer: genDrawer(3, context),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _isLoading
|
||||
? [const CircularProgressIndicator()]
|
||||
: _content.isEmpty
|
||||
? [
|
||||
Text(
|
||||
AppLocalizations.of(context)!.noDownloads,
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
]
|
||||
: _content,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void loadDownloads() async {
|
||||
var files = await StorageAccess.offline;
|
||||
_content = List.generate(
|
||||
files.length,
|
||||
(index) => SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: MediaQuery.of(context).size.height * 0.1,
|
||||
child: InkWell(
|
||||
onTap: () => Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (_) => ArticleView(
|
||||
pageKey: files[index]["key"],
|
||||
name: files[index]["title"],
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
files[index]["title"],
|
||||
textAlign: TextAlign.center,
|
||||
style: PageStyles.h1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
_isLoading = false;
|
||||
setState(() {});
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'package:voyagehandbook/util/storage.dart';
|
|||
import 'package:voyagehandbook/util/styles.dart';
|
||||
import 'package:voyagehandbook/views/pageview.dart';
|
||||
import 'package:voyagehandbook/views/search.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
/*
|
||||
Voyage Handbook - The open-source WikiVoyage reader
|
||||
|
@ -48,7 +49,7 @@ class _HomeViewState extends State<HomeView> {
|
|||
),
|
||||
child: const Icon(Icons.search),
|
||||
),
|
||||
appBar: AppBar(title: const Text("Home")),
|
||||
appBar: AppBar(title: Text(AppLocalizations.of(context)!.home)),
|
||||
drawer: genDrawer(1, context),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
|
@ -58,9 +59,8 @@ class _HomeViewState extends State<HomeView> {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: (_recents.isEmpty)
|
||||
? [
|
||||
const Flexible(
|
||||
child: Text(
|
||||
"You haven't opened anything recently, swipe right and start searching."),
|
||||
Flexible(
|
||||
child: Text(AppLocalizations.of(context)!.noRecents),
|
||||
)
|
||||
]
|
||||
: _recents),
|
||||
|
@ -79,9 +79,10 @@ class _HomeViewState extends State<HomeView> {
|
|||
DateTime.fromMillisecondsSinceEpoch(b["date"]))
|
||||
? 0
|
||||
: 1);
|
||||
if (!mounted) return;
|
||||
_recents = [
|
||||
const Text(
|
||||
"Recent pages",
|
||||
Text(
|
||||
AppLocalizations.of(context)!.recentPages,
|
||||
style: PageStyles.h1,
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:voyagehandbook/api/wikimedia.dart';
|
||||
|
@ -5,6 +6,7 @@ import 'package:voyagehandbook/util/drawer.dart';
|
|||
import 'package:voyagehandbook/util/render.dart';
|
||||
import 'package:voyagehandbook/util/storage.dart';
|
||||
import 'package:voyagehandbook/util/styles.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
/*
|
||||
Voyage Handbook - The open-source WikiVoyage reader
|
||||
|
@ -77,34 +79,55 @@ class _ArticleViewState extends State<ArticleView> {
|
|||
}
|
||||
|
||||
void loadPage() async {
|
||||
if (!mounted) return;
|
||||
var renderer = PageRenderer(
|
||||
Theme.of(context).colorScheme,
|
||||
MediaQuery.of(context).size.height,
|
||||
MediaQuery.of(context).size.width,
|
||||
context);
|
||||
if (kDebugMode) {
|
||||
_content = [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: renderer
|
||||
.renderFromPageHTML(await WikiApi.getRawPage(widget.pageKey)),
|
||||
)
|
||||
];
|
||||
} else {
|
||||
context,
|
||||
AppLocalizations.of(context)!);
|
||||
|
||||
try {
|
||||
_content = [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: renderer
|
||||
child: await renderer
|
||||
.renderFromPageHTML(await WikiApi.getRawPage(widget.pageKey)),
|
||||
)
|
||||
];
|
||||
} catch (e) {
|
||||
if (e.toString().contains("Failed host lookup")) {
|
||||
// user is offline
|
||||
var offline = await StorageAccess.getOfflinePage(widget.pageKey);
|
||||
if (offline == null) {
|
||||
// Not downloaded, show error
|
||||
if (!mounted) return;
|
||||
_content = [
|
||||
const Text(
|
||||
"Error while rendering:",
|
||||
Text(
|
||||
AppLocalizations.of(context)!.renderError,
|
||||
style: PageStyles.h1,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(AppLocalizations.of(context)!.offlineError)
|
||||
];
|
||||
} else {
|
||||
// Render offline version
|
||||
if (!mounted) return;
|
||||
_content = [
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.9,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: await renderer.renderFromPageHTML(offline),
|
||||
)
|
||||
];
|
||||
}
|
||||
} else {
|
||||
_content = [
|
||||
Text(
|
||||
AppLocalizations.of(context)!.renderError,
|
||||
style: PageStyles.h1,
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:voyagehandbook/api/classes.dart';
|
||||
import 'package:voyagehandbook/api/wikimedia.dart';
|
||||
import 'package:voyagehandbook/util/storage.dart';
|
||||
import 'package:voyagehandbook/views/pageview.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../util/drawer.dart';
|
||||
|
||||
/*
|
||||
|
@ -36,7 +38,8 @@ class _SearchViewState extends State<SearchView> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("Search WikiVoyage")),
|
||||
appBar:
|
||||
AppBar(title: Text(AppLocalizations.of(context)!.searchAppBarTitle)),
|
||||
drawer: genDrawer(2, context),
|
||||
body: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
|
@ -59,7 +62,7 @@ class _SearchViewState extends State<SearchView> {
|
|||
_searchResults = r;
|
||||
setState(() {});
|
||||
},
|
||||
child: const Text("Search"),
|
||||
child: Text(AppLocalizations.of(context)!.search),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
|
@ -129,6 +132,126 @@ class _SearchViewState extends State<SearchView> {
|
|||
child: Card(
|
||||
elevation: 2,
|
||||
child: InkWell(
|
||||
onLongPress: () {
|
||||
// Show download dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => AlertDialog(
|
||||
title: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.offlineTitle),
|
||||
content: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.offlineDialog(
|
||||
_searchResults[index]
|
||||
.title),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => Dialog(
|
||||
child: SizedBox(
|
||||
height: 100,
|
||||
child: Row(
|
||||
children: [
|
||||
const Padding(
|
||||
padding:
|
||||
EdgeInsets
|
||||
.all(
|
||||
10),
|
||||
child:
|
||||
CircularProgressIndicator(),
|
||||
),
|
||||
Text(AppLocalizations
|
||||
.of(context)!
|
||||
.downloading)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
try {
|
||||
await StorageAccess
|
||||
.downloadArticle(
|
||||
_searchResults[
|
||||
index]
|
||||
.key,
|
||||
_searchResults[
|
||||
index]
|
||||
.title);
|
||||
if (!mounted) return;
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
ScaffoldMessenger.of(
|
||||
context)
|
||||
.clearSnackBars();
|
||||
ScaffoldMessenger.of(
|
||||
context)
|
||||
.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.downloadComplete),
|
||||
duration:
|
||||
const Duration(
|
||||
seconds: 4),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
AlertDialog(
|
||||
title: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.error),
|
||||
content:
|
||||
SingleChildScrollView(
|
||||
child: Text(
|
||||
e.toString()),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(
|
||||
context)
|
||||
.pop(),
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.ok),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.yes),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context)
|
||||
.pop(),
|
||||
child: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.no),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
|
@ -145,13 +268,27 @@ class _SearchViewState extends State<SearchView> {
|
|||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(children: [
|
||||
Text(
|
||||
_searchResults[index].title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
fontSize:
|
||||
16), // TODO: responsive sizing
|
||||
),
|
||||
if (_searchResults[index]
|
||||
.downloaded)
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
if (_searchResults[index]
|
||||
.downloaded)
|
||||
const Icon(
|
||||
Icons.download,
|
||||
size: 15,
|
||||
),
|
||||
]),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
|
397
pubspec.lock
|
@ -5,42 +5,42 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "503361166f4a100e0d7eb7fb5a62c6f0322512f2bcb48d6922caf98f24b0ab90"
|
||||
sha256: "0816708f5fbcacca324d811297153fe3c8e047beb5c6752e12292d2974c17045"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "56.0.0"
|
||||
version: "62.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "93fcd81a6716e69864516750590cf1e699420615046bda19100238aa7b429785"
|
||||
sha256: "21862995c9932cd082f89d72ae5f5e2c110d1a0204ad06e4ebaee8307b76b834"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.8.0"
|
||||
version: "6.0.0"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
|
||||
sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.6"
|
||||
version: "3.3.7"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: "4cab82a83ffef80b262ddedf47a0a8e56ee6fbf7fe21e6e768b02792034dd440"
|
||||
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.4.2"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.10.0"
|
||||
version: "2.11.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -53,10 +53,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777"
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.4.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -69,34 +69,34 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
|
||||
sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "4.0.0"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95
|
||||
sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.2.1"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727
|
||||
sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
version: "2.4.6"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "14febe0f5bac5ae474117a36099b4de6f1dbc52df6c5e55534b3da9591bf4292"
|
||||
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.7"
|
||||
version: "7.2.10"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -109,10 +109,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: "31b7c748fd4b9adf8d25d72a4c4a59ef119f12876cf414f94f8af5131d5fa2b0"
|
||||
sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.4.4"
|
||||
version: "8.6.1"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -141,18 +141,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: "3d1505d91afa809d177efd4eed5bb0eb65805097a1463abdd2add076effae311"
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
version: "2.0.3"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -173,18 +173,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
|
||||
sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
version: "4.5.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
|
||||
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
version: "1.17.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -197,18 +197,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
|
||||
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.2"
|
||||
version: "1.0.0"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -217,30 +217,38 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
dart_internal:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_internal
|
||||
sha256: dae3976f383beddcfcd07ad5291a422df2c8c0a8a03c52cda63ac7b4f26e0f4e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.8"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "6d691edde054969f0e0f26abb1b30834b5138b963793e56f69d3a9a4435e6352"
|
||||
sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.3.2"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "2644a9e0965a7aa3deb09cb8ce4081db4450c178f472818c8cd34216a3070d7b"
|
||||
sha256: a9d76e72985d7087eb7c5e7903224ae52b337131518d127c554b9405936752b8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "5.2.1+1"
|
||||
dynamic_color:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: c4a508284b14ec4dda5adba2c28b2cdd34fbae1afead7e8c52cad87d51c5405b
|
||||
sha256: "74dff1435a695887ca64899b8990004f8d1232b0e84bfc4faa1fdda7c6f57cc1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.2"
|
||||
version: "1.6.5"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -253,10 +261,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
|
||||
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.2"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -290,10 +298,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flutter_cache_manager
|
||||
sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
|
||||
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
version: "3.3.1"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -306,10 +314,23 @@ packages:
|
|||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
||||
sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.2"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_map:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_map
|
||||
sha256: "5286f72f87deb132daa1489442d6cc46e986fc105cb727d9ae1b602b35b1d1f3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -332,26 +353,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: f9e130f3259f52d26f0cfc0e964513796dafed572fa52e45d2f8d6ca14db39b2
|
||||
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.3.1"
|
||||
html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: html
|
||||
sha256: "79d498e6d6761925a34ee5ea8fa6dfef38607781d2fa91e37523474282af55cb"
|
||||
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.2"
|
||||
version: "0.15.4"
|
||||
html_unescape:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -364,10 +385,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
|
||||
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.5"
|
||||
version: "1.1.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -392,6 +413,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -404,50 +433,74 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: c33da08e136c3df0190bd5bbe51ae1df4a7d96e7954d1d7249fea2968a72d317
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
version: "4.8.1"
|
||||
json_serializable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_serializable
|
||||
sha256: dadc08bd61f72559f938dd08ec20dbfec6c709bba83515085ea943d2078d187a
|
||||
sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.6.1"
|
||||
version: "6.7.1"
|
||||
latlong2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: latlong2
|
||||
sha256: "18712164760cee655bc790122b0fd8f3d5b3c36da2cb7bf94b68a197fbb0811b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.9.0"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.1.1"
|
||||
lists:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lists
|
||||
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
logger:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: logger
|
||||
sha256: "7ad7215c15420a102ec687bb320a7312afd449bac63bfb1c60d9787c27b9767f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.2.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
|
||||
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.13"
|
||||
version: "0.12.15"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -460,10 +513,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
|
||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.0"
|
||||
version: "1.9.1"
|
||||
mgrs_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mgrs_dart
|
||||
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -492,42 +553,42 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.8.2"
|
||||
version: "1.8.3"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9"
|
||||
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.13"
|
||||
version: "2.0.15"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7"
|
||||
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.24"
|
||||
version: "2.0.27"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "026b97a6c29da75181a37aae2eba9227f5fe13cb2838c6b975ce209328b8ab4e"
|
||||
sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.2.3"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
|
||||
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.10"
|
||||
version: "2.1.11"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -540,26 +601,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
|
||||
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
version: "2.1.7"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4"
|
||||
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.0"
|
||||
version: "5.4.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -580,10 +633,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: c3120a968135aead39699267f4c74bc9a08e4e909e86bc1b0af5bfd78691123c
|
||||
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.2"
|
||||
version: "3.7.3"
|
||||
polylabel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: polylabel
|
||||
sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -600,22 +661,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
proj4dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: proj4dart
|
||||
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "307de764d305289ff24ad257ad5c5793ce56d04947599ad68b3baa124105fc17"
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: ec85d7d55339d85f44ec2b682a82fea340071e8978257e5a43e69f79e98ef50c
|
||||
sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
version: "1.2.3"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -628,74 +697,74 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41
|
||||
sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.18"
|
||||
version: "2.2.0"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521
|
||||
sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.17"
|
||||
version: "2.2.0"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310"
|
||||
sha256: "0dc5c49ad8a05ed358b991b60c7b0ba1a14e16dae58af9b420d6b9e82dc024ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.3.0"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707"
|
||||
sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.3.0"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc"
|
||||
sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.3.0"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8"
|
||||
sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
version: "2.2.0"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436"
|
||||
sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
version: "2.3.0"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: c24a96135a2ccd62c64b69315a14adc5c3419df63b4d7c05832a346fdb73682c
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: a988c0e8d8ffbdb8a28aa7ec8e449c260f3deb808781fe1284d22c5bba7156e8
|
||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
version: "1.0.4"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -705,18 +774,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: c2bea18c95cfa0276a366270afaa2850b09b4a76db95d546f3d003dcc7011298
|
||||
sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.7"
|
||||
version: "1.4.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
|
||||
sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
version: "1.3.4"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -729,18 +798,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sqflite
|
||||
sha256: "500d6fec583d2c021f2d25a056d96654f910662c64f836cd2063167b8f1fa758"
|
||||
sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.6"
|
||||
version: "2.2.8+4"
|
||||
sqflite_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "963dad8c4aa2f814ce7d2d5b1da2f36f31bd1a439d8f27e3dc189bb9d26bc684"
|
||||
sha256: "8f7603f3f8f126740bc55c4ca2d1027aab4b74a1267a3e31ce51fe40e3b65b8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
version: "2.4.5+1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -777,10 +846,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "33b31b6beb98100bf9add464a36a8dd03eb10c7a8cf15aeec535e9b054aaf04b"
|
||||
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.1.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -793,10 +862,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
|
||||
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.16"
|
||||
version: "0.5.1"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -809,74 +878,82 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5"
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.2"
|
||||
unicode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: unicode
|
||||
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e"
|
||||
sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.10"
|
||||
version: "6.1.11"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: dd729390aa936bf1bdf5cd1bc7468ff340263f80a2c4f569416507667de8e3c8
|
||||
sha256: "15f5acbf0dce90146a0f5a2c4a002b1814a6303c4c5c075aa2623b2d16156f03"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.26"
|
||||
version: "6.0.36"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92"
|
||||
sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.3"
|
||||
version: "6.1.4"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc"
|
||||
sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.4"
|
||||
version: "3.0.5"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "0ef2b4f97942a16523e51256b799e9aa1843da6c60c55eefbfa9dbc2dcb8331a"
|
||||
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.4"
|
||||
version: "3.0.5"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370"
|
||||
sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
|
||||
sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.16"
|
||||
version: "2.0.17"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd
|
||||
sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.6"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -897,18 +974,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0"
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "1.1.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: ca49c0bc209c687b887f30527fb6a9d80040b072cc2990f34b9bec3e7663101b
|
||||
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.4.0"
|
||||
widget_zoom:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -921,10 +998,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
|
||||
sha256: dfdf0136e0aa7a1b474ea133e67cb0154a0acd2599c4f3ada3b49d38d38793ee
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
version: "5.0.5"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wkt_parser
|
||||
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -937,18 +1022,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5"
|
||||
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.2"
|
||||
version: "6.3.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "23812a9b125b48d4007117254bca50abb6c712352927eece9e155207b1db2370"
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=2.19.4 <3.0.0"
|
||||
flutter: ">=3.4.0-17.0.pre"
|
||||
dart: ">=3.0.0 <3.2.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
|
11
pubspec.yaml
|
@ -16,10 +16,10 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.0.0-alpha.1+1
|
||||
version: 1.0.0-alpha.2+3
|
||||
|
||||
environment:
|
||||
sdk: '>=2.19.4 <3.0.0'
|
||||
sdk: '>=3.0.0'
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
|
@ -46,6 +46,12 @@ dependencies:
|
|||
cached_network_image: ^3.2.3
|
||||
html_unescape: ^2.0.0
|
||||
widget_zoom: ^0.0.1
|
||||
flutter_map: ^5.0.0
|
||||
latlong2: ^0.9.0
|
||||
logger: ^1.4.0
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -65,6 +71,7 @@ dev_dependencies:
|
|||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
generate: true
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
|
|