在我的材质应用小部件中,路由子小部件中定义了许多屏幕,例如:
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()
}
}
所有这些屏幕都有很多 API 调用。我的问题是,当应用程序启动时,所有这些屏幕都会同时加载,并且所有 API 调用都会被触发。我希望在应用程序中打开实际屏幕时执行必要的 API 调用。但不知道如何做到这一点,甚至不知道是否可能。
提前欢迎任何建议/信息/教程!
示例如何在屏幕中处理 API 调用。
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>}
);
........
}
}
添加 Screen0 屏幕如何工作和处理其他屏幕的示例 - 当然还有 MaterialApp 的主页。相关的不是 BottomNavigationBar,而是使用 Drawer 小部件的右侧。
..........
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)
);
});
});
}
}
所有屏幕在应用程序启动时加载的原因是因为您在主屏幕中使用的 IndexedStack 内部工作方式。
IndexedStack 同时构建其所有子级,但仅显示 IndexedStack 的
index
属性所指向的子级。
您可以使用 IndexedStack 示例的修改代码进行测试,以更好地了解 IndexedStack 的工作原理:
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}'),
);
}
}
您需要避免使用 IndexedStack,因为它总是会同时构建所有子级,从而触发该屏幕中进行的每个 api 调用。您可以使用
setState
来更改屏幕,如本底部导航示例 所示,而不是使用 IndexedStack。