Flutter地图动画插件flutter_map_animations的使用
Flutter地图动画插件flutter_map_animations的使用
简介
flutter_map_animations
是为 flutter_map 包提供的动画工具,可以实现流畅的地图动画效果。你可以通过它来创建平滑的缩放、旋转和移动动画。
安装
在 pubspec.yaml
文件中添加依赖:
dependencies:
flutter_map: ^6.0.0
flutter_map_animations: ^0.5.0
然后运行 flutter pub get
来安装包。
使用示例
示例代码
以下是一个完整的示例,展示了如何使用 flutter_map_animations
实现各种动画效果:
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:flutter_map_cancellable_tile_provider/flutter_map_cancellable_tile_provider.dart';
import 'package:latlong2/latlong.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
static const _useTransformerId = 'useTransformerId';
final markers = ValueNotifier<List<AnimatedMarker>>([]);
final center = const LatLng(51.509364, -0.128928);
bool _useTransformer = true;
int _lastMovedToMarkerIndex = -1;
late final _animatedMapController = AnimatedMapController(vsync: this);
@override
void dispose() {
markers.dispose();
_animatedMapController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter Map Animations')),
body: ValueListenableBuilder<List<AnimatedMarker>>(
valueListenable: markers,
builder: (context, markers, _) {
return FlutterMap(
mapController: _animatedMapController.mapController,
options: MapOptions(
initialCenter: center,
onTap: (_, point) => _addMarker(point),
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.example.app',
tileUpdateTransformer: _animatedMoveTileUpdateTransformer,
tileProvider: CancellableNetworkTileProvider(),
),
AnimatedMarkerLayer(markers: markers),
],
);
},
),
floatingActionButton: SeparatedColumn(
separator: const SizedBox(height: 8),
children: [
FloatingActionButton(
onPressed: () => _animatedMapController.animatedRotateFrom(
90,
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Rotate 90°',
child: const Icon(Icons.rotate_right),
),
FloatingActionButton(
onPressed: () => _animatedMapController.animatedRotateFrom(
-90,
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Rotate -90°',
child: const Icon(Icons.rotate_left),
),
FloatingActionButton(
onPressed: () {
markers.value = [];
_animatedMapController.animateTo(
dest: center,
rotation: 0,
customId: _useTransformer ? _useTransformerId : null,
);
},
tooltip: 'Clear modifications',
child: const Icon(Icons.clear_all),
),
FloatingActionButton(
onPressed: () => _animatedMapController.animatedZoomIn(
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Zoom in',
child: const Icon(Icons.zoom_in),
),
FloatingActionButton(
onPressed: () => _animatedMapController.animatedZoomOut(
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Zoom out',
child: const Icon(Icons.zoom_out),
),
FloatingActionButton(
tooltip: 'Center on markers',
onPressed: () {
if (markers.value.length < 2) return;
final points = markers.value.map((m) => m.point).toList();
_animatedMapController.animatedFitCamera(
cameraFit: CameraFit.coordinates(
coordinates: points,
padding: const EdgeInsets.all(12),
),
rotation: 0,
customId: _useTransformer ? _useTransformerId : null,
);
},
child: const Icon(Icons.center_focus_strong),
),
Row(
children: [
FloatingActionButton(
tooltip: 'Move to next marker with offset',
onPressed: () {
if (markers.value.isEmpty) return;
final points = markers.value.map((m) => m.point);
setState(
() => _lastMovedToMarkerIndex =
(_lastMovedToMarkerIndex + 1) % points.length,
);
_animatedMapController.animateTo(
dest: points.elementAt(_lastMovedToMarkerIndex),
customId: _useTransformer ? _useTransformerId : null,
offset: const Offset(100, 100),
);
},
child: const Icon(Icons.multiple_stop),
),
const SizedBox(width: 8),
FloatingActionButton(
tooltip: 'Move to next marker',
onPressed: () {
if (markers.value.isEmpty) return;
final points = markers.value.map((m) => m.point);
setState(
() => _lastMovedToMarkerIndex =
(_lastMovedToMarkerIndex + 1) % points.length,
);
_animatedMapController.animateTo(
dest: points.elementAt(_lastMovedToMarkerIndex),
customId: _useTransformer ? _useTransformerId : null,
);
},
child: const Icon(Icons.polyline_rounded),
),
],
),
FloatingActionButton.extended(
label: Row(
children: [
const Text('Transformer'),
Switch(
activeColor: Colors.blue.shade200,
activeTrackColor: Colors.black38,
value: _useTransformer,
onChanged: (newValue) {
setState(() => _useTransformer = newValue);
},
),
],
),
onPressed: () {
setState(() => _useTransformer = !_useTransformer);
},
),
],
),
);
}
void _addMarker(LatLng point) {
markers.value = List.from(markers.value)
..add(
MyMarker(
point: point,
onTap: () => _animatedMapController.animateTo(
dest: point,
customId: _useTransformer ? _useTransformerId : null,
),
),
);
}
}
class MyMarker extends AnimatedMarker {
MyMarker({
required super.point,
VoidCallback? onTap,
}) : super(
width: markerSize,
height: markerSize,
builder: (context, animation) {
final size = markerSize * animation.value;
return GestureDetector(
onTap: onTap,
child: Opacity(
opacity: animation.value,
child: Icon(
Icons.room,
size: size,
),
),
);
},
);
static const markerSize = 50.0;
}
class SeparatedColumn extends StatelessWidget {
const SeparatedColumn({
super.key,
required this.separator,
this.children = const [],
this.mainAxisSize = MainAxisSize.max,
this.crossAxisAlignment = CrossAxisAlignment.start,
});
final Widget separator;
final List<Widget> children;
final MainAxisSize mainAxisSize;
final CrossAxisAlignment crossAxisAlignment;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
children: [
..._buildChildren(),
],
);
}
Iterable<Widget> _buildChildren() sync* {
for (var i = 0; i < children.length; i++) {
yield children[i];
if (i < children.length - 1) yield separator;
}
}
}
final _animatedMoveTileUpdateTransformer = TileUpdateTransformer.fromHandlers(
handleData: (updateEvent, sink) {
final id = AnimationId.fromMapEvent(updateEvent.mapEvent);
if (id == null) return sink.add(updateEvent);
if (id.customId != _MyHomePageState._useTransformerId) {
if (id.moveId == AnimatedMoveId.started) {
debugPrint('TileUpdateTransformer disabled, using default behaviour.');
}
return sink.add(updateEvent);
}
switch (id.moveId) {
case AnimatedMoveId.started:
debugPrint('Loading tiles at animation destination.');
sink.add(
updateEvent.loadOnly(
loadCenterOverride: id.destLocation,
loadZoomOverride: id.destZoom,
),
);
break;
case AnimatedMoveId.inProgress:
// Do not prune or load during movement.
break;
case AnimatedMoveId.finished:
debugPrint('Pruning tiles after animated movement.');
sink.add(updateEvent.pruneOnly());
break;
}
},
);
主要功能
AnimatedMapController
- 初始化:创建
AnimatedMapController
并设置vsync
参数。 - 全局配置:可以通过
duration
、curve
和cancelPreviousAnimations
来配置动画的行为。 - 方法:
animateTo
:移动到指定位置。animatedZoomIn
和animatedZoomOut
:缩放。animatedRotateFrom
:旋转。animatedFitCamera
:适应多个标记点。
AnimatedMarkerLayer & AnimatedMarker
- 动画标记层:用于在地图上显示带有动画效果的标记。
- 示例:通过
AnimatedMarker
创建一个标记,并在点击时触发动画。
迁移指南
- v0.5.0:移除了一些参数,如
AnimatedMarker.rotateOrigin
和AnimatedMarker.anchorPos
,并重命名了部分参数。 - v0.4.0:不再允许直接扩展
MapControllerImpl
,而是通过内部创建或传递MapController
实例。
贡献者
感谢以下贡献者的努力:
- Guillaume Roux
- Luka S
- Rory Stephenson
- Reinis Sprogis
希望这个示例能帮助你更好地理解和使用 flutter_map_animations
插件。如果有任何问题或需要进一步的帮助,请随时提问!
更多关于Flutter地图动画插件flutter_map_animations的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter地图动画插件flutter_map_animations的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_map_animations
插件来实现地图动画效果的代码案例。flutter_map_animations
插件允许你在Flutter应用中为flutter_map
添加动画效果。
首先,确保你已经在pubspec.yaml
文件中添加了必要的依赖项:
dependencies:
flutter:
sdk: flutter
flutter_map: ^10.2.0 # 确保版本与flutter_map_animations兼容
flutter_map_animations: ^0.4.0 # 请检查最新版本
然后运行flutter pub get
来安装这些依赖项。
接下来,在你的Flutter应用中,你可以按照以下步骤来添加地图动画效果:
- 导入必要的包:
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:latlong2/latlong.dart';
- 定义地图的中心点和缩放级别:
final LatLng center = LatLng(51.5, -0.09);
final double zoom = 13.0;
- 创建地图和动画:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Map Animations Demo'),
),
body: MapWithAnimations(),
),
);
}
}
class MapWithAnimations extends StatefulWidget {
@override
_MapWithAnimationsState createState() => _MapWithAnimationsState();
}
class _MapWithAnimationsState extends State<MapWithAnimations> {
MapController? mapController;
@override
Widget build(BuildContext context) {
return FlutterMap(
mapController: mapController,
options: MapOptions(
center: center,
zoom: zoom,
),
layers: [
TileLayerOptions(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c'],
),
MarkerLayerOptions(
markers: [
Marker(
point: LatLng(51.5, -0.09),
builder: (ctx) => Container(
child: Icon(Icons.location_on, color: Colors.red,),
),
),
// 你可以添加更多的Marker
],
),
AnimatedMarkerLayerOptions(
animatedMarkers: [
AnimatedMarker(
id: 'animatedMarker',
initialPosition: LatLng(51.5, -0.09),
finalPosition: LatLng(51.51, -0.11),
duration: Duration(seconds: 5),
curve: Curves.easeInOutQuad,
builder: (ctx, position) => Container(
child: Icon(Icons.directions_car, color: Colors.blue,),
alignment: Alignment.center,
),
),
],
),
],
onMapCreated: (controller) {
setState(() {
mapController = controller;
});
},
);
}
}
在这个示例中,我们:
- 使用
FlutterMap
来创建地图。 - 添加了一个静态的
Marker
。 - 使用
AnimatedMarkerLayerOptions
添加了一个动画标记(AnimatedMarker
),这个标记会从初始位置移动到最终位置,并在移动过程中应用动画效果。
你可以根据需要调整动画的id
、initialPosition
、finalPosition
、duration
和curve
等参数,以实现不同的动画效果。
希望这能帮助你开始在Flutter项目中使用flutter_map_animations
插件!