I have some pages that contain dependent content, which I retrieve from an API using multiple fetch requests.
Here, getClass()
and getValue()
are calling API from another class, returning future.
List<dynamic> pIds, ps;
apiStart() async {
pageIds = new List();
siteCls = await _baseApi.getClass(siteCName);
siteCId = siteCls.data.classId.toString();
pageCls = await _baseApi.getClass(pageCName);
pageCId = pageCls.data.classId.toString();
siteIns = await _baseApi.getValue(siteCId);
pageIns = await _baseApi.getValue(pageCId);
pageIds = getPageIds();
pages = getPagesData();
}
List getPageIds() {
//Doing some work here
return pIds;
}
List getPagesData() {
//Doing some work here
return ps;
}
Now I want to access variables like "pages" from other/multiple classes without repeating the functions. How can I do that?
Update:
This is a sample code. It return "welcome test1!". When I remove content = "test1";
from apiStart()
, returns "welcome h!". Is there an issue with using the await
?
class AppData {
factory AppData() {
return _singleton;
}
static final AppData _singleton = AppData._internal();
AppData._internal() {
print("Instance created App-Data");
}
BaseApi _baseApi = new BaseApi();
var content = "hi";
Future body() {
return Future.value(content);
}
void apiStart() async{
content = "test1";//OK when I put content here
print(content);
_ids = await _baseApi.fetchIds();
//Does not change when I put content here
//I need to use await because I need ids for next function
content = "test2";
print(content);
//Doing some work with result and get ids as list
List id = idList();
_pages = await _baseApi.fetchPages(id[0]);
//Doing some work with result and get page content
contents = contentList();
}
}
If I do something like below, it returns the "test2". But I don't know how to check if the job has finished or not; Should I add a boolean var at the end of the function and check it every seconds? Is that efficient?
Future body() {
return Future.delayed(Duration(seconds: 10)).then((value) => content);
}
Where I run singleton
void main() {
runApp(MyApp());
AppData().apiStart();
}
I show part of the results like this:
class ContentWidget extends StatefulWidget {
ContentWidget({Key key}) : super(key: key);
@override
_ContentWidgetState createState() => _ContentWidgetState();
}
class _ContentWidgetState extends State<ContentWidget> {
Future<dynamic> futureContent;
@override
void initState() {
super.initState();
futureContent = AppData().body();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<dynamic>(
future: futureContent,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("Welcome ${snapshot.data}!", style: plainTextStyle);
} else {
//return Text("Welcome ${snapshot.error}!", style: plainTextStyle);
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
);
}
}
You could use a Singleton pattern to create a service class: it gives you access to this class from anywhere in your code.
Don't over use it, because it can turn into a very messy globals-like lack of encapsulation.
Note: this is a huge simplification, but will work. Read aroudn singletons and services to find out more.
class ApiService {
// next 5 lines is a typical singleton pattern
static ApiService _singleton;
factory ApiService() {
return _singleton ??= new ApiService._internal();
}
ApiService._internal();
//here you'll keep your data
List<dynamic> pageIds, pages; //store your data in these variables
//run this on the beginning of your app to get your data. Don't forget this!
void apiStart() async {
// your code here where you hit the API and get all your data,
// eg methods getPageIdsFromAPI and getPagesDataFromAPI that you need to write. They return your data
// pageIds = getPageIdsFromAPI();
// pages = getPagesDataFromAPI();
}
}
then from ANYWHERE in your code you can access variables and functions from this class
ApiService().pageIds
ApiService().pageIds.length
ApiService().pageIds.contains(some_id)
---EDIT---
you need to decide when the waiting would happen. Either you wait for the data before you show the screen, or you show the screen in an empty/waiting mode (like facebook does) and then wait.
to do the second one:
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
futureContent = await AppData().body();
setState(() {
// refresh whatever changed. it's a good practice to specify what
});
});
}