mapbox-ad-flutter

Installation

  • Run this command:
    flutter pub add mapbox_maps_flutter
    flutter pub add mapbox_ad_flutter

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

  dependencies:
    mapbox_maps_flutter: ^0.4.1
    mapbox_ad_flutter: ^0.0.8

How to configure

  • import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';
  • import 'package:mapbox_ad_flutter/mapbox_ad_flutter.dart';

How to use

1. Initialize MapboxAdManager and load ads

In your map widget, initialize the MapboxAdManager and call loadAds after map style is loaded.

class _MapboxAdAppState extends State<MapboxAdApp> {
  final _adManager = MapboxAdManager();
  MapWidget? _mapView;
  BuildContext? _context;

  _onStyleLoadedListener(mbx.StyleLoadedEventData? data) {
    if (_context == null) return;
    _adManager.onStyleLoadedCallback();
    _adManager.loadAds(
        _mapView, null, MediaQuery.of(_context!), configuration: MapboxAdConfiguration.staging);
    _adManager.callback = this;
  }

  @override
  Widget build(BuildContext context) {
    _context = context;
    final MapWidget mapWidget = MapWidget(
      key: const ValueKey("mapWidget"),
      resourceOptions: ResourceOptions(accessToken: "MAPBOX_ACCESS_TOKEN"),
      onStyleLoadedListener: _onStyleLoadedListener,
    );
    _mapView = mapWidget;
  }
}

2. Must implement WidgetsBindingObserver mixin to provide app state to MapboxAdManager when app is resumed and paused

class _MapboxAdAppState extends State<MapboxAdApp>
    with WidgetsBindingObserver {

  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        _adManager.onResume();
        break;
      case AppLifecycleState.paused:
        _adManager.onPause();
        break;
      case AppLifecycleState.inactive:
        break;
      case AppLifecycleState.detached:
        break;
    }
  }
}

About MapboxAdMBPCardView, MapboxAdAttributionView

MapboxAdMBPCardView is a card view which contains ad contents when ad pin is tapped. MapboxAdAttributionView is a card view which contains advertisement information when advertisement button is tapped.

MapboxAdMBPCardView Method


/// Attach view to the parent with animation.
/// @param context of the parent view to be attached.
/// @param completion The block to execute after the animation finishes.
attach(BuildContext context, {bool animated = true, Function()? completion});

/// Detach view from the parent with animation.
/// @param context of the parent view to be detached.
/// @param completion The block to execute after the animation finishes.
detach(BuildContext context, {bool animated = true, Function()? completion});

How to use

Please show the MapboxAdMBPCardView in the MapboxAdCallback -> onAdDidSelect and hide the card in the MapboxAdCallback -> onAdDidDeselect callback like the below sample code.

@override
onAdDidSelect(MapboxAdMBPCardView view, MapboxAd mapboxAd) {
  if (_context != null) view.attach(_context!);
}

@override
onAdDidDeselect(MapboxAdMBPCardView view) {
  if (_context != null) view.detach(_context!);
}  

Card tap action type

Type Description
route Called when "Route" button is tapped. Please show route search widget.
navigation Called when "Navigation" action is triggered. Please show navigation widget. "Navigation" button is currently hidden.
businessHours Called when business hours field is tapped.
address Called when address field is tapped.
externalLinkBanner Called when banner is tapped. Transition to its URL by external/internal browser like below.
externalLinkDetailSite Called when detail site button is tapped. Transition to its URL by external/internal browser like below.
externalLinkCall Called when cs is tapped. Transition to its URL by external/internal browser like below.
externalLinkCallAttributionLink Called when attribution add button is tapped. Transition to its URL by external/internal browser like below.
@override
onAdDidTapCardAction(MapboxAdMBPCardTapAction action, MapboxAd mapboxAd,
    {String url = ''}) async {
  switch (action) {
    case MapboxAdMBPCardTapAction.navigation:
    //tapped navigation button
      break;
    case MapboxAdMBPCardTapAction.route:
    //tapped route button
      break;
    case MapboxAdMBPCardTapAction.businessHours:
    // tapped business hours area
      break;
    case MapboxAdMBPCardTapAction.address:
    // tapped address area
      break;
    case MapboxAdMBPCardTapAction.externalLinkBanner:
    // tapped banner
    // launch url in browser
      launchUrl(mapboxAd.url.toString());
      break;
    case MapboxAdMBPCardTapAction.externalLinkDetailSite:
    // tapped detail site
    // launch url in browser
      await launchUrl(url);
      break;
    case MapboxAdMBPCardTapAction.externalLinkCall:
    // tapped call
    // launch url in browser
      await launchUrl(url);
      break;
    case MapboxAdMBPCardTapAction.externalLinkAttributionLink:
    // tapped attribution
    // launch url in browser
      await launchUrl(url);
      break;
  }
}

Card transition state

Card transition state is notified from the following delegate.

@override
onAdDidTransitionCard(MapboxAdMBPCardTransition state) {
  switch (state) {
    case MapboxAdMBPCardTransition.collapsed:
    // called the card is at the bottom of screen
      break;
    case MapboxAdMBPCardTransition.expanded:
    // called the card is showing as full screen
      break;
    case MapboxAdMBPCardTransition.closed:
    // called the card is closed
      break;
  }
}
  • Example code
import 'package:flutter/material.dart';

import 'dart:math' as math;

import 'package:logger/logger.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mbx;
import 'package:mapbox_ad_flutter/mapbox_ad_flutter.dart' as ad;

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

  @override
  State<MapboxAdApp> createState() => _MapboxAdAppState();
}

///Widget state MUST implement `WidgetsBindingObserver` and `MapboxAdCallback`
class _MapboxAdAppState extends State<MapboxAdApp>
    with WidgetsBindingObserver
    implements ad.MapboxAdCallback {

  /// TOKEN and Server Environment
  static const environment = ad.AdsEnvironment.develop;
  static const String accessToken = "ADD_ACCESS_TOKEN_HERE";

  /// Demo Map Settings
  static final center =
  mbx.Point(coordinates: mbx.Position(139.776606, 35.682400)).toJson();
  static const zoom = 14.281;
  static const styleUrl =
      "mapbox://styles/mapbox-ads/ckt3mhrk4088z18ponqip6fi6";

  /// Add a `adManager` variable to the widget.
  final _adManager = ad.MapboxAdManager();
  mbx.MapWidget? _mapView;
  ad.MapboxAdConfiguration? _config;
  BuildContext? _context;
  final _logger = Logger();

  /// Add map to manager in `_onMapCreated` callback
  _onMapCreated(mbx.MapboxMap mapboxMap) {
    _adManager.onMapCreated(mapboxMap);
  }

  /// Load ads in `_onStyleLoadedListener` callback
  _onStyleLoadedListener(mbx.StyleLoadedEventData? data) {
    _adManager.onStyleLoadedCallback();

    _loadAds();
  }

  /// Call `onMapIdle()`
  _onMapIdle(mbx.CameraChangedEventData? data) {
    _adManager.onMapIdle();
  }

  /// Call `onCameraIdle()`
  _onCameraIdle(mbx.MapIdleEventData? data) {
    _adManager.onCameraIdle();
  }

  /// Call `onMapClick()` and pass coordinates
  _onMapClick(mbx.ScreenCoordinate coordinate) {
    _adManager.onMapClick(math.Point(coordinate.x, coordinate.y));
  }

  /// Load ads private method
  _loadAds() {
    if (_context == null || _config == null) return;
    final mediaQueryData = MediaQuery.of(_context!);
    _adManager.loadAds(_mapView, null, mediaQueryData, configuration: _config);
    _adManager.callback = this;
  }

  /// Get config private method
  _getConfiguration(String accessToken) {
    final rootConfig = ad.MapboxAdRootConfiguration();
    rootConfig.sdk.adServer = ad.MapboxAdServerConfiguration.init(environment);
    rootConfig.sdk.eventTrack =
        ad.MapboxEventTrackConfiguration.init(environment);
    rootConfig.sdk.token = accessToken;
    final config = ad.MapboxAdConfiguration(rootConfig);
    config.mediaID = "ADD_MEDIA_ID_HERE";
    return config;
  }

  /// Build `MapboxMap` private method
  _buildMapboxMap(String token) {
    if (_context == null) return;
    _config = _getConfiguration(accessToken);
    final mbx.MapWidget mapWidget = mbx.MapWidget(
        key: const ValueKey("mapWidget"),
        resourceOptions: mbx.ResourceOptions(accessToken: accessToken),
        cameraOptions: mbx.CameraOptions(center: center, zoom: zoom),
        styleUri: styleUrl,
        onMapCreated: _onMapCreated,
        onStyleLoadedListener: _onStyleLoadedListener,
        onCameraChangeListener: _onMapIdle,
        onMapIdleListener: _onCameraIdle,
        onTapListener: _onMapClick);
    _mapView = mapWidget;
  }

  /// Attach card view on `onAdDidSelect` callback
  @override
  onAdDidSelect(ad.MapboxAdMBPCardView view, ad.MapboxAd mapboxAd) {
    if (_context != null) view.attach(_context!);
    _logger.d("onAdDidSelect");
  }

  /// Detach card view on `onAdDidDeselect` callback
  @override
  onAdDidDeselect(ad.MapboxAdMBPCardView view) {
    if (_context != null) view.detach(_context!);
    _logger.d("onAdDidDeselect");
  }

  /// Attach attribution view on `onAdShowAttribution` callback
  @override
  onAdShowAttribution(ad.MapboxAdAttributionView view) {
    if (_context != null) view.attach(_context!);
    _logger.d("onAdShowAttribution");
  }

  @override
  onAdCloseAttribution(ad.MapboxAdAttributionView view) {
    _logger.d("onAdCloseAttribution");
  }

  /// Alert private method
  _showAlert(String title, String message) {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text(title),
          content: Text(message),
          actions: <Widget>[
            TextButton(
              style: TextButton.styleFrom(
                textStyle: Theme
                    .of(context)
                    .textTheme
                    .labelLarge,
              ),
              child: const Text('OK'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  /// Launch url private method
  Future<void> _launchUrl(String url) async {
    try {
      final uri = Uri.parse(url);
      await launchUrl(uri);
    } catch (e) {
      _logger.e(e);
    }
  }

  /// Card Tap Actions
  @override
  onAdDidTapCardAction(ad.MapboxAdMBPCardTapAction action, ad.MapboxAd mapboxAd,
      {String url = ''}) async {
    switch (action) {
      case ad.MapboxAdMBPCardTapAction.navigation:
        _showAlert("Open navigation", "name:${mapboxAd.name}");
        break;
      case ad.MapboxAdMBPCardTapAction.route:
        _showAlert("Open route", "name:${mapboxAd.name}");
        break;
      case ad.MapboxAdMBPCardTapAction.businessHours:
        _logger.d("Taped business hours: ${mapboxAd.name}");
        break;
      case ad.MapboxAdMBPCardTapAction.address:
        _showAlert("Open address", "name:${mapboxAd.address}");
        break;
      case ad.MapboxAdMBPCardTapAction.externalLinkBanner:
        await _launchUrl(mapboxAd.url.toString());
        break;
      case ad.MapboxAdMBPCardTapAction.externalLinkDetailSite:
        await _launchUrl(url);
        break;
      case ad.MapboxAdMBPCardTapAction.externalLinkCall:
        await _launchUrl(url);
        break;
      case ad.MapboxAdMBPCardTapAction.externalLinkCallAttributionLink:
        await _launchUrl(url);
        break;
    }
  }

  /// Card Transitions
  @override
  onAdDidTransitionCard(ad.MapboxAdMBPCardTransition state) {
    switch (state) {
      case ad.MapboxAdMBPCardTransition.collapsed:
        _logger.d("collapsed");
        break;
      case ad.MapboxAdMBPCardTransition.expanded:
        _logger.d("expanded");
        break;
      case ad.MapboxAdMBPCardTransition.closed:
        _logger.d("closed");
        break;
    }
  }

  /// App Lifecycle
  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
    super.initState();
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
    /// Call `onResume()`
      case AppLifecycleState.resumed:
        _adManager.onResume();
        break;

    /// Call `onPause()`
      case AppLifecycleState.paused:
        _adManager.onPause();
        break;

      case AppLifecycleState.inactive:
        break;
      case AppLifecycleState.detached:
        break;
    }
  }

  /// Build widget
  @override
  Widget build(BuildContext context) {
    _context = context;
    _buildMapboxMap(accessToken);
    return Scaffold(body: _mapView);
  }
}

/// Run app
void main() {
  runApp(const MaterialApp(home: MapboxAdApp()));
}

Libraries

mapbox_ad/common/ad_manager
mapbox_ad/common/cache/ad_layer_cache
mapbox_ad/common/common/constants
mapbox_ad/common/common/info_list_helper
mapbox_ad/common/common/pm_logger
mapbox_ad/common/common/user_interface_style
mapbox_ad/common/config/ad_card_view_configuration
mapbox_ad/common/config/ad_pin_view_configuration
mapbox_ad/common/config/internal_backend_configuration
mapbox_ad/common/config/internal_sdk_configuration
mapbox_ad/common/config/public_sdk_configuration
mapbox_ad/common/config/style_property_internal
mapbox_ad/common/map/ad_layer_manager
mapbox_ad/common/map/map_internal
mapbox_ad/common/map/style_internal
mapbox_ad/common/map/tile_ad_layer_manager
mapbox_ad/common/model/ad_feature
mapbox_ad/common/model/ad_feature_collection
mapbox_ad/common/model/ad_feature_properties
mapbox_ad/common/model/ad_geometry
mapbox_ad/common/model/mbp_category_item
mapbox_ad/common/model/mbp_category_items
mapbox_ad/common/model/mbp_media_content
mapbox_ad/common/model/mbp_media_content_info
mapbox_ad/common/model/mbp_media_item
mapbox_ad/common/model/mbp_media_items
mapbox_ad/common/model/mbp_media_type
mapbox_ad/common/model/mbp_news_item
mapbox_ad/common/model/mbp_news_items
mapbox_ad/common/model/mbp_product_item
mapbox_ad/common/model/mbp_product_items
mapbox_ad/common/model/mbp_profile
mapbox_ad/common/model/mbp_web_linking
mapbox_ad/common/model/remote_config_response
mapbox_ad/common/model/remote_config_variant
mapbox_ad/common/pme/pme_config
mapbox_ad/common/pme/pme_environment
mapbox_ad/common/pme/pme_event_track_creator
mapbox_ad/common/pme/pme_logger
mapbox_ad/common/pme/pme_manager
mapbox_ad/common/pme/pme_user_info
mapbox_ad/common/service/ad_service
mapbox_ad/common/service/api/ad_server_api_source
mapbox_ad/common/service/api/api_service
mapbox_ad/common/service/api/image_api_source
mapbox_ad/common/service/api/mbp_profile_api_source
mapbox_ad/common/service/api/remote_config_source
mapbox_ad/common/service/image_service
mapbox_ad/common/service/mbp/mbp_business_hours_results
mapbox_ad/common/service/mbp/mbp_business_hours_service
mapbox_ad/common/service/mbp/mbp_page_key
mapbox_ad/common/service/mbp/mbp_pagination_manager
mapbox_ad/common/service/mbp/mbp_profile_service
mapbox_ad/common/service/remote_config_api_service
mapbox_ad/common/service/resource_cache_service
mapbox_ad/common/util/geometry_helper
mapbox_ad/common/util/math_helper
Source: https://github.com/rwl/complex/blob/master/lib/fastmath.dart
mapbox_ad/common/view/mbp/mbp_card_view
mapbox_ad/common/view/mbp/mbp_tab_screen
mapbox_ad/common/view/mbp/media/media_filter_list_view
mapbox_ad/common/view/mbp/media/media_list_view
mapbox_ad/common/view/mbp/news/news
mapbox_ad/common/view/mbp/news/news_list_view
mapbox_ad/common/view/mbp/product/filter_button
mapbox_ad/common/view/mbp/product/filter_list_view
mapbox_ad/common/view/mbp/product/product
mapbox_ad/common/view/mbp/product/product_filter_list_view
mapbox_ad/common/view/mbp/product/product_list_view
mapbox_ad/common/view/mbp/summary/attribution/attribution_ad_button
mapbox_ad/common/view/mbp/summary/attribution/attribution_close_button
mapbox_ad/common/view/mbp/summary/header/header_attribution_button
mapbox_ad/common/view/mbp/summary/header/header_open_status
mapbox_ad/common/view/mbp/summary/header/header_option_button
mapbox_ad/common/view/mbp/summary/header/header_option_button_list_view
mapbox_ad/common/view/mbp/summary/header/header_option_button_screen
mapbox_ad/common/view/mbp/summary/header/header_screen
mapbox_ad/common/view/mbp/summary/info/info
mapbox_ad/common/view/mbp/summary/info/info_divider
mapbox_ad/common/view/mbp/summary/info/info_expansion_tile
mapbox_ad/common/view/mbp/summary/info/info_media_filter_list_view
mapbox_ad/common/view/mbp/summary/info/info_media_list_view
mapbox_ad/common/view/mbp/summary/info/info_news_list_vew
mapbox_ad/common/view/mbp/summary/info/info_opening_hours
mapbox_ad/common/view/mbp/summary/info/info_product_filter_list_view
mapbox_ad/common/view/mbp/summary/info/info_product_list_view
mapbox_ad/common/view/mbp/summary/info/info_screen
mapbox_ad/common/view/mbp/summary/summary_screen
mapbox_ad_flutter
platform_interface/mapbox_ad_flutter
platform_interface/mapbox_ad_flutter_method_channel
platform_interface/mapbox_ad_flutter_platform_interface