Giter VIP home page Giter VIP logo

mapbox-maps-flutter's Introduction

Mapbox Maps SDK Flutter Plugin

The Mapbox Maps SDK Flutter Plugin is an officially developed solution from Mapbox that enables use of our latest Maps SDK product (v11.2.0). The plugin allows developers to embed highly customized maps using a Flutter widget on Android and iOS.

Web and desktop are not supported.

Contributions welcome!

Supported API

Feature Android iOS
Style
Camera position
Camera animations
Events
Gestures
User Location
Circle Layer
Fill Layer
Fill extrusion Layer
Line Layer
Circle Layer
Raster Layer
Symbol Layer
Hillshade Layer
Heatmap Layer
Sky Layer
GeoJson Source
Image Source
Vector Source
Raster Source
Rasterdem Source
Circle Annotations
Point Annotations
Line Annotations
Fill Annotations
Offline
Viewport
Style DSL
Expression DSL
View Annotations

Requirements

The Maps Flutter Plugin is compatible with applications:

  • Deployed on iOS 12 or higher
  • Built using the Android SDK 21 or higher
  • Built using the Dart SDK 3.0.0 or higher

Installation

Configure credentials

To run the Maps Flutter Plugin you will need to configure the Mapbox Access Tokens. Read more about access tokens and public/secret scopes at the platform Android or iOS docs.

Secret token

To access platform SDKs you will need to create a secret access token with the Downloads:Read scope and then:

  • to download the Android SDK add the token configuration to ~/.gradle/gradle.properties :
  SDK_REGISTRY_TOKEN=YOUR_SECRET_MAPBOX_ACCESS_TOKEN
  • to download the iOS SDK add the token configuration to ~/.netrc :
  machine api.mapbox.com
  login mapbox
  password YOUR_SECRET_MAPBOX_ACCESS_TOKEN

Public token

You can set the access token for Mapbox Maps Flutter Plugin(as well as for evey Mapbox SDK) via MapboxOptions:

  MapboxOptions.setAccessToken(ACCESS_TOKEN);

It's a good practice to retrieve access tokens from some external source.

You can pass access token via the command line arguments when either building :

flutter build <platform> --dart-define PUBLIC_ACCESS_TOKEN=...

or running the application :

flutter run --dart-define PUBLIC_ACCESS_TOKEN=...

You can also persist token in launch.json :

"configurations": [
    {
        ...
        "args": [
            "--dart-define", "PUBLIC_ACCESS_TOKEN=..."
        ],
    }
]

Then to retrieve the token from the environment in the application :

String ACCESS_TOKEN = String.fromEnvironment("ACCESS_TOKEN");

Add the dependency

To use the Maps Flutter Plugin add the git dependency to the pubspec.yaml:

dependencies:
  mapbox_maps_flutter: ^1.1.0

Configure permissions

You will need to grant location permission in order to use the location component of the Maps Flutter Plugin.

You can use an existing library to request location permission, e.g. with permission_handler await Permission.locationWhenInUse.request(); will trigger permission request.

You also need to declare the permission for both platforms :

Android

Add the following permissions to the manifest:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS

Add the following to the Runner/Info.plist to explain why you need access to the location data:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>[Your explanation here]</string>

Add a map

Import mapbox_maps_flutter library and add a simple map:

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

void main() {
  runApp(MaterialApp(home: MapWidget()));
}

MapWidget widget

The MapWidget widget provides options to customize the map - you can set MapOptions, CameraOptions, styleURL etc.

It also allows or add listeners for various events - related to style loading, map rendering, map loading.

MapboxMap controller

The MapboxMap controller instance is provided with MapWidget.onMapCreated callback.

MapboxMap exposes an entry point to the most of the APIs Maps Flutter Plugin provides. It allows to control the map, camera, styles, observe map events, query rendered features, etc.

It's organized similarly to the Android and iOS counterparts.

To interact with the map after it's created store the MapboxMap object somewhere :

class FullMap extends StatefulWidget {
  const FullMap();

  @override
  State createState() => FullMapState();
}

class FullMapState extends State<FullMap> {
  MapboxMap? mapboxMap;

  _onMapCreated(MapboxMap mapboxMap) {
    this.mapboxMap = mapboxMap;
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: MapWidget(
      key: ValueKey("mapWidget"),
      onMapCreated: _onMapCreated,
    ));
  }
}

User location

Platform docs : Android, iOS.

To observe the user's location and show the location indicator on the map use LocationComponentSettingsInterface accessible via MapboxMap.location.

You need to grant location permission prior to using location component (as explained before).

Location puck

To customize the appearance of the location puck call MapboxMap.location.updateSettings method.

To use the 3D puck with model downloaded from Uri instead of the default 2D puck :

  mapboxMap.location.updateSettings(LocationComponentSettings(
      locationPuck: LocationPuck(
          locationPuck3D: LocationPuck3D(
              modelUri:
                  "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Embedded/Duck.gltf",))));

You can find more examples of customization in the sample app.

Markers and annotations

Platform docs : Android, iOS.

You have several options to add annotations on the map.

  1. Use the AnnotationManager APIs to create circle/point/polygon/polyline annotations.

To create 5 point annotations using custom icon:

  mapboxMap.annotations.createPointAnnotationManager().then((pointAnnotationManager) async {
    final ByteData bytes =
        await rootBundle.load('assets/symbols/custom-icon.png');
    final Uint8List list = bytes.buffer.asUint8List();
    var options = <PointAnnotationOptions>[];
    for (var i = 0; i < 5; i++) {
      options.add(PointAnnotationOptions(
          geometry: createRandomPoint().toJson(), image: list));
    }
    pointAnnotationManager?.createMulti(options);
  });

You can find more examples of the AnnotationManagers usage in the sample app : point annotations, circle annotations, polygon annotations, polyline annotations.

  1. Use style layers. This will require writing more code but is more flexible and provides better performance for the large amount of annotations (e.g. hundreds and thousands of them). More about adding style layers in the Map styles section.

Map styles

Platform docs : Android, iOS.

The Mapbox Maps Flutter Plugin allows full customization of the look of the map used in your application.

Set a style

You can specify the initial style uri at MapWidget.styleUri, or load it at runtime using MapboxMap.loadStyleURI / MapboxMap.loadStyleJson :

  mapboxMap.loadStyleURI(Styles.LIGHT);

Work with layers

You can familiarize with the concept of sources, layers and their supported types in the platform documentation.

To add, remove or change a source or a layer use the MapboxMap.style object.

To add a GeoJsonSource and a LineLayer using the source :

  var data = await rootBundle.loadString('assets/polyline.geojson');
  await mapboxMap.style.addSource(GeoJsonSource(id: "line", data: data));
  await mapboxMap.style.addLayer(LineLayer(
      id: "line_layer",
      sourceId: "line",
      lineJoin: LineJoin.ROUND,
      lineCap: LineCap.ROUND,
      lineOpacity: 0.7,
      lineColor: Colors.red.value,
      lineWidth: 8.0));

Using expressions

You can change the appearance of a layer based on properties in the layer's data source or zoom level. Refer to the documentation for the description of supported expressions. To apply an expression to interpolate gradient color to a line layer:

  mapboxMap.style.setStyleLayerProperty("layer", "line-gradient",
      '["interpolate",["linear"],["line-progress"],0.0,["rgb",6,1,255],0.5,["rgb",0,255,42],0.7,["rgb",255,252,0],1.0,["rgb",255,30,0]]');

Camera and animations

Platform docs : Android, iOS. The camera is the user's viewpoint above the map. The Maps Flutter Plugin provides you with options to set and adjust the camera position, listen for camera changes, get the camera position, and restrict the camera position to set bounds.

Camera position

You can set the starting camera position using MapWidget.cameraOptions :

MapWidget(
  key: ValueKey("mapWidget"),
  cameraOptions: CameraOptions(
      center: Point(coordinates: Position(-80.1263, 25.7845)).toJson(),
      zoom: 12.0),
));

or update it at runtime using MapboxMap.setCamera :

MapboxMap.setCamera(CameraOptions(
  center: Point(coordinates: Position(-80.1263, 25.7845)).toJson(),
  zoom: 12.0));

You can find more examples of interaction with the camera in the sample app.

Camera animations

Camera animations are the means by which camera settings are changed from old values to new values over a period of time. You can animate the camera using flyTo or easeTo and move to a new center location, update the bearing, pitch, zoom, padding, and anchor.

To start a flyTo animation to the specific camera options :

  mapboxMap?.flyTo(
    CameraOptions(
        anchor: ScreenCoordinate(x: 0, y: 0),
        zoom: 17,
        bearing: 180,
        pitch: 30),
    MapAnimationOptions(duration: 2000, startDelay: 0));

You can find more examples of animations in the sample app.

User interaction

Platform docs : Android, iOS.

Users interacting with the map in your application can explore the map by performing standard gestures.

You can retrieve or update the GestureSettings using MapboxMap.gestures.

You can observe gesture events using MapWidget.onTapListener, MapWidget.onLongTapListener, MapWidget.onScrollListener.

mapbox-maps-flutter's People

Contributors

dependabot[bot] avatar evil159 avatar ljubitech-maxko avatar maios avatar pjleonard37 avatar rennanagrosmart avatar sbma44 avatar thibaudlopez avatar yunikkk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mapbox-maps-flutter's Issues

onMapCreated is called when app is switching to background

When putting the application in the background and switch to another app, onMapCreated is called. Indeed it is called twice...

  _onMapCreated(MapboxMap mapboxMap) async {
    print("onMapCreated");
}

EDIT:

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

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MapView(),
    );
  }
}

class MapView extends StatefulWidget {
  const MapView({super.key});

  @override
  State<MapView> createState() => _MapViewState();
}

class _MapViewState extends State<MapView> {
  
  _onMapCreated(MapboxMap mapboxMap) async {
    print("onMapCreated");
  }

  @override
  Widget build(BuildContext context) {
    return MapWidget(
      cameraOptions: CameraOptions(
          padding: MbxEdgeInsets(
              bottom: (MediaQuery.of(context).size.height -
                      kBottomNavigationBarHeight -
                      MediaQuery.of(context).padding.top) *
                  0.15,
              left: 0,
              right: 0,
              top: 0),
          zoom: 4,
          center: {
            "coordinates": [9.142084916929944, 49.97744579860142]
          }),
      resourceOptions: ResourceOptions(
          accessToken:
              "XXX"),
      onMapCreated: _onMapCreated,
    );
  }
}

Programmatically opt-out of sending telemetry data

Is it possible to opt-out of sending telemetry data via code, e.g. to include this setting in the app settings? Currently the only way I'm finding is via the attribution button.

If it is not currently possible, are you planning to provide that option? When reading about telemetry on the Mapbox-Website it is being stated often that this should also be possible via settings in the app aside from the attribution button?

The Mapbox community Flutter plugin (originally the official predecessor of this plugin) has this option: https://pub.dev/documentation/mapbox_gl/latest/mapbox_gl/MapboxMapController/setTelemetryEnabled.html

It also looks like both the native SDKs support the option to opt-out programmatically:

How to update the source when it's already in the map?

How can I update the source when it's already in the map? Do I have to remove it first? I wrote such a method:

  Future<void> _removeSourceIfExists(String sourceId) async {
    if (!mounted) return;
    if (await mapController.style.styleSourceExists(sourceId)) {
      await mapController.style.removeStyleSource(sourceId);
    }
  }

  Future<void> _addSource(String sourceId, Map<String, dynamic> geojson) async {
    await mapController.style
        .addSource(GeoJsonSource(id: sourceId, data: json.encode(geojson)));
  }

But when removing a source, I get the error :

PlatformException (PlatformException(Throwable, java.lang.Throwable: Source 'markerSource' is in
use, cannot remove, Cause: null, Stacktrace: java.lang.Throwable: Source 'markerSource' is in use, 
cannot remove

I also tried just adding the updated source, without removing it beforehand, but then I get the following error:

PlatformException (PlatformException(Throwable, java.lang.Throwable: Source markerSource already exists, 
Cause: null, Stacktrace: java.lang.Throwable: Source markerSource already exists
at com.mapbox.maps.mapbox_maps.StyleController.addStyleSource$lambda-21(StyleController.kt:256)

MapboxMap.style.getSource(id) returns null

Why is this printing null to the console?

  await mapboxMap?.style.addStyleSource(
          "chargers",
          json.encode({
            "type": "geojson",
            "data": {
              "type": "FeatureCollection",
              "features": [
                {
                  "id": "39169",
                  "type": "Feature",
                  "properties": {
                    "fastCharger": false,
                    "powerKW": "22 KW",
                    "name": "Rathaus Altona"
                  },
                  "geometry": {
                    "type": "Point",
                    "coordinates": [9.93550387483399, 53.5464387674623]
                  }
                },
              ]
            },
            "cluster": true,
            "clusterMaxZoom": 14,
            "clusterRadius": 50
          }));

      final GeoJsonSource source =
          await mapboxMap.?.style.getSource("chargers") as GeoJsonSource;

      print(await source.data); ==> null

Can I set a min/max zoom for the camera?

I want to have a limit for the camera zoom, something like maxZoom = 20 and minZoom = 7.

So far I've forced this behaviour in the onCameraChangeListener event by doing this:

 await mapboxMap!.getCameraState().then((value) {
            if (zoom >= minZoom) {
              zoom = value.zoom;
            } else {
              zoom = minZoom;
              mapboxMap!.setCamera(CameraOptions(zoom: minZoom));
            }
          });

But it doesn't feel "clean", because every time I try to go over the threshold, the map starts to "vibrate". Is there a better way to do this? Or am I missing something?

Layer bad decoding of expression properties

Using mapboxMap.style.getLayer() will throw exception if layer properties are using expressions. In the sample code bellow, which is using default mapbox light style, text-size property is using the following expression, which is List<dynamic> when decoded, but double is expected.

"text-size": [
    "step",
    ["zoom"],
    ["step", ["get", "sizerank"], 18, 5, 12],
    17,
    ["step", ["get", "sizerank"], 18, 13, 12]
],

Reproduction code:

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late final MapboxMap _map;

  void _mapCreated(MapboxMap map) {
    _map = map;
  }
  
  void _onStyleLoaded(StyleLoadedEventData event) async {
    await _map.style.getLayer('natural-point-label');
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Get Symbol Layer Exception',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: MapWidget(
          resourceOptions: ResourceOptions(accessToken: <add access token>),
          styleUri: MapboxStyles.LIGHT,
          onMapCreated: _mapCreated,
          onStyleLoadedListener: _onStyleLoaded,
        ),
      ),
    );
  }
}

When running this code the following exception is thrown:

E/flutter (22989): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: NoSuchMethodError: Class 'List<dynamic>' has no instance method 'toDouble'.
E/flutter (22989): Receiver: Instance(length:5) of '_GrowableList'
E/flutter (22989): Tried calling: toDouble()
E/flutter (22989): #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
E/flutter (22989): #1      SymbolLayer.decode (package:mapbox_maps_flutter/src/style/layer/symbol_layer.dart:580:45)
E/flutter (22989): #2      StyleLayer.getLayer (package:mapbox_maps_flutter/src/style/style.dart:217:29)
E/flutter (22989): <asynchronous suspension>
E/flutter (22989): #3      _MyAppState._onStyleLoaded (package:mapbox_get_layer_exception/main.dart:23:5)
E/flutter (22989): <asynchronous suspension>
E/flutter (22989): 

I think this is a major issue because we can add our own layers which are using expressions but can't retrieve the same layer later.

Timeline for offline support

First of all thanks for getting this out and making the source code public!

Whats the timeline for offline support? We would need that feature before we can migrate.

Filter the data of layer

We want to show a selection on the map. With Mapbox GL JS we had different strategies to achieve this. Filter the source data or update the source to represent the selected data.

I don't see how we can represent a selection.

See this example with the filter strategy:
https://docs.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/

See this example for the setData method on the source. This could be usefull for multiple scenario like show realtime data
https://docs.mapbox.com/mapbox-gl-js/example/live-update-feature/
map.getSource('trace').setData(data);

Always get exception "java.lang.Throwable: Source line_source_view_info is not in style"

Hello.
I got some problems. So I want to share it.
I have 2 screens. They are both using MapWidget. In 1st screen, I created a line layer and source [line_source_view_info] for that layer. It worked.
When I go to the 2nd screen, it throwed this exception "java.lang.Throwable: Source line_source_view_info is not in style". I think that source data is not removed/disposed in the right way.
So could you please take a look. Thank you

Only last map built is usable when there are multiple instances

When creating multiple map instances all map controllers are working with the data from the map which was created last. The issue can be observed with the following reproduction code.

import 'package:flutter/material.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.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> {
  late final PageController _pageController;

  void _switchPage(int page) {
    _pageController.jumpToPage(_page);
  }

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Mapbox Example')),
        body: Stack(
          children: [
            PageView(
              controller: _pageController,
              physics: const NeverScrollableScrollPhysics(),
              children: const [
                MapPage(),
                MapPage(),
                MapPage(),
              ],
            ),
            Align(
              alignment: Alignment.bottomCenter,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  PageSwitch(page: 0, bgColor: Colors.red, onTap: _switchPage),
                  PageSwitch(page: 1, bgColor: Colors.green, onTap: _switchPage),
                  PageSwitch(page: 2, bgColor: Colors.blue, onTap: _switchPage),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class MapPage extends StatefulWidget {
  const MapPage({Key? key}) : super(key: key);

  @override
  State<MapPage> createState() => _MapPageState();
}

class _MapPageState extends State<MapPage> with AutomaticKeepAliveClientMixin {
  late final MapboxMap _map;

  void _mapCreated(MapboxMap map) {
    _map = map;
  }

  void _getCameraState() async {
    final CameraState camState = await _map.getCameraState();
    print(camState.encode());
  }

  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Stack(
      clipBehavior: Clip.none,
      fit: StackFit.expand,
      children: [
        MapWidget(
          resourceOptions: ResourceOptions(accessToken: <add access token>),
          onMapCreated: _mapCreated,
        ),
        Align(
          alignment: Alignment.centerRight,
          child: GestureDetector(
            onTap: _getCameraState,
            child: Container(
              width: 50,
              height: 50,
              color: Colors.blue,
            ),
          ),
        ),
      ],
    );
  }
}

class PageSwitch extends StatelessWidget {
  const PageSwitch({
    Key? key,
    required this.page,
    required this.bgColor,
    required this.onTap,
  }) : super(key: key);

  final int page;
  final Color bgColor;
  final void Function(int) onTap;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => onTap(page),
      child: SizedBox(
        width: 50,
        height: 50,
        child: DecoratedBox(
          decoration: BoxDecoration(color: bgColor),
          child: Center(child: Text('$page')),
        ),
      ),
    );
  }
}

Copy the code to main.dart file and run the app.

Steps to reproduce:

  1. Scroll the map to a random point and click the blue rectangle on the right to get the CameraState.
  2. Click the green rectangle on the bottom to switch to the second map and repeat the first step.
  3. Click the blue rectangle on the bottom to switch to third map and repeat the first step.

And then go to the first or the second map and click again the blue rectangle to get the CameraState and it will be the state from the map which was created last. This is happening with every action that can be done on the map.

Android: launch issue with newly exposed attribution in 0.4.0

Now I get this trying to launch on Android:

/android/src/main/kotlin/com/mapbox/maps/mapbox_maps/mapping/LogoMappings.kt: (8, 1): Conflicting overloads: public fun AttributionSettingsInterface.applyFromFLT(settings: FLTSettings.AttributionSettings, context: Context): Unit defined in com.mapbox.maps.mapbox_maps.mapping in file AttributionMappings.kt, public fun LogoSettingsInterface.applyFromFLT(settings: FLTSettings.LogoSettings, context: Context): Unit defined in com.mapbox.maps.mapbox_maps.mapping in file LogoMappings.kt, public fun AttributionSettingsInterface.applyFromFLT(settings: FLTSettings.AttributionSettings, context: Context): Unit defined in com.mapbox.maps.mapbox_maps.mapping in file AttributionMappings.kt, public fun LogoSettingsInterface.applyFromFLT(settings: FLTSettings.LogoSettings, context: Context): Unit defined in com.mapbox.maps.mapbox_maps.mapping in file LogoMappings.kt

Creating and destroying multiple maps causing other maps to throw PlatformException(channel-error, Unable to establish connection on channel., null, null)

When we have initial screen which contains map, and we push and pop a route with other map, calling functions on map which is on initial screen throws PlatformException. Generally creating and destroying map even without pushing and popping routes is causing the same exception. For example, we have a widget which is initially off screen, and when user clicks a button, widget slides in and displays a map which is destroyed when the user is finished with that widget.

Reproduction code:

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  MapboxMap? _map;
  bool _styleLoaded = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('First Map'),
      ),
      body: Stack(
        children: [
          MapWidget(
            resourceOptions: ResourceOptions(accessToken: <add access token>),
            styleUri: MapboxStyles.LIGHT,
            onMapCreated: (MapboxMap map) => _map = map,
            onStyleLoadedListener: (StyleLoadedEventData event) => _styleLoaded = true,
          ),
          Positioned(
            left: 16,
            bottom: 16,
            child: ElevatedButton(
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const SecondRoute()),
              ),
              child: const Text('Second map'),
            ),
          ),
          Positioned(
            right: 16,
            bottom: 16,
            child: ElevatedButton(
              onPressed: () {
                if(_styleLoaded) {
                  _map!.style.styleLayerExists('layer_id');
                }
              },
              child: const Text('Map action'),
            ),
          ),
        ],
      ),
    );
  }
}

class SecondRoute extends StatelessWidget {
  const SecondRoute({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Map'),
      ),
      body: MapWidget(
        resourceOptions: ResourceOptions(accessToken: <add access token>),
        styleUri: MapboxStyles.LIGHT,
      ),
    );
  }
}

Steps to reproduce:

  1. Click on Second Map button.
  2. Go back to previous screen.
  3. Click on Map action button.

Following exception is thrown:

E/flutter (27096): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(channel-error, Unable to establish connection on channel., null, null)
E/flutter (27096): #0      StyleManager.styleLayerExists (package:mapbox_maps_flutter/src/pigeons/map_interfaces.dart:5766:7)
E/flutter (27096): <asynchronous suspension>

Map is rerendering every frame when keyboard appears/disappears

The _onMapCreated function is triggered every single frame, when the soft keyboard is appearing or disappearing.

_onMapCreated(MapboxMap mapboxMap) async {
    print("ONMAPCREATED");
//Prints to console every single frame
}

Console:

flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED
flutter: ONMAPCREATED

how to customize markers in map

I'm using the latest version of the library, but didn't find a way to customize the marker in a particular way, currently it just adjusts to the built-in method which is difficult to customize again.

ScaleBox, Attributions, Compass Setting Interfaces not available for use

Only the LogoSettingsInterface is exposed via MapboxMap.logo.

There is no way to hide the ScaleBox via code. However, we can see that the necessary classes and interfaces to modify the ScaleBox and other counterparts are available but not exposed to use.

Please expose MapboxMap.scaleBox and MapboxMap.attributions with the next version.

View annotations

When will it be possible to view annotations? Next update I hope. Now the fact that this feature is not available yet I can't use the plugin and need to use unofficial that's a bit unstable.

How to configure live traffic tileset?

Hello, I have tried to make the settings to show the traffic layer on the map without success, I have followed the indications of the examples but I have not got an example of traffic with flutter.

      mapboxMap.style.addSource(VectorSource(
          id: "traffic", url: "mapbox://mapbox.mapbox-traffic-v1"));

      mapboxMap.style.addLayerAt(
          LineLayer(
            id: "traffic",
            sourceId: "mapbox-traffic",
            sourceLayer: "traffic",
            lineWidth: 1.9,
          ),
          LayerPosition(above: "country-label"));

This is the code I have implemented, following the example of the VectorTileSourcePage class. Please could you help me by telling me what other settings I should apply.

Update the cluster layer after filter

How can I update my clusters, after filtering the layer of single points? The single points are responding to the filter, but the clusters are not updating to reflect the new amount of single points on the map.

SymbolLayer does not have textField or textValue.

I checked source of SymbolLayer. But it seems it does not contain any properties to set text value. I am very confusing because we had all other properties for text like textColor, textSize,... But no definition for setting value for text.
Thank you.

Desktop support

Would be great to have desktop (MacOs/Windows/Linux) support for maps

Launch app with other mapbox plugin

If in addition to mapbox_maps_flutter add another mapbox plugin(in my case flutter_mapbox_navigation) it throws this error.
Any idea on what might be done to fix it would be highly appreciated. Thanks.

E/AndroidRuntime( 5233): java.lang.IllegalStateException: Please ensure that the hosting activity/fragment is a valid LifecycleOwner
E/AndroidRuntime( 5233): at com.mapbox.maps.plugin.lifecycle.ViewLifecycleOwner.doOnAttached(ViewLifecycleOwner.kt:68)
E/AndroidRuntime( 5233): at com.mapbox.maps.plugin.lifecycle.ViewLifecycleOwner.(ViewLifecycleOwner.kt:58)
E/AndroidRuntime( 5233): at com.mapbox.maps.plugin.lifecycle.MapboxLifecyclePluginImpl.registerLifecycleObserver(MapboxLifecyclePluginImpl.kt:30)
E/AndroidRuntime( 5233): at com.mapbox.maps.plugin.MapPluginRegistry.onAttachedToWindow(MapPluginRegistry.kt:173)
E/AndroidRuntime( 5233): at com.mapbox.maps.MapController.onAttachedToWindow$sdk_release(MapController.kt:333)
E/AndroidRuntime( 5233): at com.mapbox.maps.MapView.onAttachedToWindow(MapView.kt:115)
E/AndroidRuntime( 5233): at android.view.View.dispatchAttachedToWindow(View.java:21291)

PlatformException(error, java.lang.IllegalStateException: Please ensure that the hosting activity/fragment is a valid LifecycleOwner

E/flutter ( 6746): at com.mapbox.maps.plugin.lifecycle.ViewLifecycleOwner.doOnAttached(ViewLifecycleOwner.kt:68)
E/flutter ( 6746): at com.mapbox.maps.plugin.lifecycle.ViewLifecycleOwner.(ViewLifecycleOwner.kt:58)
E/flutter ( 6746): at com.mapbox.maps.plugin.lifecycle.MapboxLifecyclePluginImpl.registerLifecycleObserver(MapboxLifecyclePluginImpl.kt:30)
E/flutter ( 6746): at com.mapbox.maps.plugin.MapPluginRegistry.onAttachedToWindow(MapPluginRegistry.kt:173)
E/flutter ( 6746): at com.mapbox.maps.MapController.onAttachedToWindow$sdk_release(MapController.kt:333)
E/flutter ( 6746): at com.mapbox.maps.MapView.onAttachedToWindow(MapView.kt:115)
E/flutter ( 6746): at android.view.View.dispatchAttachedToWindow(View.java:19702)
E/flutter ( 6746): at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3518)
E/flutter ( 6746): at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3525)
E/flutter ( 6746): at android.view.ViewGroup.addViewInner(ViewGroup.java:5255)
E/flutter ( 6746): at android.view.ViewGroup.addView(ViewGroup.java:5035)
E/flutter ( 6746): at android.view.ViewGroup.addView(ViewGroup.java:4972)
E/flutter ( 6746): at android.view.ViewGroup.addView(ViewGroup.java:4945)
E/flutter ( 6746): at io.flutter.plugin.platform.PlatformViewsController$1.configureForTextureLayerComposition(PlatformViewsController.java:647)
E/flutter ( 6746): at io.flutter.plugin.platform.PlatformViewsController$1.createForTextureLayer(PlatformViewsController.java:225)
E/flutter ( 6746): at io.flutter.embedding.engine.systemchannels.PlatformViewsChannel$1.create(PlatformViewsChannel.java:128)
E/flutter ( 6746): at io.flutter.embedding.engine.systemchannels.PlatformViewsChannel$1.onMethodCall(PlatformViewsChannel.java:55)
E/flutter ( 6746): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:258)
E/flutter ( 6746): at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
E/flutter ( 6746): at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
E/flutter ( 6746): at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
E/flutter ( 6746): at android.os.Handler.handleCallback(Handler.java:914)
E/flutter ( 6746): at android.os.Handler.dispatchMessage(Handler.java:100)
E/flutter ( 6746): at android.os.Looper.loop(Looper.java:225)
E/flutter ( 6746): at android.app.ActivityThread.main(ActivityThread.java:7563)
E/flutter ( 6746): at java.lang.reflect.Method.invoke(Native Method)
E/flutter ( 6746): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
E/flutter ( 6746): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:994)

mapbox view with location enabled heating the phone too much

During testing on a Samsung galaxy A52S 5g I have noticed the phone heating a lot when the mapbox default location marker is enabled compared to if it is disabled.

I am using the following to set the location enabled:
_mapController?.location.updateSettings(LocationComponentSettings(enabled: true));

Have someone else noticed similar behaviour?

How to center camera on user location?

I'm trying to get my location from the map to avoid using external libraries like geolocator, but I can't find any option nor listener for the user location.

At the start of my view I want to center the camera at the current user location. Is there any way to do that in the current version of the plugin?

Can I have access to events like onMoveEnd?

I need to update the data on my map if it possible at the end of, for example, the camera movement, to avoid getting too much data from my API. I've already done this in web by using onDragEnd but I can't find a simple way to do this in this plugin. I basically need something like these Events, or the equivalent for the camera position.

Is there a way to do this? I found mapboxMap.addListener() but I'm not sure about how it works and if it can listen to those events.

Feature request: layer style expressions

Hi, and firstly, well done with the plugin. It is phenomenal!
Our team has been using the other unofficial plugin for a few years now. It was the best there was, but we had extreme performance issues and some difficulty adding custom layers, etc.
We finally hacked in layer/layer-style support, but it's not great.

Your plugin is extremely fast, I've been testing it for viability for the last two days.
So far only this one possible deal breaker.

In our app we use, e.g. symbol layers where the symbols have filters that make them disappear at zoom level 5 and below and also reduces their size linearly based on zoom level. We can also set symbol images on a single layer based on expressions such as the GET expression from the Mapbox docs.

Are there any plans to allow for adding these expressions?
I see currently when creating a layer you can only set a size of type double, as an example.

I'm gonna explore your code a bit and see if I can somehow pass in custom JSON directly somewhere, but the layer data classes do make things a lot easier.

How to add an onFeatureTapped callback?

Hi, since I'm migrating from a community driven mapbox package, to this one, I am struggling to find an equivalent for


  void _onMapCreated(MapboxMap mapController) {
    this.mapController.onFeatureTapped.add(_onTappedFeature); // this line
  }
  
    Future<void> _onTappedFeature(
    feature,
    Point<double> point,
    LatLng latlng,
  ) async {
  ...
  }

What is the equivalent or similar thing for that in the official package?

Could not find module 'MapboxMaps' for target 'arm64-apple-ios-simulator'

Apple silicon m1

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.3.10, on macOS 13.1 22C65 darwin-arm, locale
    en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.1)
[✓] VS Code (version 1.73.1)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

• No issues found!

Pass Custom Data through PointAnnotation

Hello. First of all, I really love this project. Thank you so much for your working on it. I am now stucking with passing custom data through PointAnnotaion.

I have some markers/anotations on the Map. When clicking on a marker, base on the marker's id, it will get data of that marker and display it. But it seems that there is no field for that purpose in PointAnnotation.

Here is my source. How can I do this?

@OverRide
void onPointAnnotationClick(PointAnnotation annotation) async {
Navigator.pushNamed(mapState.context, Routes.viewPinInfo, arguments: annotation.customData.id);
}

Cluster is not updating when setting filters

This is my Cluster source:

{
  "type": "geojson",
  "data": "https://firebasestorage.googleapis.com/v0/b/charge-club.appspot.com/o/chargers.json?alt=media&token=0146c264-4e84-4af3-a6e0-8bb4992314d1",
  "cluster": true,
  "clusterMaxZoom": 14,
  "clusterRadius": 50
}

This is my Cluster layer:

{
  "id": "clusters",
  "type": "circle",
  "source": "chargers",
  "filter": [
    "has",
    "point_count"
  ],
  "paint": {
    "circle-color": [
      "step",
      [
        "get",
        "point_count"
      ],
      "#54FACF",
      100,
      "#54FACF",
      750,
      "#f28cb1"
    ],
    "circle-radius": [
      "step",
      [
        "get",
        "point_count"
      ],
      20,
      100,
      30,
      750,
      40
    ]
  }
}

I also have a layer for the unclustered points:

{
  "id": "unclustered-point",
  "type": "symbol",
  "source": "chargers",
  "filter": ["!", ["has", "point_count"]],
  "layout": {
    "icon-image": [
      "case",
      ["boolean", ["get", "fastCharger"], false],
      "fastCharger",
      "charger"
    ],
    "icon-anchor": "bottom",
    "text-field": ["get", "name"],
    "text-anchor": "bottom",
    "text-offset": [0, -3.3],
    "text-size": 14,
    "text-ignore-placement": true,
    "icon-ignore-placement": true,
    "text-padding": 0
  },
  "paint": {
    "text-color": "#fff",
    "text-halo-color": "#000",
    "text-halo-width": 1,
    "text-opacity": [
      "case",
      ["boolean", ["feature-state", "selected"], false],
      1,
      0
    ],
    "icon-opacity": [
      "case",
      ["boolean", ["feature-state", "selected"], false],
      1,
      0.7
    ]
  }
}

When setting a filter to the unclustered layer, the cluster is not updating. The single points are working correct, but the cluster do not reflect the new amount of single points.

 await mapboxMap?.style.setStyleLayerProperty("unclustered-point", "filter",  '["==", "fastCharger", true]');

The layers and sources are added in the onMapCreated method of the Map Widget:

    mapboxMap?.style.styleLayerExists("clusters").then((value) async {
      if (!value) {
        var layer =
            await rootBundle.loadString('assets/cluster/cluster_layer.json');
       mapboxMap?.style.addStyleLayer(layer, null);

        var clusterCountLayer = await rootBundle
            .loadString('assets/cluster/cluster_count_layer.json');
        mapboxMap?.style.addStyleLayer(clusterCountLayer, null);

        var unclusteredLayer = await rootBundle
            .loadString('assets/cluster/unclustered_point_layer.json');
        mapboxMap?style.addStyleLayer(unclusteredLayer, null);

      }
    });

`toScreenLocation()` function support

Hi, I love this plugin and I want to say thank you!

I am looking to implement toScreenLocation() function which is in mapbox-gl.
With this function, you can convert latitude and longitude coordinates to screen coordinates.

/// Returns the point on the screen that corresponds to a geographical coordinate ([latLng]). The screen location is in screen pixels (not display pixels) relative to the top left of the map (not of the whole screen)
  ///
  /// Note: The resulting x and y coordinates are rounded to [int] on web, on other platforms they may differ very slightly (in the range of about 10^-10) from the actual nearest screen coordinate.
  /// You therefore might want to round them appropriately, depending on your use case.
  ///
  /// Returns null if [latLng] is not currently visible on the map.
  Future<Point> toScreenLocation(LatLng latLng) async {
    return _mapboxGlPlatform.toScreenLocation(latLng);
  }

FROM: https://github.com/flutter-mapbox-gl/maps/blob/f505fcf5c79b75c165703028af77f3af62d382ae/lib/src/controller.dart#L1211-L1219

Order of Annotations

While it appears to be possible to stack annotations relative to each other within the same manager, I am struggling to reorder different annotations; in my specific case I want a PolylineAnnotation to be displayed below some PointAnnotations, however the line remains steadily above the icons:

Notice how the line comes out of the avatar and overlays it; instead I'd like to have the avatar above the line.

Feedback

I added some feedback in a Github repo before this one was made public: motrieux-thomas/mapbox_maps_flutter-0.3.0#1 (comment).

I think it would be good to address them:

  • The API for setting location is quite weird. We don't actually have a toJson function on Point. It's not exactly clear how the Map should look. Please consider using a LatLng (or find a better name) type which you provide the serialization for (rather than asking us to provide a JSON). Instead of:
  /// Coordinate at the center of the camera.
  Map<String?, Object?>? center;

do

LatLng center;
  • Package documentation assumes people have turf (https://pub.dev/packages/turf) installed, since that is how they can do Position(-80.1263, 25.7845)).toJson(). This is kind of confusing, since docs don't mention turf. It would be good to avoid asking developers to instead turf though.
    docs screenshot

  • Please consider renaming the controller type to MapController or MapboxController. It's confusing that the map widget is MapWidget, and the controller is MapboxMap. The convention is to suffix names Controller if they are controllers.

How to disable the compass or the scale?

How do I disable the compass, or change it's appearance - since it looks awful on Android)?

Also would like to disable the scale showing up in the upper left corner of the map.

How can I add an image to the map?

I'm trying to add an image (Uint8List) to the maps style, so that I can reference it later in a feature when I assemble a source geojson. This is what I tried:

    ByteData byteData =
        await rootBundle.load('assets/map_icons/$assetName.png');
    Uint8List image = byteData.buffer.asUint8List();
    // So far everything is okay, the list is correct.
    // But here I struggle with understanding what half of the properties do. The next line throws an error.
    await mapController.style.addStyleImage(
      assetName, // A string (which I later use to reference the image in a geojson source)
      1,
      MbxImage(width: 50, height: 50, data: image),
      true,
      [],
      [],
      null,
    );
    

The error thrown is:

PlatformException (PlatformException(Throwable, java.lang.Throwable: mismatched image size, Cause: null, Stacktrace: java.lang.Throwable: mismatched image size
	at com.mapbox.maps.mapbox_maps.StyleController.addStyleImage$lambda-49(StyleController.kt:641)
	at com.mapbox.maps.mapbox_maps.StyleController.$r8$lambda$9qzAKUZTMYPcXQ8_jKe8hkSxnyU(Unknown Source:0)
	at com.mapbox.maps.mapbox_maps.StyleController$$ExternalSyntheticLambda0.onStyleLoaded(Unknown Source:17)
	at com.mapbox.maps.MapboxMap.getStyle(MapboxMap.kt:355)
	at com.mapbox.maps.mapbox_maps.StyleController.addStyleImage(StyleController.kt:610)
	at com.mapbox.maps.mapbox_maps.StyleController.addStyleImage(StyleController.kt:17)
	at com.mapbox.maps.pigeons.FLTMapInterfaces$StyleManager$-CC.lambda$setup$34(FLTMapInterfaces.java:7898)
	at com.mapbox.maps.pigeons.FLTMapInterfaces$StyleManager$$ExternalSyntheticLambda20.onMessage(Unknown Source:2)
	at io.flutter.plugin.common.BasicMessageChannel$IncomingMessageHandler.onMessage(BasicMessageChannel.java:217)
	at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
	at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:319)
	at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loop(Looper.java:250)
	at android.app.ActivityThread.main(ActivityThread.java:7806)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
, null))

remove listener on PointAnnotation

I am using a "addOnPointAnnotationClickListener" to allow click on the symbols for a specific screen however when i dispose the current screen and navigate to a different screen with other symbols the listener is still active.

Is there an option to dispose/cancel the listener? Could not find any info in the documentation.

Btw great to see official mapbox support to flutter!

Infinite bounds in CoordinateBounds crashing

Hi

I used CoodinateBounds with an empty map for southwest and northeast and setting infiniteBounds to true.
Like this:

CoordinateBounds(
        infiniteBounds: true,
        southwest: {},
        northeast: {},
      );

The doc says that for infiniteBounds property: "If set to true, an infinite (unconstrained) bounds covering the world coordinates would be used. Coordinates provided in southwest and northeast fields would be omitted and have no effect."

My problem is that I got this error:
Unhandled Exception: PlatformException(NullPointerException, java.lang.NullPointerException: null cannot be cast to non-null type kotlin.collections.List<kotlin.Double>, Cause: null, Stacktrace: java.lang.NullPointerException: null cannot be cast to non-null type kotlin.collections.List<kotlin.Double>

I suppose that southwest and northeast are not omitted as they would be.

How to use data driven properties

Hello, how can I use data driven properties to pass a color for a line on LineLayer? The LineLayer object takes an int for the line-layer property, so I can't pass the ['get', 'custom-color'] to it :/ Actually that applies to any property in any type of layer...

Not work vertical scrolling on the map

After turning off the map rotation does not work vertical scrolling on the map. How to solve this problem?

mapboxMap?.gestures.updateSettings(GesturesSettings(rotateEnabled: false));

Web support

The README states that "Web and desktop are not supported" - I'm wondering if that's a limitation as of this moment, or something that you don't intend to support even if the implementation is done by someone?

ktlint issue on library usage

flutter run gives an error related to referencing ktlint.gradle.

* Where:
Build file '/Users/praveen/development/flutter/.pub-cache/hosted/pub.dartlang.org/mapbox_maps_flutter-0.3.0/android/build.gradle' line: 57

* What went wrong:
A problem occurred evaluating project ':mapbox_maps_flutter'.
> Could not read script '/Users/praveen/***/android/gradle/ktlint.gradle' as it does not exist.

Upon checking the library, it's referencing these two files on the root directory.

project.apply {
    from("$rootDir/gradle/ktlint.gradle")
    from("$rootDir/gradle/lint.gradle")
}

The end user need not have these files.

Listener for tap on map but not on marker

Hey there,
as there is no listener for a tap on the map (but not on a marker), how do we do that?
Is there an ordering to the call sequence of listeners? Like, is the onTapListener always called before the onPointAnnotationClick listener? Can we rely on that?

Also: i need to be able to cluster (so i will have to go with a GeoJson source), but how does a onTap work then (i dont have PointAnnotations anymore, so no onPointAnnotationClickListener)... do i have to map the coordinates from onTapListener to a entity from my GeoJson source?

Cheers and thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.