Monday, April 4, 2022

Flutter Tutorial: Save GPS Data from the Devices

In this article we will cover how to collect the value of the speed of a vehicle through GPS. We will use as a basis for this project the Flutter framework that has been widely used by several developers.

We will cover the following topics:

  • Creating a Standard Project with Flutter
  • Installing and configuring the “geolocator: ^8.2.0” plugin
  • Save the speed data as the device moves.

Step 1. Creating a Standard Project with Flutter

flutter create -i objc -a java my_speed

Step 2. Install & Configure Geolocator Plugin

To save GPS device moves, we will use the “geolocator: ^8.2.0” flutter plugin available in the official package repository for Dart and Flutter applications.

The “geolocator: ^8.2.0” is a simple yet powerful tool to collect this data. To use this plugin, we need to install and configure some settings.

To simplify what was done in this project, I will bring here all the configurations made in a simpler way.

flutter pub add geolocator:8.1.1

or by editing your pubspec.yaml file:

dependencies: 

    geolocator: ^8.2.0

Import the plugin to your main.dart file:

import 'package:geolocator/geolocator.dart';

3. Save the speed data as the device moves

add new permission to plaform specific manifest, like Android or iOS. For android, edit the android/app/src/main/AndroidManifest.xml files. Add this permission:







also the gradle properties:

android.useAndroidX=true
android.enableJetifier=true

for iOS platform, edit ios/Runner/Info.plist files and add:

NSLocationWhenInUseUsageDescription
This app needs access to location when open.
NSLocationAlwaysUsageDescription
This app needs access to location when in the background.
NSLocationTemporaryUsageDescriptionDictionary

NSLocationTemporaryUsageDescriptionDictionary

YourPurposeKey
The example App requires temporary access to the device's precise location.

In your main.dart files, add geolocator variables:

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

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

class _MyHomePageState extends State {
  double? latitude = 0.0;
  double? longitude = 0.0;
  double? altitude = 0.0;
  double? speed = 0.0;
  DateTime? timestamp = DateTime.now();
 
  //Resto do codigo

}

Create the run Permission() method in the My HomePage class, this class will be responsible for checking if the application has permission to use the device's GPS.

 runPermission() async {
    LocationPermission permission;
    permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied ||
        permission == LocationPermission.whileInUse) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.deniedForever) {
        _showDialog();
      }
    }
  }

At first it checks if it has the permission with the parameter ”Geolocator.checkPermission()” if it doesn't have it, or if it only allowed it while the “application is in use” it will make a request through the parameter “Geolocator.requestPermission()”.

In order for the application to respond appropriately to the user if he does not give permission, create a _showDialog(), this method will execute a pop-up on the user's screen informing him that permission has been denied.

  void _showDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text("Permissao para Acessar local"),
          content: const Text(
              "Deve escolher opção de permição: 'Permitir o tempo todo'"),
          actions: [
            TextButton(
              child: Text("OK"),
              onPressed: () async {
                Navigator.of(context).pop;
              },
            ),
          ],
        );
      },
    );
  }

Continued in the MyHomePage class create the runLocation() method. This method will be responsible for implementing the code that will verify the location as the vehicle moves. In this method, by calling getPositionStream(), it is possible to “listen” to the location changes.

Set the accuracy and distanceFilter according to the code below.

 runLocation() async {
    // ignore: prefer_const_constructors
    final LocationSettings locationSettings = LocationSettings(
      accuracy: LocationAccuracy.high,
      distanceFilter: 10,
    );
    StreamSubscription positionStream =
        Geolocator.getPositionStream(locationSettings: locationSettings)
            .listen((Position? position) {
      setState(() {
        latitude = position?.latitude;
        longitude = position?.longitude;
        altitude = position?.altitude;
        speed = position?.speed;
        timestamp = position?.timestamp;
      });

      print(position == null
          ? 'Unknown'
          : '${position.latitude.toString()}, ${position.longitude.toString()}');
    });
  }
  

Use the setState() method and insert the variables for the data to be collected. setState() is used to update the values of your View in real time.

The runLocation() has to be asynchronous and as the device moves, this method will be executed and automatically update the values of the latitude, longitude, altitude, speed and timestamp variables.

A good place to insert external API communication for data synchronization is inside the getPositionStream() method. Since this method will always be executed as the device moves.

Use the standard flutter method initState() to call the two created methods runPermission() and runLocation().

   @override
 void initState() {
   runPermission();
   runLocation();
 }

All the methods we need to get the GPS parameters were created, to make the application look more pleasant, we can edit the Widget from within the MyHomePage class and take advantage of it and pass our variables

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        toolbarHeight: 150,
        backgroundColor: Color.fromARGB(255, 230, 229, 229),
        centerTitle: true,
        title: GradientText(
          'My Speed',
          style: const TextStyle(fontSize: 40),
          gradient: LinearGradient(colors: [
            Colors.blue.shade400,
            Color.fromARGB(255, 144, 13, 161),
          ]),
        ),
      ),
      body: Center(
          child: Container(
              child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: ListView(children: [
                    //Latitude Card
                    Card(
                      clipBehavior: Clip.antiAlias,
                      child: Column(
                        children: [
                          ListTile(
                            leading: const RotatedBox(
                              quarterTurns: 1,
                              child: Icon(Icons.height_rounded),
                            ),
                            title: const Text('Latitude'),
                            subtitle: Text(
                              '$latitude',
                              style: TextStyle(
                                  color: Colors.black.withOpacity(0.6)),
                            ),
                          ),
                        ],
                      ),
                    ),
                    //Longitude Card
                    Card(
                      clipBehavior: Clip.antiAlias,
                      child: Column(
                        children: [
                          ListTile(
                            leading: const Icon(Icons.height_rounded),
                            title: const Text('Longitude'),
                            subtitle: Text(
                              '$longitude',
                              style: TextStyle(
                                  color: Colors.black.withOpacity(0.6)),
                            ),
                          ),
                        ],
                      ),
                    ),
                    //Altitude Card
                    Card(
                      clipBehavior: Clip.antiAlias,
                      child: Column(
                        children: [
                          ListTile(
                            leading: const Icon(Icons.terrain),
                            title: const Text('Altitude'),
                            subtitle: Text(
                              '$altitude',
                              style: TextStyle(
                                  color: Colors.black.withOpacity(0.6)),
                            ),
                          ),
                        ],
                      ),
                    ),
                    //speed Card
                    Card(
                      clipBehavior: Clip.antiAlias,
                      child: Column(
                        children: [
                          ListTile(
                            leading: const Icon(Icons.speed),
                            title: const Text('Velocidade'),
                            subtitle: Text(
                              "${(speed! * 3.6)} km/h",
                              style: TextStyle(
                                  color: Colors.black.withOpacity(0.6)),
                            ),
                          ),
                        ],
                      ),
                    ),
                    //Date Time Card
                    Card(
                      clipBehavior: Clip.antiAlias,
                      child: Column(
                        children: [
                          ListTile(
                            leading: const Icon(Icons.access_time),
                            title: const Text('Data & Hora'),
                            subtitle: Text(
                              '$timestamp',
                              style: TextStyle(
                                  color: Colors.black.withOpacity(0.6)),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ])))),
      
    );
  }
}

Create cards for each GPS variable that will allow you to see each one independently. The GradientText that shows in the Title of the AppBar is another class made inside the main.dart file

  class GradientText extends StatelessWidget {
  const GradientText(
    this.text, {
    required this.gradient,
    this.style,
  });

  final String text;
  final TextStyle? style;
  final Gradient gradient;

  @override
  Widget build(BuildContext context) {
    return ShaderMask(
      blendMode: BlendMode.srcIn,
      shaderCallback: (bounds) => gradient.createShader(
        Rect.fromLTWH(0, 0, bounds.width, bounds.height),
      ),
      child: Text(text, style: style),
    );
  }
}

After completing all these steps in the application, it is now possible to capture GPS data as the device is moved.

0 comments:

Post a Comment