Reputation: 26
In my materialapp widget many screens defined in the routes child widget, like:
MaterialApp(
..........
home: new Screen0(),
routes: <String, WidgetBuilder>{
Screen0.routeName: (BuildContext context) => new Screen0(),
Screen1.routeName: (BuildContext context) => new Screen1(),
Screen2.routeName: (BuildContext context) => new Screen2(),
Screen3.routeName: (BuildContext context) => new Screen3(),
......
ScreenN.routeName: (BuildContext context) => new ScreenN()
}
}
All of these screens having many-many API calls. My problem is that, when the APP started then all of these screens loaded at the same time and all of their API calls triggered. I would like to achieve to execute the necessary API calls just then when the actual screen is opened in the app. But don't know how to do this or even possible or not.
Any advice/info/tutorial welcomed in advance!
Example how API calls handled in screens.
class Screen1 extends StatefulWidget {
.............
}
class Screen1State extends State<Screen1> {
..............
@override
void initState() {
super.initState();
.......
checkLoggedIn();
}
Future checkLoggedIn() async {
var prefs = await SharedPreferences.getInstance();
setState(() {
KEY = prefs.getString("apiKey");
USERID = prefs.getString("userId");
});
......
makeAnAPICall();
}
Future makeAnAPICall() async {
var response = await http.get(
Uri.parse("<URI>"),
headers: {"X-API-KEY" : <KEY>}
);
........
}
}
Adding Example how the Screen0 screen works and handles the other screens - and of course the home of the MaterialApp. Not the BottomNavigationBar, rather the right hand side using Drawer widget is the relevant.
..........
import 'package:Screen1.dart';
import 'package:Screen2.dart';
import 'package:Screen3.dart';
.....
import 'package:ScreenN.dart';
final _scaffoldKey = GlobalKey<ScaffoldState>();
var _KEY;
int? _indexRHSMenu;
String? _currentPage;
late List<OurNavigationDestination> _RHSMenu;
class Screen0 extends StatefulWidget {
.......
}
class Screen0State extends State<Screen0> {
Screen0State();
@override
void initState() {
checkLoggedIn();
_RHSMenu = <OurNavigationDestination>[];
_indexRHSMenu = 0;
_currentPage = "Screen1";
}
@override
Widget build(BuildContext context) {
return new Scaffold(
......
endDrawer: Theme(
.........
child: Drawer (
child: Container(
child: ListView (
children: getRHSMenu(),
),
),
),
),
.....
body: SafeArea(
child: Column(
children: <Widget>[
........
Expanded(
child: IndexedStack(
children: _RHSMenu.map((destination) => destination.page).toList(),
index: _indexRHSMenu,
),
),
],
.....
),
),
);
} /* build */
Widget getPage(String page){
switch(page){
case "screen1" :
return Screen1();
case "screen2":
return Screen2();
case "screen3":
return Screen3();
.....
case "screenN":
return ScreenN();
}
}
Widget getIcon(String page, bool active) {
......
}
void navigateToMenu(String page, screen){
Navigator.of(context).pop();
setState(() {
_currentPage = page;
});
Navigator.push(context,
MaterialPageRoute(
builder: (context) {
return screen;
},
),
);
}
List<Widget> getRHSMenu() {
List<Widget> rhsMenu = <Widget>[];
if (_RHSMenu.length > 0) {
for (int i = 0; i < _RHSMenu.length; i++) {
var menu = _RHSMenu[i];
rhsMenu.add(Container(
child: Row(
children: <Widget>[
OurTextButtonIcon(
onPressed: () => navigateToMenu(menu.name, menu.page),
........
),
],
),
);
}
}
return rhsMenu;
}
Future checkLoggedIn() async{
.....
getNavigationItems();
.....
}
Future getNavigationItems() async{
var response = await http.get(
Uri.parse("<URI>"),
headers: {"X-API-_KEY" : <_KEY>}
);
var responseBody = JsonDecoder().convert(response.body);
setState(() {
responseBody["_RHSMenu"].forEach((name, title) {
_RHSMenu.add(
OurNavigationDestination(name, getIcon(name, false), getPage(name), getIcon(name, true), title)
);
});
});
}
}
Upvotes: 0
Views: 56
Reputation: 11
The reason why all the screens are loaded at the app start is because of how the IndexedStack works internally which you have used in your home screen.
IndexedStack builds all its children at the same time, but displays only the child which is pointed by the index
property of IndexedStack.
You can test with this modified code of IndexedStack example to better understand how IndexedStack works:
import 'package:flutter/material.dart';
/// Flutter code sample for [IndexedStack].
void main() => runApp(const IndexedStackApp());
class IndexedStackApp extends StatelessWidget {
const IndexedStackApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('IndexedStack Sample')),
body: const IndexedStackExample(),
),
);
}
}
class IndexedStackExample extends StatefulWidget {
const IndexedStackExample({super.key});
@override
State<IndexedStackExample> createState() => _IndexedStackExampleState();
}
class _IndexedStackExampleState extends State<IndexedStackExample> {
List<String> names = <String>['Dash', 'John', 'Mary'];
int index = 0;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
if (index == 0) {
index = names.length - 1;
} else {
index -= 1;
}
});
},
child: const Icon(Icons.chevron_left, key: Key('gesture1')),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IndexedStack(
index: index,
children: <Widget>[
for (final String name in names) PersonTracker(name: name)
],
)
],
),
GestureDetector(
onTap: () {
setState(() {
if (index == names.length - 1) {
index = 0;
} else {
index += 1;
}
});
},
child: const Icon(Icons.chevron_right, key: Key('gesture2')),
),
],
)
],
);
}
}
class PersonTracker extends StatefulWidget {
const PersonTracker({super.key, required this.name});
final String name;
@override
State<PersonTracker> createState() => _PersonTrackerState();
}
class _PersonTrackerState extends State<PersonTracker> {
void initState(){
super.initState();
///Watch this being called for every children when the app runs
print('${widget.name} - init');
}
@override
Widget build(BuildContext context) {
return Container(
key: Key(widget.name),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 239, 248, 255),
border: Border.all(color: const Color.fromARGB(255, 54, 60, 244)),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
padding: const EdgeInsets.all(16.0),
child:
Text('Name: ${widget.name}'),
);
}
}
You need to avoid using IndexedStack as it will always build all the children at the same time triggering every api calls being made in that screen. You can use setState
to change your screens as illustrated in this Bottom Navigation Example instead of using IndexedStack.
Upvotes: 0