canvas_map 0.0.2 canvas_map: ^0.0.2 copied to clipboard
High performance 2d map for flutter that render with canvas, provide smooth scrolling and zooming with pinch/double tap support.
import 'dart:ui';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:canvas_map/canvas_map.dart';
import 'package:flutter/material.dart' hide Image;
import 'package:universal_platform/universal_platform.dart';
import 'api.dart';
void main() {
runApp(const App());
}
class ScrollBehavior extends MaterialScrollBehavior {
const ScrollBehavior();
@override
Set<PointerDeviceKind> get dragDevices => PointerDeviceKind.values.toSet();
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
scrollBehavior: const ScrollBehavior(),
theme: ThemeData(
useMaterial3: true,
fontFamily: UniversalPlatform.isWindows ? 'Microsoft YaHei' : null,
),
darkTheme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
fontFamily: UniversalPlatform.isWindows ? 'Microsoft YaHei' : null,
),
home: const Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late final CanvasMapController controller;
final icons = <String, dynamic>{};
final images = <String, Image>{};
var markers = <String, List>{};
@override
void initState() {
super.initState();
api.fetchIcons().then((icons) async {
this.icons.addAll(icons);
markers = await api.fetchMarkers(
areaIdList: [6, 17, 2, 3, 12, 13, 14, 19, 21, 22, 23, 28],
typeIdList: [5],
);
setState(() {});
});
}
void onTap(TapDetails details) {
final data = details.data;
if (data is MarkerItem) {
final marker = data.data;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(marker['markerTitle']),
content: SingleChildScrollView(
child: Column(mainAxisSize: MainAxisSize.min, children: [
if (marker['content']?.isNotEmpty ?? false)
Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(bottom: 16),
child: Text(marker['content'].replaceAll('\\n', '\n')),
),
if (marker['picture']?.isNotEmpty ?? false)
CachedNetworkImage(
imageUrl: marker['picture'],
fit: BoxFit.fitWidth,
),
]),
),
);
},
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CanvasMap(
size: const Size(18432, 18432),
origin: const Offset(3568 + 6144, 6969 + 2048),
maxZoom: 1,
onTap: onTap,
layers: [
TileLayer(
getImageProvider: (x, y, z) => CachedNetworkImageProvider(
'https://assets.yuanshen.site/tiles_twt411/$z/${x}_$y.png',
),
minZoom: 10,
maxZoom: 13,
offset: const Offset(-6144, -2048),
),
...markers.keys.map((icon) {
final items = markers[icon] ?? [];
return AreaItemLayer(icon: icons[icon]['url'], items: items);
}),
],
),
);
}
}
class AreaItemLayer extends StatefulWidget {
final String icon;
final List items;
const AreaItemLayer({super.key, required this.icon, this.items = const []});
@override
State<AreaItemLayer> createState() => _AreaItemLayerState();
}
class _AreaItemLayerState extends State<AreaItemLayer> {
Image? image;
@override
void initState() {
super.initState();
queue.run(() async {
final image = await resolveImage(CachedNetworkImageProvider(widget.icon));
setState(() {
this.image = image;
});
});
}
@override
Widget build(BuildContext context) {
if (image == null) return const SizedBox();
return MarkerLayer(
items: [
...widget.items.map((i) => MarkerItem(Offset(i['x'], i['y']), i))
],
child: Padding(
padding: const EdgeInsets.all(2),
child: Container(
decoration: const BoxDecoration(
border: Border.fromBorderSide(
BorderSide(color: Colors.white),
),
borderRadius: BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(blurRadius: 2),
BoxShadow(blurRadius: 2, blurStyle: BlurStyle.inner),
],
),
child: RawImage(image: image, width: 20, height: 20),
),
),
);
}
}
class TaskQueue {
final tasks = <Future<void> Function()>[];
var running = false;
void run(Future<void> Function() task) async {
tasks.add(task);
if (running) return;
running = true;
while (tasks.isNotEmpty) {
await tasks.removeLast()();
}
running = false;
}
}
final queue = TaskQueue();