我试图避免在颤抖中重建FutureBuilder
。我已经尝试了以下Q提出的解决方案。
How to parse JSON only once in Flutter
Flutter Switching to Tab Reloads Widgets and runs FutureBuilder
每次我导航至该页面时,我的应用仍会触发API。请指出我要去哪里了。
实用程序飞镖
//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
,从中加载了此页面。
答案 0 :(得分:1)
您正在getEmployees
中调用initState
函数,这意味着每次将小部件插入树中时都会被调用。如果要在第一次调用函数后保存数据,则必须有一个持久化的小部件。
一个简单的实现方法是使用InheritedWidget
和一个数据类:
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
,但是我提供的示例应该可以正常工作。
现在,您将要在employees
中调用此didChangeDependencies
吸气剂,因为您的_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
总是返回false
(不会有其他版本)。
答案 1 :(得分:0)
我有另一种解决此问题的方法,也可以应用到我的应用程序中
应用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 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