Flutter

Parse Datatypes on Flutter

Introduction

In this guide you will learn about the Parse Datatypes using Flutter. You will read and save the Parse Datatythis on Back4App from a Flutter App.

Storing data on Parse is built around the ParseObject. Each ParseObject contains key-value pairs of JSON-compatible data. This data is schemaless, which means that you don’t need to specify ahead of time what keys exist on each ParseObject. You can set whatever key-value pairs you want, and our backend will store them. For example, let’s say you’re tracking high scores for a game. A single ParseObject could contain:

1
score: 1337, playerName: "Sean Plott", cheatMode: false

Keys must be alphanumeric strings, and values can be:

  • String
  • Number (primitive numeric values such as int, double)
  • Bool
  • DateTime
  • Map
  • Null
  • List (any type of data)
  • File
  • Pointer (association with another ParseObject: 1 to N)
  • Relation (association with another ParseObject: N to N)
  • Geopoint (store latitude and longitude coordinate)

Each ParseObject has a class name that you can use to distinguish different sorts of data. For example, we could call the high score object a GameScore. There are also a few fields you don’t need to specify that are provided as a convenience:

  • objectId is a unique identifier for each saved object.
  • createdAt and updatedAt represent the time that each object was created and last modified in the cloud.

Each of these fields is automatically filled in by Back4app at the moment you save a new ParseObject.

We recommend you NameYourClassesLikeThis and nameYourKeysLikeThis, just to keep your code looking pretty.

Prerequisites

To complete this tutorial, you will need:

Understanding our App

To better understand Back4app in Flutter, we will create an application that will save and retrieve records with the main types of data supported. We won’t explain the Flutter application code once this guide’s primary focus is using the Flutter with Parse. The data types Pointer, Relation, File, Geopoint will be covered later in specific guides.

Let’s get started!

In the following steps, you will build an application that will save and read the data in the Back4App database.

Step 1 - Create App Template

Open your Flutter project from the previous guide Flutter plugin for Parse Server.

Go to the main.dart file, clean up all the code, and replace it with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final keyApplicationId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  final keyClientKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  final keyParseServerUrl = 'https://parseapi.back4app.com';

  await Parse().initialize(keyApplicationId, keyParseServerUrl,
      clientKey: keyClientKey, debug: true);

  runApp(MaterialApp(
    home: Home(),
  ));
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  String objectId = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Parse Data Types"),
          backgroundColor: Colors.blueAccent,
          centerTitle: true,
        ),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Container(
                  height: 200,
                  child: Image.network(
                      'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
                ),
                SizedBox(
                  height: 16,
                ),
                ElevatedButton(onPressed: doSaveData, child: Text('Save Data')),
                ElevatedButton(onPressed: doReadData, child: Text('Read Data')),
                ElevatedButton(
                    onPressed: doUpdateData, child: Text('Update Data'))
              ],
            ),
          ),
        ));
  }

  void doSaveData() async {
    
  }

  void doReadData() async {
    
  }

  void doUpdateData() async {
    
  }
  
  void showMessage(String message) {
    ScaffoldMessenger.of(context)
      ..removeCurrentSnackBar()
      ..showSnackBar(SnackBar(content: Text(message)));
  }
}

Note:
When debug parameter in function Parse().initialize is true, allows displaying Parse API calls on the console. This configuration can assist in debugging the code. It is advisable to disable debug in the release version.

Step 2 - Connect Template to Back4app Project

Find your Application Id and Client Key credentials navigating to your app Dashboard at Back4App Website.

Update your code in main.dart with the values of your project’s ApplicationId and ClientKey in Back4app.

  • keyApplicationId = App Id
  • keyClientKey = Client Key

Run the project, and the app will load as shown in the image.

flutter-datatypes-app

Step 3 - Code for Save Object

The create function will create a new object in Back4app database. Search for the function doSaveData in file main.dart, and insert the code below inside the doSaveData function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    var parseObject = ParseObject("DataTypes")
      ..set("stringField", "String")
      ..set("doubleField", 1.5)
      ..set("intField", 2)
      ..set("boolField", true)
      ..set("dateField", DateTime.now())
      ..set("jsonField", {"field1": "value1", "field2": "value2"})
      ..set("listStringField", ["a", "b", "c", "d"])
      ..set("listIntField", [0, 1, 2, 3, 4])
      ..set("listBoolField", [false, true, false])
      ..set("listJsonField", [
        {"field1": "value1", "field2": "value2"},
        {"field1": "value1", "field2": "value2"}
      ]);
      
    final ParseResponse parseResponse = await parseObject.save();

    if (parseResponse.success) {
      objectId = (parseResponse.results.first as ParseObject).objectId;
      showMessage('Object created: $objectId');
    } else {
      showMessage('Object created with failed: ${parseResponse.error.toString()}');
    }

To build this function, follow these steps:

  1. Make a new instance of the Parse DataTypes class with the command ParseObject("DataTypes").
  2. Use the set function to set the parameters for this object,
  3. Call the save function in ParseObject, which will effectively register the object to your database in the Back4app Dashboard.
  4. Display a message with the objectId of the saved object.

To test it, click on the Run button in Android Studio/VSCode.
Click on the Save Data button.
To confirm that the new object is in the database, you can access the Back4app Dashboard or the doReadData function code.

Step 4 - Code for Read Object

The read function is responsible for querying the database and returning the object created in doSaveData function. Search for the function doReadData in the file main.dart, then insert the code below inside doReadData function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    if (objectId.isEmpty) {
      showMessage('None objectId. Click button Save Date before.');
      return;
    }

    QueryBuilder<ParseObject> queryUsers =
        QueryBuilder<ParseObject>(ParseObject('DataTypes'))
          ..whereEqualTo('objectId', objectId);
    final ParseResponse parseResponse = await queryUsers.query();
    if (parseResponse.success && parseResponse.results != null) {
      final object = (parseResponse.results.first) as ParseObject;
      print('stringField: ${object.get<String>('stringField')}');
      print('stringField: ${object.get<String>('stringField')}');
      print('doubleField: ${object.get<double>('doubleField')}');
      print('intField: ${object.get<int>('intField')}');
      print('boolField: ${object.get<bool>('boolField')}');
      print('dateField: ${object.get<DateTime>('dateField')}');
      print('jsonField: ${object.get<Map<String, dynamic>>('jsonField')}');
      print('listStringField: ${object.get<List>('listStringField')}');
      print('listNumberField: ${object.get<List>('listNumberField')}');
      print('listIntField: ${object.get<List>('listIntField')}');
      print('listBoolField: ${object.get<List>('listBoolField')}');
      print('listJsonField: ${object.get<List>('listJsonField')}');
    }

To build this function, follow these steps:

  1. Create an instance of ParseQuery object for DataTypes class. Insert a condition in the query, to search for the object created in the doSaveData function using the value of the objectId.
  2. Do a Query’s search method using query() method.
  3. If the operations succeed, one object DataTypes will be returned.
  4. To access the values of the fields of our ParseObject use the get method, informing the data type.

To test it, click on the Run button in Android Studio/VSCode.
First, click on the Save Data button e later click on Read Data button.
Check the query result on the console.

Step 4 - Code for Update Object and Special Methods

The update function is responsible for updating data in the object created on the doSaveData function. Search for the function doUpdateData in the file main.dart, and insert the code below inside the doUpdateData function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    if (objectId.isEmpty) {
      ScaffoldMessenger.of(context)
        ..removeCurrentSnackBar()
        ..showSnackBar(
            SnackBar(content: Text('None objectId. Click Save before.')));
      return;
    }

    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      ..set("intField", 3)
      ..set("listStringField", ["a", "b", "c", "d", "e"]);

    final ParseResponse parseResponse = await parseObject.save();

    if (parseResponse.success) {
      showMessage('Object updated: $objectId');
    } else {
      showMessage('Object updated with failed: ${parseResponse.error.toString()}');
    }

To build this function, follow these steps:

  1. Make a new instance of the Parse DataTypes class with the command ParseObject("DataTypes").
  2. Use the set function to define objectId of the object that we want to update and the field that we want to update
  3. Call the save function in ParseObject, which will effectively register the object to your database in the Back4app Dashboard.
  4. Display a message with the objectId of the updated object.

To test it, click on the Run button in Android Studio/VSCode.
First, click on the Save Data button e later click on Update Data button.
To confirm that the updated object is in the database, you can access the Back4app Dashboard or you can execute the doReadData function.

Step 5 - Using Counters

The above example contains a common use case. The intField field can be a counter that we’ll need to update continually. The above solution works, but it’s cumbersome and can lead to problems if you have multiple clients trying to update the same counter. Parse provides methods that atomically increment (or decrement) any number field to help with storing counter-type data. So, the same update can be rewritten as:

1
2
3
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      ..setIncrement("intField", 1);

To decrement:

1
2
3
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      ..setDecrement("intField", 1);

Step 6 - Using Lists

Parse also provides methods to help in storing list data. There are three operations that can be used to change a list field atomically:

  • setAdd and setAddAll: append the given objects to the end of an array field.
  • setAddUnique and setAddAllUnique: add only the given objects which aren’t already contained in an array field to that field. The position of the insert is not guaranteed.
  • remove and removeAll: removes all instances of the given objects from an array field.

Step 6.1 - Examples with setAdd and setAddAll

listStringField has the value:

1
["a","b","c","d","e","f","g","g"]

Running the code below:

1
2
3
4
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      ..setAdd("listStringField", "e");
    await parseObject.save();
1
2
3
4
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      ..setAddAll("listStringField", ["e", "f", "g", "g"]);
    await parseObject.save();

After this command the result of the stringList field will be:

1
["a","b","c","d","e","e","f","g","g"]

Step 6.2 - Examples with setAddUnique and setAddAllUnique

listStringField has the value:

1
["a","b","c","d","e"]

Running the code below:

1
2
3
4
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      .. setAddUnique("listStringField", "e");
    await parseObject.save();
1
2
3
4
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      .. setAddAllUnique("listStringField", ["c", "d", "e", "f"]);
    await parseObject.save();

After this command the result of the stringList field will be:

1
["a","b","c","d","e","f"]

No values were repeated.

Step 6.3 - Examples with remove and removeAll

listStringField has the value:

1
["a","b","c","d","e","f"]

Running the code below:

1
2
3
4
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      .. setRemove("listStringField", "f");
    await parseObject.save();
1
2
3
4
    final parseObject = ParseObject("DataTypes")
      ..objectId = objectId
      .. setRemoveAll("listStringField", ["c", "d", "e", "f"]);
    await parseObject.save();

After this command the result of the stringList field will be:

1
["a","b"]

Note that it is not currently possible to atomically add and remove items from an array in the same save. You will have to call the save for every different array operation you want to perform separately.

It’s done!

At this point, you learned about the Datatypes available on Parse Server and used them through a Flutter app. You learned how to read and save this data, special functions and learned a little more about ParseObject.