Creating the place model and helper classes

In order to save the user's favorite places, we will use an SQLite database. As we did for the project in Chapter 6, Store That Data - Using Sq(F)Lite to Store Data in a Local Database, we'll use a model to insert the places to the database. So, let's begin, as follows:

  1. Create a new file in our project, called place.dart. Inside the file, we'll create a class, called Place, which will contain five properties:
    • The id integer.
    • The name String.
    • Two doubles for the latitude and longitude. 
    • A String that will later contain an image. 

The properties under Place will look like the following: 

class Place {
int id;
String name;
double lat;
double lon;
String image;
}
  1. Next, let's create a constructor that will set all the properties, like this:
Place(this.id, this.name, this.lat, this.lon, this.image);
  1. Finally, we'll create a toMap() method that will return a Map of type String, dynamic. As you might recall, a Map is a collection of key/value pairs: the key is a String, and, as we have different types in the table, the value will be dynamic. The code is shown in the following block:
  Map<String, dynamic> toMap() {
return {
'id': (id==0)?null:id,
'name': name,
'lat': lat,
'lon': lon,
'image': image
};
}
  1. Now that we have completed the Place class, let's also create a helper file that will interact with the database: let's call it dbhelper.dart. This file will contain the methods to create the database and to retrieve and write data.

As we'll be using the sqflite package, we need to add the dependency in the pubspec.yaml file, as follows:

dependencies:
[…]
sqflite: ^1.2.1
path: ^1.6.4

In order to find the latest version of the dependency, please visit https://pub.dev/packages/sqflite. We'll also be using the path package so that we can access the database with the same code for iOS and Android.
  1. In the dbHelper.dart file, we'll import sqflite.dart and path.dart, as follows:
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
  1. Next, let's create the DbHelper class, like this:
class DbHelper {}
  1. Inside the class, we'll create two variables. One is an integer with the version of the database, which at the beginning is 1; then, we will create the variable that will contain the database itself, called db, as follows:
final int version = 1;
Database db;
  1. Next, we'll create the openDb() method: this will open the database if it exists, or create it if it doesn't. All database operations are asynchronous, so the openDb() function will be asynchronous and will return a Future of type Database, as illustrated in the following code snippet:
Future<Database> openDb() async {  }
  1. Inside the function, we'll first check whether the db object is null by running the following code:
if (db == null) {}

If db is null, we need to open the database. We'll call the sqflite opendatabase() method, passing the path and version of the database, and the onCreate parameter that will be called if the database at the path specified is not found. We'll call this database mapp.db, and it will only contain a single table that has the same schema as the Place class.

  1. After opening or creating the database, we'll return it to the caller, like this:
Future<Database> openDb() async {
if (db == null) {
db = await openDatabase(join(await getDatabasesPath(),
'mapp.db'),
onCreate: (database, version) {
database.execute(
'CREATE TABLE places(id INTEGER PRIMARY KEY, name TEXT,
lat DOUBLE, lon DOUBLE, image TEXT)');
}, version: version);
}
return db;
}
  1. We don't need to have multiple instances of the DbHelper class throughout the app, so we'll create a Factory constructor that, instead of creating a new instance each time it's called, only returns a single instance of the class, as follows:
static final DbHelper _dbHelper = DbHelper._internal();
DbHelper._internal();

factory DbHelper() {
return _dbHelper;
}

Now, let's insert some fake data so that we can see the markers on our map and test if everything is working correctly.

  1. We'll create a new method, called insertMockData(), whose purpose is to insert some default data into our database. We'll insert three records in the places table (feel free to change the coordinates so that they are closer to where you are right now), and, as usual, this method will be asynchronous, as illustrated in the following code block:
  Future insertMockData() async {
db = await openDb();
await db.execute('INSERT INTO places VALUES (1,
"Beautiful park", 41.9294115, 12.5380785, "")');
await db.execute('INSERT INTO places VALUES (2,
"Best Pizza in the world", 41.9294115, 12.5268947, "")');
await db.execute('INSERT INTO places VALUES (3,
"The best icecream on earth", 41.9349061, 12.5339831, "")');
List places = await db.rawQuery('select * from places');
print(places[0].toString());
}
  1. At the top of the DbHelper class, declare a List of the Place object that will contain the result of the query, as follows:
 List<Place> places = List<Place>();
  1. Then, let's create a method that will retrieve all records from the places table. Here, we will use the query() helper method to retrieve all records from the places table. The query() method returns a List of Map, and we will use it to transform each Map into a Place. We'll call the method getPlaces(), as illustrated in the following code block:
Future<List<Place>> getPlaces() async {
final List<Map<String, dynamic>> maps = await
db.
query('places');
this.places = List.generate(maps.length, (i) {
return Place(
maps[i]['id'],
maps[i]['name'],
maps[i]['lat'],
maps[i]['lon'],
maps[i]['image'],
);
});
return places;
}
  1. Now, in the main.dart file, let's import the dbhelper.dart and place.dart files, like this:
import 'dbhelper.dart';
import 'place.dart';
  1. Then, in the _MainMapState class, we'll declare a DbHelper object, as follows:
DbHelper helper;
  1. In the initState() method, we'll call the object instance, like this:
helper = DbHelper();
  1. Still in the _MainMapState class, let's create a new method that will retrieve the places from the database. We'll call it _getData(), as shown in the following code snippet:
Future _getData() async {}

Inside the method, we'll call the helper openDb() method, then the insertMockData() method to add the first markers to our app, and then, we'll read them with the getPlaces() method. The _places list will contain the places that were retrieved.

  1. Next, for each Place in the _places list, we'll call the addMarker() method that we've created previously, as follows:
    await helper.openDb();
// await helper.testDb();
List <Place> _places = await helper.getPlaces();
for (Place p in _places) {
addMarker(Position(latitude: p.lat, longitude: p.lon),
p.id.toString(), p.name) ;
}
setState(() {
markers = markers;
});}
  1. Finally, at the end of the initState() method, let's also call insertMockData() (only the first time the app executes) and _getData(), as follows:
helper.insertMockData();
_getData();

From the second execution of our app, we'll comment out the helper.insertMockData() instruction.

Now that we can retrieve our current position and all the saved places we've saved, let’s try out the app. You should see a screen similar to the one in the following screenshot:

If you tap on any of the markers on the map, you should also be able to see its title.

To sum up, our app is now showing all the data to our user. When they first enter the app, they immediately see their current position and the saved places on the map. 

At the moment, users cannot insert, edit, or delete any data relative to the saved places. This is what we will cover next.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.57.172