Back to Blog

How Do I Integrate a JAMB API Into a Flutter App?

If you're building a JAMB CBT app with Flutter, you can integrate JAMB/UTME past questions into your application using SdashAPI.

SdashAPI provides a REST API that allows Flutter developers to fetch structured JAMB questions, answer options, correct answers, solutions and examination information as JSON.

Instead of manually creating and maintaining thousands of examination questions inside your Flutter application, your app can request the questions it needs from SdashAPI.

The basic architecture looks like this:

Flutter App
     ↓
Your Backend
     ↓
SdashAPI
     ↓
JAMB/UTME Questions
     ↓
JSON Response

For development and testing, you can also call the API directly from Flutter.

Let's build a basic integration.

Step 1: Create a SdashAPI Developer Account

Before making API requests, create a developer account with SdashAPI.

After registration, your developer dashboard gives you access to your Access Token.

The Access Token acts as your API credential when making requests.

SdashAPI expects it in the request header:

AccessToken: YOUR_ACCESS_TOKEN

Replace YOUR_ACCESS_TOKEN with the actual token from your SdashAPI account.

Step 2: Understand the JAMB Questions Endpoint

For basic CBT applications, you can use the SdashAPI V1 questions endpoint:

https://sdashapi.com/api/v1/q

For JAMB questions, specify:

type=utme

For example, to request Biology UTME questions:

https://sdashapi.com/api/v1/q?subject=biology&type=utme

You can also specify the examination year:

https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022

And request multiple questions:

https://sdashapi.com/api/v1/q?subject=biology&type=utme&year=2022&limit=20

Your Flutter application can then process the JSON returned by SdashAPI.

Step 3: Install the Flutter HTTP Package

Flutter applications commonly use the http package for REST API requests.

Add it to your project:

flutter pub add http

Or add it to your pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  http: ^1.0.0

Then run:

flutter pub get

Import the package:

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

You'll also need Dart's JSON utilities:

import 'dart:convert';

Step 4: Make Your First JAMB API Request

Now you can request questions from SdashAPI.

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

Future<void> fetchJambQuestions() async {
  final url = Uri.parse(
    'https://sdashapi.com/api/v1/q'
    '?subject=biology'
    '&type=utme'
    '&year=2022'
    '&limit=20',
  );

  final response = await http.get(
    url,
    headers: {
      'AccessToken': 'YOUR_ACCESS_TOKEN',
    },
  );

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body);

    print(data);
  } else {
    print('Request failed: ${response.statusCode}');
  }
}

The important part is:

final data = jsonDecode(response.body);

This converts the JSON response into Dart objects that your Flutter application can work with.

What Does the JAMB API Return?

SdashAPI returns structured JSON.

A simplified response can look like:

{
  "status": 200,
  "data": {
    "id": 4821,
    "question": "Which of the following is the chemical formula for table salt?",
    "section": null,
    "option": {
      "a": "NaCl",
      "b": "KCl",
      "c": "CaCO3",
      "d": "NaOH"
    },
    "answer": "a",
    "solution": "NaCl is sodium chloride.",
    "image": null,
    "examtype": "UTME",
    "examyear": "2022"
  }
}

Your Flutter application can use these fields to construct the CBT interface.

For example:

question
    ↓
Display question text

option
    ↓
Display A, B, C and D

answer
    ↓
Mark student's response

solution
    ↓
Show correction

examyear
    ↓
Display examination year

Step 5: Create a Question Model in Flutter

For a proper Flutter application, it is better to convert the JSON into a Dart model.

Create:

jamb_question.dart

Then:

class JambQuestion {
  final int id;
  final String question;
  final Map<String, dynamic> options;
  final String answer;
  final String? solution;
  final String? examType;
  final String? examYear;

  JambQuestion({
    required this.id,
    required this.question,
    required this.options,
    required this.answer,
    this.solution,
    this.examType,
    this.examYear,
  });

  factory JambQuestion.fromJson(
    Map<String, dynamic> json,
  ) {
    return JambQuestion(
      id: json['id'],
      question: json['question'] ?? '',
      options: Map<String, dynamic>.from(
        json['option'] ?? {},
      ),
      answer: json['answer'] ?? '',
      solution: json['solution'],
      examType: json['examtype'],
      examYear: json['examyear']?.toString(),
    );
  }
}

Now each question returned by the API can become a JambQuestion object.

Step 6: Create a SdashAPI Service

Instead of placing your HTTP logic directly inside your widgets, create a service.

For example:

lib/services/sdash_api_service.dart

Then:

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

class SdashApiService {
  static const String baseUrl =
      'https://sdashapi.com/api/v1/q';

  final String accessToken;

  SdashApiService(this.accessToken);

  Future<dynamic> getQuestions({
    required String subject,
    String type = 'utme',
    String? year,
    int limit = 20,
  }) async {
    final queryParameters = {
      'subject': subject,
      'type': type,
      'limit': limit.toString(),
      if (year != null) 'year': year,
    };

    final uri = Uri.parse(baseUrl).replace(
      queryParameters: queryParameters,
    );

    final response = await http.get(
      uri,
      headers: {
        'AccessToken': accessToken,
      },
    );

    if (response.statusCode == 200) {
      return jsonDecode(response.body);
    }

    throw Exception(
      'Unable to fetch questions. '
      'Status: ${response.statusCode}',
    );
  }
}

Now the API code is separate from your user interface.

Step 7: Fetch Questions From Your Flutter Screen

You can now call the service:

final api = SdashApiService(
  'YOUR_ACCESS_TOKEN',
);

final result = await api.getQuestions(
  subject: 'biology',
  year: '2022',
  limit: 20,
);

Your app requests:

Biology
+
UTME
+
2022
+
20 Questions

and SdashAPI returns the matching question data.

Step 8: Store Questions in Flutter State

Inside a StatefulWidget, you could use:

List<JambQuestion> questions = [];
bool loading = true;
int currentIndex = 0;

Then create a function to load the questions:

Future<void> loadQuestions() async {
  try {
    final result = await api.getQuestions(
      subject: 'biology',
      year: '2022',
      limit: 20,
    );

    final rawData = result['data'];

    final List<dynamic> questionList =
        rawData is List ? rawData : [rawData];

    setState(() {
      questions = questionList
          .map(
            (item) => JambQuestion.fromJson(
              item,
            ),
          )
          .toList();

      loading = false;
    });
  } catch (error) {
    print(error);

    setState(() {
      loading = false;
    });
  }
}

Call it from initState():

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

Step 9: Display a JAMB Question

Get the current question:

final question = questions[currentIndex];

Then display it:

Text(
  'Question ${currentIndex + 1} '
  'of ${questions.length}',
),

const SizedBox(height: 20),

Text(
  question.question,
  style: const TextStyle(
    fontSize: 18,
    fontWeight: FontWeight.w500,
  ),
),

Your user might see:

Question 1 of 20

Which of the following is the
chemical formula for table salt?

Step 10: Display the Answer Options

The options are stored as a map.

For example:

{
  "a": "NaCl",
  "b": "KCl",
  "c": "CaCO3",
  "d": "NaOH"
}

You can render them like this:

Column(
  children: question.options.entries.map(
    (entry) {
      return ListTile(
        title: Text(
          '${entry.key.toUpperCase()}. '
          '${entry.value}',
        ),
        onTap: () {
          selectAnswer(entry.key);
        },
      );
    },
  ).toList(),
)

Now your student can select an option.

Step 11: Save the Student's Answers

Create a map:

Map<int, String> selectedAnswers = {};

Then:

void selectAnswer(String option) {
  final question = questions[currentIndex];

  setState(() {
    selectedAnswers[question.id] = option;
  });
}

After several questions, the map might look like:

{
  4821: 'a',
  4822: 'c',
  4823: 'b',
  4824: 'd'
}

This allows you to remember each student's selected answer.

Step 12: Add Previous and Next Buttons

Create navigation functions:

void nextQuestion() {
  if (currentIndex < questions.length - 1) {
    setState(() {
      currentIndex++;
    });
  }
}

void previousQuestion() {
  if (currentIndex > 0) {
    setState(() {
      currentIndex--;
    });
  }
}

Then:

Row(
  mainAxisAlignment:
      MainAxisAlignment.spaceBetween,
  children: [
    ElevatedButton(
      onPressed:
          currentIndex > 0
              ? previousQuestion
              : null,
      child: const Text('Previous'),
    ),
    ElevatedButton(
      onPressed:
          currentIndex < questions.length - 1
              ? nextQuestion
              : null,
      child: const Text('Next'),
    ),
  ],
)

You now have basic CBT navigation.

Step 13: Calculate the JAMB CBT Score

When the student submits the test, compare their selected answers with the answers returned by SdashAPI.

int calculateScore() {
  int score = 0;

  for (final question in questions) {
    final selected =
        selectedAnswers[question.id];

    if (selected == question.answer) {
      score++;
    }
  }

  return score;
}

For example:

final score = calculateScore();

print(
  'You scored $score/${questions.length}',
);

If the student gets 16 correct out of 20:

Score: 16/20

You can also calculate the percentage:

final percentage =
    (score / questions.length) * 100;

Step 14: Display Corrections and Solutions

After submission, you can show the student:

Text(
  'Correct Answer: '
  '${question.answer.toUpperCase()}',
),

Text(
  question.solution ??
      'No explanation available.',
),

This makes the application useful for revision as well as examination practice.

Instead of simply saying:

Wrong

your application can show the correct answer and available solution.

Allow Students to Choose a JAMB Subject

Don't hardcode Biology permanently.

Create a list of subjects:

final subjects = [
  'english',
  'mathematics',
  'biology',
  'chemistry',
  'physics',
  'economics',
  'government',
];

Store the selected subject:

String selectedSubject = 'biology';

Then pass it into your API request:

final result = await api.getQuestions(
  subject: selectedSubject,
  year: selectedYear,
  limit: 40,
);

Your Flutter application can now dynamically request questions based on the student's selection.

Allow Students to Select a JAMB Year

You can also create a year selector:

final years = [
  '2021',
  '2022',
  '2023',
  '2024',
  '2025',
  '2026',
];

Then:

String selectedYear = '2024';

The resulting API request can become:

/api/v1/q?subject=biology&type=utme&year=2024&limit=40

This allows you to build a feature such as:

Select Examination

JAMB 2026
JAMB 2025
JAMB 2024
JAMB 2023
JAMB 2022

Add a CBT Timer

SdashAPI handles your question data.

Your Flutter application should handle the CBT timer.

For example:

int secondsRemaining = 1800;
Timer? timer;

Import:

import 'dart:async';

Then:

void startTimer() {
  timer = Timer.periodic(
    const Duration(seconds: 1),
    (_) {
      if (secondsRemaining > 0) {
        setState(() {
          secondsRemaining--;
        });
      } else {
        timer?.cancel();
        submitExam();
      }
    },
  );
}

Now your application can automatically submit when time expires.

Show a Question Navigation Grid

A more realistic CBT application should let students jump between questions.

For example:

[1] [2] [3] [4] [5]
[6] [7] [8] [9] [10]

You can use:

GridView.builder(
  shrinkWrap: true,
  itemCount: questions.length,
  gridDelegate:
      const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 5,
  ),
  itemBuilder: (context, index) {
    return InkWell(
      onTap: () {
        setState(() {
          currentIndex = index;
        });
      },
      child: Center(
        child: Text('${index + 1}'),
      ),
    );
  },
)

This creates a more traditional CBT experience.

Should I Put My SdashAPI Access Token Directly in Flutter?

For local development and quick testing, putting your Access Token in your request makes it easy to understand the integration.

For a production Flutter application, however, you should not treat a token compiled into your mobile application as secret.

Someone can potentially inspect the application and extract it.

A better production structure is:

Flutter App
     ↓
Your Backend
     ↓
SdashAPI

Your Flutter app might call:

https://api.yourapp.com/jamb/questions

Your backend then calls:

https://sdashapi.com/api/v1/q

using your private SdashAPI Access Token.

This keeps your main credential away from the client application.

Why Use a Backend?

A backend also gives you control over things like:

  • User authentication
  • API usage
  • Subscription plans
  • Rate limiting
  • Caching
  • Abuse prevention
  • Analytics
  • Daily question limits
  • Premium features

For example:

Student
   ↓
Flutter App
   ↓
Your API
   ↓
Check Student Subscription
   ↓
Request Questions From SdashAPI
   ↓
Return Questions

This is a much better architecture for a commercial CBT application.

Can I Use SdashAPI With FlutterFlow?

If your Flutter-based platform can make authenticated REST API requests and process JSON responses, you can connect it to a REST API such as SdashAPI.

The important requirements are the ability to:

  1. Send a GET request.
  2. Add the required Access Token header.
  3. Add query parameters.
  4. Parse the JSON response.
  5. Map the returned fields into your interface.

For production applications, the same advice about protecting private API credentials still applies.

Can I Build a Complete JAMB CBT App With Flutter and SdashAPI?

Yes.

A typical application could follow this flow:

Splash Screen
      ↓
Login/Register
      ↓
Dashboard
      ↓
Select JAMB
      ↓
Select Subject
      ↓
Select Year
      ↓
Instructions
      ↓
Start CBT
      ↓
Answer Questions
      ↓
Submit
      ↓
Results
      ↓
Corrections

SdashAPI handles the structured examination content while Flutter handles the user experience.

What About More Advanced Features?

SdashAPI provides multiple API versions for different levels of educational functionality.

V1: Core Questions

Useful for standard CBT applications.

It provides the core question data needed to display and mark examination questions.

V2: Question Metadata

Useful when you want to organise practice around information such as topics, subtopics and other classifications.

V3: Detailed Explanations

Useful when you want your application to teach students rather than simply mark their answers.

V4: Intelligent and Related Questions

Useful for building more adaptive learning experiences and recommending related questions.

This means your Flutter app can start as a basic CBT platform and gradually become a more advanced learning system.

Can I Add JAMB Novel Features?

SdashAPI also provides API functionality around educational novels.

This allows a Flutter developer to create an application containing features such as:

JAMB CBT
+
Past Questions
+
Solutions
+
JAMB Novel
+
Novel Summaries
+
Novel Practice Questions

without building every educational dataset separately.

Can I Test the JAMB API for Free?

Yes.

SdashAPI provides developers with a Sandbox containing 2,000 API credits for development and testing.

You can use the Sandbox while building your Flutter prototype and testing:

  • API authentication
  • HTTP requests
  • JSON decoding
  • Subject filtering
  • Year filtering
  • Question rendering
  • Answer selection
  • CBT navigation
  • Scoring
  • Error handling

Once your project requires broader production access, you can move to the appropriate SdashAPI plan.

How Do I Integrate a JAMB API Into a Flutter App?

The complete process is:

  1. Create a SdashAPI developer account.
  2. Get your Access Token.
  3. Add the Flutter http package.
  4. Make a GET request to the SdashAPI questions endpoint.
  5. Set type=utme for JAMB questions.
  6. Add your subject.
  7. Add the examination year if needed.
  8. Set the number of questions you want.
  9. Send your Access Token in the request header.
  10. Decode the JSON response with jsonDecode().
  11. Convert the JSON into Dart models.
  12. Store the questions in your Flutter state.
  13. Display the question and options.
  14. Record the student's selected answers.
  15. Add previous and next navigation.
  16. Calculate the score after submission.
  17. Display solutions and corrections.
  18. Move your private Access Token to your backend before production.

The basic Flutter request is:

final response = await http.get(
  Uri.parse(
    'https://sdashapi.com/api/v1/q'
    '?subject=biology'
    '&type=utme'
    '&year=2024'
    '&limit=20',
  ),
  headers: {
    'AccessToken': 'YOUR_ACCESS_TOKEN',
  },
);

final data = jsonDecode(response.body);

From there, you can build the CBT experience however you want.

SdashAPI provides the structured JAMB examination data while Flutter gives you the tools to build the Android, iOS and cross-platform user experience around it.

Avatar

How can we help?

We reply immediately

Hello! 👋 How can we help you today?