flutter_dynamic_map 0.1.0 copy "flutter_dynamic_map: ^0.1.0" to clipboard
flutter_dynamic_map: ^0.1.0 copied to clipboard

flutter_map plugins for animated polylines or marker movements.

example/lib/main.dart

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

import 'toggleable_marker/toggleable_marker.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Map Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  @override
  void initState() {
    animatedMapController = AnimatedMapController(vsync: this);
    toggleMarkers = [
      ToggleMarkerWrapper(
        initialPosition: const LatLng(38.93132871414923, -77.07046226186635),
      ),
      ToggleMarkerWrapper(
          initialPosition:
              const LatLng(38.930236019389184, -77.05503322891566)),
      ToggleMarkerWrapper(
          initialPosition: const LatLng(38.94030046698981, -77.03774754300335)),
    ];
    polylineController = StatefulPolylineController(
      navigationHandler: defaultORSNavigationHandler(
          '5b3ce3597851110001cf6248a1a1addc948a48c0b5adf2c4d07589e4'),
    );
    // WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
    //   _centerOnPoints();
    // });
    super.initState();
  }

  late List<ToggleMarkerWrapper> toggleMarkers;

  late StatefulPolylineController<ORSModel> polylineController;

  late final AnimatedMapController animatedMapController;

  void _centerOnPoints() {
    animatedMapController.animatedFitCamera(
      cameraFit: CameraFit.coordinates(
        padding: const EdgeInsets.all(64),
        coordinates: toggleMarkers
                .map((e) => e.animatedMarkerController.currentPosition)
                .toList() +
            (polylineController.currentPolylinePoints ?? []),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: DynamicMap(
        mapOptions: const MapOptions(
            initialCenter: LatLng(38.930236019389184, -77.05503322891566),
            maxZoom: 18.5),
        markers: List.generate(
          toggleMarkers.length,
          (index) => DynamicMapMarker(
            controller: toggleMarkers[index].animatedMarkerController,
            // anchorPos: AnchorPos.align(AnchorAlign.top),
            builder: (context) => ToggleableMarker(
                nameVisibilityNotifier:
                    toggleMarkers[index].nameVisibilityNotifier,
                markerIndex: index),
            width: 200,
            height: 100,
          ),
        ),
        animatedMapController: animatedMapController,
        polylines: [
          StatefulPolyline(
            statefulPolylineController: polylineController,
            initialRoutingPoints: toggleMarkers
                .map((e) => e.animatedMarkerController.currentPosition)
                .toList(),
          ),
        ],
      ),
      floatingActionButton: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          FloatingActionButton.extended(
              onPressed: () async {
                toggleMarkers[0]
                    .animatedMarkerController
                    .moveTo(animatedMapController.mapController.camera.center);
                _centerOnPoints();

                await polylineController.updateRouteFromAPI([
                  toggleMarkers[0].animatedMarkerController.currentPosition,
                  toggleMarkers[1].animatedMarkerController.currentPosition,
                  toggleMarkers[2].animatedMarkerController.currentPosition
                ]);

                _centerOnPoints();
              },
              label: const Text('Center 1')),
          const SizedBox(
            height: 8,
          ),
          FloatingActionButton.extended(
              onPressed: () async {
                toggleMarkers[1]
                    .animatedMarkerController
                    .moveTo(animatedMapController.mapController.camera.center);
                _centerOnPoints();

                await polylineController.updateRouteFromAPI([
                  toggleMarkers[0].animatedMarkerController.currentPosition,
                  toggleMarkers[1].animatedMarkerController.currentPosition,
                  toggleMarkers[2].animatedMarkerController.currentPosition
                ]);
                _centerOnPoints();
              },
              label: const Text('Center 2')),
          const SizedBox(
            height: 8,
          ),
          FloatingActionButton.extended(
              onPressed: () async {
                // await
                toggleMarkers[2].animatedMarkerController.moveTo(
                      animatedMapController.mapController.camera.center,
                      // focus: true
                    );
                _centerOnPoints();

                await polylineController.updateRouteFromAPI([
                  toggleMarkers[0].animatedMarkerController.currentPosition,
                  toggleMarkers[1].animatedMarkerController.currentPosition,
                  toggleMarkers[2].animatedMarkerController.currentPosition,
                ]);
                _centerOnPoints();
              },
              label: const Text('Center 3')),
          const SizedBox(
            height: 8,
          ),
          FloatingActionButton.extended(
              onPressed: () async {
                for (final element in toggleMarkers) {
                  element.changeTooltipVisibility();
                }
              },
              label: const Text('Labels')),
          const SizedBox(
            height: 8,
          ),
          FloatingActionButton.extended(
              onPressed: () async {
                _centerOnPoints();
              },
              label: const Text('Center Map')),
          const SizedBox(
            height: 8,
          ),
          StreamBuilder<ORSModel?>(
            stream: polylineController.dataStream,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return _RoutingFAB(
                  data: snapshot.data!,
                  key: ValueKey(snapshot.data!),
                );
              }
              if (snapshot.hasError) {
                return FloatingActionButton.extended(
                  onPressed: () {
                    showDialog(
                        context: context,
                        builder: (_) => AlertDialog(
                              title: const Text('Error'),
                              content: Text(snapshot.error.toString()),
                              actions: [
                                TextButton(
                                  child: const Text('Close'),
                                  onPressed: () {
                                    Navigator.of(context).pop();
                                  },
                                ),
                              ],
                            ));
                  },
                  label: const Text('Routing Error'),
                  backgroundColor: Colors.red,
                );
              } else {
                return Container(
                  width: 0,
                );
              }
            },
          ),
        ],
      ),
    );
  }
}

class _RoutingFAB extends StatefulWidget {
  const _RoutingFAB({Key? key, required this.data}) : super(key: key);
  final ORSModel data;

  @override
  State<_RoutingFAB> createState() => _RoutingFABState();
}

class _RoutingFABState extends State<_RoutingFAB>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<Color?> animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 750),
      vsync: this,
    );
    animation = ColorTween(
      begin: Colors.green,
      end: Colors.blue,
    ).animate(_controller)
      ..addListener(() {
        setState(() {});
      });
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton.extended(
        backgroundColor: animation.value,
        onPressed: () async {
          showModalBottomSheet(
              context: context,
              isScrollControlled: true,
              shape: const RoundedRectangleBorder(
                  borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(15),
                      topRight: Radius.circular(15))),
              builder: (_) {
                final duration = Duration(
                    seconds: widget.data.routes!.first.summary!.duration! ~/ 1);
                return SingleChildScrollView(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    children: [
                      Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Text(
                          '${duration.inHours}h ${duration.inMinutes % 60}m ETA',
                          style: const TextStyle(
                              fontWeight: FontWeight.bold, fontSize: 26),
                        ),
                      ),
                      const Divider(
                        height: 16,
                      ),
                      ...List.generate(
                          widget.data.routes!.first.segments!.length, (index) {
                        final segment =
                            widget.data.routes!.first.segments![index];
                        return _Segment(segment: segment);
                      })
                    ],
                  ),
                );
              });
        },
        label: const Text('Routing Info'));
  }
}

class _Segment extends StatelessWidget {
  const _Segment({Key? key, required this.segment}) : super(key: key);
  final Segments segment;

  @override
  Widget build(BuildContext context) {
    final duration = Duration(seconds: segment.duration! ~/ 1);
    return Column(
      children: [
        const Divider(
          thickness: 2,
          height: 16,
        ),
        Text(
          '${duration.inHours}h ${duration.inMinutes % 60}m ETA, ${segment.distance} meters',
          style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
        ),
        const Divider(
          thickness: 1,
        ),
        ...List.generate(segment.steps!.length, (index) {
          final item = segment.steps![index];
          return ListTile(
            title: Text(item.instruction!),
            subtitle: Text(item.name!),
            leading: Text(
              '${item.distance}\nmeters',
              textAlign: TextAlign.center,
            ),
            trailing: Text(
              '${((item.duration ?? 0) / 60).toStringAsFixed(2)}\nminutes',
              textAlign: TextAlign.center,
            ),
          );
        })
      ],
    );
  }
}

class ToggleMarkerWrapper {
  ToggleMarkerWrapper({required LatLng initialPosition})
      : animatedMarkerController =
            AnimatedMarkerController(initialPosition: initialPosition);
  final AnimatedMarkerController animatedMarkerController;
  final ValueNotifier<bool> nameVisibilityNotifier = ValueNotifier<bool>(false);

  bool changeTooltipVisibility() {
    return nameVisibilityNotifier.value = !nameVisibilityNotifier.value;
  }
}
0
likes
150
points
26
downloads

Publisher

unverified uploader

Weekly Downloads

flutter_map plugins for animated polylines or marker movements.

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_map, flutter_map_animations, flutter_map_location_marker, http, latlong2, synchronized

More

Packages that depend on flutter_dynamic_map