Flutter地图缓存插件flutter_map_cache的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter地图缓存插件flutter_map_cache的使用

简介

flutter_map_cache 是一个轻量级但功能强大的Flutter地图瓦片层缓存插件。它不仅有助于减少服务器负载,还能降低商业图块提供商的成本,并提升用户体验。

动机

  1. 许多图块提供者在其图块使用政策中要求用户缓存图块请求
  2. 通过缓存图块可以减少图块请求的数量,从而降低成本
  3. 缓存地图图块能为用户提供更好的体验,因为用户访问过的区域会立即加载。
  4. 缓存图块可以减少移动数据和带宽的使用

功能

该包使用diodio_cache_interceptor支持多种存储后端:

存储后端 描述
In-Memory - 适用于测试
- flutter_map本身有内存缓存
File System - 易于设置,无需额外的存储后端包
- 可能比使用数据库慢
Drift - SQLite数据库
- 良好的平台支持
Hive - 键值数据库
- 易于集成
ObjectBox - NoSQL, ACID兼容
- 快速库
- 集成更复杂
Isar - NoSQL
- 快速库
- 集成更复杂

入门指南

  1. 将需要使用的包添加到pubspec.yaml文件中:
dependencies:
  flutter_map: ^6.0.0 # 如果你还没有这个包 
  flutter_map_cache: ^1.3.0 # 这个包
  path_provider: ^2.1.2 # 如果存储后端需要路径

  # drift
  dio_cache_interceptor_db_store: ^5.1.0
  sqlite3_flutter_libs: ^0.5.15

  # file system
  dio_cache_interceptor_file_store: ^1.2.2

  # hive
  dio_cache_interceptor_hive_store: ^3.2.1

  # objectbox
  dio_cache_interceptor_objectbox_store: ^1.1.3
  objectbox_flutter_libs: ^1.4.1

  # isar
  isar: ^3.1.0+1
  isar_flutter_libs: ^3.1.0+1
  1. ⚠️ 某些存储后端有自己的安装要求,请查阅相关包的文档。

使用方法

Hive 示例

import 'package:path_provider/path_provider.dart';

Future<String> getPath() async {
  final cacheDirectory = await getTemporaryDirectory();
  return cacheDirectory.path;
}

class MyMap extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: getPath(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          String path = snapshot.data!;
          return FlutterMap(
            options: MapOptions(),
            children: [
              TileLayer(
                urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                tileProvider: CachedTileProvider(
                  maxStale: const Duration(days: 30),
                  store: HiveCacheStore(
                    path,
                    hiveBoxName: 'HiveCacheStore',
                  ),
                ),
              ),
            ],
          );
        }
        if (snapshot.hasError) {
          return Center(child: Text(snapshot.error.toString()));
        }
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}

内存缓存(用于测试)

import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';

class MyMap extends StatelessWidget {
  MyMap({super.key});

  // 创建缓存存储作为字段变量
  final _cacheStore = MemCacheStore();

  @override
  Widget build(BuildContext context) {
    return FlutterMap(
      options: MapOptions(),
      children: [
        TileLayer(
          urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
          tileProvider: CachedTileProvider(
            store: _cacheStore,
          ),
        ),
      ],
    );
  }
}

文件系统缓存

import 'dart:io';

import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
import 'package:dio_cache_interceptor_file_store/dio_cache_interceptor_file_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';
import 'package:path_provider/path_provider.dart';

class MyMap extends StatefulWidget {
  const MyMap({super.key});

  @override
  State<MyMap> createState() => _MyMapState();
}

class _MyMapState extends State<MyMap> {
  final Future<CacheStore> _cacheStoreFuture = _getCacheStore();

  static Future<CacheStore> _getCacheStore() async {
    final dir = await getTemporaryDirectory();
    return FileCacheStore('${dir.path}${Platform.pathSeparator}MapTiles');
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<CacheStore>(
      future: _cacheStoreFuture,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final cacheStore = snapshot.data!;
          return FlutterMap(
            options: MapOptions(),
            children: [
              TileLayer(
                urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                tileProvider: CachedTileProvider(
                  store: cacheStore,
                ),
              ),
            ],
          );
        }
        if (snapshot.hasError) {
          return Center(child: Text(snapshot.error.toString()));
        }
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}

常见问题

Web 支持

图块请求取消支持

是的,此包包含图块取消功能。

如何在缓存前移除URL中的API密钥?

final _uuid = Uuid();

CachedTileProvider(
  keyBuilder: (request) {
    return _uuid.v5(
      Uuid.NAMESPACE_URL,
      request.uri.replace(queryParameters: {}).toString(),
    );
  },
),

是否可以用作离线地图?

此包不支持自动下载图块。只有之前在有网络连接时访问过的图块会在地图上显示。

是否支持sqflite?

由于dio_cache_interceptor已经支持Drift作为SQLite解决方案,因此不太可能很快支持sqflite。如果必须使用sqflite,建议创建自己的图块提供程序。

更多信息

如果你需要更多帮助,可以在GitHub上提交问题或加入flutter_map Discord服务器


更多关于Flutter地图缓存插件flutter_map_cache的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter地图缓存插件flutter_map_cache的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 flutter_map_cache 插件来缓存 Flutter 中地图图层的示例代码。这个插件可以帮助你提高地图的加载速度和用户体验,特别是在离线或网络状况不佳的情况下。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_mapflutter_map_cache 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_map: ^0.x.x  # 请替换为最新版本号
  flutter_map_cache: ^0.x.x  # 请替换为最新版本号

然后运行 flutter pub get 来获取这些依赖。

接下来,在你的 Flutter 应用中,你可以按照以下步骤使用 flutter_map_cache 来缓存地图图层。

import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cache/flutter_map_cache.dart';
import 'package:latlong2/latlong.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Map Cache Example'),
        ),
        body: MapPage(),
      ),
    );
  }
}

class MapPage extends StatefulWidget {
  @override
  _MapPageState createState() => _MapPageState();
}

class _MapPageState extends State<MapPage> {
  final MapController _controller = MapController();
  late FlutterMapCache _cache;

  @override
  void initState() {
    super.initState();
    // 初始化 FlutterMapCache
    _cache = FlutterMapCache(
      baseDirectory: 'tiles_cache', // 缓存存储的目录
      maxTiles: 1000, // 最大缓存瓦片数量
    );
  }

  @override
  Widget build(BuildContext context) {
    return FlutterMap(
      mapController: _controller,
      options: MapOptions(
        center: LatLng(51.5, -0.09),
        zoom: 13,
      ),
      layers: [
        TileLayerOptions(
          urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
          subdomains: ['a', 'b', 'c'],
          tileProvider: CachedNetworkTileProvider(_cache),
        ),
      ],
    );
  }

  @override
  void dispose() {
    // 清理缓存
    _cache.dispose();
    super.dispose();
  }
}

在这个示例中,我们做了以下几件事:

  1. 初始化 FlutterMapCache:在 initState 方法中,我们初始化了 FlutterMapCache,并指定了缓存存储的目录和最大缓存瓦片数量。

  2. 使用 CachedNetworkTileProvider:在 TileLayerOptions 中,我们使用 CachedNetworkTileProvider 来提供瓦片。这个提供者会首先从缓存中查找瓦片,如果缓存中没有,则从网络上下载并缓存瓦片。

  3. 清理缓存:在 dispose 方法中,我们调用 _cache.dispose() 来清理缓存资源,防止内存泄漏。

这样,当你加载地图时,flutter_map_cache 插件会自动处理瓦片的缓存,提高地图加载性能。注意,实际使用中你可能需要根据具体需求调整缓存策略,比如缓存大小、缓存位置等。

回到顶部