skip to Main Content

here is my code

import 'package:flutter/material.dart';
import 'package:mobile_smarcerti/app/modules/sertifikasi/views/detail_sertifikasi_body.dart';
import 'package:mobile_smarcerti/app/modules/sertifikasi/views/detail_sertifikasi_page.dart';
import 'package:mobile_smarcerti/pages/upload_sertifikasi_dosen.dart';
import 'package:mobile_smarcerti/services/api_service.dart';

class SertifikasiBody extends StatefulWidget {
  const SertifikasiBody({super.key});

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

class __SertifikasiScreenState extends State<SertifikasiBody> {
  late Future<List<dynamic>> _sertifikasiFuture;

  @override
  void initState() {
    super.initState();
    _sertifikasiFuture = ApiService().fetchSertifikasi();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: TextField(
                    decoration: InputDecoration(
                      hintText: 'Search',
                      prefixIcon: const Icon(Icons.search),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                      fillColor: const Color.fromARGB(145, 255, 249, 249),
                      filled: true,
                    ),
                  ),
                ),
                const SizedBox(width: 10),
                Container(
                  height: 54,
                  width: 48,
                  decoration: BoxDecoration(
                    color: const Color.fromARGB(145, 255, 249, 249),
                    borderRadius: BorderRadius.circular(12),
                    border: Border.all(color: Colors.black),
                  ),
                  child: IconButton(
                    icon: const Icon(Icons.filter_list),
                    color: Colors.grey[700],
                    onPressed: () {
                      showDialog(
                        context: context,
                        builder: (BuildContext context) {
                          return AlertDialog(
                            title: const Text("Filter Options"),
                            content: Column(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                ListTile(
                                  title: const Text("Filter 1"),
                                  onTap: () => Navigator.pop(context),
                                ),
                                ListTile(
                                  title: const Text("Filter 2"),
                                  onTap: () => Navigator.pop(context),
                                ),
                                ListTile(
                                  title: const Text("Filter 3"),
                                  onTap: () => Navigator.pop(context),
                                ),
                              ],
                            ),
                          );
                        },
                      );
                    },
                  ),
                ),
              ],
            ),
            const SizedBox(height: 10),
            Expanded(
              child: FutureBuilder<List<dynamic>>(
                future: _sertifikasiFuture,
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.waiting) {
                    return const Center(child: CircularProgressIndicator());
                  } else if (snapshot.hasError) {
                    return Center(child: Text('Error: ${snapshot.error}'));
                  } else if (!snapshot.hasData || snapshot.data!.isEmpty) {
                    return const Center(child: Text('No data available'));
                  } else {
                    final sertifikasis = snapshot.data!;
                    return ListView.builder(
                      itemCount: sertifikasis.length,
                      itemBuilder: (context, index) {
                        final sertifikasi = sertifikasis[index];
                        return Card(
                          color: Colors.white,
                          margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),
                          child: ListTile(
                            leading: const Icon(
                              Icons.library_books,
                              size: 35.0,
                              color: Color.fromARGB(255, 55, 94, 151),
                            ),
                            title: Text(
                              sertifikasi['nama_sertifikasi'],
                              style: const TextStyle(
                                fontFamily: 'Poppins',
                                fontSize: 16,
                                color: Color.fromARGB(255, 55, 94, 151),
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                            onTap: () {
                              Navigator.push(
                                context,
                                MaterialPageRoute(
                                  builder: (context) => DetailSertifikasi(
                                    idSertifikasi: sertifikasi['id_sertifikasi'],
                                  ),
                                ),
                              );
                            },
                            trailing: const Icon(
                              Icons.arrow_forward_ios,
                              size: 20.0,
                              color: Color.fromARGB(255, 55, 94, 151),
                            ),
                            contentPadding: const EdgeInsets.all(20),
                          ),
                        );
                      },
                    );
                  }
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

here is my api_service code

// lib/services/api_service.dart
import 'package:dio/dio.dart';
import 'package:mobile_smarcerti/app/utils/constant.dart';
import 'package:mobile_smarcerti/app/utils/dio_interceptors.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ApiService {
  late Dio _dio;

  ApiService() {
    _dio = Dio(
      BaseOptions(
        baseUrl: ApiConstants.baseUrl,
        connectTimeout: const Duration(seconds: 5),
        receiveTimeout: const Duration(seconds: 3),
        validateStatus: (status) {
          return status! < 500;
        },
      ),
    );

    _dio.interceptors.add(DioInterceptor());
  }

  Future<Response> get(
    String path, {
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final token = prefs.getString('auth_token');

      final response = await _dio.get(
        path,
        queryParameters: queryParameters,
        options: Options(headers: {
          'Authorization': 'Bearer $token',
          'Accept': 'application/json',
          if (options?.headers != null) ...options!.headers!,
        }),
      );

      return response;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Future<Response> post(
    String path, {
    dynamic data,
    Map<String, dynamic>? queryParameters,
    Options? options,
  }) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final token = prefs.getString('auth_token');

      final response = await _dio.post(
        path,
        data: data,
        queryParameters: queryParameters,
        options: Options(
          headers: {
            'Authorization': 'Bearer $token',
            'Accept': 'application/json',
            if (options?.headers != null) ...options!.headers!,
          },
        ),
      );

      return response;
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }

  Exception _handleError(DioException e) {
    if (e.type == DioExceptionType.connectionTimeout) {
      return Exception('Connection timeout');
    }
    if (e.type == DioExceptionType.receiveTimeout) {
      return Exception('Unable to connect to the server');
    }
    if (e.type == DioExceptionType.unknown) {
      return Exception('Something went wrong');
    }

    return Exception(e.message);
  }

  Future<List<dynamic>> fetchSertifikasi() async {
    try {
      final response = await _dio.get('/sertifikasis/');
      if (response.data['success']) {
        return response.data['data'];
      } else {
        throw Exception('Failed to load sertifikasi');
      }
    } catch (e) {
      throw Exception('Failed to load sertifikasi: $e');
    }
  }

I have tried by creating a new flutter project with just that certification feature and it works properly but somehow it doesn’t work when I try on my main project

I’ve been trying to solve this problem for 3 hours and the result is still the same.

here is the error in my virtual phone:

Error: Exception: Failed to load sertifikasi: type 'String' is not a subtype of type 'int' of 'index'

and here is the error in debug console:

D/EGL_emulation(17071): app_time_stats: avg=426.41ms min=6.15ms max=6485.81ms count=16
W/WindowOnBackDispatcher(17071): OnBackInvokedCallback is not enabled for the application.
W/WindowOnBackDispatcher(17071): Set 'android:enableOnBackInvokedCallback="true"' in the application manifest.
D/EGL_emulation(17071): app_time_stats: avg=28.53ms min=4.21ms max=433.69ms count=29

2

Answers


  1. type ‘String’ is not a subtype of type ‘int’ of ‘index’ generally faced when you try to access a value from a map or list where the key or index is expected to be of type int, but you’re providing a String (or vice versa). In your code, the problem is likely with how the API response data is being parsed in the fetchSertifikasi method.

    Possible issue can be:

    In fetchSertifikasi, you’re returning response.data[‘data’], assuming it contains a list of dynamic objects (like maps). However, in your main project, the data field might not have the expected structure, or some fields (e.g., id_sertifikasi) might not have the correct data type (e.g., being a String instead of an int).

    Debuging steps

    Step 1: Log the response from the API to see its exact structure and confirm data types.

    Step 2: Ensure Consistent Data Types
    If the API is returning id_sertifikasi as a String instead of int, you should modify how you handle this data.

    Step 3: Check JSON Parsing
    If you expect specific data types from the API, use a model class to parse and validate the JSON

    Login or Signup to reply.
  2. Under the SertifikasiBody class,

    Replace

    final sertifikasi = sertifikasis[index];
    

    with

    var sertifikasi = sertifikasis[index];
    

    It should work as expected as long as there is no data decoding or some conversion needed before it touches the widget.

    Like, for example, if the generated data coming from sertifikasis[index] is not conflicting with the widget you’re using. Then replacing final with the var keyword will resolve the issue of having a type exception.

    However, if sertifikasis[index] generates base64 data, and this data will be used to attach with the Image.memory widget, even the variable you use is set to var sertifikasi = sertifikasis[index]; but since Image.memory only accepts Uint8List bytes, so you need to decode the data first into byte data.

    Like this one:

    Image.memory(
      base64Decode(sertifikasi['some_base64_data']),
    ),
    

    Conclusion, one of the most important parts of handling data from your api_service is that you need to know what data type is needed by your widget.

    Hope it helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search