DEV Community

Cover image for Auto-detect a user country and locale in Flutter
Femi-ige Muyiwa for Hackmamba

Posted on • Edited on

8

Auto-detect a user country and locale in Flutter

According to the Oxford dictionary, geo-fencing involves using GPS or radio frequency identification (RFID) technology to create a virtual geographic boundary, enabling software to trigger a response when a mobile device enters or leaves a particular area. A famous use case of geo-fencing in the modern era of software is PayPal, which provides service to a user's location.

This tutorial aims to show how to manage the geo-fencing of applications available to a specific country. Thus, this article will explain how to auto-detect a user's country and locale in Flutter using Appwrite's locale API.

During this tutorial, we will display the location information of a user currently logged in. Here is the link to the GitHub repository containing all the code for this current project.

Prerequisites

The processes involved in this tutorial are relatively uncomplicated to follow but can be cumbersome. Thus, we strongly advise the reader to have the following prerequisites settled:

Creating an Appwrite project and setting up the platform

Before we delve into the coding aspects, we will need to create a new Appwrite project. To do that, we will start by heading to our browser and typing the hostname or IP address in our browser to open up the Appwrite console. Next, we will create a new project by clicking create project, followed by adding the project name and an ID (ID can be autogenerated).

create project
project info

Finally, within the home section, we will scroll down, select create platform, and in the popup, select Flutter. Within the Flutter section, we will choose Android and input our application name and package name (we can find them in our app-level build.gradle file by heading to android>app folder).

project platform

flutter app registration

Cloning the Flutter project

In this section, we will need to clone the repository specified in the prerequisites. To do that, we will click on the link to the repository, and within the repository, we will click on code and download it as a zip file. To use the other methods, check out the GitHub docs on how to clone a repository.

repo home
clone repo

The content of this repository is a user verification template that checks whether there is an account or active login session in our Appwrite console.

Connecting Appwrite to the Flutter project

Next, we must connect the Flutter application to the Appwrite console. This feature varies from one platform to another; thus, we will show how to connect the console to IOS and Android devices.

IoS
First, we will need to obtain the bundle ID, which we can do by going to the project.pbxproj file (ios > Runner.xcodeproj > project.pbxproj) and search for our PRODUCT_BUNDLE_IDENTIFIER.

Now, we will head to the Runner.xcworkspace folder in the applications IOS folder in the project directory on Xcode. Now, we want to select the Runner target, and to do this, we will select the Runner project in the Xcode project navigator and then find the Runner target. Next, we will select General and IOS 11.0 in the deployment info section as the target.

Android
To do this, copy the XML script below and paste it below the activity tag in the Androidmanifest.xml file (to find this file, head to android > app > src > main).



<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" android:exported="true">
    <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="appwrite-callback-[PROJECT-ID]" />
    </intent-filter>
</activity>


Enter fullscreen mode Exit fullscreen mode

Note: change [PROJECT-ID] to the id you used when creating the Appwrite project

Finally, we will add two dependencies to our project — the Appwrite package and provider. To do this, we will head to the pubspec.yaml file and add the packages below to the dependencies list:



provider: ^6.0.3
appwrite: ^6.0.0


Enter fullscreen mode Exit fullscreen mode

Now, we can move on to creating the project components.

Getting started

Now, we will work on the home page section of our project. The home page will display the user's locale information, and to achieve that, we will need to make slight changes to the authenticate.dart file in the cloned repository and create a model class for the locale list object. Let’s begin!

app_constants.dart
This file contains only some specific information like our endpoint, projectID, or any other ID, if applicable, created in our Appwrite console. Thus, we will start by creating a class called Appconstants and create two static constants: endpoint and projectID. Here is an implementation of the explanation above:



class Appconstants {
  static const String projectid = "<projectID>";
  static const String endpoint = "http://localhost/v1";
}


Enter fullscreen mode Exit fullscreen mode

localedata.dart
In this file, we will create a model class to determine the data structure; in this case, we are defining the design of a JSON object, locale.

We will start by creating a new class and static constants for the data of the JSON objects. Next, we will create a new class outside our previous one, map out the data we want to use as instance variables, and then create a constructor for those instances. Next, we will create a function that will accept JSON as input, and we are good to go.

We have below the implementation of the explanation above:



class LocaleFields {
  static const String ip = "ip";
  static const String countrycode = "countryCode";
  static const String country = "country";
  static const String continentcode = "continentCode";
  static const String continent = "continent";
  static const String eu = "eu";
  static const String currency = "currency";
}
class LocaleData {
  String ip;
  String countryCode;
  String country;
  String continentCode;
  String continent;
  bool eu;
  String currency;
  LocaleData(
      {required this.ip,
      required this.countryCode,
      required this.country,
      required this.continentCode,
      required this.continent,
      required this.eu,
      required this.currency});
  LocaleData.fromJson(Map<String, dynamic> json)
      : this(
            ip: json[LocaleFields.ip],
            countryCode: json[LocaleFields.countrycode],
            country: json[LocaleFields.country],
            continentCode: json[LocaleFields.continentcode],
            continent: json[LocaleFields.continent],
            eu: json[LocaleFields.eu] == 1,
            currency: json[LocaleFields.currency]);
  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data[LocaleFields.ip] = ip;
    data[LocaleFields.continent] = continent;
    data[LocaleFields.continentcode] = continentCode;
    data[LocaleFields.country] = country;
    data[LocaleFields.countrycode] = countryCode;
    data[LocaleFields.eu] = eu ? 1 : 0;
    data[LocaleFields.currency] = currency;
    return data;
  }
}



Enter fullscreen mode Exit fullscreen mode

authenticate.dart
According to the article specified in the prerequisites, this file contains the signup and login functions. For this tutorial, we want to update it so that it will contain the locale functions we will use within the homepage.dart file.

Therefore, we will start by calling the Appwrite locale API and call the model class we created earlier. Next, we will create a getter for the model class and a new Appwrite locale. Finally, we will create an async function _localise() and use it to get the locale object of the currently logged-in user (the data specified in the model class). We will also create a log-out function that deletes the user's session from Appwrite once we click a button. Here is the implementation of this explanation below:

import 'dart:convert';
import 'package:appwrite/appwrite.dart';
import 'package:ecommerce/data/localedata.dart';
import 'package:flutter/material.dart';
import '../constants/app_constants.dart';
class Authenticate extends ChangeNotifier {
Client client = Client(); // create a new Appwrite client
Account? account; //call the Appwrite acccount API
Locale? locale; //call the Appwrite Locale API
late bool _isLoggedin; //Creates a boolean instance to check if user is logged in
late bool _createaccount; //Creates a boolean instance to check if user is logged in
LocaleData? _localeData; // calls our model class for userdata
bool get isLoggedin => _isLoggedin; //getter for _isLoggedin
bool get createaccount => _createaccount; //getter for _createaccount
LocaleData? get localedata => _localeData; //getter for _localeData
// initializer
Authenticate() {
_init();
}
// This function creates our appwrite instance
_init() {
_isLoggedin = false; // initial boolean value for _isLoggedin
_createaccount = false; // initial boolean value for _createaccount
_localeData = null;
client
.setEndpoint(Appconstants.endpoint)
.setProject(Appconstants.projectid);
account = Account(client); // create a new Appwrite account
locale = Locale(client); // create a new Appwrite locale
_checkisLoggedin(); // checks two future functions (_user and _localeData)
}
// This function checks if the user is logged in
_checkisLoggedin() async {
try {
_localeData = await _localise(); //_localise function checks for the user's locale and loads it once the user is logged in
_isLoggedin = true;
notifyListeners();
} catch (e) {
print(e);
}
}
// This function gets the locale list object and use the model class with the objects in the array
Future _localise() async {
try {
var result = await locale?.get();
_isLoggedin = true;
_user = await _getaccount();
notifyListeners();
if (result?.ip != null) {
var data = jsonEncode(result?.toMap());
var jsondata = jsonDecode(data);
return LocaleData.fromJson(jsondata);
} else {
return null;
}
} catch (e) {
print(e);
}
}
// this function gets the locale object
localisation() async {
return await locale?.get();
}
// this function deletes a current user's active session on Appwrite
signout() async {
try {
await account?.deleteSession(sessionId: 'current'); // delete the current logged in user session
_isLoggedin = false; // sets _isLoggedin to false thus the page won't automatically be logged in when re initialised
notifyListeners();
} catch (e) {
print(e);
}
}
}

homepage.dart
Finally, we have reached the last component of our project. In this file, we want to display the location information of a currently logged-in user. To do that, we will create a stateful widget to check whether there is available locale information on the currently logged-in user.

import 'package:ecommerce/home.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../auth/authenticate.dart';
class Mainhomepage extends StatefulWidget {
late var user;
Mainhomepage({super.key, required this.user});
@override
State<Mainhomepage> createState() => _MainhomepageState();
}
class _MainhomepageState extends State<Mainhomepage> {
bool loading = true;
void initializeLocaleAPI() {
Authenticate state = Provider.of<Authenticate>(context, listen: false);
var localization = state.localisation();
localization.then((response) {
setState(() {
loading = false;
});
}).catchError((error) {
print(error.response);
});
}
@override
void initState() {
initializeLocaleAPI();
super.initState();
}
final scaffoldKey = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
}
}
view raw homepage.dart hosted with ❤ by GitHub

Next, within the build method, we will create an Appbar widget that will contain a centered title and exit action button, which, when clicked, initiates that signout function we created earlier. Finally, we will wrap the body in a Consumer widget and then return a ListView widget containing a row of Text widgets that will display the location information provided by the model class created earlier.

return Scaffold(
key: scaffoldKey,
appBar: AppBar(
backgroundColor: const Color(0xFF1A1F24),
automaticallyImplyLeading: false,
title: const Padding(
padding: EdgeInsetsDirectional.fromSTEB(10, 10, 10, 10),
child: Text('Swagga',textAlign: TextAlign.start,style: TextStyle(fontFamily: 'Poppins',color: Color(0x5AF1F4F8),),),
),
actions: [
Align(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.all(16),
child: Consumer<Authenticate>(
builder: (context, state, child) {
if (!state.isLoggedin) return Container();
return Text("Welcome, ${state.user?.name ?? ''}");
},
)),
),
Align(
alignment: Alignment.center,
child: IconButton(
onPressed: () {
Authenticate state =
Provider.of<Authenticate>(context, listen: false);
state.signout();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Homepage(),
));
},
icon: const Icon(Icons.exit_to_app),
)),
],
centerTitle: true,
elevation: 0,
),
body: loading
? const CircularProgressIndicator(
color: Colors.blue,
)
: Consumer<Authenticate>(
builder: (context, state, child) {
if (!state.isLoggedin) return Container();
return ListView(
padding: const EdgeInsets.all(16),
children: <Widget>[
Row(
children: [
Center(
child: Text("Name:- ${state.user?.name ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
Row(
children: [
Center(
child: Text("IP:- ${state.localedata?.ip ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
Row(
children: [
Center(
child: Text("Country Code:- ${state.localedata?.countryCode ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
Row(
children: [
Center(
child: Text("Country:- ${state.localedata?.country ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
Row(
children: [
Center(
child: Text("Continent Code:- ${state.localedata?.continentCode ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
Row(
children: [
Center(
child: Text("Continent:- ${state.localedata?.continent ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
Row(
children: [
Center(
child: Text("Currency:- ${state.localedata?.currency ?? ''}",style: const TextStyle(color: Colors.black,)),
),
],
),
]);
},
),
);
view raw homepage.dart hosted with ❤ by GitHub

Here is the final result of how our code works:

result

Note: the IP address is visible, but we decided to keep ours hidden for security purposes.

Conclusion

This tutorial explained how to create a model class and display the locale data of the user using the Appwrite locale API.

We urge the reader to build on this project, and we will provide some references to help:

Thanks for reading, and happy coding 💻

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay