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
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
Under the
SertifikasiBody class
,Replace
with
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 replacingfinal
with thevar
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 tovar sertifikasi = sertifikasis[index];
but since Image.memory only acceptsUint8List bytes
, so you need to decode the data first intobyte data
.Like this one:
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!