Flutter楼层地图展示插件floors_map_widget的使用

Flutter楼层地图展示插件floors_map_widget的使用

Floors Map Widget

这个插件允许你从SVG图像创建一个交互式的楼层地图。

Logo

目录

关于项目

这个插件允许你基于SVG图像创建一个交互式的楼层地图。

特性

  • 基于SVG图像生成交互式地图。
  • 在不同点之间创建并可视化路径。
  • 配置点击块时的交互。

支持的对象类别

  • shop
  • parkingspace
  • atmmachine
  • toilet
  • stairs

卫生间子类型

  • male
  • female
  • mother_and_child

楼梯子类型

  • simple
  • fire_escape
  • escalator
  • elevator

如何使用

与库的工作分为几个阶段。

阶段1 - 添加路线点

使用Figma或其他任何SVG编辑器,创建一个楼层地图或使用现有的地图。现在,你需要准备这张地图以供FloorMapWidget使用。你需要设置路线点和入口点到房间对象。你可以使用现成的Figma扩展程序,也可以手动完成。

Figma扩展程序

放置点并将它们连接成路径。你可以阅读更详细的描述 这里

MapPluginExample

手动实现

你可以使用圆形状或自定义形状使用路径。通过id属性进行点绑定。例如,在这个例子中,我们以两种方式创建了一个点,并将其连接到三个相邻的点。

point-43=44-39-45
│     │  └‒‒└‒‒└‒‒‒‒‒ 唯一ID的相邻邻居
│     └‒‒‒‒‒ 点的唯一ID
└‒‒‒‒‒ 对象的类 - 点
<circle id="point-43=44-39-45" cx="4.5" cy="4.5" r="4.5" fill="black"/>
<path id="point-43=44-39-45" d="M4.2 8.39999C6.52 8.39999 8.39999 6.52 8.39999 4.2C8.39999 1.88 6.52 0 4.2 0C1.88 0 0 1.88 0 4.2C0 6.52 1.88 8.39999 4.2 8.39999Z" fill="black"/>

阶段2 - 为房间对象分配正确的ID

你可以在这里看到支持的对象。 创建ID的规则与点相同。

shop-1=2
│    │ └‒‒‒‒‒ 连接入口点的唯一ID(只能有一个)
│    └‒‒‒‒‒ 对象的唯一ID
└‒‒‒‒‒ 对象的类
stairs-elevator-1=2
│      │        │ └‒‒‒‒‒ 连接入口点的唯一ID(只能有一个)
│      │        └‒‒‒‒‒ 对象的唯一ID
│      └‒‒‒‒‒ 对象的子类型(楼梯和卫生间需要)
└‒‒‒‒‒ 对象的类
完整示例
<path id="shop-3=5" d="M477.648 206.928H428.712V323.772H565.44V315H569.448V235.404H481.044H477.648V231.996V206.928Z" fill="#EEF9FE" />

阶段3 - 将库集成到你的项目中

将库添加到你的项目中,并嵌入包含必要参数的准备好的小部件。

FloorMapWidget(
    // SVG Map字符串
    _svgContent,
    // 楼层小部件
    _listWidgets,
    // 用于构建路线
    startIdPoint: _startPointItem?.idPoint,
    endIdPoint:_endPointItem?.idPoint,
    // 用于从SVG中删除点
    unvisiblePoints: true,
),

要向地图添加交互对象,你需要使用FloorItemWidget初始化它们,并作为列表传递给FloorItemWidget。

FloorItemWidget(
    // FloorItem
    item: element,
    // 函数 (FloorItem)
    onTap: _handleFloorItemTap,
    // 交互动画颜色变化示例
    selectedColor: Colors.orange[200]!.withOpacity(0.5),
    // 对象闪烁示例
    // 通过这种方式,你可以突出显示地图上的某些对象。例如厕所或ATM
    isActiveBlinking: false,
),

你可以使用FloorSvgParser获取对象。

final parser = FloorSvgParser(svgContent: svgContent);
// 可以从地图中获取锚点
final listPoints = parser.getPoints();
// 可以获取库支持的所有对象
final listItems = parser.getItems();

更多详情,请访问带有准备好的代码和地图的示例 这里

Example

如何贡献

开始前,请阅读 CONTRIBUTING.md,了解该项目的指南。

更新日志

查看 CHANGELOG.md,获取所有发布说明。

维护者

ADC STUDIO

Valerij Shishov | Arthur Lokhov

该库欢迎问题和拉取请求。如果你有任何改进建议或发现bug,请随时贡献!


示例代码

import 'package:example/widgets/example_bottom_sheet.dart';
import 'package:floors_map_widget/floors_map_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

// 为了使示例更容易理解,
// 我们没有使用状态管理库。
final class SvgMapExample extends StatefulWidget {
  const SvgMapExample({
    super.key,
  });

  [@override](/user/override)
  State<SvgMapExample> createState() => _SvgMapExampleState();
}

class _SvgMapExampleState extends State<SvgMapExample> {
  // 为了简化示例,我们没有使用状态库,
  // 因此如果变量为空,则会显示加载小部件
  String? _svgContent;
  // 地图元素表
  List<FloorItemWidget> _listWidgets = [];
  // 路径点
  FloorItem? _startPointItem;
  FloorItem? _endPointItem;

  [@override](/user/override)
  void initState() {
    super.initState();
    _initializeMap();
  }

  // 对象闪烁示例
  void searchObjects<T extends FloorItem>({final FloorSubTypes? subType}) {
    _listWidgets = _listWidgets.map((final item) {
      if (item.item is T && (subType == null || subType == item.item.subType)) {
        return item.copyWith(isActiveBlinking: true);
      }

      return item.copyWith(isActiveBlinking: false);
    }).toList();

    setState(() {});
  }

  // 获取信息并构建路径示例
  Future<void> _handleFloorItemTap(final FloorItem floorItem) async {
    await ExampleBottomSheet.showBottomSheet(
      context,
      floorItem,
      () => _setStartPoint(floorItem),
      () => _setEndPoint(floorItem),
    );
  }

  void _setStartPoint(final FloorItem floorItem) {
    setState(() {
      _startPointItem = floorItem;
    });
  }

  void _setEndPoint(final FloorItem floorItem) {
    setState(() {
      _endPointItem = floorItem;
    });
  }

  Future<void> _initializeMap() async {
    try {
      final svgContent =
          await rootBundle.loadString('assets/map_with_points_example.svg');
      // 解析器初始化
      final parser = FloorSvgParser(svgContent: svgContent);
      // 可以从地图中获取锚点
      // 忽略:未使用的局部变量
      final listPoints = parser.getPoints();
      // 可以获取库支持的所有对象
      final listItems = parser.getItems();
      // 基于FloorItem创建FloorItemWidget
      final listWidgets = listItems
          .map(
            (final element) => FloorItemWidget(
              element,
              onTap: _handleFloorItemTap,
              // 交互动画颜色变化示例
              selectedColor: Colors.orange[200]!.withOpacity(0.5),
              // 对象闪烁示例
              // 通过这种方式,你可以突出显示地图上的某些对象。例如厕所或ATM
              // 忽略:避免冗余参数值
              isActiveBlinking: false,
            ),
          )
          .toList();

      // 更新UI上接收到的数据
      setState(
        () {
          _svgContent = svgContent;
          _listWidgets = listWidgets;
          _startPointItem = null;
          _endPointItem = null;
        },
      );
    } catch (e) {
      debugPrint('Error initial map: $e');
      setState(() {
        _svgContent = null;
      });
    }
  }

  [@override](/user/override)
  Widget build(final BuildContext context) => Scaffold(
        appBar: AppBar(
          title: const Text('示例地图小部件'),
        ),
        body: _svgContent == null
            ? const Center(child: CircularProgressIndicator())
            : SafeArea(
                child: Stack(
                  children: [
                    // 用于缩放和移动
                    InteractiveViewer(
                      maxScale: 3,
                      child: FloorMapWidget(
                        // SVG Map字符串
                        _svgContent!,
                        // 楼层小部件
                        _listWidgets,
                        // 用于构建路径
                        startIdPoint: _startPointItem?.idPoint,
                        endIdPoint: _endPointItem?.idPoint,
                        // 用于从SVG中删除点
                        unvisiblePoints: true,
                      ),
                    ),
                    Align(
                      alignment: Alignment.bottomCenter,
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          IconButton(
                            onPressed: () => searchObjects<FloorShop>(),
                            icon: const Icon(Icons.store),
                          ),
                          IconButton(
                            onPressed: () => searchObjects<FloorHygieneZone>(
                              subType: FloorHygieneZoneType.maleRoom,
                            ),
                            icon: const Icon(Icons.wc),
                          ),
                          IconButton(
                            onPressed: () => searchObjects<FloorHygieneZone>(
                              subType: FloorHygieneZoneType.motherAndChildRoom,
                            ),
                            icon: const Icon(Icons.child_care),
                          ),
                          IconButton(
                            onPressed: () => searchObjects<FloorStairs>(
                              subType: FloorStairsType.escalator,
                            ),
                            icon: const Icon(Icons.escalator),
                          ),
                          IconButton(
                            onPressed: () => searchObjects<FloorStairs>(
                              subType: FloorStairsType.elevator,
                            ),
                            icon: const Icon(Icons.elevator),
                          ),
                          IconButton(
                            onPressed: () => searchObjects<FloorAtmMachine>(),
                            icon: const Icon(Icons.atm),
                          ),
                          IconButton(
                            onPressed: _initializeMap,
                            icon: const Icon(Icons.cancel),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
      );
}

void main() async {
  runApp(
    const MaterialApp(
      home: SvgMapExample(),
    ),
  );
}

更多关于Flutter楼层地图展示插件floors_map_widget的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

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


当然,以下是一个关于如何在Flutter中使用floors_map_widget插件来展示楼层地图的示例代码。这个示例假设你已经将floors_map_widget添加到了你的pubspec.yaml文件中,并且已经完成了必要的配置。

首先,确保你的pubspec.yaml文件中包含以下依赖项:

dependencies:
  flutter:
    sdk: flutter
  floors_map_widget: ^最新版本号 # 替换为实际版本号

然后,运行flutter pub get来安装依赖。

接下来,是一个简单的Flutter应用示例,展示了如何使用floors_map_widget来展示楼层地图:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Floors Map Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FloorsMapScreen(),
    );
  }
}

class FloorsMapScreen extends StatefulWidget {
  @override
  _FloorsMapScreenState createState() => _FloorsMapScreenState();
}

class _FloorsMapScreenState extends State<FloorsMapScreen> {
  // 示例楼层数据
  final List<Floor> floors = [
    Floor(
      id: '1',
      name: 'First Floor',
      imageUrl: 'assets/images/floor_1.png', // 替换为你的楼层图片路径
      rooms: [
        Room(
          id: '1_1',
          name: 'Room 101',
          coordinates: [
            Coordinate(x: 50, y: 100),
            Coordinate(x: 200, y: 100),
            Coordinate(x: 200, y: 200),
            Coordinate(x: 50, y: 200),
          ],
        ),
        // 添加更多房间...
      ],
    ),
    // 添加更多楼层...
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Floors Map'),
      ),
      body: FloorsMapWidget(
        floors: floors,
        onFloorSelected: (floorId) {
          print("Floor selected: $floorId");
        },
        onRoomSelected: (room) {
          print("Room selected: ${room.name}");
        },
      ),
    );
  }
}

// 定义楼层数据模型
class Floor {
  final String id;
  final String name;
  final String imageUrl;
  final List<Room> rooms;

  Floor({required this.id, required this.name, required this.imageUrl, required this.rooms});
}

// 定义房间数据模型
class Room {
  final String id;
  final String name;
  final List<Coordinate> coordinates;

  Room({required this.id, required this.name, required this.coordinates});
}

// 定义坐标数据模型
class Coordinate {
  final double x;
  final double y;

  Coordinate({required this.x, required this.y});
}

在这个示例中,我们定义了一个简单的楼层(Floor)和房间(Room)数据模型,以及一个坐标(Coordinate)模型来表示房间的边界。然后,我们在FloorsMapScreen中使用FloorsMapWidget来展示楼层地图,并处理楼层和房间的选择事件。

请注意,你需要提供实际的楼层图片,并将路径替换为imageUrl字段中的值。此外,你可能还需要根据实际需求调整房间坐标。

这个示例只是一个简单的起点,你可以根据需要进一步扩展和自定义FloorsMapWidget的使用。

回到顶部