Search code examples
flutterdarthttprequest

API request to fetch conversation and messages in flutter dart


I'm working with APIs on an App to get the User information and in another tab to get the conversation of that particular user and the messages thread inside it knowing that I need as parameters the pid and tac (tab access code), and to get them they are located in the response body of another API (getTabAccessCode), I use Postman to try the calls and they work.

Below is user_service.dart, inside the getTabAccessCode function in its body response in postman I get the User id, pid, and tac (tab access code) :

 static Future<Map<String, dynamic>> fetchUserInfo(String accessToken) async {
    final response = await http.get(
      Uri.parse(
          'https://example.app/auth/example/protocol/userinfo'),
      headers: {
        'Authorization': 'Bearer $accessToken',
      },
    );

    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else {
      throw Exception(
          'Error fetching user information: ${response.statusCode}');
    }
  }

// This function takes a global ID and an access token as parameters

static Future<Map<String, dynamic>> getTabAccessCode(
  String globalId, String accessToken) async {
    // This is the URL for the device registration
    final tokenUrl =
        'http://example.com:9080/exp/rest/DeviceRegistration';

    // This is the HTTP GET request with the query parameters
    final response = await http.get(
      Uri.parse(
          '$tokenUrl?country=United%20Kingdom&emailID=example%40gmail.com&name=Patient%20Eleven&zoneinfo=Europe%2FParis&GlobalId=$globalId&postal_code=70001&gender=Female&accesstoken=$accessToken&appname=example'),
    );
    

    // This checks if the response status code is 200, which means OK
    if (response.statusCode == 200) {
      // This parses the response body into a Dart object
      final responseBody = jsonDecode(response.body);
      // Print the entire response line
      print('Response: $responseBody');
      // This gets the value of "TabAccCode"
      final tabAccessCode = responseBody["TabAccCode"];

      // If the response includes "pid" and "doctor id", retrieve them as well
      final pid = responseBody["pid"];
      final doctorid = responseBody["doctorid"];

      // Create a map to return all the values
      final resultMap = {
        "tabAccessCode": tabAccessCode,
        "pid": pid,
        "doctorid": doctorid,
      };

      // This returns the map containing all the values
      return resultMap;
    } else {
      // This throws an exception if the response status code is not 200
      throw Exception('Error getting Tab Access Info: ${response.statusCode}');
    }
  }



Note: the name of tab access code in getTabAccessCode API call's response is TabAccCode, whereas in the parameters of conversation and messages API call is called tac.

Can someone please help me I'm new to working with APIs and I'm stuck on this.

This is the conversation_page.dart, below I tried to call the function (getTabAccessCode) that has the pid and tac inside its response but it says that the globalid and the accesstoken are undefined knowing that they are already defined but in user_service.dart:

import 'dart:convert';
import 'package:project/screens/messages_page.dart';
import 'package:project/screens/user_service.dart';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class ConversationPage extends StatefulWidget {
  @override
  _ConversationPageState createState() => _ConversationPageState();
}

class _ConversationPageState extends State<ConversationPage> {
  List<Map<String, dynamic>> conversations = [];
  String pid = '';
  String tac = '';
  @override
  void initState() {
    super.initState();
    initConversations();
  }

  Future<void> initConversations() async {
    var accessInfo = await UserService.getTabAccessCode(globalId, accessToken);
    var pid = accessInfo["pid"];
    var tac = accessInfo["tabAccessCode"];
    await fetchConversations(pid, tac);
  }

  Future<void> fetchConversations(String pid, String tac) async {
    final response = await http.get(Uri.parse(
      'https://example.com/docs/getMessages?pid=$pid&user_type=doctor&function=conversation&tac=$tac&view_as=doctor',
    ));

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      if (data['success']) {
        final List<dynamic> conversationData = data['data'];
        conversations = conversationData
            .map((conversation) => Map<String, dynamic>.from(conversation))
            .toList();
      }
    } else {
      throw Exception('Failed to load conversation data');
    }

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Conversation Page'),
      ),
      body: ListView.builder(
        itemCount: conversations.length,
        itemBuilder: (context, index) {
          final conversation = conversations[index];
          return ListTile(
            title: Text(conversation['title']),
            subtitle: Text(conversation['created']),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => MessagePage(
                      conversationId: conversation['id'], pid: pid, tac: tac),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

this is messages_page.dart:

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class MessagePage extends StatefulWidget {
  final String conversationId;
 
 
  final String pid;
  final String tac;
  MessagePage({required this.conversationId, required this.pid, required this.tac});

  @override
  _MessagePageState createState() => _MessagePageState();
}

class _MessagePageState extends State<MessagePage> {
  List<Map<String, dynamic>> messages = [];

  @override
  void initState() {
    super.initState();
    fetchMessages();
  }

Future<void> fetchMessages() async {
  final response = await http.get(Uri.parse(
    'https://example.com/doc/getMessages?user_type=doctor&function=messages&conversation_id=${widget.conversationId}&pid=$pid&tac=$tac&view_as=doctor',
  ));

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      if (data['success']) {
        final List<dynamic> messagesData = data['data']['messages'];
        messages = messagesData.map((message) => Map<String, dynamic>.from(message)).toList();
      }
    } else {
      throw Exception('Failed to load messages data');
    }

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Message Page'),
      ),
      body: ListView.builder(
        itemCount: messages.length,
        itemBuilder: (context, index) {
          final message = messages[index];
          return ListTile(
            title: Text(message['sender_name']),
            subtitle: Text(message['message']),
            // You can customize how you want to display the message data here.
            // For example, you can format the sent_at date, display sender's type, etc.
          );
        },
      ),
    );
  }
}

Solution

  • I look at your code and seems you have to deepen a bit your understanding of async / sync functions.

    Your widget is build synchronously, whereas your functions are asynchronous. It means that your Widget build first and tries to access data that haven't be fetched yet.

    What you need to do is use FutureBuilder. They are widgets that waits the API response before building themselves.

    You can display a loadingIndicator or whatever if your Widget hasn't fetch the data yet.

    Hope it can helps you, and I made it clear.

    Looking forward your updates :)