如何避免每次导航到页面时都重新加载数据

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