Flutter

Query in Flutter using Parse

Introduction

One of the essential features in many applications is finding specific data in a fast and straightforward way. Parse has excellent tools for data querying, such as comparing and ordering results, while being able to chain and combine queries into more complex ones.
In this guide, you will perform basic queries in Parse and implement Flutter widgets using these queries. You will learn how to set up and query realistic data using your Back4App and Flutter.

Prerequisites

To complete this tutorial, you will need:

Goal

Show how basic data queries work and how to perform them in Parse.

Step 1 - Understanding the QueryBuilder class

Any Parse query operation uses the QueryBuilder object type, which will help you retrieve specific data from your database throughout your app. It is crucial to know that a QueryBuilder will only resolve after calling a retrieve method query, so a query can be set up and several modifiers can be chained before actually being called.

To create a new QueryBuilder, you need to pass as a parameter the desired QueryBuilder subclass, which is the one that will contain your query results.

An example query can be seen below, in which a fictional Profile subclass is being queried.

1
2
3
4
5
6
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));
    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

You can read more about the QueryBuilder class here at the official documentation.

Step 2 - Populate Profile Class

Before going into this step, let’s create our example Profile class, which will be the target of our queries in this guide.

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
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';

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

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

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

  doCreateData();
}

void doCreateData() async {
  // Add Profile objects and create table
  var profile = ParseObject('Profile');
  profile.set('name', 'Adam Sandler');
  profile.set('birthDay', DateTime.parse('1966-09-09'));
  profile.set('friendCount', 2);
  profile.set('favoriteFoods', ['Lobster', 'Bread']);
  await profile.save();

  profile = ParseObject('Profile');
  profile.set('name', 'Adam Levine');
  profile.set('birthDay', DateTime.parse('1979-03-18'));
  profile.set('friendCount', 52);
  profile.set('favoriteFoods', ['Cake', 'Bread']);
  await profile.save();

  profile = ParseObject('Profile');
  profile.set('name', 'Carson Kressley');
  profile.set('birthDay', DateTime.parse('1969-11-11'));
  profile.set('friendCount', 12);
  profile.set('favoriteFoods', ['Fish', 'Cookies']);
  await profile.save();

  profile = ParseObject('Profile');
  profile.set('name', 'Dan Aykroyd');
  profile.set('birthDay', DateTime.parse('1952-07-01'));
  profile.set('friendCount', 66);
  profile.set('favoriteFoods', ['Jam', 'Peanut Butter']);
  await profile.save();

  profile = ParseObject('Profile');
  profile.set('name', 'Eddie Murphy');
  profile.set('birthDay', DateTime.parse('1961-04-03'));
  profile.set('friendCount', 49);
  profile.set('favoriteFoods', ['Lettuce', 'Pepper']);
  await profile.save();

  profile = ParseObject('Profile');
  profile.set('name', 'Fergie');
  profile.set('birthDay', DateTime.parse('1975-03-27'));
  profile.set('friendCount', 55);
  profile.set('favoriteFoods', ['Lobster', 'Shrimp']);
  await profile.save();
}

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 create Profile class.

flutter-back4app-basic-query-1

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 3 - Performing Basic Queries

Now that you have a populated class, we can now perform some basic queries in it.

Let’s begin by filtering Profile results by name, which is a string type field, searching for values that contain the name Adam using the QueryBuilder.whereContains method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));
	// `whereContains` is a basic query method that checks if string field
	// contains a specific substring
    parseQuery.whereContains('name', 'Adam');

	// The query will resolve only after calling this method, retrieving
	// an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }
    }

Let’s now query by the number type field friendCount by using another common query method, QueryBuilder.whereGreaterThan. In this case, we want user Profiles in which the friend count is greater than 20.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    // `whereGreaterThan` is a basic query method that does what it
    // says on the tin
    parseQuery.whereGreaterThan('friendCount', 20);

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }
    }

Other recurring query methods are QueryBuilder.orderByAscending and QueryBuilder.orderByDescending, responsible for ordering your queries. This ordering can be done in most data types, so let’s order a query by the date field birthDay by the youngest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    // `orderByDescending` and `orderByAscending(` can and should be chained
    // with other query methods to improve your queries
    parseQuery.orderByDescending('birthDay');

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }
    }

As stated here before, you can and should chain query methods to achieve more refined results. Let’s then combine the previous examples in a single query request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    parseQuery
      ..whereContains('name', 'Adam')
      ..whereGreaterThan('friendCount', 20)
      ..orderByDescending('birthDay');

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }
    }

Step 4 - Parse querying in Flutter

Let’s now use our example queries inside a app in Flutter, with a simple interface having a list showing results and also 4 buttons for calling the queries. This is how the component code is laid out, note the doQuery functions, containing the example code form before.

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';

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

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

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

  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: HomePage(),
  ));
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<ParseObject> results = <ParseObject>[];

  void doQueryByName() async {
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    // `whereContains` is a basic query method that checks if string field
    // contains a specific substring
    parseQuery.whereContains('name', 'Adam');

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }

      setState(() {
        results = apiResponse.results as List<ParseObject>;
      });
    } else {
      results = [];
    }
  }

  void doQueryByFriendCount() async {
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    // `whereGreaterThan` is a basic query method that does what it
    // says on the tin
    parseQuery.whereGreaterThan('friendCount', 20);

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }

      setState(() {
        results = apiResponse.results as List<ParseObject>;
      });
    } else {
      results = [];
    }
  }

  void doQueryByOrdering() async {
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    // `orderByDescending` and `orderByAscending(` can and should be chained
    // with other query methods to improve your queries
    parseQuery.orderByDescending('birthDay');

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }
      setState(() {
        results = apiResponse.results as List<ParseObject>;
      });
    } else {
      results = [];
    }
  }

  void doQueryByAll() async {
    // Create your query
    final QueryBuilder<ParseObject> parseQuery =
        QueryBuilder<ParseObject>(ParseObject('Profile'));

    parseQuery
      ..whereContains('name', 'Adam')
      ..whereGreaterThan('friendCount', 20)
      ..orderByDescending('birthDay');

    // The query will resolve only after calling this method, retrieving
    // an array of `ParseObjects`, if success
    final ParseResponse apiResponse = await parseQuery.query();

    if (apiResponse.success && apiResponse.results != null) {
      // Let's show the results
      for (var o in apiResponse.results!) {
        print((o as ParseObject).toString());
      }
      setState(() {
        results = apiResponse.results as List<ParseObject>;
      });
    } else {
      results = [];
    }
  }

  void doClearResults() async {
    setState(() {
      results = [];
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Container(
            height: 200,
            child: Image.network(
                'https://blog.back4app.com/wp-content/uploads/2017/11/logo-b4a-1-768x175-1.png'),
          ),
          Center(
            child: const Text('Flutter on Back4app - Basic Queries',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
          ),
          SizedBox(
            height: 8,
          ),
          Container(
            height: 50,
            child: ElevatedButton(
                onPressed: doQueryByName,
                child: Text('Query by name'),
                style: ElevatedButton.styleFrom(primary: Colors.blue)),
          ),
          SizedBox(
            height: 8,
          ),
          Container(
            height: 50,
            child: ElevatedButton(
                onPressed: doQueryByFriendCount,
                child: Text('Query by friend count'),
                style: ElevatedButton.styleFrom(primary: Colors.blue)),
          ),
          SizedBox(
            height: 8,
          ),
          Container(
            height: 50,
            child: ElevatedButton(
                onPressed: doQueryByOrdering,
                child: Text('Query by Ordening'),
                style: ElevatedButton.styleFrom(primary: Colors.blue)),
          ),
          SizedBox(
            height: 8,
          ),
          Container(
            height: 50,
            child: ElevatedButton(
                onPressed: doQueryByAll,
                child: Text('Query by all'),
                style: ElevatedButton.styleFrom(primary: Colors.blue)),
          ),
          SizedBox(
            height: 8,
          ),
          Container(
            height: 50,
            child: ElevatedButton(
                onPressed: doClearResults,
                child: Text('Clear results'),
                style: ElevatedButton.styleFrom(primary: Colors.blue)),
          ),
          SizedBox(
            height: 8,
          ),
          Text(
            'Result List: ${results.length}',
          ),
          Expanded(
            child: ListView.builder(
                itemCount: results.length,
                itemBuilder: (context, index) {
                  return Container(
                    padding: const EdgeInsets.all(4),
                    decoration:
                        BoxDecoration(border: Border.all(color: Colors.black)),
                    child: Text(results[index].toString()),
                  );
                }),
          )
        ],
      ),
    ));
  }
}

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.

This is how the app should look like after rendering and querying by all the query functions:

flutter-back4app-basic-query-2

Conclusion

At the end of this guide, you learned how basic data queries work and how to perform them in Parse on Flutter.

In the next guide, you will learn about all of querying possibilities and methods in Parse.