05 – A basic Flutter app

Situm SDK can be used to build Wayfinding and Tracking applications, and dozens of use cases within those realms. However, there is a common pattern or skeleton that most Situm based applications will follow. In this Section, we provide an Android and iOS step-by-step guide to build your first Situm app using the Flutter framework.

There are many things you can do with the Situm SDK Flutter Plugin. In this guide, we will show you how to do the basic stuff. For more info, take a look at our Flutter sample app and at the Plugin Method Reference

Pre-requisites: Configuring Flutter #

First, you need to set up your Flutter development environment. To get started, please follow instructions under the Flutter installation guide.

After installing Flutter you can directly create an example app with the following command:

$ mkdir flutter_getting_started

$ flutter create -t app --platform android,ios flutter_getting_started

$ cd flutter_getting_started

Installing the plugin #

We assume that you have already created a Flutter application. To install the Flutter Situm plugin, you have different options using the Flutter dependency management.

Alternative 1: Using package manager #

To add the Situm dependency to your Flutter project, you can use the “flutter pub add” command. To add this dependency to your project, you can use the following command in your terminal:

flutter pub add situm_flutter_wayfinding

It’s important to note that you should run this command in the root directory of your Flutter project. Once the command is executed, the package will be added to your project’s dependencies and you can start using it in your code. Also, you can check your dependencies in your pubspec.yaml file to verify if the package has been added to your project.

Alternative 2: Using the git repository #

Another way to add the Situm dependency to your Flutter project is by directly adding it using the Situm github repository, for that you can use the “flutter pub add” command with the “–git-url” option. This allows you to specify the git repository URL for the package you want to add. In this case, the URL for the Situm Flutter wayfinding package is https://github.com/situmtech/flutter-wayfinding.git .

flutter pub add situm_flutter_wayfinding --git-url=https://github.com/situmtech/flutter-wayfinding.git

If you prefer to manually handle the dependencies, you alternatively can add the dependency manually to the pubspec.yaml file. This file is located in the root directory of your project and it’s used to manage your project’s dependencies. To add the Situm dependency, you will need to add the following lines under the dependencies section:

  situm_flutter_wayfinding:
    git:
      url: https://github.com/situmtech/flutter-wayfinding.git

After saving the pubspec.yaml file, you need to run the command flutter pub get in the root directory of your project to download and install the package. Once the package is installed, you can start using it in your code. You can check your dependencies in your pubspec.yaml file to verify if the package has been added to your project. It’s important to note that if you make changes to the pubspec.yaml file, you need to run flutter pub get again to apply the changes.

Configure native dependencies #

After adding the Situm dependency to your Flutter project, you will need to configure the native dependencies to ensure that the package will work correctly on both Android and iOS devices.

Android #

To use the Situm package in for the Android side, you must add a custom repository URL to your project’s build.gradle file.

allprojects {
    repositories {
        maven {
            url "https://repo.situm.com/artifactory/libs-release-local"
        }
    }
}

Next, you will need to set the min, target and compile sdk versions. Open the file local.properties, inside the android folder on the project and add the following lines:

flutter.minSdkVersion=21
flutter.targetSdkVersion=31
flutter.compileSdkVersion=31

iOS #

After adding the Situm dependency to your project, whether it is by using the “flutter pub add” command or by adding it directly to the pubspec.yaml file, you will need to run the command flutter pub get to download and install the package. Once the package is installed, you are ready to start using it in your code.

However, if your project is an iOS project, there is one more step you will need to take before you can start using the package. Since the Situm package uses CocoaPods to manage its iOS dependencies, you will need to run the command pod install in the iOS directory of your project after you run flutter pub get. This command will install the necessary dependencies for the package to work correctly on iOS devices.

Note: It is important to note that you need to run pod install every time you make changes to your dependencies in the pubspec.yaml file, or if you update the package, to ensure that the dependencies are up to date.

Import and initialize the plugin #

In order to initialize the Flutter Situm plugin, the following steps should be taken. It is important to note that this code should be placed in a stateful widget that is active throughout the lifetime of your app. This is necessary because the Situm SDK should only be initialized and configured once, and this should happen as soon as the app is launched. The stateful widget that contains the code responsible for maintaining the SDK’s state and ensuring that it is properly set up.

First, import the situm_sdk into your main file:

import 'package:situm_flutter_wayfinding/situm_flutter_sdk.dart';

On your root stateful widget instantiate the SitumFlutterSDK class on the initState method. Below you can see the complete example of a basic situmsdk variable declaration and initState function for the Widget state that contains the Situm SDK.

  late SitumFlutterSDK situmSDK;
@override
  void initState() {
    // Instantiate SitumSdk for flutter.
    situmSDK = SitumFlutterSDK();
//Next, initialize the SDK by calling the init() method on the situmSDK instance and passing in the user and API key for the Situm account that the app will be using.
// Remember to set your user and Api Key on the following line
    situmSDK.init("SITUM-USER", "SITUM-API-KEY");
//Then, set the configuration options for the SDK by calling the setConfiguration() method on the situmSDK instance
    situmSDK.setConfiguration(ConfigurationOptions(
//In this case, it's used to set the useRemoteConfig option to true.
      useRemoteConfig: true,
    ));
    situmSDK.onEnterGeofences((geofencesResult) {
      print("SDK> Enter geofences: ${geofencesResult.geofences}.");
    });
    situmSDK.onExitGeofences((geofencesResult) {
      print("SDK> Exit geofences: ${geofencesResult.geofences}.");
    });
    super.initState();
  }

Requesting permissions #

In order for the Situm SDK to function correctly on devices, your app must have permission to access the device’s location data.

Android #

To function correctly on Android devices, your app must have the ACCESS_LOCATION and BLUETOOTH_ADMIN permission.

To add the ACCESS_LOCATION permission to your app, you will need to edit the app’s AndroidManifest.xml file. This file is located in the android/app/src/main directory of your project.

In the AndroidManifest.xml file, you will need to add the following line:

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

Also, you may need to get location even when the app is not the foreground app. See our documentation about this topic for more information.

In case you decide that you need to use location even when the app is on the background, You need to add the ACCESS_BACKGROUND_LOCATION permission:

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

Also checking for the permission in runtime is necessary, you can use the package permission_handler to do so.

Future<bool> _requestPermission() async {
    bool allpermissions = true;
    final status = await Permission.locationWhenInUse.status;
    _echo("Requesting permission location " + status.toString());
    if (!status.isGranted) {
      final result = await Permission.locationWhenInUse.request();

      allpermissions = result.isGranted;
    }

    final statusBLE = await Permission.bluetoothScan.status;
    _echo("Requesting permission BLE " + status.toString());
    if (!statusBLE.isGranted) {
      final result = await Permission.bluetoothScan.request();
      allpermissions = allpermissions && result.isGranted;
    }

    return allpermissions;
  }

You should call this method at the appropriate time in your app, for example, when the user first starts using the location-based features of your app.

Note: If your app is running on a device with Android 6.0 (Marshmallow) or later, the user will be prompted to grant the ACCESS_FINE_LOCATION permission at runtime. So, it’s important to handle the case where the user denies the permission and inform the user about the app’s need for the permission.

Note: If your app is running on a device with Android 10 and later, you need to add the ACCESS_BACKGROUND_LOCATION permission and use the request with the `always` flag to be able to use the location even when the app is in background.

iOS #

In order for the Situm SDK to function correctly on iOS devices, your app must have the necessary permission of Location Services (to access the device’s location data)

To add these permissions to your app, you will need to edit the app’s Info.plist file. This file is located in the ios/Runner directory of your project.

In the Info.plist file, you will need to add the following keys and values, to add the Location Services permission:

<key>NSLocationWhenInUseUsageDescription</key>
<string>Your app requires access to location services to function correctly.</string>

Same way as in Android, you should also check for the permissions in runtime and make sure that you call this method at the appropriate time in your app, for example, when the user first starts using the location-based features of your app.

Note: If your app is running on a device with iOS 8.0 or later, the user will be prompted to grant the permissions at runtime. So, it’s important to handle the case where the user denies the permission and inform the user about the app’s need for the permission.

Positioning #

Starting positioning #

To start positioning with the Situm SDK, you will need to call the requestLocationUpdates() method on the situmSdk previously declared object. This method takes two arguments: the first is an implementation of the LocationListener interface, and the second is a map of options.

The implementation of the LocationListener interface is responsible for receiving the location updates from the SDK. You can create a new class that implements this interface and provides an onLocationChanged() method. This method will be called every time the SDK receives a new location update.

To start positioning with default options, you will need to call the requestLocationUpdates() method on the situmSdk object and pass an instance of your LocationInterface implementation class as the first argument, and an empty map as the second argument.

situmSdk.requestLocationUpdates(_MyLocationListener(), {});

For example, you can create a class called _MyLocationListener that implements the LocationListener interface and provides an onLocationChanged() method like this:

class _MyLocationListener implements LocationListener {
  @override
  void onError(Error error) {
    print("SDK> ERROR: ${error.message}");
  }

  @override
  void onLocationChanged(OnLocationChangedResult locationChangedResult) {
    print(
        "SDK> Location changed, building ID is: ${locationChangedResult.buildingId}");
  }

  @override
  void onStatusChanged(String status) {
    print("SDK> STATUS: $status");
  }
}

Stopping positioning #

To stop the positioning process with the Situm SDK, you will need to call the removeUpdates() method on the situmSdk object, like this:

situmSdk.removeUpdates();

By calling this method, the SDK will stop sending location updates and the positioning process will be stopped.

It’s important to note that you should call this method when the user stops using the location-based features of your app, to conserve battery and release resources.

Bundling everything together with a 3 button interface #

For the sake of the example, we will create a simple interface with 3 buttons:

  • Request permissions.
  • Start: to call the requestLocationUpdates method.
  • Stop: to call the removeUpdates() method.
import 'package:flutter/material.dart';
import 'package:situm_flutter_wayfinding/situm_flutter_sdk.dart';
import 'package:permission_handler/permission_handler.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

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

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late SitumFlutterSDK situmSDK;
  String _logs = "";
  final ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    // Instantiate SitumSdk for flutter:
    situmSDK = SitumFlutterSDK();
    //Next, initialize the SDK by calling the init() method on the situmSDK instance and passing in the user and API key for the Situm account that the app will be using.
    situmSDK.init("SITUM_USER",
        "SITUM_API_KEY");
    //Then, set the configuration options for the SDK by calling the setConfiguration() method on the situmSDK instance
    situmSDK.setConfiguration(ConfigurationOptions(
      //In this case, it's used to set the useRemoteConfig option to true.
      useRemoteConfig: false,
    ));
    situmSDK.onEnterGeofences((geofencesResult) {
      print("SDK> Enter geofences: ${geofencesResult.geofences}.");
    });
    situmSDK.onExitGeofences((geofencesResult) {
      print("SDK> Exit geofences: ${geofencesResult.geofences}.");
    });
    super.initState();
  }

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  Future<bool> _requestPermission() async {
    bool allpermissions = true;
    final status = await Permission.locationWhenInUse.status;
    _echo("Requesting permission location " + status.toString());
    if (!status.isGranted) {
      final result = await Permission.locationWhenInUse.request();

      allpermissions = result.isGranted;
    }

    final statusBLE = await Permission.bluetoothScan.status;
    _echo("Requesting permission BLE " + status.toString());
    if (!statusBLE.isGranted) {
      final result = await Permission.bluetoothScan.request();
      allpermissions = allpermissions && result.isGranted;
    }

    return allpermissions;
  }

  void _startPositioning() {
    _echo("Start Positioning. Start Positioning.");
    situmSDK.requestLocationUpdates(_MyLocationListener(fun_echo: _echo), {});
  }

  void _stopPositioning() {
    _echo("Stop Positioning . Stop Positioning");

    situmSDK.removeUpdates();
  }

  void _echo(String message) {
    setState(() {
      _logs += "${DateTime.now()..toString()} - $message\n";
    });
    _scrollController.animateTo(
      _scrollController.position.maxScrollExtent,
      duration: Duration(milliseconds: 100),
      curve: Curves.easeOut,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: _startPositioning,
              child: Text('Start Positioning'),
            ),
            ElevatedButton(
              onPressed: _stopPositioning,
              child: Text('Stop Positioning'),
            ),
            ElevatedButton(
              onPressed: _requestPermission,
              child: Text('Request Permission'),
            ),
            Expanded(
              child: Container(
                color: Colors.black,
                padding: EdgeInsets.all(20),
                child: SingleChildScrollView(
                  controller: _scrollController,
                  child: Text(_logs,
                      style: TextStyle(
                          fontFamily: 'monospace', color: Colors.green)),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

class _MyLocationListener implements LocationListener {
  final Function(String) fun_echo;
  _MyLocationListener({required this.fun_echo});

  @override
  void onError(Error error) {
    // TODO: implement onError
    fun_echo(error.message);
  }

  @override
  void onStatusChanged(String status) {
    // TODO: implement onStatusChanged
    fun_echo(status);
  }

  @override
  void onLocationChanged(OnLocationChangedResult locationChangedResult) {
    print(locationChangedResult.toString());
    fun_echo(locationChangedResult.toString());
    // TODO: implement onLocationChanged
  }
}

Thanks for reading our guide on how to create a basic app using Flutter and the Situm plugin. We hope that this guide has been helpful in getting you started on your wayfinding and tracking app development journey. Remember, there are many things you can do with the Situm SDK, so be sure to explore the sample app and the plugin method reference for more information. If you have any questions or feedback, feel free to reach out to us through email or contact form. Happy coding!

Subscribe to our newsletter

BASIC INFORMATION ON DATA PROTECTION

Data controller: SITUM TECHNOLOGIES, S.L.
Contact: Data controller: situm@situm.es
Responsible for protection: dpo@situm.es
Purpose and legal basis: To manage the sending of SITUM newsletters only with consent.
Legitimation: Express consent of the interested party.
Recipients: The data will not be passed on to third parties with the exception of legal obligations.
Retention period: As long as the interested party remains subscribed to the newsletter (a link to unsubscribe will be available in each newsletter sent by Situm).
Rights: The interested party may at any time revoke their consent, as well as exercise their rights of opposition, access, conservation, rectification, limitation, deletion of data and not be subject to a decision based only on automated data processing, by writing to SITUM at the addresses indicated.
Additional Information: You can consult additional and detailed information on Data Protection in our privacy policy.

Please, download your copy here

Thank you for downloading our whitepaper. Please do not hesitate to contact us if you would like to know more about how our solutions can help your business. Download whitepaper


Close window