1

I have FutureBuilders all over my app and the problem is when opening a page there would be CircularProgressIndicators everywhere on the screen, and whenever the page rebuilds itself the CircularProgressIndicators is displays again. I have 2 solutions in mind.

1- load the data from the API before the user opens the page. 2- load the data once during the app lifecycle.

I have tried the second one with the help of this article but with no use

ApiService.dart

  Future getUserData() async {
String token = await AuthProvider().getToken();
String userID = await AuthProvider().getUserId();

final response = await Dio().get(
  '$apiUrl/$userID',
  options: Options(headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Authorization': 'Bearer $token',
  }),
);

if (response.statusCode == 200) {
  await Future.delayed(Duration(seconds: 5));
  return response.data;
} else {
  throw Exception('Failed to load data');
}
}

This is where I call it

class Budget extends StatefulWidget {
final int budget;

Budget({@required this.budget});

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

class _BudgetState extends State<Budget> {
  Future<User> getUserData;
 ApiService apiService;
 SharedPreferences sharedPreferences;

@override
void initState() {
apiService = ApiService();
apiService.getUserData();
super.initState();
}

 @override
 Widget build(BuildContext context) {
return LayoutBuilder(
  builder: (context, constratints) => Container(
    child: FutureBuilder(
      future: apiService.getUserData(),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasError) {
          return Center(
            child: Text(
                "Something wrong with message: ${snapshot.error.toString()}"),
          );
        } else if (snapshot.connectionState == ConnectionState.done) {
          return Center(
            child: RichText(
              textAlign: TextAlign.center,
              text: TextSpan(
                style: TextStyle(
                  color: kDarkTextColor,
                  fontFamily: "Poppins",
                ),
                children: [
                  TextSpan(
                    text: "Your budget is\n",
                    style: TextStyle(
                      fontSize: constratints.maxWidth * .06,
                      fontWeight: FontWeight.normal,
                    ),
                  ),
                  TextSpan(
                    text: snapshot.data['budget'],
                    style: TextStyle(
                      fontSize: constratints.maxWidth * .2,
                      height: 1,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ),
          );
        } else {
          return Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    ),
   ),
  );
 }
   }

Thanks in advance!

  • 1
    I use option 2 is my app. I have a class that persists the data and can access it using Provider. When the future builder accesses the Data Provider the first time the data is loaded and stored in memory. Then Returned. Any times after that I simply return the cached data. Works will and I don’t have the progress indicator you are experiencing. Recommend looking into provider. – Kent May 20 '21 at 05:50
  • @Kent Ok that's actually good, Do u have any reference to that? ill be thankful. – Shahad Alharbi May 22 '21 at 01:02

1 Answers1

1

The problem is that every time the page rebuilds, apiService.getUserData() is called and causes reload. To avoid this you need to call apiService.getUserData() in initState and set the result to a variable (Without await);

so minimal solution is:

class _BudgetState extends State<Budget> {
 future<dynamic> userDataFuture; 

 @override
   void initState() {
     userDataFuture = apiService.getUserData();
     super.initState();
   }
}

then Instead of setting Future: apiService.getUserData() you need to use the variable you just set to: Future: userDataFuture in FutureBuilder widget.

Then if you really need to reload data you can just set the state again:

setState(() => userDataFuture = apiService.getUserData());
omidh
  • 2,526
  • 2
  • 20
  • 37