地图组件和ECS框架完成

This commit is contained in:
2026-01-13 15:39:45 +08:00
parent 9d45d4c726
commit 566ec47a73
24 changed files with 17072 additions and 66 deletions

25
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,25 @@
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "llm_chat",
"request": "launch",
"type": "dart"
},
{
"name": "llm_chat (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "llm_chat (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"cmake.ignoreCMakeListsMissing": true
}

File diff suppressed because it is too large Load Diff

2066
assets/config/style.json Normal file

File diff suppressed because it is too large Load Diff

76
assets/osm_style.json Normal file
View File

@@ -0,0 +1,76 @@
{
"version": 8,
"projection": {
"type": "globe"
},
"name": "My Martin Map",
"sources": {
"worlddom_source": {
"type": "raster",
"tiles": [
"http://localhost:5656/dom_source/{z}/{x}/{y}"
],
"tileSize": 256,
"attribution": "Custom Satellite Data"
},
"hillshadeSource": {
"type": "raster-dem",
"url": "http://localhost:5656/dem_terrain/{z}/{x}/{y}",
"tileSize": 256
},
"elevation_source": {
"type": "raster-dem",
"tiles": [
"http://localhost:5656/dem_terrain/{z}/{x}/{y}"
],
"tileSize": 256,
"encoding": "terrarium"
}
},
"layers": [
{
"id": "hillshade_layer",
"type": "hillshade",
"source": "elevation_source",
"layout": {
"visibility": "visible"
},
"paint": {
"hillshade-shadow-color": "#473B24",
"hillshade-highlight-color": "#FFFFE0",
"hillshade-illumination-direction": 335,
"hillshade-exaggeration": 0.5
}
},
{
"id": "satellite_layer",
"type": "raster",
"source": "worlddom_source",
"minzoom": 0,
"maxzoom": 11
}
],
"terrain": {
"source": "elevation_source",
"exaggeration": 0.01
},
"sky": {
"atmosphere-blend": [
"interpolate",
["linear"],
["zoom"],
0, 1,
5, 1,
7, 0
]
},
"light": {
"anchor": "map",
"position": [1.5, 60, 45]
}
}

10925
assets/pmtiles_style.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
{
"version": 8,
"projection": {
"type": "globe"
},
"sources": {
"satellite": {
"tiles": ["https://tiles.maps.eox.at/wmts/1.0.0/s2cloudless-2020_3857/default/g/{z}/{y}/{x}.jpg"],
"type": "raster"
}
},
"layers": [
{
"id": "Satellite",
"type": "raster",
"source": "satellite"
}
],
"sky": {
"atmosphere-blend": [
"interpolate",
["linear"],
["zoom"],
0, 1,
5, 1,
7, 0
]
},
"light": {
"anchor": "map",
"position": [1.5, 60, 45]
}
}

26
assets/style.json Normal file
View File

@@ -0,0 +1,26 @@
{
"version": 8,
"sources": {
"my-mbtiles-source": {
"type": "vector",
"tiles": [
"http://localhost:5656/osm_vector/{z}/{x}/{y}.pbf"
],
"minzoom": 0,
"maxzoom": 14
}
},
"layers": [
{
"id": "my-layer",
"type": "line",
"source": "my-mbtiles-source",
"source-layer": "your_layer_name_in_mbtiles",
"paint": {
"line-color": "#ff0000",
"line-width": 2
}
}
]
}

3
devtools_options.yaml Normal file
View File

@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

90
lib/ecs/component.dart Normal file
View File

@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
import 'entity.dart';
/// 组件基类 - 代表实体的属性
abstract class Component {
final String id;
Entity? entity;
Component({String? id}) : id = id ?? "${DateTime.now().millisecondsSinceEpoch}";
}
/// 标绘组件 - 用于在地图上绘制图形
class PlotComponent extends Component {
final String plotType;
final List<LatLng> coordinates;
final Color color;
final double strokeWidth;
PlotComponent({
super.id,
required this.plotType,
required this.coordinates,
this.color = Colors.red,
this.strokeWidth = 2.0,
});
}
/// 图标组件 - 用于在地图上显示图标
class IconComponent extends Component {
final String iconPath;
LatLng position;
final double size;
IconComponent({
super.id,
required this.iconPath,
required this.position,
this.size = 30.0,
});
}
/// 拖尾组件 - 用于显示移动实体的轨迹
class TrailComponent extends Component {
final List<LatLng> trailPoints;
final Color trailColor;
final double maxLength;
TrailComponent({
super.id,
List<LatLng>? trailPoints,
this.trailColor = Colors.blue,
this.maxLength = 20.0,
}) : trailPoints = trailPoints ?? [];
void addPoint(LatLng point) {
trailPoints.add(point);
if (trailPoints.length > maxLength) {
trailPoints.removeAt(0);
}
}
}
/// 音频组件 - 用于播放与实体相关的音频
class AudioComponent extends Component {
final String audioPath;
final bool loop;
final double volume;
AudioComponent({
super.id,
required this.audioPath,
this.loop = false,
this.volume = 1.0,
});
}
/// 移动组件 - 用于控制实体的移动
class MovementComponent extends Component {
LatLng position;
LatLng? target;
double speed;
MovementComponent({
super.id,
required this.position,
this.target,
this.speed = 1.0,
});
}

39
lib/ecs/entity.dart Normal file
View File

@@ -0,0 +1,39 @@
import 'component.dart';
/// 实体ID生成器
class EntityIdGenerator {
static int _id = 0;
static int nextId() => _id++;
}
/// 实体类 - 代表地图上的一个对象
class Entity {
final int id;
final String name;
int entityType;
final Map<String, Component> _components = {};
Entity({int? id, required this.name, this.entityType = 0}) : id = id ?? EntityIdGenerator.nextId();
/// 添加组件
void addComponent<T extends Component>(T component) {
_components[T.toString()] = component;
}
/// 获取组件
T? getComponent<T extends Component>() {
return _components[T.toString()] as T?;
}
/// 检查是否有特定类型的组件
bool hasComponent<T extends Component>() {
return _components.containsKey(T.toString());
}
/// 移除组件
void removeComponent<T extends Component>() {
_components.remove(T.toString());
}
/// 获取所有组件
Map<String, Component> get components => _components;
}

389
lib/ecs/system.dart Normal file
View File

@@ -0,0 +1,389 @@
import 'dart:collection';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:maplibre/maplibre.dart';
import 'package:maplibre_gl/maplibre_gl.dart' as gl;
import '../screens/map_screen.dart';
import 'entity.dart';
import 'component.dart';
/// 系统基类 - 用于处理特定类型的组件
abstract class System {
void update(List<Entity> entities, Duration deltaTime);
}
/// 地图系统 - 处理地图相关的实体和组件
class MapSystem implements System {
gl.MapLibreMapController? mapController;
MapUiBodyState? mapUiBodyState;
final List<String> _plotIds = [];
final List<String> _trailIds = [];
final List<String> _iconIds = [];
// 单例实例
static MapSystem? _instance;
// 工厂构造函数,实现单例
factory MapSystem({gl.MapLibreMapController? mapController, MapUiBodyState? mapUiBodyState}) {
_instance ??= MapSystem._internal(mapController: mapController, mapUiBodyState: mapUiBodyState);
if (mapController != null) _instance!.mapController = mapController;
if (mapUiBodyState != null) _instance!.mapUiBodyState = mapUiBodyState;
return _instance!;
}
// 私有构造函数
MapSystem._internal({this.mapController, this.mapUiBodyState});
/// 获取单例实例
static MapSystem get instance {
_instance ??= MapSystem._internal();
return _instance!;
}
@override
void update(List<Entity> entities, Duration deltaTime) {
// 更新地图上的标绘
for (var entity in entities) {
if (entity.hasComponent<PlotComponent>()) {
_updatePlot(entity.getComponent<PlotComponent>()!);
}
if (entity.hasComponent<IconComponent>()) {
_updateIcon(entity.getComponent<IconComponent>()!);
}
if (entity.hasComponent<TrailComponent>()) {
_updateTrail(entity.getComponent<TrailComponent>()!);
}
if (entity.hasComponent<MovementComponent>()) {
_updateMovement(entity);
}
}
}
void _updatePlot(PlotComponent plot) {
// 实现标绘更新逻辑
// 这里可以根据plotType绘制不同类型的图形
if (plot.coordinates.length >= 2) {
// 示例:绘制线段
// mapController?.addLine() // 这里需要根据实际API实现
}
}
void _updateIcon(IconComponent icon) {
// 实现图标更新逻辑
// mapController?.addMarker() // 这里需要根据实际API实现
}
void _updateTrail(TrailComponent trail) {
// 实现拖尾更新逻辑
// 绘制轨迹线
}
void _updateMovement(Entity entity) {
var movement = entity.getComponent<MovementComponent>();
var plot = entity.getComponent<PlotComponent>();
var icon = entity.getComponent<IconComponent>();
if (movement != null && (plot != null || icon != null)) {
if (movement.target != null) {
// 简单的移动逻辑 - 向目标点移动
double dx = movement.target!.longitude - movement.position.longitude;
double dy = movement.target!.latitude - movement.position.latitude;
double distance = sqrt(dx * dx + dy * dy);
if (distance > 0.0001) { // 如果距离目标足够近就停止
double ratio = movement.speed * 0.0001 / distance;
movement.position = gl.LatLng(
movement.position.latitude + dy * ratio,
movement.position.longitude + dx * ratio,
);
// 更新图标位置
if (icon != null) {
icon.position = movement.position;
}
// 如果有拖尾组件,添加新点
var trail = entity.getComponent<TrailComponent>();
if (trail != null) {
trail.addPoint(movement.position);
}
} else {
movement.target = null; // 到达目标
}
}
}
}
// ==================== 便捷方法:通过 mapUiBodyState 调用地图接口 ====================
/// 添加自定义图片标记
void addCustomMarker(Geographic position, {Map<String, dynamic>? properties}) {
mapUiBodyState?.addCustomMarker(position, properties: properties);
}
/// 添加圆形
void addCircle(
Geographic position, {
double radius = 20,
Color color = Colors.orange,
Color strokeColor = Colors.red,
double strokeWidth = 2,
}) {
mapUiBodyState?.addCircle(
position,
radius: radius,
color: color,
strokeWidth: strokeWidth,
);
}
/// 添加扇形
/// [center] 扇形中心的地理位置
/// [radius] 扇形的半径(单位:米)
/// [startAngle] 起始角度单位0度表示正东方向
/// [endAngle] 结束角度(单位:度)
/// [segments] 扇形的分段数,值越大越平滑
/// [color] 填充颜色
/// [outlineColor] 边框颜色
void addSector(
Geographic center, {
required double radius,
required double startAngle,
required double endAngle,
int segments = 36,
Color color = Colors.lightBlueAccent,
Color outlineColor = Colors.blue,
}) {
mapUiBodyState?.addSector(
center,
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
segments: segments,
color: color,
outlineColor: outlineColor,
);
}
/// 添加长方形
/// [center] 长方形中心的地理位置
/// [width] 长方形的宽度(单位:米)
/// [height] 长方形的高度(单位:米)
/// [color] 填充颜色
/// [outlineColor] 边框颜色
void addRectangle(
Geographic center, {
required double width,
required double height,
Color color = Colors.lightBlueAccent,
Color outlineColor = Colors.blue,
}) {
mapUiBodyState?.addRectangle(
center,
width: width,
height: height,
color: color,
outlineColor: outlineColor,
);
}
/// 清除所有自定义标记
void clearCustomMarkers() {
mapUiBodyState?.clearCustomMarkers();
}
/// 清除所有圆形
void clearCircles() {
mapUiBodyState?.clearCircles();
}
/// 清除所有多边形(包括扇形和长方形)
void clearPolygons() {
mapUiBodyState?.clearPolygons();
}
/// 清除所有自定义图层
void clearAllCustomLayers() {
mapUiBodyState?.clearAllCustomLayers();
}
/// 添加固定大小的圆形(不随地图缩放)
void addFixedCircle(
Geographic position, {
double radius = 20,
Color color = Colors.orange,
Color strokeColor = Colors.red,
double strokeWidth = 2,
}) {
mapUiBodyState?.addFixedCircle(
position,
radius: radius,
color: color,
outlineColor: strokeColor,
);
}
}
/// 音频系统 - 处理音频相关的实体和组件
class AudioSystem implements System {
@override
void update(List<Entity> entities, Duration deltaTime) {
for (var entity in entities) {
if (entity.hasComponent<AudioComponent>()) {
var audioComponent = entity.getComponent<AudioComponent>()!;
_playAudio(audioComponent);
}
}
}
void _playAudio(AudioComponent audio) {
// 实现音频播放逻辑
// 这里可以使用Flutter的音频插件如audioplayers
print("Playing audio: ${audio.audioPath}");
}
}
/// 组件系统 - 管理各种组件的交互
class ComponentSystem implements System {
@override
void update(List<Entity> entities, Duration deltaTime) {
// 处理组件间的交互
for (var entity in entities) {
// 可以根据需要实现组件交互逻辑
if (entity.hasComponent<PlotComponent>() && entity.hasComponent<IconComponent>()) {
// 当一个实体同时有标绘和图标组件时的特殊处理
}
}
}
}
/// ECS管理器 - 管理实体、系统和整个ECS框架
class ECSManager {
static ECSManager? _instance;
factory ECSManager() {
_instance ??= ECSManager._internal();
return _instance!;
}
ECSManager._internal();
/// 获取单例实例
static ECSManager get instance {
_instance ??= ECSManager._internal();
return _instance!;
}
final List<Entity> _entities = [];
final List<System> _systems = [];
final Queue<Entity> _entitiesToAdd = Queue<Entity>();
final Queue<Entity> _entitiesToRemove = Queue<Entity>();
void addEntity(Entity entity) {
_entitiesToAdd.add(entity);
}
void removeEntity(Entity entity) {
_entitiesToRemove.add(entity);
}
void addSystem(System system) {
_systems.add(system);
}
void removeSystem(System system) {
_systems.remove(system);
}
void update(Duration deltaTime) {
// 添加新实体
while (_entitiesToAdd.isNotEmpty) {
_entities.add(_entitiesToAdd.removeFirst());
}
// 更新所有系统
for (var system in _systems) {
system.update(_entities, deltaTime);
}
// 移除实体
while (_entitiesToRemove.isNotEmpty) {
var entity = _entitiesToRemove.removeFirst();
_entities.remove(entity);
}
}
List<Entity> get entities => _entities;
/// 工厂方法:创建一个带位置的实体
Entity createEntityAtPosition({
required String name,
required gl.LatLng position,
String? plotType,
Color plotColor = Colors.red,
double strokeWidth = 2.0,
String? iconPath,
double iconSize = 30.0,
bool hasTrail = false,
Color trailColor = Colors.blue,
double trailMaxLength = 20.0,
bool hasMovement = false,
gl.LatLng? movementTarget,
double movementSpeed = 1.0,
String? audioPath,
bool audioLoop = false,
double audioVolume = 1.0,
}) {
// 创建实体
final entity = Entity(name: name);
// 添加位置相关的组件
if (plotType != null) {
entity.addComponent(PlotComponent(
plotType: plotType,
coordinates: [position],
color: plotColor,
strokeWidth: strokeWidth,
));
}
if (iconPath != null) {
entity.addComponent(IconComponent(
iconPath: iconPath,
position: position,
size: iconSize,
));
}
if (hasTrail) {
entity.addComponent(TrailComponent(
trailColor: trailColor,
maxLength: trailMaxLength,
));
}
if (hasMovement) {
entity.addComponent(MovementComponent(
position: position,
target: movementTarget,
speed: movementSpeed,
));
}
if (audioPath != null) {
entity.addComponent(AudioComponent(
audioPath: audioPath,
loop: audioLoop,
volume: audioVolume,
));
}
// 添加到管理器
addEntity(entity);
return entity;
}
}

View File

@@ -6,6 +6,12 @@ import '../providers/chat_provider.dart';
import '../widgets/message_bubble.dart';
import '../widgets/typing_indicator.dart';
import '../widgets/chat_input.dart';
import 'map_screen.dart'; // 导入地图界面
// 定义一个 GlobalKey 来访问 _ChatScreenState
final GlobalKey<_ChatScreenState> chatScreenStateKey = GlobalKey<_ChatScreenState>();
class ChatScreen extends StatefulWidget {
const ChatScreen({super.key});
@@ -16,6 +22,13 @@ class ChatScreen extends StatefulWidget {
class _ChatScreenState extends State<ChatScreen> {
final ScrollController _scrollController = ScrollController();
bool _mapViewOpen = false; // 控制地图界面是否展开
void _toggleMapView() {
setState(() {
_mapViewOpen = !_mapViewOpen;
});
}
void _scrollToBottom() {
if (_scrollController.hasClients) {
@@ -62,18 +75,43 @@ class _ChatScreenState extends State<ChatScreen> {
context.read<ChatProvider>().clearMessages();
},
),
// 添加地图按钮
CommandBarButton(
icon: const Icon(FluentIcons.map_layers),
label: const Text('Map'),
onPressed: _toggleMapView, // 切换地图视图
),
],
),
),
content: Column(
content: Row( // 修改为Row布局实现左右分屏
children: [
Expanded(child: _buildMessageList()),
_buildInputArea(),
// 聊天界面部分使用Expanded来正确分配空间
Expanded(
flex: 1,
child: _buildChatContent(),
),
// 地图界面部分,根据状态决定是否显示
if (_mapViewOpen)
Expanded(
child: const MapUiPage(), // 使用现有的地图页面
),
],
),
);
}
// 提取聊天内容部分到单独的方法
Widget _buildChatContent() {
return Column(
children: [
Expanded(child: _buildMessageList()),
_buildInputArea(),
],
);
}
Widget _buildMessageList() {
return Consumer<ChatProvider>(
builder: (context, chatProvider, child) {
@@ -161,7 +199,7 @@ class _ChatScreenState extends State<ChatScreen> {
color: theme.accentColor,
borderRadius: BorderRadius.circular(20),
),
child: const Icon(FluentIcons.robot, color: Colors.white, size: 40),
child: Icon(FluentIcons.robot, color: Colors.white, size: 40),
),
const SizedBox(height: 24),
Text('Hello! I\'m your AI Assistant', style: theme.typography.title),

613
lib/screens/map_screen.dart Normal file
View File

@@ -0,0 +1,613 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as Math;
import 'package:flutter/material.dart';
import 'package:maplibre/maplibre.dart';
import 'package:maplibre_gl/maplibre_gl.dart' as gl;
import '../ecs/system.dart';
abstract class ExamplePage extends StatelessWidget {
const ExamplePage(
this.leading,
this.title, {
this.needsLocationPermission = true,
super.key,
});
final Widget leading;
final String title;
final bool needsLocationPermission;
}
class MapUiPage extends ExamplePage {
const MapUiPage({super.key}) : super(const Icon(Icons.map), '态势');
@override
Widget build(BuildContext context) {
return const MapUiBody();
}
}
class MapUiBody extends StatefulWidget {
const MapUiBody({super.key});
@override
State<MapUiBody> createState() => MapUiBodyState();
/// 静态方法,用于通过 GlobalKey 获取 MapUiBodyState 实例
static MapUiBodyState? of(BuildContext context) {
final mapUiBody = context.findAncestorWidgetOfExactType<MapUiBody>();
if (mapUiBody?.key is GlobalKey<MapUiBodyState>) {
final key = mapUiBody?.key as GlobalKey<MapUiBodyState>;
return key.currentState;
}
return null;
}
}
class MapUiBodyState extends State<MapUiBody> with TickerProviderStateMixin {
late final MapController _controller;
final GlobalKey _mapKey = GlobalKey();
// MapSystem 实例,用于通过系统调用地图接口
late final MapSystem mapSystem;
final _markerPositions = [
const Geographic(lon: -10, lat: 0),
const Geographic(lon: -5, lat: 0),
const Geographic(lon: 0, lat: 0),
const Geographic(lon: 5, lat: 0),
];
// 自定义图片标记数据
final _customMarkers = <Feature<Point>>[];
bool _customMarkerImageLoaded = false;
// 圆形数据
final _circles = <Feature<Point>>[];
// 多边形数据(包括扇形和长方形)
final _polygons = <Feature<Polygon>>[];
Geographic? _originalPosition;
MapGestures _mapGestures = const MapGestures.all();
@override
void initState() {
super.initState();
// 初始化 MapSystem并将 this 传递给它
mapSystem = MapSystem(mapUiBodyState: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
//appBar: AppBar(title: const Text('Interactive Widget Layer')),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.only(left: 16, bottom: 8),
),
Expanded(
child: MapLibreMap(
key: _mapKey,
options: MapOptions(
initZoom: 3,
initCenter: const Geographic(lon: 0, lat: 0),
initStyle: "assets/osm_style.json",
gestures: _mapGestures,
),
onMapCreated: (controller) => _controller = controller,
onEvent: (event) async {
if (event is MapEventStyleLoaded) {
// 加载自定义标记图片
await event.style.addImageFromIconData(
id: 'custom-marker',
iconData: Icons.location_on,
color: Colors.blue,
);
setState(() {
_customMarkerImageLoaded = true;
});
} else if (event is MapEventLongClick) {
final position = event.point;
_markerPositions.add(position);
setState(() {});
}
},
layers: [
// 自定义图片标记层
MarkerLayer(
points: _customMarkers.where((marker) =>
marker.properties['type'] != 'fixed_circle'
).toList(), // 过滤掉固定圆形
iconImage: _customMarkerImageLoaded ? 'custom-marker' : null,
iconSize: 0.15,
iconAnchor: IconAnchor.bottom,
),
// 圆形层
CircleLayer(
points: _circles,
color: Colors.orange.withValues(alpha: 0.5),
radius: 20,
strokeColor: Colors.red,
strokeWidth: 2,
),
// 多边形层(扇形、长方形和圆形)
PolygonLayer(
polygons: _polygons,
color: Colors.lightBlueAccent.withValues(alpha: 0.6),
outlineColor: Colors.blue,
),
],
children: [
WidgetLayer(
allowInteraction: true,
markers: List.generate(
_markerPositions.length,
(index) => Marker(
size: const Size.square(50),
point: _markerPositions[index],
child: GestureDetector(
onTap: () => _onTap(index),
onLongPressStart: (details) =>
_onLongPress(index, details),
onPanStart: (details) => _onLongPanStart(details, index),
onPanUpdate: (details) async =>
_onPanUpdate(details, index),
onPanEnd: (details) async => _onPanEnd(details, index),
child: const Icon(
Icons.location_on,
color: Colors.red,
size: 50,
),
),
alignment: Alignment.bottomCenter,
),
),
),
// 添加固定圆形的Widget
WidgetLayer(
allowInteraction: false, // 固定圆形不需要交互
markers: _customMarkers
.where((marker) => marker.properties['type'] == 'fixed_circle')
.map((marker) {
final pointGeometry = marker.geometry as Point;
// 使用Point的position属性获取坐标
final geographic = Geographic(
lon: pointGeometry.position.x,
lat: pointGeometry.position.y,
);
return Marker(
size: Size.fromRadius(marker.properties['radius']?.toDouble() ?? 20.0),
point: geographic,
child: IgnorePointer( // 忽略指针事件,确保不会干扰地图手势
child: Container(
width: (marker.properties['radius']?.toDouble() ?? 20.0) * 2,
height: (marker.properties['radius']?.toDouble() ?? 20.0) * 2,
decoration: BoxDecoration(
color: marker.properties['color'] ?? Colors.orange,
shape: BoxShape.circle,
border: Border.all(
color: marker.properties['strokeColor'] ?? Colors.red,
width: marker.properties['strokeWidth']?.toDouble() ?? 2.0,
),
),
),
),
alignment: Alignment.center,
);
})
.toList(),
),
// display the UI widgets above the widget markers.
const MapScalebar(),
const SourceAttribution(),
const MapControlButtons(),
const MapCompass(),
],
),
),
],
),
);
}
Future<Geographic> _toLngLat(Offset eventOffset) async {
final mapRenderBox =
_mapKey.currentContext?.findRenderObject() as RenderBox?;
assert(mapRenderBox != null, 'RenderBox of Map should never be null');
final mapOffset = mapRenderBox!.localToGlobal(Offset.zero);
final offset = Offset(
eventOffset.dx - mapOffset.dx,
eventOffset.dy - mapOffset.dy,
);
return _controller.toLngLat(offset);
}
void _onLongPress(int index, LongPressStartDetails details) {
final offset = details.globalPosition;
showMenu(
context: context,
position: RelativeRect.fromLTRB(
offset.dx,
offset.dy,
MediaQuery.of(context).size.width - offset.dx,
MediaQuery.of(context).size.height - offset.dy,
),
items: [
const PopupMenuItem<void>(child: Text('Edit')),
PopupMenuItem<void>(
onTap: () async {
final isConfirmed = await _showConfirmationDialogDelete(index);
if (isConfirmed) {
_markerPositions.removeAt(index);
setState(() {});
}
},
child: const Text('Delete'),
),
],
);
}
Future<void> _onPanEnd(DragEndDetails details, int index) async {
final isAccepted = await _showConfirmationDialogMove();
if (!isAccepted) {
_markerPositions[index] = _originalPosition!;
} else {
final newPosition = await _toLngLat(details.globalPosition);
_markerPositions[index] = newPosition;
}
_originalPosition = null;
setState(() {
_mapGestures = const MapGestures.all();
});
}
void _onLongPanStart(DragStartDetails details, int index) {
// Keep original position in case of discarded move
_originalPosition = Geographic.from(_markerPositions[index]);
setState(() {
// Disable camera panning while a marker gets moved.
_mapGestures = const MapGestures.all(pan: false);
});
}
Future<void> _onPanUpdate(DragUpdateDetails details, int index) async {
final newPosition = await _toLngLat(details.globalPosition);
_markerPositions[index] = newPosition;
setState(() {});
}
void _onTap(int index) {
_showMarkerDetails(index);
}
Future<bool> _showConfirmationDialogDelete(int index) async {
final isConfirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text('Delete marker [$index]?'),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Delete'),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
),
);
return isConfirmed ?? false;
}
Future<bool> _showConfirmationDialogMove() async {
final isConfirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Accept new position?'),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Discard'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Accept'),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
),
);
return isConfirmed ?? false;
}
Future<void> _showMarkerDetails(int index) async {
await showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: Text('Details marker with index: $index'),
content: Text('Show here the details of Marker with index $index'),
actions: <Widget>[
TextButton(
style: TextButton.styleFrom(
textStyle: Theme.of(context).textTheme.labelLarge,
),
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
return;
}
// ==================== 公共接口方法 ====================
/// 添加自定义图片标记
///
/// [position] 标记的地理位置
/// [properties] 可选的属性数据
void addCustomMarker(Geographic position, {Map<String, dynamic>? properties}) {
setState(() {
_customMarkers.add(Feature(
geometry: Point(position),
properties: properties,
));
});
}
/// 添加圆形
///
/// [position] 圆心的地理位置
/// [radius] 圆的半径(单位:像素)
/// [color] 填充颜色
/// [strokeColor] 边框颜色
/// [strokeWidth] 边框宽度
void addCircle(
Geographic position, {
double radius = 20,
Color color = Colors.orange,
Color strokeColor = Colors.red,
double strokeWidth = 2,
}) {
setState(() {
_circles.add(Feature(
geometry: Point(position),
properties: {
'radius': radius,
'color': color,
'strokeColor': strokeColor,
'strokeWidth': strokeWidth,
},
));
});
}
/// 添加固定大小的圆形(不随地图缩放)
///
/// [center] 圆心的地理位置
/// [radius] 圆的半径(单位:米)
/// [color] 填充颜色
/// [outlineColor] 边框颜色
void addFixedCircle(
Geographic center, {
required double radius,
Color color = Colors.orange,
Color outlineColor = Colors.blue,
}) {
// 将半径从米转换为经纬度(度)
final radiusInDegrees = _metersToDegrees(center, radius);
final points = <Geographic>[];
// 计算圆周上的点360度
for (int i = 0; i <= 36; i++) { // 使用36个点来构成圆形
final angle = (360 * i / 36) * 3.14159265359 / 180; // 角度转弧度
// 将角度转换为经纬度偏移
final latOffset = radiusInDegrees * Math.sin(angle);
final lonOffset = radiusInDegrees * Math.cos(angle);
points.add(Geographic(
lon: center.lon + lonOffset,
lat: center.lat + latOffset,
));
}
setState(() {
_polygons.add(Feature(
geometry: Polygon.from([points]),
properties: {
'color': color,
'outlineColor': outlineColor,
'type': 'fixed_circle_geo', // 标记为地理圆形
},
));
});
}
/// 添加扇形
///
/// [center] 扇形中心的地理位置
/// [radius] 扇形的半径(单位:米)
/// [startAngle] 起始角度单位0度表示正东方向
/// [endAngle] 结束角度(单位:度)
/// [segments] 扇形的分段数,值越大越平滑
/// [color] 填充颜色
/// [outlineColor] 边框颜色
void addSector(
Geographic center, {
required double radius,
required double startAngle,
required double endAngle,
int segments = 36,
Color color = Colors.lightBlueAccent,
Color outlineColor = Colors.blue,
}) {
// 将半径从米转换为经纬度(度)
final radiusInDegrees = _metersToDegrees(center, radius);
final points = <Geographic>[];
// 添加中心点
points.add(center);
// 计算扇形的弧线上的点
for (int i = 0; i <= segments; i++) {
final angle = startAngle + (endAngle - startAngle) * i / segments;
final angleRad = angle * 3.14159265359 / 180;
// 将角度转换为经纬度偏移
final latOffset = radiusInDegrees * Math.sin(angleRad);
final lonOffset = radiusInDegrees * Math.cos(angleRad);
points.add(Geographic(
lon: center.lon + lonOffset,
lat: center.lat + latOffset,
));
}
setState(() {
_polygons.add(Feature(
geometry: Polygon.from([points]),
properties: {
'color': color,
'outlineColor': outlineColor,
},
));
});
}
/// 添加长方形
///
/// [center] 长方形中心的地理位置
/// [width] 长方形的宽度(单位:米)
/// [height] 长方形的高度(单位:米)
/// [color] 填充颜色
/// [outlineColor] 边框颜色
void addRectangle(
Geographic center, {
required double width,
required double height,
Color color = Colors.lightBlueAccent,
Color outlineColor = Colors.blue,
}) {
// 将宽高从米转换为经纬度(度)
final widthInDegrees = _metersToDegrees(center, width / 2);
final heightInDegrees = _metersToDegrees(center, height / 2);
final points = [
Geographic(lon: center.lon - widthInDegrees, lat: center.lat - heightInDegrees),
Geographic(lon: center.lon + widthInDegrees, lat: center.lat - heightInDegrees),
Geographic(lon: center.lon + widthInDegrees, lat: center.lat + heightInDegrees),
Geographic(lon: center.lon - widthInDegrees, lat: center.lat + heightInDegrees),
Geographic(lon: center.lon - widthInDegrees, lat: center.lat - heightInDegrees), // 闭合多边形
];
setState(() {
_polygons.add(Feature(
geometry: Polygon.from([points]),
properties: {
'color': color,
'outlineColor': outlineColor,
},
));
});
}
/// 将距离(米)转换为经纬度(度)
/// 此方法使用近似计算,适用于小范围区域
double _metersToDegrees(Geographic center, double meters) {
// 地球半径约为6378137米
const earthRadius = 6378137.0;
// 将米转换为度
// 1度大约等于111320米在赤道附近
// 但需要考虑纬度的影响,因为经度的距离会随纬度变化
final latCircumference = 2 * Math.pi * earthRadius; // 纬线周长
final latFactor = meters / latCircumference * 360; // 纬度方向转换因子
// 经度方向需要考虑纬度的影响
final lonCircumference = 2 * Math.pi * earthRadius * Math.cos(center.lat * Math.pi / 180); // 经线周长
final lonFactor = meters / lonCircumference * 360; // 经度方向转换因子
// 返回平均值,对于小范围区域,这个近似是足够的
return (latFactor + lonFactor) / 2;
}
/// 清除所有自定义标记
void clearCustomMarkers() {
setState(() {
_customMarkers.clear();
});
}
/// 清除所有圆形
void clearCircles() {
setState(() {
_circles.clear();
});
}
/// 清除所有多边形(包括扇形和长方形)
void clearPolygons() {
setState(() {
_polygons.clear();
});
}
/// 清除所有自定义图层
void clearAllCustomLayers() {
setState(() {
_customMarkers.clear();
_circles.clear();
_polygons.clear();
});
}
}

174
lib/utils/map_styles.dart Normal file
View File

@@ -0,0 +1,174 @@
enum MapStyles {
openMapTilesLiberty(
name: 'OpenMapTiles Liberty',
uri: 'https://tiles.openfreemap.org/styles/liberty',
),
openMapTilesBright(
name: 'OpenMapTiles Bright',
uri: 'https://tiles.openfreemap.org/styles/bright',
),
openMapTilesPositron(
name: 'OpenMapTiles Positron',
uri: 'https://tiles.openfreemap.org/styles/positron',
),
openMapTilesStreets(
name: 'OpenMapTiles Streets',
uri:
'https://api.maptiler.com/maps/streets-v2/style.json?key=$_maptilerKey',
),
stadiaMapsAlidadeSmooth(
name: 'Alidade Smooth',
uri:
'https://tiles.stadiamaps.com/styles/alidade_smooth.json?api_key=$_stadiamapsKey',
),
stadiaMapsAlidadeSmoothDark(
name: 'Alidade Smooth Dark',
uri:
'https://tiles.stadiamaps.com/styles/alidade_smooth_dark.json?api_key=$_stadiamapsKey',
),
versaTilesColorful(
name: 'VersaTiles Colorful',
uri: 'https://tiles.versatiles.org/assets/styles/colorful/style.json',
),
versaTilesGraybeard(
name: 'VersaTiles Graybeard',
uri: 'https://tiles.versatiles.org/assets/styles/graybeard/style.json',
),
versaTilesEclipse(
name: 'VersaTiles Eclipse',
uri: 'https://tiles.versatiles.org/assets/styles/eclipse/style.json',
),
versaTilesNeutrino(
name: 'VersaTiles Neutrino',
uri: 'https://tiles.versatiles.org/assets/styles/neutrino/style.json',
),
protomapsLight(
name: 'Protomaps Light',
uri: 'https://api.protomaps.com/styles/v2/light.json?key=$_protomapsKey',
),
protomapsLightPmTiles(
name: 'Protomaps Light (PMTiles)',
uri: 'assets/styles/protomaps-light.json',
),
protomapsDark(
name: 'Protomaps Dark',
uri: 'https://api.protomaps.com/styles/v2/dark.json?key=$_protomapsKey',
),
protomapsWhite(
name: 'Protomaps White',
uri: 'https://api.protomaps.com/styles/v2/white.json?key=$_protomapsKey',
),
// mapboxStreets(
// name: 'Mapbox Streets (legacy)',
// uri: 'mapbox://styles/mapbox/streets-v12?access_token=$_mapboxKey',
// ),
// mapboxOutdoor(
// name: 'Mapbox Outdoors (legacy)',
// uri: 'mapbox://styles/mapbox/outdoors-v12?access_token=$_mapboxKey',
// ),
// mapboxLight(
// name: 'Mapbox Light (legacy)',
// uri: 'mapbox://styles/mapbox/light-v11?access_token=$_mapboxKey',
// ),
// mapboxDark(
// name: 'Mapbox Dark (legacy)',
// uri: 'mapbox://styles/mapbox/dark-v11?access_token=$_mapboxKey',
// ),
// mapboxSatellite(
// name: 'Mapbox Satellite (legacy)',
// uri: 'mapbox://styles/mapbox/satellite-v9?access_token=$_mapboxKey',
// ),
// mapboxSatelliteStreets(
// name: 'Mapbox Satellite Streets (legacy)',
// uri:
// 'mapbox://styles/mapbox/satellite-streets-v12?access_token=$_mapboxKey',
// ),
maplibreWorld(
name: 'MapLibre World',
uri: 'https://demotiles.maplibre.org/style.json',
),
maplibreDebug(
name: 'MapLibre Debug',
uri: 'https://demotiles.maplibre.org/debug-tiles/style.json',
),
translucent(name: 'Translucent', uri: 'assets/styles/translucent_style.json'),
countries(
name: 'Countries',
uri: '''
{
"version": 8,
"name": "MapLibre",
"center": [17.65431710431244, 32.954120326746775],
"zoom": 0.8619833357855968,
"bearing": 0,
"pitch": 0,
"sources": {
"maplibre": {
"url": "https://demotiles.maplibre.org/tiles/tiles.json",
"type": "vector"
}
},
"glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf",
"layers": [
{
"id": "background",
"type": "background",
"maxzoom": 24,
"filter": ["all"],
"paint": {"background-color": "#D8F2FF"}
},
{
"id": "countries-fill",
"type": "fill",
"source": "maplibre",
"source-layer": "countries",
"maxzoom": 24,
"filter": ["all"],
"paint": {"fill-color": "#FFFFFF"}
},
{
"id": "countries-boundary",
"type": "line",
"source": "maplibre",
"source-layer": "countries",
"maxzoom": 24,
"layout": {
"line-cap": "round",
"line-join": "round",
"visibility": "visible"
},
"paint": {
"line-color": "#198EC8",
"line-width": {"stops": [[1, 1], [6, 2], [14, 6], [22, 12]]},
"line-opacity": {"stops": [[3, 0.5], [6, 1]]}
}
}
],
"id": "43f36e14-e3f5-43c1-84c0-50a9c80dc5c7"
}
''',
);
const MapStyles({required this.name, required this.uri});
final String name;
final String uri;
}
/// **Use your own key for your project!**
/// This key will be rotated occasionally.
///
/// https://cloud.maptiler.com/account/keys/
const _maptilerKey = 'OPCgnZ51sHETbEQ4wnkd';
/// **Use your own key for your project!**
/// This key will be rotated occasionally.
///
/// https://protomaps.com/account
const _protomapsKey = 'a6f9aebb3965458c';
/// **Use your own key for your project!**
/// This key will be rotated occasionally.
///
/// https://client.stadiamaps.com/dashboard
const _stadiamapsKey = '0d5e614b-aaf7-4bfd-9bf3-1f7c3062248e';

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import '/utils/map_styles.dart';
class StyleDropdown extends StatefulWidget {
const StyleDropdown({required this.onChanged, super.key});
final void Function(MapStyles style) onChanged;
static MapStyles get initStyle => MapStyles.openMapTilesLiberty;
@override
State<StyleDropdown> createState() => _StyleDropdownState();
}
class _StyleDropdownState extends State<StyleDropdown> {
late MapStyles _selectedStyle = StyleDropdown.initStyle;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: DropdownButton<MapStyles>(
value: _selectedStyle,
items: MapStyles.values
.map((e) => DropdownMenuItem(value: e, child: Text(e.name)))
.toList(growable: false),
onChanged: (value) {
if (value == null) return;
setState(() => _selectedStyle = value);
widget.onChanged(value);
},
),
);
}
}

View File

@@ -7,20 +7,28 @@
#include "generated_plugin_registrant.h"
#include <flutter_acrylic/flutter_acrylic_plugin.h>
#include <maplibre/maplibre_plugin.h>
#include <screen_retriever_linux/screen_retriever_linux_plugin.h>
#include <system_theme/system_theme_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin");
flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar);
g_autoptr(FlPluginRegistrar) maplibre_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "MaplibrePlugin");
maplibre_plugin_register_with_registrar(maplibre_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin");
screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);
g_autoptr(FlPluginRegistrar) system_theme_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SystemThemePlugin");
system_theme_plugin_register_with_registrar(system_theme_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar);

View File

@@ -4,12 +4,15 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_acrylic
maplibre
screen_retriever_linux
system_theme
url_launcher_linux
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@@ -6,13 +6,17 @@ import FlutterMacOS
import Foundation
import macos_window_utils
import maplibre
import screen_retriever_macos
import system_theme
import url_launcher_macos
import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin"))
MaplibrePlugin.register(with: registry.registrar(forPlugin: "MaplibrePlugin"))
ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin"))
SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
}

View File

@@ -1,12 +1,20 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.7"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.7.0"
async:
@@ -14,7 +22,7 @@ packages:
description:
name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.13.0"
boolean_selector:
@@ -22,7 +30,7 @@ packages:
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
characters:
@@ -30,7 +38,7 @@ packages:
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
clock:
@@ -38,7 +46,7 @@ packages:
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
collection:
@@ -46,23 +54,63 @@ packages:
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.19.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.7"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.2"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
fl_nodes:
dependency: "direct main"
description:
name: fl_nodes
sha256: "4597284f72369885e8b6bfc5221835a3a6472d9c61769b45c6c64b8f242b2b28"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0+1"
fluent_ui:
dependency: "direct main"
description:
name: fluent_ui
sha256: "64223237a1d873b426d578d4d8b2703b8f7d9731608a5beb7b9745194568d484"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.13.0"
flutter:
@@ -75,15 +123,23 @@ packages:
description:
name: flutter_acrylic
sha256: b3996dbde5abf5823cc9ead4cf2e5267c3181f15585fe47ce4dc4472e7ec827a
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.4"
flutter_context_menu:
dependency: transitive
description:
name: flutter_context_menu
sha256: "79fe00cd7e3ac3840a552cb3e653d8eb61d827b6c04c559e219973a1dc769165"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.0"
flutter_localizations:
@@ -96,33 +152,105 @@ packages:
description:
name: flutter_markdown
sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.7+1"
flutter_shaders:
dependency: transitive
description:
name: flutter_shaders
sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.3"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
geobase:
dependency: transitive
description:
name: geobase
sha256: "3a4e2eb17a7ab452dda78fb45ee498a3ab02469ded749a5f4a9abea21fc95919"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.0"
go_router:
dependency: "direct main"
description:
name: go_router
sha256: eff94d2a6fc79fa8b811dde79c7549808c2346037ee107a1121b4a644c745f2a
url: "https://pub.flutter-io.cn"
source: hosted
version: "17.0.1"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.15.6"
http:
dependency: "direct main"
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.6.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.2"
image:
dependency: transitive
description:
name: image
sha256: "492bd52f6c4fbb6ee41f781ff27765ce5f627910e1e0cbecfa3d9add5562604c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.7.2"
intl:
dependency: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.20.2"
jni:
dependency: transitive
description:
name: jni
sha256: "8706a77e94c76fe9ec9315e18949cc9479cc03af97085ca9c1077b61323ea12d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.15.2"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.2"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.9.0"
leak_tracker:
@@ -130,7 +258,7 @@ packages:
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
@@ -138,7 +266,7 @@ packages:
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.10"
leak_tracker_testing:
@@ -146,7 +274,7 @@ packages:
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
lints:
@@ -154,23 +282,79 @@ packages:
description:
name: lints
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.0"
lists:
dependency: transitive
description:
name: lists
sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.1"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
macos_window_utils:
dependency: transitive
description:
name: macos_window_utils
sha256: d4df3501fd32ac0d2d7590cb6a8e4758337d061c8fa0db816fdd636be63a8438
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.0"
maplibre:
dependency: "direct main"
description:
name: maplibre
sha256: d58260f7163baa05305a8700706325b9f8715a71887091370cab28fc327ef9d7
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.2"
maplibre_gl:
dependency: "direct main"
description:
name: maplibre_gl
sha256: "5c7b1008396b2a321bada7d986ed60f9423406fbc7bd16f7ce91b385dfa054cd"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.22.0"
maplibre_gl_platform_interface:
dependency: "direct main"
description:
name: maplibre_gl_platform_interface
sha256: "08ee0a2d0853ea945a0ab619d52c0c714f43144145cd67478fc6880b52f37509"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.22.0"
maplibre_gl_web:
dependency: "direct main"
description:
name: maplibre_gl_web
sha256: "2b13d4b1955a9a54e38a718f2324e56e4983c080fc6de316f6f4b5458baacb58"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.22.0"
maplibre_ios:
dependency: transitive
description:
name: maplibre_ios
sha256: "8c2c4ebba25039f7a4001f2d369fb187b0583c805b71b695e1d3c96ee3b7ba0a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.2"
markdown:
dependency: transitive
description:
name: markdown
sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "7.3.0"
matcher:
@@ -178,7 +362,7 @@ packages:
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.17"
material_color_utilities:
@@ -186,7 +370,7 @@ packages:
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.11.1"
math_expressions:
@@ -194,7 +378,7 @@ packages:
description:
name: math_expressions
sha256: "218dc65bed4726562bb31c53d8daa3cc824664b26fb72d77bc592757edf74ba0"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.7.0"
meta:
@@ -202,23 +386,47 @@ packages:
description:
name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.17.0"
mgrs_dart:
dependency: transitive
description:
name: mgrs_dart
sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
objective_c:
dependency: transitive
description:
name: objective_c
sha256: "87103d96b19be76dc34b5ef859fecae78561d49779d5480b73c23b4dec8b9ec4"
url: "https://pub.flutter-io.cn"
source: hosted
version: "8.1.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.1"
petitparser:
@@ -226,7 +434,7 @@ packages:
description:
name: petitparser
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.0"
plugin_platform_interface:
@@ -234,23 +442,79 @@ packages:
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.8"
pointer_interceptor:
dependency: transitive
description:
name: pointer_interceptor
sha256: "57210410680379aea8b1b7ed6ae0c3ad349bfd56fe845b8ea934a53344b9d523"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.10.1+2"
pointer_interceptor_ios:
dependency: transitive
description:
name: pointer_interceptor_ios
sha256: "03c5fa5896080963ab4917eeffda8d28c90f22863a496fb5ba13bc10943e40e4"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.10.1+1"
pointer_interceptor_platform_interface:
dependency: transitive
description:
name: pointer_interceptor_platform_interface
sha256: "0597b0560e14354baeb23f8375cd612e8bd4841bf8306ecb71fcd0bb78552506"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.10.0+1"
pointer_interceptor_web:
dependency: transitive
description:
name: pointer_interceptor_web
sha256: "460b600e71de6fcea2b3d5f662c92293c049c4319e27f0829310e5a953b3ee2a"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.10.3"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.0.3"
proj4dart:
dependency: transitive
description:
name: proj4dart
sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
provider:
dependency: "direct main"
description:
name: provider
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.5+1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.0"
screen_retriever:
@@ -258,7 +522,7 @@ packages:
description:
name: screen_retriever
sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_linux:
@@ -266,7 +530,7 @@ packages:
description:
name: screen_retriever_linux
sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_macos:
@@ -274,7 +538,7 @@ packages:
description:
name: screen_retriever_macos
sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_platform_interface:
@@ -282,7 +546,7 @@ packages:
description:
name: screen_retriever_platform_interface
sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
screen_retriever_windows:
@@ -290,7 +554,7 @@ packages:
description:
name: screen_retriever_windows
sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0"
scroll_pos:
@@ -298,7 +562,7 @@ packages:
description:
name: scroll_pos
sha256: cebf602b2dd939de6832bb902ffefb574608d1b84f420b82b381a4007d3c1e1b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
sky_engine:
@@ -311,7 +575,7 @@ packages:
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.1"
stack_trace:
@@ -319,7 +583,7 @@ packages:
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.12.1"
stream_channel:
@@ -327,7 +591,7 @@ packages:
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.4"
string_scanner:
@@ -335,31 +599,31 @@ packages:
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.1"
system_theme:
dependency: "direct main"
description:
name: system_theme
sha256: "5f93485401689601d4636a695f99f7c70a30873ee68c1d95025d908a3386be7e"
url: "https://pub.dev"
sha256: "827cc5f94a6c3097a4c08d4f9eb11d345322f9deea5be1cb73611ff24bdf45bb"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.2"
version: "3.2.0"
system_theme_web:
dependency: transitive
description:
name: system_theme_web
sha256: "900c92c5c050ce58048f241ef9a17e5cd8629808325a05b473dc62a6e99bae77"
url: "https://pub.dev"
sha256: a354b25ff0788ed802b48b632187d344a841b2034f16e4f6cdaa295e4a41a8aa
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.0.3"
version: "0.0.4"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.2"
test_api:
@@ -367,15 +631,103 @@ packages:
description:
name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.7"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0"
unicode:
dependency: transitive
description:
name: unicode
sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
url_launcher:
dependency: transitive
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.28"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.6"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.2"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.5"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.4.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.5"
uuid:
dependency: transitive
description:
name: uuid
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.5.2"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
vm_service:
@@ -383,7 +735,7 @@ packages:
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "15.0.2"
web:
@@ -391,7 +743,7 @@ packages:
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
window_manager:
@@ -399,9 +751,25 @@ packages:
description:
name: window_manager
sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059"
url: "https://pub.dev"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.3"
wkt_parser:
dependency: transitive
description:
name: wkt_parser
sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.5.0"
sdks:
dart: ">=3.10.0 <4.0.0"
flutter: ">=3.32.0"
flutter: ">=3.35.6"

View File

@@ -30,16 +30,23 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter
go_router: ^17.0.0
http: ^1.2.2
maplibre: ^0.3.2
# Fluent UI
fluent_ui: ^4.9.2
system_theme: ^3.1.2
window_manager: ^0.4.3
flutter_acrylic: ^1.1.4
maplibre_gl: ^0.22.0
maplibre_gl_web: ^0.22.0
maplibre_gl_platform_interface: ^0.22.0
# Utilities
flutter_markdown: ^0.7.4+3
provider: ^6.1.2
fl_nodes: ^0.5.0+1
dev_dependencies:
flutter_test:
@@ -64,10 +71,11 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
assets:
- assets/config/
- assets/style.json
- assets/osm_style.json
- assets/pmtiles_style.json
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
@@ -88,7 +96,7 @@ flutter:
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# - fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,

View File

@@ -31,6 +31,11 @@
<title>llm_chat</title>
<link rel="manifest" href="manifest.json">
<!-- MapLibre -->
<script src='https://unpkg.com/maplibre-gl@^5.3.0/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@^5.3.0/dist/maplibre-gl.css' rel='stylesheet'/>
<script src="https://unpkg.com/pmtiles@3.2.0/dist/pmtiles.js"></script>
</head>
<body>
<script src="flutter_bootstrap.js" async></script>

View File

@@ -7,17 +7,23 @@
#include "generated_plugin_registrant.h"
#include <flutter_acrylic/flutter_acrylic_plugin.h>
#include <maplibre/maplibre_plugin_c_api.h>
#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>
#include <system_theme/system_theme_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_manager/window_manager_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterAcrylicPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterAcrylicPlugin"));
MaplibrePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MaplibrePluginCApi"));
ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi"));
SystemThemePluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SystemThemePlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
WindowManagerPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowManagerPlugin"));
}

View File

@@ -4,12 +4,15 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_acrylic
maplibre
screen_retriever_windows
system_theme
url_launcher_windows
window_manager
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)