Skip to content

TigerGraph Functionality

Now for the meat and potatoes of the project. We have already added the HTTP dependency to our pubspec.yml file, so we should be good to integrate the GET requests! First import HTTP into product_page.dart and user_page.dart like so:

import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';

Building a User Class

Let's begin with getting our User information in user_page.dart. We must first create a User class to contain what is returned from our userExists endpoint.

class User {
 final bool success;

 User({
   required this.success,
 });

 factory User.fromJson(Map<String, dynamic> json) {
   return User(
     success: json['res'],
   );
 }
}

Function Call

Then, a function must be created to call the userExists endpoint within the _UserPageState class.

Future<User> _userExists() async {
   final url = 'http://127.0.0.1:8000/userExists?user=$id';
   final response = await http.get(Uri.parse(url));

   if (response.statusCode == 200) {
     return User.fromJson(jsonDecode(response.body));
   } else {
     throw Exception('Failed to load user');
   }
 }

Text Controllers

Create some text controllers and variablesto get live changes to the inputs.

TextEditingController idController = TextEditingController();
String id = "";
String _idString = 'Your user will appear here if it exists! ';
bool isVisible = false;

Update Widgets and their Containers

In the body of our main widget, we will be including three Containers. These are nested in the body as a list of 'children'.

body: Padding(
           padding: EdgeInsets.all(10),
           child: ListView(children:

The first container is for the 'User Search' text above the search box.

Container(
                 alignment: Alignment.center,
                 padding: const EdgeInsets.all(10),
                 margin: const EdgeInsets.only(top: 50),
                 child: const Text(
                   'User Search',
                   style: TextStyle(
                       color: Colors.orange,
                       fontWeight: FontWeight.w500,
                       fontSize: 30),
                 )),

The second container is for the actual search box that contains our text controller for our input. This is where the user of the application will search for a user name in our data to see if it exists.

Container(
               padding:
                   EdgeInsets.only(top: 10, left: 150, right: 150, bottom: 10),
               child: TextField(
                 controller: idController,
                 decoration: InputDecoration(
                   border: OutlineInputBorder(),
                   labelText: 'User ID',
                 ),
               ),
             ),

Now the next container will add the functionality of a user searching the input and populate with the response from the endpoint call. The below code does the following within the controller: set stat with our user id input text on button press, call our _userExists function, set our return string to input text IF the _userExists function returns the proper result, or else let the user of the application know that they did not input a user id that exists, update the input boxes.

Container(
                 height: 50,
                 padding: EdgeInsets.only(
                     top: 0, left: 150, right: 150, bottom: 10),
                 child: RaisedButton(
                   textColor: Colors.white,
                   color: Colors.orange,
                   child: Text('Search'), 
                   onPressed: () {
                     print(idController.text);
                     setState(() {
                       id = idController.text;
                     });

                     _userExists().then((result) {
                       if (result.success) {
                         print("SUCCESS");
                         setState(() {
                           _idString = idController.text;
                         });
                       } else {
                         setState(() {
                           isVisible = true;
                           _idString =
                               "The user you searched does not exist! Oops!";
                         });
                       }
                     });
                   },
                 )),

The final container populates the text below the input box that follows 'User Name' with either the user name returned from the endpoint if it existed, or if a user name wasn't found, it will populate with a message letting the user know that the search was not successful.

padding: const EdgeInsets.only(
                     top: 0, left: 150, right: 150, bottom: 10),
                 child: Column(children: [
                   Text(
                     "User Name: " + _idString,
                     style: const TextStyle(fontSize: 30),
                   ),
                 ])
                 ),

Repeat for Recommender Endpoint

It is a similar process for the other endpoints we have created, but there are some differences with the JSON parsing. The Recommender API call returns a list, so the JSON response must be handled differently. We must also create our Recommendation class a bit differently. Notive we have now included a toString method to handle the output of the instance of our new class.

class Recommendation {
 final String id;
 final String name;
 final double avgRating;
 final int numRating;

 Recommendation(
     {required this.id,
     required this.name,
     required this.avgRating,
     required this.numRating});

 factory Recommendation.fromJson(Map<String, dynamic> json) {
   return Recommendation(
       id: json['id'],
       name: json['name'],
       avgRating: json['average rating'],
       numRating: json['num rating']);
 }
 @override
 String toString() {
   return '''
   Recommended ID: ${id}
   Product Name: ${name}
   Average Rating: ${avgRating}
   Number Rating: ${numRating}
   ''';
 }
}

The url response must be parsed and transformed into a list as well.

Future<List<Recommendation>> _fetchRecommendations() async {
   final url = 'http://127.0.0.1:8000/recommender?user=$recommendedUser';
   final response = await http.get(Uri.parse(url));

   if (response.statusCode == 200) {
     var jsonResponse = json.decode(response.body)['children'] as List;
     return jsonResponse
         .map((recommendation) => Recommendation.fromJson(recommendation))
         .toList();
   } else {
     throw Exception('Failed to load recommendation');
   }
 }

And now to call the new _fetchRecommendations() function when our search button is pressed.

onPressed: () {
                     setState(() {
                       recommendedUser = recommendationController.text;
                     });

                     _fetchRecommendations().then((result) {
                       // ignore: unnecessary_null_comparison
                       if (result != null) {
                         setState(() {
                           _recommendationName = recommendationController.text;
                           _recommendedProducts =
                               result.join((", ")).replaceAll(",", "");
                         });
                       } else {
                         setState(() {
                           isVisible = true;
                         });
                       }
                     });
                   },
                 )

Sample Input

Utilize the below sample input to test the TigerGraph Functionality

User IDs

  • richard
  • nash
  • Johnnyblack
  • llyyue

Product IDs

  • AVqVGZNvQMlgsOJE6eUY
  • AWFUWc8THh53nbDRF6YO
  • AVpftoij1cnluZ0-p5n2
  • AVpjEN4jLJeJML43rpUe

JSON Resources Sample Input

JSON parsing in Flutter was the most difficult concept for me to grasp, so below are a few resources that helped me understand it better:

Now follow the code provided to add the product search and product recommendation for the product page.

Back to top