Flutter二维地图渲染插件tilemap_2d的使用

Flutter二维地图渲染插件tilemap_2d的使用

简介

tilemap_2d 是一个用于在 Flutter 中渲染二维地图的插件。它允许开发者通过图块(tiles)来加载和显示地图,并支持添加标记层(Marker Layer)和图像层(Image Layer)。

使用示例

以下是一个完整的示例,展示了如何使用 tilemap_2d 插件来创建一个带有标记和图像层的地图应用。

import 'dart:convert';
import 'dart:ui';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter/services.dart';
import 'package:screenshot/screenshot.dart';
import 'package:tilemap_2d/tilemap_2d.dart';

void main() {
  runApp(const App());
}

class ScrollBehavior extends MaterialScrollBehavior {
  const ScrollBehavior();

  [@override](/user/override)
  Set<PointerDeviceKind> get dragDevices => PointerDeviceKind.values.toSet();
}

class App extends StatelessWidget {
  const App({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(
      scrollBehavior: ScrollBehavior(),
      home: Home(),
    );
  }
}

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

  [@override](/user/override)
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final icons = <String, String>{};
  final markers = [];
  late final TilemapController controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      rootBundle.loadString('assets/icons.json').then((json) async {
        for (final icon in jsonDecode(json)) {
          icons[icon['name']] = icon['url'];
        }
        json = await rootBundle.loadString('assets/markers.json');
        markers.addAll(jsonDecode(json));

        addMarkerLayer(0, await getIcon(0), 24, zIndex: 3);
        await addDefaultMarkerLayer(1);
        await addDefaultMarkerLayer(2);
        await addDefaultMarkerLayer(3);

        controller.animateTo(
          coordinate: const Offset(-6299 + 1427, -1190),
          zoom: -2.8,
          duration: const Duration(milliseconds: 1200),
        );
      });
    });
  }

  Future<Image> getIcon(int index) async {
    final iconUrl = icons[markers[index][0]['itemList'][0]['iconTag']];
    return resolveImage(CachedNetworkImageProvider(iconUrl!));
  }

  Future<void> addDefaultMarkerLayer(int index) async {
    final icon = await getIcon(index);
    final screenshot = ScreenshotController();
    final imageData = await screenshot.captureFromWidget(
      Container(
        decoration: const BoxDecoration(
          color: Colors.white70,
          borderRadius: BorderRadius.all(Radius.circular(30)),
        ),
        child: RawImage(image: icon),
      ),
      delay: Duration.zero,
    );
    final image = await decodeImageFromList(imageData);
    addMarkerLayer(index, image, 30);
  }

  void addMarkerLayer(int index, Image image, double size, {int zIndex = 2}) {
    final markers = this.markers[index];
    final items = markers.map((i) {
      final position = i['position'].split(',');
      return MarkerItem(
          Offset(double.parse(position[0]), double.parse(position[1])), i);
    }).cast<MarkerItem>();
    controller.addLayer(MarkerLayer(
      image: image,
      items: items.toList(),
      scale: size / image.width,
      zIndex: zIndex,
    ));
  }

  void onMapCreated(TilemapController controller) {
    this.controller = controller;
    controller.addLayer(TileLayer(
      getImageProvider: (x, y, z) => CachedNetworkImageProvider(
        'https://assets.yuanshen.site/tiles_twt36/$z/${x}_$y.png',
      ),
      minZoom: 10,
      maxZoom: 13,
      offset: const Offset(-6144, 0),
    ));

    initNonGroundMaps();
  }

  void initNonGroundMaps() async {
    const images = [
      'https://assets.yuanshen.site/overlay/YL/1.png',
      'https://assets.yuanshen.site/overlay/YL/2.png',
      'https://assets.yuanshen.site/overlay/YL/3.png',
      'https://assets.yuanshen.site/overlay/YL/4.png',
      'https://assets.yuanshen.site/overlay/YL/5.png',
      'https://assets.yuanshen.site/overlay/YL/6.png',
      'https://assets.yuanshen.site/overlay/YL/7.png',
      'https://assets.yuanshen.site/overlay/YL/8.png',
      'https://assets.yuanshen.site/overlay/YL/9.png',
      'https://assets.yuanshen.site/overlay/YL/10.png',
      'https://assets.yuanshen.site/overlay/YL/11.png',
      'https://assets.yuanshen.site/overlay/YL/12.png',
    ];
    const rects = [
      [-6299 + 1427, -2190 + 1353, 404, 504],
      [-6299 + 1299, -2190 + 2040, 648, 570],
      [-6299 + 2199, -2190 + 1671, 398, 306],
      [-6299 + 2177, -2190 + 2168, 570, 515],
      [-6299 + 2734, -2190 + 937, 433, 660],
      [-6299 + 3323, -2190 + 507, 443, 377],
      [-6299 + 3426, -2190 + 924, 278, 360],
      [-6299 + 3796, -2190 + 1024, 344, 393],
      [-6299 + 3563, -2190 + 1604, 443, 799],
      [-6299 + 3787, -2190 + 2710, 378, 560],
      [-6299 + 3086, -2190 + 3297, 521, 433],
      [-6299 + 1887, -2190 + 3637, 740, 346],
    ];
    for (var i = 0; i < images.length; i += 1) {
      final rect = rects[i];
      await controller.addLayer(
        ImageLayer(
          zIndex: 1,
          image: CachedNetworkImageProvider(images[i]),
          rect: Rect.fromLTWH(
            rect[0].toDouble(),
            rect[1].toDouble(),
            rect[2].toDouble(),
            rect[3].toDouble(),
          ),
        ),
      );
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Tilemap(
        size: const Size(18432, 16384),
        origin: const Offset(3568 + 6144, 6286),
        maxZoom: 0.5,
        onCreated: onMapCreated,
        onTap: (details) {
          final data = details?.markerItem?.data;
          if (data != null) {
            showDialog(
              context: context,
              builder: (context) {
                return AlertDialog(
                  title: Text(data['markerTitle']),
                  content: SingleChildScrollView(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        if (data['content']?.isNotEmpty ?? false)
                          Padding(
                            padding: const EdgeInsets.only(bottom: 16),
                            child: Text(data['content'].replaceAll('\\n', '\n')),
                          ),
                        if (data['picture']?.isNotEmpty ?? false)
                          CachedNetworkImage(
                            imageUrl: data['picture'],
                            fit: BoxFit.fitWidth,
                          ),
                      ],
                    ),
                  ),
                );
              },
            );
          }
        },
      ),
    );
  }
}

更多关于Flutter二维地图渲染插件tilemap_2d的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


tilemap_2d 是一个用于在 Flutter 中渲染二维地图的插件。它允许你加载和显示基于瓦片(tile)的地图,通常用于游戏或地理信息系统(GIS)应用中。以下是如何使用 tilemap_2d 插件的基本步骤。

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 tilemap_2d 依赖:

dependencies:
  flutter:
    sdk: flutter
  tilemap_2d: ^latest_version

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

2. 导入包

在你的 Dart 文件中导入 tilemap_2d 包:

import 'package:tilemap_2d/tilemap_2d.dart';

3. 创建地图

tilemap_2d 插件允许你加载和显示基于瓦片的地图。你可以使用 TileMap 类来创建地图。

class MyMapWidget extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TileMap 2D Example'),
      ),
      body: Center(
        child: TileMap(
          tileSize: 32.0, // 每个瓦片的大小
          mapSize: Size(10, 10), // 地图的大小(以瓦片为单位)
          tiles: [
            // 这里定义地图的瓦片
            Tile(0, 0, TileType.grass),
            Tile(1, 0, TileType.grass),
            Tile(2, 0, TileType.water),
            // 更多瓦片...
          ],
        ),
      ),
    );
  }
}

4. 定义瓦片类型

你可以定义不同的瓦片类型,比如草地、水等。通常你会有一个 TileType 枚举来表示不同的瓦片类型。

enum TileType {
  grass,
  water,
  sand,
  // 更多类型...
}

5. 加载瓦片图像

你需要为每种瓦片类型加载对应的图像。你可以使用 Image.assetImage.network 来加载图像。

class Tile {
  final int x;
  final int y;
  final TileType type;

  Tile(this.x, this.y, this.type);

  Widget getTileImage() {
    switch (type) {
      case TileType.grass:
        return Image.asset('assets/grass.png');
      case TileType.water:
        return Image.asset('assets/water.png');
      case TileType.sand:
        return Image.asset('assets/sand.png');
      default:
        return Container();
    }
  }
}

6. 渲染地图

TileMap 中,你可以通过 tiles 属性来传递瓦片列表,并在 TileMap 内部使用 getTileImage 方法来渲染每个瓦片。

TileMap(
  tileSize: 32.0,
  mapSize: Size(10, 10),
  tiles: [
    Tile(0, 0, TileType.grass),
    Tile(1, 0, TileType.grass),
    Tile(2, 0, TileType.water),
    // 更多瓦片...
  ],
)

7. 处理交互

你还可以处理用户与地图的交互,比如点击某个瓦片时触发事件。

TileMap(
  tileSize: 32.0,
  mapSize: Size(10, 10),
  tiles: [
    Tile(0, 0, TileType.grass),
    Tile(1, 0, TileType.grass),
    Tile(2, 0, TileType.water),
    // 更多瓦片...
  ],
  onTileTap: (x, y) {
    print('Tile tapped at ($x, $y)');
  },
)

8. 完整示例

以下是一个简单的完整示例:

import 'package:flutter/material.dart';
import 'package:tilemap_2d/tilemap_2d.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyMapWidget(),
    );
  }
}

enum TileType {
  grass,
  water,
  sand,
}

class Tile {
  final int x;
  final int y;
  final TileType type;

  Tile(this.x, this.y, this.type);

  Widget getTileImage() {
    switch (type) {
      case TileType.grass:
        return Image.asset('assets/grass.png');
      case TileType.water:
        return Image.asset('assets/water.png');
      case TileType.sand:
        return Image.asset('assets/sand.png');
      default:
        return Container();
    }
  }
}

class MyMapWidget extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TileMap 2D Example'),
      ),
      body: Center(
        child: TileMap(
          tileSize: 32.0,
          mapSize: Size(10, 10),
          tiles: [
            Tile(0, 0, TileType.grass),
            Tile(1, 0, TileType.grass),
            Tile(2, 0, TileType.water),
            // 更多瓦片...
          ],
          onTileTap: (x, y) {
            print('Tile tapped at ($x, $y)');
          },
        ),
      ),
    );
  }
}
回到顶部