|
1 | 1 | import 'dart:async'; |
2 | 2 | import 'dart:math' as math; |
3 | 3 |
|
| 4 | +import 'package:flutter/material.dart'; |
4 | 5 | import 'package:flutter_map/flutter_map.dart'; |
5 | 6 | import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart' as fmtc; |
6 | 7 | import 'package:hive/hive.dart'; |
@@ -532,21 +533,92 @@ class OfflineMapService { |
532 | 533 | await _regionsBox?.clear(); |
533 | 534 | } |
534 | 535 |
|
| 536 | + /// CartoDB Voyager tile URL - used for both light and dark mode |
| 537 | + /// Voyager has excellent road visibility and supports zoom levels 0-20 |
| 538 | + /// For dark mode, we apply a color inversion filter at the widget level |
| 539 | + static const String _voyagerTileUrl = |
| 540 | + 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png'; |
| 541 | + |
| 542 | + /// Subdomains for CartoDB tile servers (load balancing) |
| 543 | + static const List<String> _cartoSubdomains = ['a', 'b', 'c', 'd']; |
| 544 | + |
| 545 | + /// Color inversion matrix for dark mode |
| 546 | + /// This inverts the light Voyager tiles to create a dark appearance with visible roads |
| 547 | + static const ColorFilter darkModeInvertFilter = ColorFilter.matrix(<double>[ |
| 548 | + -1, |
| 549 | + 0, |
| 550 | + 0, |
| 551 | + 0, |
| 552 | + 255, |
| 553 | + 0, |
| 554 | + -1, |
| 555 | + 0, |
| 556 | + 0, |
| 557 | + 255, |
| 558 | + 0, |
| 559 | + 0, |
| 560 | + -1, |
| 561 | + 0, |
| 562 | + 255, |
| 563 | + 0, |
| 564 | + 0, |
| 565 | + 0, |
| 566 | + 1, |
| 567 | + 0, |
| 568 | + ]); |
| 569 | + |
| 570 | + /// Wraps a widget with the dark mode color inversion filter |
| 571 | + /// |
| 572 | + /// Use this to wrap TileLayer widgets when displaying maps in dark mode. |
| 573 | + /// The filter inverts the light Voyager tiles to create a dark appearance. |
| 574 | + static Widget wrapWithDarkModeFilter(Widget child) { |
| 575 | + return ColorFiltered(colorFilter: darkModeInvertFilter, child: child); |
| 576 | + } |
| 577 | + |
535 | 578 | /// Gets a tile layer that uses the FMTC cache. |
536 | 579 | /// |
537 | 580 | /// Falls back to network tiles when cache misses occur. |
| 581 | + /// Uses light mode tiles by default. |
538 | 582 | TileLayer getCachedTileLayer() { |
| 583 | + return getThemedCachedTileLayer(isDarkMode: false); |
| 584 | + } |
| 585 | + |
| 586 | + /// Gets a theme-aware tile layer that uses the FMTC cache. |
| 587 | + /// |
| 588 | + /// Uses CartoDB Voyager tiles for both light and dark mode. |
| 589 | + /// For dark mode, the calling widget should wrap this with [wrapWithDarkModeFilter]. |
| 590 | + /// Falls back to network tiles when cache misses occur. |
| 591 | + TileLayer getThemedCachedTileLayer({required bool isDarkMode}) { |
539 | 592 | _ensureInitialized(); |
540 | 593 |
|
| 594 | + // Both light and dark mode use Voyager tiles |
| 595 | + // Dark mode applies color inversion at the widget level |
541 | 596 | return TileLayer( |
542 | | - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', |
| 597 | + urlTemplate: _voyagerTileUrl, |
| 598 | + subdomains: _cartoSubdomains, |
543 | 599 | userAgentPackageName: 'com.ph_fare_calculator', |
| 600 | + maxZoom: 20, // Voyager supports up to zoom 20 |
544 | 601 | tileProvider: fmtc.FMTCTileProvider( |
545 | 602 | stores: {_storeName: fmtc.BrowseStoreStrategy.readUpdateCreate}, |
546 | 603 | ), |
547 | 604 | ); |
548 | 605 | } |
549 | 606 |
|
| 607 | + /// Gets a tile layer without FMTC caching (for fallback scenarios). |
| 608 | + /// |
| 609 | + /// Uses CartoDB Voyager tiles for both light and dark mode. |
| 610 | + /// For dark mode, the calling widget should wrap this with [wrapWithDarkModeFilter]. |
| 611 | + static TileLayer getNetworkTileLayer({required bool isDarkMode}) { |
| 612 | + // Both light and dark mode use Voyager tiles |
| 613 | + // Dark mode applies color inversion at the widget level |
| 614 | + return TileLayer( |
| 615 | + urlTemplate: _voyagerTileUrl, |
| 616 | + subdomains: _cartoSubdomains, |
| 617 | + userAgentPackageName: 'com.ph_fare_calculator', |
| 618 | + maxZoom: 20, // Voyager supports up to zoom 20 |
| 619 | + ); |
| 620 | + } |
| 621 | + |
550 | 622 | /// Checks if a point is within any downloaded region. |
551 | 623 | bool isPointCached(LatLng point) { |
552 | 624 | for (final region in _allRegions) { |
|
0 commit comments