flutterlistviewflutter-futurebuilder

my Future builder is returning error i have no clue what to do


here is my Future builder its returning an error i think its because of riverpod but i cant find a solution. the error is at the bottom i tried changedDependencies and didnt work im receiving the list normally and its showing in the console but its not showing in initState. my objective is to have self loading list whenever the page starts yet everything is fine concerning the data handling the list is being received and the api is connecting its just the snapshot.data is turning out null for some reason. its my first time using this type of builder so im hoping im clear enough on the matter.

import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import '../models/account.dart';
import '../provider/company_provider.dart';
import '../provider/user_provider.dart';

class AccountFinder extends ConsumerStatefulWidget {
  const AccountFinder({super.key});

  @override
  ConsumerState<AccountFinder> createState() => _AccountFinderState();
}

class _AccountFinderState extends ConsumerState<AccountFinder> {
  late Future<List<Account>> accounts;
  String searchString = "";
  Future<List<Account>> fetchAccounts() async {
    var userID = ref.watch(userStateProvider).id;
    var companyID = ref.watch(companyStateProvider).comID;
    print(userID);
    final url = Uri.parse(
      'http://api_adress/GetAllActs?CID=$companyID',
    );
    final headers = {'Content-Type': 'application/json'};
    final response = await http.post(
      url,
      headers: headers,
      body: json.encode({
        "id": userID,
        "userName": "string",
        "password": "string",
        "loggedIn": true,
        "userActive": true,
        "userDeleteable": true
      }),
    );

    if (response.statusCode == 200) {
      List<dynamic> listAccountsJson = jsonDecode(response.body);

      return listAccountsJson
          .map((account) => Account.fromJson(account))
          .toList();
    } else {
      throw Exception('Failed to load items');
    }
  }

  @override
  void initState() {
    super.initState();
    accounts = fetchAccounts();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        SafeArea(
          child: Container(
            height: 40,
            decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                color: Theme.of(context)
                    .colorScheme
                    .onBackground
                    .withOpacity(0.1)),
            margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
            child: TextField(
              onTapOutside: (event) =>
                  FocusManager.instance.primaryFocus?.unfocus(),
              onChanged: (value) {
                setState(() {
                  searchString = value.toLowerCase();
                });
              },
              style: Theme.of(context).textTheme.bodyMedium!,
              decoration: InputDecoration(
                  border: InputBorder.none,
                  hintText: AppLocalizations.of(context)!.search,
                  hintStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
                      fontSize: 16,
                      fontWeight: FontWeight.w300,
                      color: Theme.of(context)
                          .colorScheme
                          .onBackground
                          .withOpacity(0.7)),
                  prefixIcon: const Icon(
                    Icons.search,
                  )),
            ),
          ),
        ),
        IconButton(onPressed: fetchAccounts, icon: Icon(Icons.abc)),
        const SizedBox(height: 10),
        Expanded(
          child: FutureBuilder(
            builder: (context, AsyncSnapshot<List<Account>> snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return Center(child: Text('Error: ${snapshot.error}'));
              } else if (!snapshot.hasData) {
                return Center(child: Text('No data available'));
              } else {
                return SingleChildScrollView(
                  child: ListView.separated(
                    padding: const EdgeInsets.all(8),
                    itemCount: snapshot.data!.length,
                    itemBuilder: (BuildContext context, int index) {
                      return snapshot.data![index].actName!
                              .toLowerCase()
                              .contains(searchString)
                          ? Card(
                              elevation: 3,
                              child: ListTile(
                                title: Text('${snapshot.data?[index].actName}'),
                                subtitle: Text(
                                    ' ${snapshot.data?[index].actMobileNo}\n${snapshot.data?[index].actPhoneNo}\n'),
                              ),
                            )
                          : Container();
                    },
                    separatorBuilder: (BuildContext context, int index) {
                      return snapshot.data![index].actName!
                              .toLowerCase()
                              .contains(searchString)
                          ? const Divider()
                          : Container();
                    },
                  ),
                );
              }
            },
            future: accounts,
          ),
        ),
      ],
    );
  }
}

This the error im getting:

dependOnInheritedWidgetOfExactType<UncontrolledProviderScope>() or dependOnInheritedElement() was called before _AccountFinderState.initState() completed.
I/flutter (14799): When an inherited widget changes, for example if the value of Theme.of() changes, its dependent widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor or an initState() method, then the rebuilt dependent widget will not reflect the changes in the inherited widget.
I/flutter (14799): Typically references to inherited widgets should occur in widget build() methods. Alternatively, initialization based on inherited widgets can be placed in the didChangeDependencies method, which is called after initState and whenever the dependencies change thereafter.```



Solution

  • The error message you're getting provides a clear hint about the problem:

    dependOnInheritedWidgetOfExactType<UncontrolledProviderScope>() or dependOnInheritedElement() was called before _AccountFinderState.initState() completed.

    Inherited widgets, which Riverpod's providers are built upon, cannot be accessed in initState(). This is because initState() is called before the widget is fully integrated into the widget tree, which means it doesn't yet have access to the context from the widgets above it.

    Here's the solution based on the error:

    1. Use didChangeDependencies:

    Instead of fetching the data in initState(), override the didChangeDependencies() method which is called immediately after initState() and whenever the dependencies (like providers) change:

    @override
    void didChangeDependencies() {
      super.didChangeDependencies();
    
      // Check if accounts is not already set or you can use another mechanism 
      // to ensure you don't call fetchAccounts() multiple times
      if (accounts == null) {
        accounts = fetchAccounts();
      }
    }
    

    Note: Be cautious when using didChangeDependencies(). It can be called multiple times throughout the lifecycle of a widget, especially if the widget depends on multiple InheritedWidgets (e.g., providers, themes, etc.). Thus, you'll want to make sure you don't redundantly re-fetch data.

    2. Remove the call from initState():

    You can now remove the data fetching from the initState() method:

    @override
    void initState() {
      super.initState();
      // Remove accounts = fetchAccounts(); from here
    }
    

    By making this adjustment, you should no longer see the error related to accessing Riverpod providers (or any InheritedWidgets) inside initState().