Solution 1 :
Execution is not advancing beyond this line:
var temp = await compute(parseEvents, response.data);
Probably parseEvents
is throwing something.
Notice how you never use try/catch
. You have to write code to catch exceptions. parseEvents
is probably throwing an exception (malformed json?, empty json->null?) and since you are not catching it you have no idea what is happening.
I would first try to get rid of the compute
and just call parseEvents
to see if the exception is silently ignored because of compute
. Then I would place try/catch
inside the parseEvents function and gradually have all execution paths to have try/catch
in them, including async
functions and await
calls.
Problem :
I have an issue with futureBuilders in my whole flutter app. In each screen there can be 3 or 4 sections, each one loads data from a specific API endpoint. The problem is that sometimes, whole screen loads fine, and sometimes a section or two are stuck with the progress bar!.. I debugged and searched a lot and can’t find the real cause.
This is making me hate flutter :/
For example, here is the Home Page Code:
import 'package:carousel_pro/carousel_pro.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:soul_space/PODOs/categories.dart';
import 'package:soul_space/PODOs/event.dart';
import 'package:soul_space/PODOs/image.dart';
import 'package:soul_space/PODOs/space.dart';
import 'package:soul_space/custom_widgets/category-card.dart';
import 'package:soul_space/custom_widgets/error-card.dart';
import 'package:soul_space/custom_widgets/no-data.dart';
import 'package:soul_space/custom_widgets/search-widget.dart';
import 'package:soul_space/services/categories-service.dart';
import 'package:soul_space/services/events-service.dart';
import 'package:flutter/foundation.dart';
import 'package:soul_space/services/spaces-service.dart';
import '../custom_widgets/home-row.dart';
import 'package:async/async.dart';
class HomeTab extends StatefulWidget {
HomeTab({Key key, this.title, this.mainScaffoldKey}) : super(key: key);
final GlobalKey<ScaffoldState> mainScaffoldKey;
final String title;
@override
HomeTabState createState() => HomeTabState();
}
class HomeTabState extends State<HomeTab> with AutomaticKeepAliveClientMixin {
Future _homeImagesFuture;
final AsyncMemoizer _categoriesMemoizer = AsyncMemoizer();
Future _categoriesFuture;
final AsyncMemoizer _eventsMemoizer = AsyncMemoizer();
Future _eventsFuture;
final AsyncMemoizer _spacesMemoizer = AsyncMemoizer();
Future _spacesFuture;
var isLoading = false;
final List<String> rowsTitles = ['New Events', 'Featured Spaces'];
List colors = [
Colors.red[500],
Colors.teal[300],
Colors.yellow[500],
Colors.orange[300],
Colors.red[400],
Colors.blue
];
Random random = new Random();
@override
void initState() {
super.initState();
_categoriesFuture = fetchCategories();
_eventsFuture = fetchEvents();
_spacesFuture = fetchSpaces();
_homeImagesFuture = getHomeImages();
}
fetchCategories() async {
return this._categoriesMemoizer.runOnce(() async {
return await getCategories();
});
}
fetchEvents() async {
return this._eventsMemoizer.runOnce(() async {
return await fetchAllEvents('approved');
});
}
fetchSpaces() async {
return this._spacesMemoizer.runOnce(() async {
return await fetchAllSpaces('approved');
});
}
String _homeSliderImages;
Future<List<String>> getHomeImages() async {
List<LoadedAsset> assets = await fetchSliderImages();
List<String> temp = [];
for (var i = 0; i < assets.length; i++) {
temp.add(assets[i].link);
}
return temp;
}
@override
Widget build(BuildContext context) {
super.build(context);
debugPrint('REBUILD:' + DateTime.now().toString());
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Stack(
children: [
SearchBar(scaffoldKey: widget.mainScaffoldKey),
],
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: FutureBuilder(
future: _eventsFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
debugPrint('***************************DONE1');
if (snapshot.hasError) {
debugPrint(snapshot.error.toString());
return ErrorCard(
isSmall: true,
);
} else if (snapshot.hasData) {
final List<Event> events = snapshot.data;
return HomeRow(
title: rowsTitles[0],
events: events,
route: '/allEvents');
} else {
return NoDataCard(
isSmall: true,
);
}
} else {
debugPrint('***************************LOADER1');
return Container(
margin: EdgeInsets.symmetric(vertical: 0.0),
height: 250.0,
child: Center(child: RefreshProgressIndicator()));
}
}),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: FutureBuilder(
future: _spacesFuture,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
debugPrint('***************************DONE2');
if (snapshot.hasError) {
debugPrint(snapshot.error.toString());
return ErrorCard(
isSmall: true,
);
} else if (snapshot.hasData) {
final List<Space> spaces = snapshot.data;
return HomeRow(
title: rowsTitles[1],
spaces: spaces,
route: '/allSpaces');
} else {
return NoDataCard(
isSmall: true,
);
}
} else {
debugPrint('***************************LOADER2');
return Container(
margin: EdgeInsets.symmetric(vertical: 0.0),
height: 250.0,
child: Center(child: RefreshProgressIndicator()));
}
}),
)
],
),
));
}
@override
bool get wantKeepAlive => true;
}
And this is a sample of a service function that parses json data from API:
Future<List<Event>> fetchAllEvents(String approval, {int id = -1}) async {
var url = globals.apiUrl +
'/events/' +
(id != -1 ? id.toString() + '/' : '') +
_getQueryParams(false, approval);
dio.Response<String> response = await get(url);
debugPrint('***************************GET SUCCEES1');
var temp = await compute(parseEvents, response.data);
debugPrint('***************************PARSE SUCCEES1');
return temp;
}
.
.
List<Event> parseEvents(String responseBody) {
final parsedJson = json.decode(responseBody);
var list = parsedJson['data'] as List;
if (list != null) {
List<Event> eventsList = list.map((i) => Event.fromJson(i)).toList();
return eventsList;
} else {
return [];
}
}
Furthermore, here are examples of good and bad build logs, hope they help conclude the cause:
- ‘LOADER’ is printed when progress bar is displayed
- ‘GET SUCCESS’ is printed when get returns
- ‘PARSE SUCCESS’ is printed when compute function succeeds in parsing json
- ‘DONE’ is printed when the widget is loaded correctly
Any solutions?
EDIT
I removed “compute” function as suggested in the accepted answer and it fixed it, but not sure why it failed sometimes without exceptions
Comments
Comment posted by Kahou
There is no
Comment posted by Abeer Sul
@Kahou, yes but what is the explanation?
Comment posted by Abeer Sul
good suggestion. I tried adding try/catch and keeping compute => still same issue without any execptions. Then I tried removing compute, tested it many times and
Comment posted by Gazihan Alankus
Did you place try/catch inside