如何避免每次导航到页面时都重新加载数据
How to avoid reloading data every time navigating to page
我试图避免在 flutter 中重建 FutureBuilder
。我已经尝试了下面 Q 中建议的解决方案。
我的应用程序仍然会在我每次导航到该页面时触发 API。请指出我哪里出错了。
Util.dart
//function which call API endpoint and returns Future in list
class EmpNetworkUtils {
...
Future<List<Employees>> getEmployees(data) async {
List<Employees> emps = [];
final response = await http.get(host + '/emp', headers: { ... });
final responseJson = json.decode(response.body);
for (var empdata in responseJson) {
Employees emp = Employees( ... );
emps.add(emp);
}
return emps;
}
}
EmpDetails.dart
class _EmpPageState extends State<EmpPage>{
...
Future<List<Employees>>_getAllEmp;
@override
initState() {
_getAllEmp = _getAll();
super.initState();
}
Future <List<Employees>>_getAll() async {
_sharedPreferences = await _prefs;
String authToken = AuthSessionUtils.getToken(_sharedPreferences);
return await EmpNetworkUtils().getEmployees(authToken);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar( ... ),
body: Container(
child: FutureBuilder(
future: _getAllEmp,
builder: (BuildContext context, AsyncSnapshot snapshot) { ... }
)))
}
}
更新:
我在我的应用程序中使用 bottomNavigationBar
,从中加载此页面。
您正在 initState
中调用 getEmployees
函数,每次将小部件插入树中时都会调用该函数。如果您想在第一次调用您的函数后保存数据,您将必须有一个持续存在的小部件。
一个简单的实现是使用 InheritedWidget
和数据 class:
class InheritedEmployees extends InheritedWidget {
final EmployeeData employeeData;
InheritedEmployees({
Key key,
@required Widget child,
}) : assert(child != null),
employeeData = EmployeeData(),
super(key: key, child: child);
static EmployeeData of(BuildContext context) => (context.inheritFromWidgetOfExactType(InheritedEmployees) as InheritedEmployees).employeeData;
@override
bool updateShouldNotify(InheritedEmployees old) => false;
}
class EmployeeData {
List<Employees> _employees;
Future<List<Employees>> get employees async {
if (_employees != null) return _employees;
_sharedPreferences = await _prefs;
String authToken = AuthSessionUtils.getToken(_sharedPreferences);
return _employees = await EmpNetworkUtils().getEmployees(authToken);
}
}
现在,您只需将 InheritedEmployees
放在 不会被处理的地方 ,例如关于您的主页,或者如果您愿意,甚至可以关于您的 MaterialApp
(runApp(InheritedEmployees(child: MaterialApp(..));
)。这样,数据只会被获取一次并在之后被缓存。如果更适合您,您也可以查看 AsyncMemoizer
,但我提供的示例应该可以正常工作。
现在,您需要在 didChangeDependencies
中调用此 employees
getter,因为您的 _EmpPageState
依赖于 InheritedEmployees
,您需要查看向上,这不会发生在 initState
:
class _EmpPageState extends State<EmpPage>{
Future<List<Employees>>_getAllEmp;
@override
void didChangeDependencies() {
_getAllEmp = InheritedEmployees.of(context).employees;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar( ... ),
body: Container(
child: FutureBuilder(
future: _getAllEmp,
builder: (BuildContext context, AsyncSnapshot snapshot) { ... }
)))
}
}
我提到你的 State
现在依赖于你的 InheritedWidget
,但这并不重要,因为 updateShouldNotify
总是 returns false
(有不会有任何额外的构建)。
我找到了解决这个问题的另一种方法,并且也适用于我的应用程序
- 应用 GetX 控制器调用 API 并呈现响应数据
- 删除 FutureBuilder 以调用 API 数据
应用GetX控制器调用API数据,点赞
class NavMenuController extends GetxController {
Api api = new Api();
var cart = List<NavMenu>().obs;
@override
void onInit() {
// TODO: implement onInit
getNavMenuData();
super.onInit();
}
Future<List<NavMenu>> getNavMenuData() async {
var nav_data = await api.getNavMenus();
if(nav_data!=null) {
cart.value = nav_data;
}
return cart;
}
}
在 initState() 上使用控制器调用 API 到所需的 class
class NavMenuDrawer extends StatefulWidget {
@override
_NavMenuDrawerState createState() => _NavMenuDrawerState();
}
class _NavMenuDrawerState extends State<NavMenuDrawer> {
final NavMenuController navMenuController = Get.put(NavMenuController());
@override
void initState() {
// TODO: implement initState
super.initState();
navMenuController.getNavMenuData();
}
删除下面用于调用 API 的 FutureBuilder 代码,[如果您使用 FutureBuilder/StreamBuilder 随便什么]
return FutureBuilder<List<NavMenu>>(
future: api.getNavMenus(),
builder: (context, snapshot) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
title: Text("${snapshot.data[index].title}"),
就用GetX控制器获取数据就好了,喜欢
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: navMenuController.cart?.length ?? 0,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
title: Obx(() {
return Text(
"${navMenuController.cart.value[index].title}");
}),
注意:有关更多信息,您可以搜索如何应用 GetX
我试图避免在 flutter 中重建 FutureBuilder
。我已经尝试了下面 Q 中建议的解决方案。
我的应用程序仍然会在我每次导航到该页面时触发 API。请指出我哪里出错了。
Util.dart
//function which call API endpoint and returns Future in list
class EmpNetworkUtils {
...
Future<List<Employees>> getEmployees(data) async {
List<Employees> emps = [];
final response = await http.get(host + '/emp', headers: { ... });
final responseJson = json.decode(response.body);
for (var empdata in responseJson) {
Employees emp = Employees( ... );
emps.add(emp);
}
return emps;
}
}
EmpDetails.dart
class _EmpPageState extends State<EmpPage>{
...
Future<List<Employees>>_getAllEmp;
@override
initState() {
_getAllEmp = _getAll();
super.initState();
}
Future <List<Employees>>_getAll() async {
_sharedPreferences = await _prefs;
String authToken = AuthSessionUtils.getToken(_sharedPreferences);
return await EmpNetworkUtils().getEmployees(authToken);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar( ... ),
body: Container(
child: FutureBuilder(
future: _getAllEmp,
builder: (BuildContext context, AsyncSnapshot snapshot) { ... }
)))
}
}
更新:
我在我的应用程序中使用 bottomNavigationBar
,从中加载此页面。
您正在 initState
中调用 getEmployees
函数,每次将小部件插入树中时都会调用该函数。如果您想在第一次调用您的函数后保存数据,您将必须有一个持续存在的小部件。
一个简单的实现是使用 InheritedWidget
和数据 class:
class InheritedEmployees extends InheritedWidget {
final EmployeeData employeeData;
InheritedEmployees({
Key key,
@required Widget child,
}) : assert(child != null),
employeeData = EmployeeData(),
super(key: key, child: child);
static EmployeeData of(BuildContext context) => (context.inheritFromWidgetOfExactType(InheritedEmployees) as InheritedEmployees).employeeData;
@override
bool updateShouldNotify(InheritedEmployees old) => false;
}
class EmployeeData {
List<Employees> _employees;
Future<List<Employees>> get employees async {
if (_employees != null) return _employees;
_sharedPreferences = await _prefs;
String authToken = AuthSessionUtils.getToken(_sharedPreferences);
return _employees = await EmpNetworkUtils().getEmployees(authToken);
}
}
现在,您只需将 InheritedEmployees
放在 不会被处理的地方 ,例如关于您的主页,或者如果您愿意,甚至可以关于您的 MaterialApp
(runApp(InheritedEmployees(child: MaterialApp(..));
)。这样,数据只会被获取一次并在之后被缓存。如果更适合您,您也可以查看 AsyncMemoizer
,但我提供的示例应该可以正常工作。
现在,您需要在 didChangeDependencies
中调用此 employees
getter,因为您的 _EmpPageState
依赖于 InheritedEmployees
,您需要查看向上,这不会发生在 initState
:
class _EmpPageState extends State<EmpPage>{
Future<List<Employees>>_getAllEmp;
@override
void didChangeDependencies() {
_getAllEmp = InheritedEmployees.of(context).employees;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar( ... ),
body: Container(
child: FutureBuilder(
future: _getAllEmp,
builder: (BuildContext context, AsyncSnapshot snapshot) { ... }
)))
}
}
我提到你的 State
现在依赖于你的 InheritedWidget
,但这并不重要,因为 updateShouldNotify
总是 returns false
(有不会有任何额外的构建)。
我找到了解决这个问题的另一种方法,并且也适用于我的应用程序
- 应用 GetX 控制器调用 API 并呈现响应数据
- 删除 FutureBuilder 以调用 API 数据
应用GetX控制器调用API数据,点赞
class NavMenuController extends GetxController {
Api api = new Api();
var cart = List<NavMenu>().obs;
@override
void onInit() {
// TODO: implement onInit
getNavMenuData();
super.onInit();
}
Future<List<NavMenu>> getNavMenuData() async {
var nav_data = await api.getNavMenus();
if(nav_data!=null) {
cart.value = nav_data;
}
return cart;
}
}
在 initState() 上使用控制器调用 API 到所需的 class
class NavMenuDrawer extends StatefulWidget {
@override
_NavMenuDrawerState createState() => _NavMenuDrawerState();
}
class _NavMenuDrawerState extends State<NavMenuDrawer> {
final NavMenuController navMenuController = Get.put(NavMenuController());
@override
void initState() {
// TODO: implement initState
super.initState();
navMenuController.getNavMenuData();
}
删除下面用于调用 API 的 FutureBuilder 代码,[如果您使用 FutureBuilder/StreamBuilder 随便什么]
return FutureBuilder<List<NavMenu>>(
future: api.getNavMenus(),
builder: (context, snapshot) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
title: Text("${snapshot.data[index].title}"),
就用GetX控制器获取数据就好了,喜欢
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: navMenuController.cart?.length ?? 0,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
title: Obx(() {
return Text(
"${navMenuController.cart.value[index].title}");
}),
注意:有关更多信息,您可以搜索如何应用 GetX