skip to Main Content

I have a vendors page where tap on a listview item and it takes me to another page called categories where I try to show the categories from an API call which requires an ID from the vendors page which I pass to the constructor of Categories… The issue is in the categories page where I get actual data from the API but doesn’t show up and it’s stuck at circular progress bar…

CategoriesList.dart

class CategoriesList extends StatefulWidget {
  final int vendorId;
  const CategoriesList({super.key, required this.vendorId});

  @override
  State<CategoriesList> createState() => _CategoriesListState();
}

class _CategoriesListState extends State<CategoriesList> {
  final _storage = const FlutterSecureStorage();
  String token = "";

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

  Future<void> retrieveToken() async {
    String? token = await _storage.read(key: 'jwt_token');
    setState(() {
      this.token = token!;
    });
    log(token!, name: 'token from categories page');
  }

  Future<List<CategoryModel>> fetchCategory(String token) async {
    var url =
        'http://example.com/api/Category/isActive?vendor_id=${widget.vendorId}';

    var response = await http.get(
      Uri.parse(url),
      headers: {
        'Authorization': 'Bearer $token',
        'accept': 'application/json',
        'Content-Type': 'application/json-patch+json',
      },
    );

    if (response.statusCode == 200) {
      List mapData = jsonDecode(response.body);
      List<CategoryModel> categories =
          mapData.map((category) => CategoryModel.fromJson(category)).toList();
      return categories;
    } else {
      throw Exception('Failed to load category: ${response.statusCode}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SafeArea(
          minimum: const EdgeInsets.all(10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const SizedBox(height: 20),
              const Padding(
                padding: EdgeInsets.only(left: 10.0),
                child: Text(
                  'Categories:',
                  style: TextStyle(
                    fontSize: 22,
                  ),
                ),
              ),
              const SizedBox(height: 20),
              Flexible(
                child: FutureBuilder<List<CategoryModel>>(
                  future: fetchCategory(token),
                  builder: (context, snapshot) {
                    if (snapshot.hasData) {
                      List<CategoryModel>? categories = snapshot.data;
                      return ListView.builder(
                          shrinkWrap: true,
                          itemCount: categories?.length,
                          itemBuilder: (BuildContext context, index) {
                            return Container(
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(8),
                                color: const Color.fromARGB(255, 231, 231, 231),
                              ),
                              height: 75,
                              margin: const EdgeInsets.only(
                                  top: 4, right: 8, left: 8, bottom: 8),
                              child: ListTile(
                                onTap: () => {
                                  // Navigator.of(context).push(MaterialPageRoute(
                                  //     builder: (context) => Storefront()))
                                  //log(token.toString(), name: 'token when click'),
                                },
                                leading: Image.network(
                                  "https://wallpaperaccess.com/full/2637581.jpg",
                                  width: 70,
                                  height: 70,
                                  fit: BoxFit.fitWidth,
                                ),
                                title: Text(categories![index].name),
                                subtitle: Text('categories[index].description'),
                              ),
                            );
                          });
                    } else {
                      return const Center(child: CircularProgressIndicator());
                    }
                  },
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

CategoriesModel.dart is my model class generated from https://quicktype.io/

import 'dart:convert';

class CategoryModel {
  final int id;
  final String name;
  final String description;
  final String createdAt;
  final String updatedAt;
  final bool isActive;
  final CategoryModel brandClass;
  final UserClass userClass;
  final dynamic vendorClass;

  CategoryModel({
    required this.id,
    required this.name,
    required this.description,
    required this.createdAt,
    required this.updatedAt,
    required this.isActive,
    required this.brandClass,
    required this.userClass,
    required this.vendorClass,
  });

  factory CategoryModel.fromRawJson(String str) =>
      CategoryModel.fromJson(json.decode(str));

  String toRawJson() => json.encode(toJson());

  factory CategoryModel.fromJson(Map<String, dynamic> json) => CategoryModel(
        id: json["id"],
        name: json["name"],
        description: json["description"],
        createdAt: json["created_at"],
        updatedAt: json["updated_at"],
        isActive: json["isActive"],
        brandClass: CategoryModel.fromJson(json["brandClass"]),
        userClass: UserClass.fromJson(json["userClass"]),
        vendorClass: json["vendorClass"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "name": name,
        "description": description,
        "created_at": createdAt,
        "updated_at": updatedAt,
        "isActive": isActive,
        "brandClass": brandClass.toJson(),
        "userClass": userClass.toJson(),
        "vendorClass": vendorClass,
      };
}

class UserClass {
  final int id;
  final dynamic email;
  final dynamic password;
  final String name;
  final dynamic mobile;
  final dynamic description;
  final bool isActive;
  final dynamic createdAt;
  final dynamic updatedAt;
  final dynamic userRoleClass;
  final dynamic userVerificationClass;

  UserClass({
    required this.id,
    required this.email,
    required this.password,
    required this.name,
    required this.mobile,
    required this.description,
    required this.isActive,
    required this.createdAt,
    required this.updatedAt,
    required this.userRoleClass,
    required this.userVerificationClass,
  });

  factory UserClass.fromRawJson(String str) =>
      UserClass.fromJson(json.decode(str));

  String toRawJson() => json.encode(toJson());

  factory UserClass.fromJson(Map<String, dynamic> json) => UserClass(
        id: json["id"],
        email: json["email"],
        password: json["password"],
        name: json["name"],
        mobile: json["mobile"],
        description: json["description"],
        isActive: json["isActive"],
        createdAt: json["created_at"],
        updatedAt: json["updated_at"],
        userRoleClass: json["userRoleClass"],
        userVerificationClass: json["userVerificationClass"],
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "email": email,
        "password": password,
        "name": name,
        "mobile": mobile,
        "description": description,
        "isActive": isActive,
        "created_at": createdAt,
        "updated_at": updatedAt,
        "userRoleClass": userRoleClass,
        "userVerificationClass": userVerificationClass,
      };
}

2

Answers


  1. Chosen as BEST ANSWER

    The issue was with FutureBuilder getting called more than once, apparently you have to make sure that FutureBuilder or StreamBuilder is only called once...

    var _categories; // later I pass this to the FutureBuilder 
    
      @override
      void initState() {
        super.initState();
        _categories = fetchCategory(token);
      }
    

    In the FutureBuilder,

    future: _categories,
    

  2. Not sure if you want it to stop spinning if no data is found… if should try this check

    Flexible(
                    child: FutureBuilder<List<CategoryModel>>(
                      future: fetchCategory(token),
                      builder: (context, snapshot) {
                        if (!snapshot.hasData) {
                          return const Center(
                            child: Text("Return some text"),
                          );
                        }
                        if (snapshot.hasData) {
                          List<CategoryModel>? categories = snapshot.data;
                          return ListView.builder(
                              shrinkWrap: true,
                              itemCount: categories?.length,
                              itemBuilder: (BuildContext context, index) {
                                return Container(
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(8),
                                    color: const Color.fromARGB(255, 231, 231, 231),
                                  ),
                                  height: 75,
                                  margin: const EdgeInsets.only(
                                      top: 4, right: 8, left: 8, bottom: 8),
                                  child: ListTile(
                                    onTap: () => {
                                      // Navigator.of(context).push(MaterialPageRoute(
                                      //     builder: (context) => Storefront()))
                                      //log(token.toString(), name: 'token when click'),
                                    },
                                    leading: Image.network(
                                      "https://wallpaperaccess.com/full/2637581.jpg",
                                      width: 70,
                                      height: 70,
                                      fit: BoxFit.fitWidth,
                                    ),
                                    title: Text(categories![index].name),
                                    subtitle: Text(categories[index].description),
                                  ),
                                );
                              });
                        } else {
                          return const Center(child: CircularProgressIndicator());
                        }
                      },
                    ),
                  )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search