Flutter Farm Mapping App With Google Maps Integration

In this tutorial, we will create a Flutter application for farm mapping with Google Maps integration.

Flutter Farm Mapping App with Google Maps Integration

The app enables users to draw and save geofenced areas on a map, each represented by a different color.

flutter farm app tutorial ad

flutter farm app tutorial ad

Prerequisites

  1. Flutter SDK installed on your machine.

  2. An IDE such as Visual Studio Code or Android Studio.

  3. A Google Cloud Platform (GCP) account to obtain a Google Maps API Key.

Step 1: Create A New Flutter Project

Open your terminal and run the following commands:

bashCopy code

flutter create geo_farm
 cd geo_farm

Step 2: Add Dependencies

Open the pubspec.yaml file and add the necessary dependencies:

/Pubspec.yaml

dependencies: flutter: 
sdk: 
flutter cupertino_icons: ^1.0.2 
google_maps_flutter: ^2.5.3
geolocator: ^10.1.0 
flutter_colorpicker: ^1.0.3 
shared_preferences: ^2.2.2
flutter_polyline_points: ^2.0.0 
hive: ^2.2.3 
hive_flutter: ^1.1.0 



dev_dependencies:
flutter_test: 
sdk: flutter 
flutter_lints: ^2.0.0 
hive_generator: ^2.0.0 
build_runner:

Run flutter pub get in the terminal to fetch the dependencies.

Step 3: Google Maps API Key

  1. Go to the Google Cloud Console.

  2. Create a new project or select an existing one.

  3. Enable the “Maps SDK for Android” and “Maps SDK for iOS” APIs.

  4. Create an API Key in the “Credentials” section.

  5. Restrict the API Key for Android and iOS apps by specifying package names and SHA-1/SHA-256 fingerprints (OPTIONAL).

  6. Copy the generated API Key.

Step 4: Configure Android And IOS

Android Configuration

Open android/app/build.gradle and update the defaultConfig block:

/android/app/build.gradle

android {
 ... defaultConfig 
{ ... 
minSdkVersion 23
 targetSdkVersion flutter.targetSdkVersion ... 
  } }

Open android/app/src/main/AndroidManifest.xml and add the required permissions and API Key:

android/app/src/main/AndroidManifest.xml

<manifest 
xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.geo_farming"> 
<uses-permissionandroid:name="android.permission.INTERNET"/> 
<uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/>
 <application>
 ... 
<meta-data
android:name="com.google.android.geo.API_KEY" 
android:value="YOUR_API_KEY"/>
 ...
</application> </manifest>

IOS Configuration

Open ios/Runner/AppDelegate.swift and add the following code:

ios/Runner/AppDelegate.swift

import UIKit 
import Flutter 
import GoogleMaps
@UIApplicationMain 
@objc class AppDelegate: FlutterAppDelegate { 
override func application( 
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GMSServices.provideAPIKey("YOUR_API_KEY")
GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
 }
 }

Open ios/Runner/Info.plist and add the following:

ios/Runner/Info.plist

<key>NSLocationWhenInUseUsageDescription</key> 
<string>We need your location for...</string>
<key>io.flutter.embedded_views_preview</key> 
<string>YES</string>

Replace YOUR_ANDROID_API_KEY and YOUR_IOS_API_KEY with the API keys obtained from the Google Cloud Console.

Step 5: Implement The Main Dart File

Open lib/main.dart and replace the existing code with the following:

lib/main.dart

import 'package:flutter/material.dart'; 
import 'package:google_maps_flutter/google_maps_flutter.dart'; 
import 'package:flutter_colorpicker/flutter_colorpicker.dart'; 
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart'; 
import 'models/field.dart'; 
void main() async {  
  await Hive.initFlutter();   Hive.registerAdapter(FieldAdapter()); 
  await Hive.openBox<Field>('fields'); 
  runApp(MyApp()); 
}
class MyApp extends StatelessWidget { 
  @override Widget build(BuildContext context) { 
    return MaterialApp( home: MyFarmMap(), 
    ); 
  } 
}

Step 6: Create The Model

Create a new file lib/models/field.dart and add the following code:

lib/models/field.dart

import 'package:google_maps_flutter/google_maps_flutter.dart'; 
import 'package:hive/hive.dart'; 

part 'field.g.dart'; 

 @HiveType(typeId: 0)
 class Field extends HiveObject { 

  @HiveField(0) 
  final String id; 

  @HiveField(1) 
  final String color; 

  @HiveField(2) 
  final String label; 
  @HiveField(3) 
  final List<LatLng> borderCoordinates; 
Field({ required this.id, required this.color, required this.label, required this.borderCoordinates, });

Map<String, dynamic> toJson() { 
 return { 
   'id': id, 
   'color': color,
   'label': label, 
   'borderCoordinates': borderCoordinates.map((latLng) => {'latitude': latLng.latitude, 'longitude': latLng.longitude}).toList(), 
  }; 
} 

factory Field.fromJson(Map<String, dynamic> json) { 
  return Field( 
    id: json['id'],
    color: json['color'], 
    label: json['label'],
    borderCoordinates: (json['borderCoordinates'] as List<dynamic>).map((coords) => LatLng(coords['latitude'], coords['longitude'])).toList(), ); 
  } 
}

Step 6.1: Run Build Runner

After creating the field.dart model, you need to run the build runner command to generate the necessary code for Hive.

Open your terminal, navigate to the project directory, and run:

flutter pub run build_runner build

This command triggers code generation for Hive adapters. It’s essential to execute this command whenever you make changes to your model classes annotated with Hive annotations (@HiveType, @HiveField).

Step 7: Implement The Main Map Widget

Open lib/main.dart and add the following code for the MyFarmMap widget:

lib/main.dart

class MyFarmMap extends StatefulWidget { 
@override _MyFarmMapState createState() => _MyFarmMapState(); } 

class _MyFarmMapState extends State<MyFarmMap> {
  GoogleMapController? mapController; 
  List<Field> fields = []; 
  List<LatLng> currentPolyline = []; 
  Color currentPolylineColor = Colors.red; 

void _saveFields() async { 
  final box = Hive.box<Field>('fields'); 
  await box.clear(); 
  // Clear previous fields 
  await box.addAll(fields); 
} 

@override void initState() { 
  super.initState(); 
  _loadFields(); 
  // Load saved fields when the app starts 
} 

Future<void> _loadFields() async { 
  final box = Hive.box<Field>('fields'); 
  setState(() { 
  fields = box.values.toList(); 
  }); } 

@override Widget build(BuildContext context) { 
return Scaffold( 
  appBar: AppBar(
  title: Text('Farm Map')), 
  body: GoogleMap( 
    onMapCreated: (controller) { 
    mapController = controller; 
  }, 
  initialCameraPosition: CameraPosition( target: LatLng(-34.0425, 22.2313889), 
  zoom: 13.0, ), 
  onTap: _handleMapTap, 
  polylines: _getPolylines(), 
  mapType: MapType.satellite, 
  markers: _getMarkers(),
 ), 
floatingActionButton: Column( 
  mainAxisAlignment: MainAxisAlignment.end, 
  crossAxisAlignment: CrossAxisAlignment.end, 
  children: [ 
    FloatingActionButton( onPressed: () => _showColorPicker(), 
    child: Icon(Icons.color_lens),
    ), 
    SizedBox(height: 16), 
    FloatingActionButton( onPressed: () => _toggleMapType(),
    child: Icon(Icons.map), 
    ), 
  SizedBox(height: 16),  FloatingActionButton( onPressed: () => _handleMapLongPress(),
  child: Icon(Icons.add), 
    ), 
   ], 
  ), 
 ); 
} 

Set<Polyline> _getPolylines() { 
  Set<Polyline> polylines = fields.map((field) { 
    return Polyline( 
      polylineId: PolylineId(field.id), 
      points: field.borderCoordinates, 
      color: Color(int.parse(field.color)), 
      width: 2, 
      );
   }).toSet(); 

  if (currentPolyline.isNotEmpty) { 
    polylines.add( Polyline( 
    polylineId: PolylineId('currentPolyline'), 
    points: currentPolyline, 
    color: currentPolylineColor, 
    width: 2, 
     ), 
    ); 
   } 
  return polylines; 
} 

Set<Marker> _getMarkers() { 
  return fields.map((field) { 
    return Marker( 
      markerId: MarkerId(field.id), 
      position: field.borderCoordinates[0], // You can adjust this to position the label marker 
      infoWindow: InfoWindow( 
        title: field.label, 
        snippet: 'Geofenced Area',
         ), 
       ); 
     }).toSet(); 
} 

void _toggleMapType() {
   setState(() { // Toggle between normal and satellite map types // 
      mapController!.animateCamera(CameraUpdate.newLatLng(mapController!.cameraPosition.target)); }); 
 } 

void _handleMapTap(LatLng latLng) { 
  setState(() { 
    currentPolyline.add(latLng); 
  });
 } 

void _handleMapLongPress() {
   if (currentPolyline.isNotEmpty) { 
     showDialog( context: context, builder: (BuildContext context) { 
     return AlertDialog( 
       title: Text('Enter Name for Geofenced Area:'), 
       content: TextField( onChanged: (value) { // You can handle the entered name here }, 
      ), 
      actions: <Widget>[ 
      TextButton( onPressed: () {
         Navigator.of(context).pop(); 
       }, 
      child: Text('Cancel'),
       ), 
      TextButton( onPressed: () { 
        setState(() { 
          fields.add(Field( 
            id: UniqueKey().toString(), 
            label: 'Field ${fields.length + 1}', 
            color: currentPolylineColor.value.toString(), 
            borderCoordinates: List.from(currentPolyline), 
             )); 
       currentPolyline.clear(); 
       _saveFields();
       }); 
     Navigator.of(context).pop(); 
     }, 
     child: Text('OK'), 
       ),
      ], 
     );
    },
   );
  }
 } 

void _showColorPicker() { 
  showDialog( 
    context: context, builder: (BuildContext context) { 
      return AlertDialog( 
         title: const Text('Pick a color'), 
           content: SingleChildScrollView( child: 
         ColorPicker( 
           pickerColor: currentPolylineColor, 
           onColorChanged: (Color color) { setState(() => currentPolylineColor = color); }, 
           showLabel: true, 
           pickerAreaHeightPercent: 0.8, 
           ), 
          ), 
         actions: <Widget>[ 
         TextButton( onPressed: () { 
            Navigator.of(context).pop();
          }, 
         child: const Text('Cancel'), 
          ), 
         TextButton( onPressed: () { 
           setState(() { 
             _handleMapLongPress(); 
            }); 
           Navigator.of(context).pop(); 
            }, 
           child: const Text('OK'), 
            ), 
           ], 
          ); 
         }, 
        ); 
     } 
}

This completes the implementation of the Flutter Farm Mapping App with Google Maps integration. Run your app using flutter run in the terminal and see your farm map in action!

Feel free to customize and enhance the app based on your specific requirements. Happy coding!

Full Code : https://github.com/iDigiSolWeb

Visit iDigiSolWeb.com if you require app development services