Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Caching #48

Closed
AlysonTrizotto opened this issue Mar 22, 2022 · 43 comments
Closed

Implement Caching #48

AlysonTrizotto opened this issue Mar 22, 2022 · 43 comments
Assignees
Labels
bug invalid This cannot be verified question/help

Comments

@AlysonTrizotto
Copy link

Hello, I have a project in a very advanced stage already, and I wanted to implement caching in it, so I can use your api, will I need to redo all of it? Or do you have one that you don't need? I saw that your api is for Flutter_map, and I'm using flutter_map. Do I need to redo it all to be able to use your api?

@JaffaKetchup
Copy link
Owner

Hi there,
You shouldn't need to redo all of it if you're new here. Nothing about flutter_map needs to change to use this: nothing is overridden or changed in any way.
I'm always happy to give more help if that wasn't quite what you needed!

@JaffaKetchup JaffaKetchup changed the title implement caching Implement caching Mar 22, 2022
@AlysonTrizotto
Copy link
Author

So, since yesterday I've been trying to figure out what I need to do so I don't have to redo everything by looking at the examples and reading your documentation, I'll show you my code, maybe you have some idea of ​​what you need.

this is part of the map:

` var _needLoadingError = true;

return Scaffold(
  appBar: AppBar(
    title: Text('Mapa'),
  ),
  body: Center(
    child: Stack(children: [
      FlutterMap(
          mapController: mapController,
          options: MapOptions(
            maxZoom: 18,
            minZoom: 4,
            center: currentCenter,
            zoom: zoomAtual,
            onTap: (k, latlng) {
              removeMarker();
              setState(() {
                addMarker(latlng);
              });
            },
            plugins: [],
            slideOnBoundaries: true,
            screenSize: MediaQuery.of(context).size,
            
          ),
          layers: [
            TileLayerOptions(
                tileProvider: MyTileProvider(onError: _onTileError),
                urlTemplate:
                    "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
                subdomains: ['a', 'b', 'c'],
                //tileProvider: NonCachingNetworkTileProvider(),
                //tileProvider: const CachedTileProvider(),
                updateInterval: 1,
                errorTileCallback: (Tile tile, error) {
                  if (_needLoadingError) {
                    WidgetsBinding.instance!.addPostFrameCallback((_) {
                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                        duration: Duration(seconds: 1),
                        content: Text(
                          error.toString(),
                          style: TextStyle(color: Colors.black),
                        ),
                        backgroundColor: Colors.deepOrange,
                      ));
                    });

                    _needLoadingError = false;
                  }
                  throw Exception('Unknown error, description: $error');
                }),
            MarkerLayerOptions(markers: [
              for (int i = 0; i < markers.length; i++) markers[i]
            ]),
            MarkerLayerOptions(markers: [
              for (int i = 0; i < markerDb.length; i++) markerDb[i]
            ]),
            MarkerLayerOptions(markers: [
              for (int i = 0; i < markersTracker.length; i++)
                markersTracker[i],
            ]),
          ]),

`

This is what I'm trying to cache:

`import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_map/flutter_map.dart';

class CachedTileProvider extends TileProvider {
const CachedTileProvider();
@OverRide
ImageProvider getImage(Coords coords, TileLayerOptions options) {
//print(getImage(coords, options));
return CachedNetworkImageProvider(
getTileUrl(coords, options),
);
}
}

typedef ErrorResponseHandler = Future<http.Response> Function(Object error,
[StackTrace stack]);

class MyTileProvider extends TileProvider {
MyTileProvider({ required this.onError });

final VoidCallback onError;

@OverRide
ImageProvider getImage(Coords coords, TileLayerOptions options) {
final url = getTileUrl(coords, options);
return CachedNetworkImageProvider(url, errorListener: onError);
}
}
`

@AlysonTrizotto
Copy link
Author

All I want is to make it store the tiles that have already been loaded so that it doesn't get lost, maybe for a day or two it's great, then it can be discarded, the problem is that I'm getting to flutter now, so...

@JaffaKetchup
Copy link
Owner

Ok, so all you need to change is the tile provider to StorageCachingTileProvider() and explore it's arguments. You don't need any of that second block of code.
Note that an asynchronous value is required, so you may need a FutureBuilder().

@JaffaKetchup
Copy link
Owner

All I want is to make it store the tiles that have already been loaded so that it doesn't get lost, maybe for a day or two it's great, then it can be discarded, the problem is that I'm getting to flutter now, so...

You won't be able to transfer existing cached tiles - without manual migration code which is beyond the scope of this library - if that's what you're asking.
There are options available to change the valid cache duration and the method of retrieval.

@AlysonTrizotto
Copy link
Author

I think I expressed myself poorly!

What I need is to store the cache and be able to use it when I'm not on the network, I believe this is the function of the cache, right? And also increase its duration, for example, extend its lifespan by 2 days, you know?

@JaffaKetchup
Copy link
Owner

Yes this is possible.
By default, tiles are loaded from cache unless the valid duration has expired, after which they are re-cached from network whenever possible.
You can change this to online first, where the cache is only used if there is no Internet connection; or cache only, where tiles are only used from cache (if they are missing, the Internet is not attempted).

@JaffaKetchup
Copy link
Owner

You can also set the valid cache duration, which defaults to 16 days per tile.

@AlysonTrizotto
Copy link
Author

Did you mention up there that I'm going to need something like this?

final RecoveredRegion region = (await StorageCachingTileProvider( parentDirectory: cacheDir, storeName: name) .recoverDownload())!;

@JaffaKetchup
Copy link
Owner

No, I think you've misunderstood. For now, you can ignore any ...Regions, as that is more advanced usage.
You will want something like:

    tileProvider: StorageCachingTileProvider(parentDirectory: cacheDir, storeName: 'Name',),

@AlysonTrizotto
Copy link
Author

AlysonTrizotto commented Mar 22, 2022

So, trying to do this, it doesn't even start... I even tried to copy part of your code to see if it would work, but it didn't...
Look how it turned out and the error it gave:

main.dart

import 'package:flutter/material.dart';
import 'package:feature_discovery/feature_discovery.dart';
import 'package:localiza_favoritos/componentes/general_provider.dart';

import 'package:provider/provider.dart';
import 'package:localiza_favoritos/componentes/mapa.dart';
import 'package:localiza_favoritos/componentes/nethort_help.dart';
import 'package:localiza_favoritos/componentes/rota.dart';
import 'package:localiza_favoritos/componentes/tema.dart';
import 'package:localiza_favoritos/screens/cadastro/editar_favoritos.dart';
import 'package:localiza_favoritos/screens/cadastro/formulario_categoria.dart';
import 'package:localiza_favoritos/screens/cadastro/formulario_favoritos.dart';
import 'package:localiza_favoritos/screens/dashboard/chama_paginas_pesquisa.dart';

import 'screens/dashboard/inicio.dart';

void main() {
  runApp(CLHApp());
}

// ignore: use_key_in_widget_constructors
class CLHApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FeatureDiscovery(
      child: MultiProvider(
        providers: [
          ChangeNotifierProvider<GeneralProvider>(
            create: (context) => GeneralProvider(),
          ),
        ],
        child: MaterialApp(
          theme: localizaTema,
          debugShowCheckedModeBanner: false,
          //home: FormularioCategoria(),
          // home: FormularioCadastro(0.0,0.0),
          home: dashboard(0),

          routes: {
            '/retornoEditaFavorios': (BuildContext context) => dashboard(1),
          },
        ),
      ),
    );
  }
}

Example code

// ignore_for_file: prefer_void_to_null

import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';

import 'package:shared_preferences/shared_preferences.dart';

class GeneralProvider extends ChangeNotifier {
  //! CACHING !//

  bool _cachingEnabled = false;
  bool get cachingEnabled => _cachingEnabled;
  set cachingEnabled(bool newVal) {
    _cachingEnabled = newVal;
    notifyListeners();
  }

  String _storeName = 'Default Store';
  String get storeName => _storeName;
  set storeNameQuiet(String newVal) => _storeName = newVal;
  set storeName(String newVal) {
    _storeName = newVal;
    notifyListeners();
  }

  Directory? parentDirectory; // Should only be set once
  SharedPreferences? persistent; // Should only be set once

  //! MISC !//

  final StreamController<Null> _resetController = StreamController.broadcast();
  StreamController<Null> get resetController => _resetController;
  void resetMap() {
    _resetController.add(null);
  }

  String? _storeModalCompletedString;
  String? get storeModalCompletedString => _storeModalCompletedString;
  set storeModalCompletedString(String? newString) {
    _storeModalCompletedString = newString;
    notifyListeners();

    if (newString != null) {
      Future.delayed(const Duration(seconds: 3), () {
        _storeModalCompletedString = null;
        notifyListeners();
      });
    }
  }
}

My map_page

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:localiza_favoritos/componentes/Calculo_de_rota.dart';
import 'package:localiza_favoritos/componentes/cacche_disco.dart';
import 'package:localiza_favoritos/database/DAO/categoria_dao.dart';
import 'package:localiza_favoritos/models/pesquisa_categoria.dart';
import 'package:sliding_up_panel/sliding_up_panel.dart';

import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:latlong2/latlong.dart';
import 'package:routing_client_dart/routing_client_dart.dart';
import 'package:flutter/services.dart';
import 'package:location/location.dart';

import 'package:localiza_favoritos/componentes/search.dart';
import 'package:localiza_favoritos/componentes/nethort_help.dart';
import 'package:localiza_favoritos/componentes/rota.dart';
import 'package:localiza_favoritos/database/DAO/favoritos_dao.dart';
import 'package:localiza_favoritos/models/pesquisa_cliente.dart';
import 'package:localiza_favoritos/screens/cadastro/formulario_favoritos.dart';

class mapa extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MapaState();
  }
}

class MapaState extends State<mapa> {
  /********************variveis********************/
  double long = 106.816666;
  double lat = -6.200000;
  double zoom = 15.0;
  double rotation = 0.0;
  late double zoomAtual = 5.0;
  LatLng currentCenter = LatLng(51.5, -0.09);
  LatLng point = LatLng(-6.200000, 106.816666);
  final FitBoundsOptions options =
      const FitBoundsOptions(padding: EdgeInsets.all(12.0));
  late String pesquisa = '';
  List<Marker> markers = [];
  List<Marker> markerDb = [];
  List<Marker> markersTracker = [];
  List<redistro_favoritos> banco = [];
  List<registro_categoria> bancoCategoria = [];
  String? _error;
  /**************************************************/

  /*****************Notifier*************************/
  ValueNotifier<bool> rastreio = ValueNotifier(false);
  /**************************************************/

  /*****************Controllers**********************/
  MapController mapController = MapController();
  late MapState map;
  late bool _serviceEnabled;
  late Location location = Location();
  LocationData? _locationData;
  StreamSubscription<LocationData>? _locationSubscription;

  /**************************************************/

  @override
  void initState() {
    super.initState();
    getCurrentLocation();

    addMarkerDb();
  }

  late int quantidade = 0;

  getCurrentLocation() async {
    Location getLocation = new Location();

    bool _serviceEnabled;
    PermissionStatus _permissionGranted;
    LocationData? _locationData;

    _serviceEnabled = await getLocation.serviceEnabled();
    if (!_serviceEnabled) {
      _serviceEnabled = await getLocation.requestService();
      if (!_serviceEnabled) {
        return;
      }
    }

    _permissionGranted = await getLocation.hasPermission();
    if (_permissionGranted == PermissionStatus.denied) {
      _permissionGranted = await getLocation.requestPermission();
      if (_permissionGranted != PermissionStatus.granted) {
        return;
      }
    }

    _locationData = await getLocation.getLocation();
    setState(() {
      print(LatLng(parseToDouble(_locationData?.latitude),
          parseToDouble(_locationData?.longitude)));

      currentCenter = LatLng(parseToDouble(_locationData?.latitude),
          parseToDouble(_locationData?.longitude));
      mapController.move(
          LatLng(parseToDouble(_locationData?.latitude),
              parseToDouble(_locationData?.longitude)),
          13.0);
    });
  }

  @override
  Widget build(BuildContext context) {
    final TextEditingController controladorCampoPesquisa =
        TextEditingController();
    var _needLoadingError = true;

    return Scaffold(
      appBar: AppBar(
        title: Text('Mapa'),
      ),
      body: Center(
        child: Stack(children: [
          FlutterMap(
              mapController: mapController,
              options: MapOptions(
                maxZoom: 18,
                minZoom: 4,
                center: currentCenter,
                zoom: zoomAtual,
               onTap: (latlng) {
                  removeMarker();
                  setState(() {
                    addMarker(latlng);
                  });
                },
                plugins: [],
                slideOnBoundaries: true,
                screenSize: MediaQuery.of(context).size,
                
              ),
              layers: [
                TileLayerOptions(
                    //tileProvider: MyTileProvider(onError: _onTileError),
                        tileProvider: StorageCachingTileProvider(cachedValidDuration: Duration(days: 15), cacheName: 'Mapa_cache'),
                    urlTemplate:
                        "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
                    subdomains: ['a', 'b', 'c'],
                    //tileProvider: NonCachingNetworkTileProvider(),
                    //tileProvider: const CachedTileProvider(),
                    updateInterval: 1,
                    errorTileCallback: (Tile tile, error) {
                      if (_needLoadingError) {
                        WidgetsBinding.instance!.addPostFrameCallback((_) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                            duration: Duration(seconds: 1),
                            content: Text(
                              error.toString(),
                              style: TextStyle(color: Colors.black),
                            ),
                            backgroundColor: Colors.deepOrange,
                          ));
                        });

                        _needLoadingError = false;
                      }
                      throw Exception('Unknown error, description: $error');
                    }),
                MarkerLayerOptions(markers: [
                  for (int i = 0; i < markers.length; i++) markers[i]
                ]),
                MarkerLayerOptions(markers: [
                  for (int i = 0; i < markerDb.length; i++) markerDb[i]
                ]),
                MarkerLayerOptions(markers: [
                  for (int i = 0; i < markersTracker.length; i++)
                    markersTracker[i],
                ]),
              ]),
[...]

Fatal error

 - package:feature_discovery
 - package:provider
 - package:shared_preferences
 - package:shared_preferences_platform_interface

For solutions, see [https://dart.dev/go/unsound-null-safety]()
/[C:/Users/PESSOAL/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/flutter_map_tile_caching-3.0.3/lib/src/main.dart:182:45](): Error: Too many positional arguments: 2 allowed, but 3 found.
Try removing the extra positional arguments.
                  AndroidNotificationDetails(
                                            ^
/[C:/Users/PESSOAL/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/flutter_local_notifications-9.4.0/lib/src/platform_specifics/android/notification_details.dart:12:9](): Context: Found this candidate, but the arguments don't match.
  const AndroidNotificationDetails(
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
2

FAILURE: Build failed with an exception.

* Where:
Script '[C:\flutter\flutter\packages\flutter_tools\gradle\flutter.gradle]()' line: 1102

* What went wrong:
Execution failed for task ':app:compileFlutterBuildDebug'.
> Process 'command '[C:\flutter\flutter\bin\flutter.bat]()'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at [https://help.gradle.org]()

BUILD FAILED in 2m 16s
Exception: Gradle task assembleDebug failed with exit code 1
Exited (sigterm)

@JaffaKetchup
Copy link
Owner

  1. First things first, I notice that you are using v3.0.3. Please prefer v4 if possible, as I can provide much more support for it than a deprecated version.
  2. Second, you shouldn't need any of the extra packages like I have in the example app in your app: they are all there to enhance the functionality of the example app. For example, you shouldn't need any of the 'provider' code. They are causing your build to crash as the mixture of null-safety and non-null-safety does not play well with Flutter.
  3. The error about AndroidNotificationDetails seems similar to [BUG] 'flutter_local_notifications' Integration Error #41, which I thought I fixed, but it appears maybe not. Besides, I can't release bug fixes for an older version. Please prefer v4.

I can't see any major issues in your map page though, so it should work after you follow the above steps.

@AlysonTrizotto
Copy link
Author

Hello, so... I followed your tips there, and really, those bugs were gone... However, the Storege is asking me for a directory... How can I pass a directory to it without me creating one like I was trying to do in the previous code?

`TileLayerOptions(
// tileProvider: MyTileProvider(onError: _onTileError),
//tileProvider: NonCachingNetworkTileProvider(),
//tileProvider: const CachedTileProvider(),

                tileProvider: StorageCachingTileProvider(cachedValidDuration: Duration(days: 15), storeName: 'Nome', parentDirectory:  ), 

                urlTemplate:
                    "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
                subdomains: ['a', 'b', 'c'],
               
                updateInterval: 1,
                errorTileCallback: (Tile tile, error) {
                  if (_needLoadingError) {
                    WidgetsBinding.instance!.addPostFrameCallback((_) {
                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                        duration: Duration(seconds: 1),
                        content: Text(
                          error.toString(),
                          style: TextStyle(color: Colors.black),
                        ),
                        backgroundColor: Colors.deepOrange,
                      ));
                    });

                    _needLoadingError = false;
                  }
                  throw Exception('Unknown error, description: $error');
                }),`

@AlysonTrizotto
Copy link
Author

I tried to create a directory to be able to pass a cache address to it and it returned this error

**What did I do

pubspec.yaml**

`name: localiza_favoritos
description: Localiza favoritos

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
version: 1.0.0+1

environment:
sdk: ">=2.15.1 <3.0.0"

dependencies:
flutter:
sdk: flutter
sqflite:
path:
flutter_osm_plugin:
permission_handler:
latlong: ^0.6.1
flutter_map: any
flutter_map_marker_cluster: any
flutter_slidable: ^1.0.0
osm_nominatim: ^2.0.1
routing_client_dart: ^0.3.0
flutter_bloc: ^6.0.0
cupertino_icons: ^1.0.2
pull_to_refresh: ^2.0.0
sliding_up_panel: ^2.0.0+1
location: ^4.3.0
geocoder2: ^1.1.0
flutter_polyline_points: ^1.0.0
geolocator: ^8.2.0
geocoding: ^2.0.4
cached_network_image: ^3.2.0
flutter_map_tile_caching: ^4.0.1

dev_dependencies:
flutter_test:
sdk: flutter

flutter_lints: ^1.0.0

flutter:
uses-material-design: true

assets:
- assets/map_cache/

mapa.dart

TileLayerOptions( // tileProvider: MyTileProvider(onError: _onTileError), tileProvider: StorageCachingTileProvider(cachedValidDuration: Duration(days: 15), storeName: 'Nome', parentDirectory: Directory('assets/map_cache/')), urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c'], //tileProvider: NonCachingNetworkTileProvider(), //tileProvider: const CachedTileProvider(), updateInterval: 1, errorTileCallback: (Tile tile, error) { if (_needLoadingError) { WidgetsBinding.instance!.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( duration: Duration(seconds: 1), content: Text( error.toString(), style: TextStyle(color: Colors.black), ), backgroundColor: Colors.deepOrange, )); }); _needLoadingError = false; } throw Exception('Unknown error, description: $error'); }),

error

`Launching lib\main.dart on LM K200 in debug mode...
lib\main.dart:1
Warning: Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01
Warning: Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01
Error: unable to find directory entry in pubspec.yaml: E:\Projeto Freelance Mobile\LocalizaCliente\LocalizaCliente\assets\map_cache
Formato de par�metros incorreto -

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:checkDebugAarMetadata'.

Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
Could not find any matches for com.transistorsoft:tsbackgroundfetch:+ as no versions of com.transistorsoft:tsbackgroundfetch are available.
Searched in the following locations:
- https://dl.google.com/dl/android/maven2/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml
- https://repo.maven.apache.org/maven2/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml
- https://storage.googleapis.com/download.flutter.io/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml
- file:/E:/Projeto Freelance Mobile/LocalizaCliente/LocalizaCliente/android/app/libs/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml
- https://jcenter.bintray.com/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml
- https://jitpack.io/com/transistorsoft/tsbackgroundfetch/maven-metadata.xml
Required by:
project :app > project :background_fetch

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

  • Get more help at https://help.gradle.org

BUILD FAILED in 2m 53s
Exception: Gradle t`

@AlysonTrizotto
Copy link
Author

This is solved:
Error: unable to find directory entry in pubspec.yaml: E:\Projeto Freelance Mobile\LocalizaCliente\LocalizaCliente\assets\map_cache

@AlysonTrizotto
Copy link
Author

I solved this error by doing this

https://github.com/transistorsoft/flutter_background_fetch/blob/master/help/INSTALL-ANDROID.md

after following the above link, do:
flutter clean
flutter run
it will probably give this error:
'
App:mergedebugresources'

delete the 'multi-v2' folder that is in the directory:
...\build\app\intermediates\merged_res_blame_folder\debug\out

run flutter clean
flutter run
and ready

@AlysonTrizotto
Copy link
Author

Now I'm back to the previous point... Create a directory for the cache that the function is asking for... But how?

@JaffaKetchup
Copy link
Owner

Use await MapCachingManager.normalCache.
And for that other setup you mentioned, I have linked that from the README installation instructions. If you have not fully read the README, please do, it will help you to get started.

@AlysonTrizotto
Copy link
Author

Then,
I tried it like this, I even think I'm doing it wrong, because it returns this error whenever I try to use it like this

FileSystemException (FileSystemException: Creation failed, path = '/Nome' (OS Error: Read-only file system, errno = 30))

I'm doing like this


late Directory valorCache;
  MapaState(this.valorCache);

  @override
  void initState() {
    super.initState();
    getCurrentLocation();

    pegaDiretorio();

    addMarkerDb();
  }

  void pegaDiretorio() async {
    valorCache = await MapCachingManager.normalCache;
  }


[..]

tileProvider: StorageCachingTileProvider(
                      cachedValidDuration: Duration(days: 15),
                      storeName: 'Nome',
                      parentDirectory: valorCache,
                    ),

@JaffaKetchup
Copy link
Owner

Are you sure that the valorCache variable is initialised and defined before it is used in the tile provider? I can't see a FutureBuilder in the code block, but you might have one that you haven't included in the code block?

@AlysonTrizotto
Copy link
Author

So, for me to use a FutureBuilder I would have to change the whole structure to be able to return the directory, right?

I'm trying to find a way to use the cache without having to change the entire structure... Because the app is already working, and for me to change everything it would take two or three weeks

@AlysonTrizotto
Copy link
Author

And I have 3 days to deliver this project, I can't delay any longer

@AlysonTrizotto
Copy link
Author

Unless, what you're trying to tell me is that I can wrap FlutterMap inside a FutureBuilder , expecting as Future a Directory object or maybe even the MapCachingManager.normalCache object as a return...

@AlysonTrizotto
Copy link
Author

Then, the code is this...

I still believe I need more than one FutureBuilder in there... I'm having the following error now:
https://github.com/fleaflet/flutter_map/issues/1195 but with this code here

`import 'dart:io';
import 'package:path_provider/path_provider.dart';

pegaDiretorio() async {
Directory appDocDirectory = await getApplicationDocumentsDirectory();

new Directory(appDocDirectory.path + '/' + 'dir')
    .create(recursive: true)
    .then((Directory directory) {
  print('Path of New Dir: ' + directory.path);
});
return appDocDirectory.path.toString();

}`

FutureBuilder( future: pegaDiretorio(), builder: (context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data != null) { return FlutterMap( mapController: mapController, options: MapOptions( maxZoom: 18, minZoom: 4, center: currentCenter, zoom: zoomAtual, onTap: (k, latlng) { removeMarker(); setState(() { addMarker(latlng); }); }, plugins: [], slideOnBoundaries: true, screenSize: MediaQuery.of(context).size, ), layers: [ TileLayerOptions( tileProvider: StorageCachingTileProvider( cachedValidDuration: Duration(days: 15), storeName: 'Nome', parentDirectory: Directory(snapshot.data), ), urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: ['a', 'b', 'c'], updateInterval: 1, errorTileCallback: (Tile tile, error) { if (_needLoadingError) { WidgetsBinding.instance! .addPostFrameCallback((_) { ScaffoldMessenger.of(context) .showSnackBar(SnackBar( duration: Duration(seconds: 1), content: Text( error.toString(), style: TextStyle(color: Colors.black), ), backgroundColor: Colors.deepOrange, )); }); _needLoadingError = false; } throw Exception( 'Unknown error, description: $error'); }), MarkerLayerOptions(markers: [ for (int i = 0; i < markers.length; i++) markers[i] ]), MarkerLayerOptions(markers: [ for (int i = 0; i < markerDb.length; i++) markerDb[i] ]), MarkerLayerOptions(markers: [ for (int i = 0; i < markersTracker.length; i++) markersTracker[i], ]), ]); } else { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ CircularProgressIndicator(), Text('Carregando favoritos'), ], ), ); } }),

@JaffaKetchup
Copy link
Owner

The easiest way to do it in your case would probably be to await the normal cache in the main() function, before calling runApp(), where it won't interfere with anything else.
Then put the returned Directory into an object that you can reference anywhere.

@JaffaKetchup
Copy link
Owner

You shouldn't need to use path_provider.

@AlysonTrizotto
Copy link
Author

I'm trying to implement caching to work with Flutter and Flutter_Map. I found an api to work with that makes the process a little easier. However, when I disable the internet, (I put it in airplane mode) it gives me an error. I'm not finding the source of this error. Can someone help me?

Code block I'm using to work with cache:

import 'dart:io';
import 'package:path_provider/path_provider.dart';

pegaDiretorio() async {
Directory appDocDirectory = await getApplicationDocumentsDirectory();

new Directory(appDocDirectory.path + '/' + 'dir')
    .create(recursive: true)
    .then((Directory directory) {
  print('Path of New Dir: ' + directory.path);
});
return appDocDirectory.path.toString();
}

Code block I'm using to map and pull cache:

Widget build(BuildContext context) {
final TextEditingController controladorCampoPesquisa =
    TextEditingController();
var _needLoadingError = true;
return Scaffold(
  appBar: AppBar(
    title: Text('Mapa'),
  ),
  body: Center(
    child: Stack(children: [
      FutureBuilder(
          future: MapCachingManager.normalCache,
          builder: (context, AsyncSnapshot snapshot) {
            if (snapshot.hasData && snapshot.data != null) {

              return FlutterMap(
                  mapController: mapController,
                  options: MapOptions(
                    maxZoom: 18,
                    minZoom: 4,
                    center: currentCenter,
                    zoom: zoomAtual,
                    onTap: (k, latlng) {
                      removeMarker();
                      setState(() {
                        addMarker(latlng);
                      });
                    },
                    plugins: [],
                    slideOnBoundaries: true,
                    screenSize: MediaQuery.of(context).size,
                  ),
                  layers: [
                    TileLayerOptions(
                        tileProvider: StorageCachingTileProvider(
                          cachedValidDuration: Duration(days: 15),
                          storeName: 'Nome',
                          parentDirectory: snapshot.data,
                          //parentDirectory: Directory(snapshot.data),
                        ),
                        urlTemplate:
                            "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
                        subdomains: ['a', 'b', 'c'],
                        updateInterval: 1,
                        errorTileCallback: (Tile tile, error) {
                          if (_needLoadingError) {
                            WidgetsBinding.instance!
                                .addPostFrameCallback((_) {
                              ScaffoldMessenger.of(context)
                                  .showSnackBar(SnackBar(
                                duration: Duration(seconds: 1),
                                content: Text(
                                  error.toString(),
                                  style: TextStyle(color: Colors.black),
                                ),
                                backgroundColor: Colors.deepOrange,
                              ));
                            });
                            _needLoadingError = false;
                          }
                          throw Exception(
                              'Unknown error, description: $error');
                        }),
                    MarkerLayerOptions(markers: [
                      for (int i = 0; i < markers.length; i++) markers[i]
                    ]),
                    MarkerLayerOptions(markers: [
                      for (int i = 0; i < markerDb.length; i++) markerDb[i]
                    ]),
                    MarkerLayerOptions(markers: [
                      for (int i = 0; i < markersTracker.length; i++)
                        markersTracker[i],
                    ]),
                  ]);
            } else {
              return Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(),
                    Text('Carregando Mapa'),
                  ],
                ),
              );
            }
          }),

This is the error:

SocketException (SocketException: Failed host lookup: 'a.tile.openstreetmap.org' (OS Error: No address associated with hostname, errno = 7))

This is the Output :

Latitude: -25.5120266, Longitude: -49.1645672
I/aliza_favorito(22009): NativeAlloc concurrent copying GC freed                     14438(1593KB) AllocSpace objects, 0(0B) LOS objects, 88% free, 1612KB/13MB,     paused 806us total 151.005ms
W/Gralloc3(22009): mapper 3.x is not supported
Restarted application in 6.524ms.
I/flutter (22009): LatLng(latitude:-25.512038, longitude:-49.164567)
I/flutter (22009): Latitude: -25.5120377, Longitude: -49.1645672
I/flutter (22009): Latitude: -25.512037, Longitude: -49.1645668    
I/flutter (22009): Latitude: -25.5120368, Longitude: -49.16456676
I/flutter (22009): Latitude: -25.5120367, Longitude: -49.1645667
I/flutter (22009): FMTCBrowsingError: Failed to load the tile from the     cache or the network because it was missing from the cache and a connection to     the server could not be established.

════════ Exception caught by image resource service         ════════════════════════════
The following _Exception was thrown when reporting an error to an image     listener:
Exception: Unknown error, description: FMTCBrowsingError: Failed to load     the tile from the cache or the network because it was missing from the cache     and a connection to the server could not be established.

When the exception was thrown, this was the stack
#0      MapaState.build.<anonymous closure>.<anonymous closure>
package:localiza_favoritos/componentes/mapa.dart:173
#1      _TileLayerState._tileReady
package:flutter_map/…/layer/tile_layer.dart:1122
#2      Tile._tileOnError
package:flutter_map/…/layer/tile_layer.dart:1324
#3      ImageStreamCompleter.reportError
package:flutter/…/painting/image_stream.dart:702
#4      new MultiFrameImageStreamCompleter.<anonymous closure>
package:flutter/…/painting/image_stream.dart:847
#12     FMTCImageProvider._loadAsync         (package:flutter_map_tile_caching/src/internal/image_provider.dart)
package:flutter_map_tile_caching/…/internal/image_provider.dart:1
<asynchronous suspension>
(elided 7 frames from dart:async and dart:async-patch)
    ════════════════════════════════════════════════════════════════════════════════

════════ Exception caught by image resource service     ════════════════════════════
FMTCBrowsingError: Failed to load the tile from the cache or the network     because it was missing from the cache and a connection to the server could not     be established.
════════════════════════════════════════════════════════════════════════════════
I/flutter (22009): FMTCBrowsingError: Failed to load the tile from the     cache or the network because it was missing from the cache and a connection to     the server could not be established.

I'm assuming the error being returned is not about where I'm loading the cache... I tried loading the cache in main and it didn't work... I couldn't...

It is loading the cache as is, this is shown in the output below

Restarted application in 9.358ms.
I/flutter (22009): Directory: '/data/user/0/alysontrizotto.github.io.localiza_favoritos/app_flutter/mapCache'
I/flutter (22009): Latitude: -25.5120373, Longitude: -49.1645743
I/flutter (22009): LatLng(latitude:-25.512037, longitude:-49.164574)
I/flutter (22009): Latitude: -25.5120373, Longitude: -49.1645743
I/flutter (22009): Directory: '/data/user/0/alysontrizotto.github.io.localiza_favoritos/app_flutter/mapCache'
I/flutter (22009): Latitude: -25.5120373, Longitude: -49.1645743
I/flutter (22009): Directory: '/data/user/0/alysontrizotto.github.io.localiza_favoritos/app_flutter/mapCache'
3
I/flutter (22009): Latitude: -25.5120373, Longitude: -49.1645743
2
I/flutter (22009): Latitude: -25.5120363, Longitude: -49.1645751
I/flutter (22009): Latitude: -25.5120361, Longitude: -49.1645753
2
I/flutter (22009): Directory: '/data/user/0/alysontrizotto.github.io.localiza_favoritos/app_flutter/mapCache'

@JaffaKetchup
Copy link
Owner

Ok, so it appears it is correctly setup now, as I can see from the paths.
Your new errors:

FMTCBrowsingError: Failed to load the tile from the cache or the network because it was missing from the cache and a connection to the server could not be established.

SocketException (SocketException: Failed host lookup: 'a.tile.openstreetmap.org' (OS Error: No address associated with hostname, errno = 7))

are created by this line in this file (link to github.dev), and indicate that:

  • The tile needs creating, according to this algorithm: !(await file.exists())
  • The tile server could not be connected to, which is if a try/catch block throws on a GET request to the server

Therefore, it would appear that the tile is not already in the cache, and there is no Internet, and therefore this library cannot do anything else but throw.
There is a possibility of a bug, but can you just ensure that tile that you are trying to load has been loaded and cached before, by browsing/panning over it with an active Internet connection.

Many thanks.

@AlysonTrizotto
Copy link
Author

So what you're telling me is it's a bug in your library?

@AlysonTrizotto
Copy link
Author

I tried doing checks to make sure the tile already existed in cache, but it didn't work... It seems it doesn't recognize it as a provider and won't let me check it...

@JaffaKetchup
Copy link
Owner

JaffaKetchup commented Mar 25, 2022

I tried doing checks to make sure the tile already existed in cache, but it didn't work... It seems it doesn't recognize it as a provider and won't let me check it...

What do you mean by 'it doesn't recognize it as a provider and won't let me check it...'? To check map statistics, you should use MapCachingManager().storeLength with similar options to the StorageCachingTileProvider(): this will return the number of tiles in the store.

@JaffaKetchup
Copy link
Owner

So what you're telling me is it's a bug in your library?

There is very little chance of a bug on my side:

  • no other reports
  • working the last time I changed the code that deals with that section
  • no obvious syntax or logic errors

@JaffaKetchup
Copy link
Owner

Have you resolved your issue? Happy to help more if required!

@AlysonTrizotto
Copy link
Author

Ah Jaffa Ketchup, I'm very frustrated. I couldn't... I've been trying for days and I haven't made any progress on this. It always returns the same error

@JaffaKetchup
Copy link
Owner

Sorry I haven't been more help; is there anything that I can do better, or give you more help?
So far I think it should be working, but there might just be some detail that isn't quite right in your implementation. If you want to give more details I'll happily help, but I appreciate you have time and resource restraints :).

@AlysonTrizotto
Copy link
Author

AlysonTrizotto commented Mar 28, 2022

My page where I call the map and cache
https://github.com/AlysonTrizotto/LocalizaCliente/blob/main/lib/componentes/mapa.dart

My pubspec.yaml
https://github.com/AlysonTrizotto/LocalizaCliente/blob/main/pubspec.yaml

Meu projeto inteiro
https://github.com/AlysonTrizotto/LocalizaCliente

Error when activating airplane mode (multiple times):

════════ Exception caught by image resource service ═══════════════════════════
Exception: Unknown error, description: FMTCBrowsingError: Failed to load the tile from the cache or the network because it was missing from the cache and a connection to the server could not be established.
═══════════════════════════════════════════════════════════════════════════════

@AlysonTrizotto
Copy link
Author

Thank you so much for making yourself available to help me. I really don't know what else to do...

@AlysonTrizotto
Copy link
Author

As in some posts ago you commented about loading the cache in main.dart, I tried to do that, but it didn't work...

@JaffaKetchup
Copy link
Owner

Hi @AlysonTrizotto,
Your application seems to be working for me - besides some state errors and performance bottlenecks which are unrelated. I can navigate and zoom the map with Internet connected, then disconnect the Internet, perform a hot restart, and the tiles are loaded from the cache correctly.
Indeed, if you start the app with no Internet and haven't cached any tiles with Internet before, then the error above is shown - this is expected behaviour.

@AlysonTrizotto
Copy link
Author

Hi @JaffaKetchup, well if this is the expected behavior my problem is in handling this output!! Thank you so much for sharing your knowledge and taking the time to help me!!!

@JaffaKetchup
Copy link
Owner

No problem, hope you can find a way to handle it :)

@JaffaKetchup JaffaKetchup added bug invalid This cannot be verified labels Mar 29, 2022
@JaffaKetchup JaffaKetchup changed the title Implement caching Implement Caching Mar 29, 2022
@AlysonTrizotto
Copy link
Author

Yes, I was looking for a solution to a non-existent problem... Now the situation has cleared up.

gitbook-com bot pushed a commit that referenced this issue Oct 19, 2022
@sikandernoori
Copy link
Contributor

I had the same error, I made some changes in plugin and it fixed the problem.

Especially not closing the httpclient on dispose.

here is my implementation.

https://github.com/sikandernoori/flutter_map_tile_caching-6.1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug invalid This cannot be verified question/help
Projects
None yet
Development

No branches or pull requests

3 participants