微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用提供程序有效地获取初始数据

如何解决如何使用提供程序有效地获取初始数据

最近,我参加了扑通课程。 讲师使来自API的获取请求变得如此困难。对于像Flutter这样的混合框架,我从未想到过如此困难。

下面是我的代码。我正在使用provider进行状态管理。

Future<void> fetchAndSetProducts() async {
    try {
      const url = 'fetch-url';
      final response = await http.get(url);
      final data = json.decode(response.body) as Map<String,dynamic>;
      final List<Product> loadedProducts = [];
      data.forEach((key,value) {
        loadedProducts.add(Product(
          id: key,title: value['title'],description: value['description'],imageUrl: value['imageUrl'],price: value['price'],isFavorite: value['isFavorite'],));
      });

      _items = loadedProducts;
      notifyListeners();
    } catch (error) {
      throw (error);
    }
  }

在“产品概述”屏幕上,我正在显示产品页面,该方法如下所示:

bool _isInit = true;
  bool _isLoading = false;

  @override
  void didChangeDependencies() {
    if (_isInit) {
      setState(() {
        _isLoading = true;
      });
      Provider.of<Products>(context).fetchAndSetProducts().then((_) => {
            setState(() {
              _isLoading = false;
            })
          });
    }
    _isInit = false;
    super.didChangeDependencies();
  }

另一种方法包括使用零持续时间的偷偷摸摸的方法,就像我们在javascript设置超时中使用零时间一样。

值得注意的是,在didChangeDependencies中,我们无法使用异步等待,因此很可能会发生回调。 另外,仅在加载一次调用api时就需要初始化变量。

对此没有简单的解决方案吗?还是行业解决方案?

解决方法

here is a minimal working example of what you should do,这不是世界上最好的事情,但这对我有用,请告诉我是否可以做得更好。

问题的答案非常简单,但是,您需要先重新排列一些内容。

Flutter应用程序可以分为多个层,例如(例如)数据,状态和UI,在数据层中,您将拥有与API进行通信的所有方法,并在状态内调用它们(即提供程序),结果将可从提供程序访问,并将结果保存在变量中,然后UI便能够从提供程序检索这些数据,我知道这似乎有点多余,但这是有原因的我们为什么这样做,如果将API调用放在提供程序本身内部,并且应用程序中的其他地方使用相同的终结点,那么您将有重复的代码,对于提供程序,这是存储数据的地方在运行时,这些数据决定了您应用的状态,最后,UI可以相对轻松地处理来自提供程序的数据显示,只需在提供程序中在布尔值中指示API调用是否正在执行,以及在使用者内部在用户界面中显示基于布尔的不同窗口小部件

如果我们要可视化操作流程,它将是这样的:
1-用户界面中触发提供者方法的操作。
2-在provider方法内部,您将指示API调用正在执行的boolean值设置为true,然后调用notifyListeners()。
3-调用API请求并在其上调用.then()。
4-在.then()内部,将boolean设置为false以通知调用已结束,并将接收到的数据设置为提供程序内部的变量,然后再次调用notifyListeners。
5-在用户界面中,应该让使用者监听您的提供者并处理布尔值,如果为true,则显示例如CircularProgressIndicator,如果为false,则显示所需的小部件


关于initState中的上下文,您可以通过3种方式解决此问题:
1-使用WidgetsBinding.instance .addPostFrameCallback((_)=> yourProviderFunction(context));
2-通过在服务定位器中注册您的提供程序,因此您完全不必使用上下文。 (这是我在上面发布的示例项目中使用的内容)
3-通过在提供程序的构造函数中执行所需的功能,因此在初始化时,将调用API请求

,

这是学院课程吗?

这也是正确的方法。

要使用提供程序,您需要上下文。

编辑:在答案中添加了BaselAbuhadrous的评论。

您需要使用didChangeDependencies,因为initState实际上提供了上下文,但是尚未构建屏幕布局,因此会出现错误,但是如果您使用WidgetsBindings.instance并且调用其中的提供程序,则不会收到错误消息。

,
  //your model,product_model.dart
  import 'dart:convert';

   List<Product> availableTicketsFromJson(String str) => List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));

  class Product {
  String title;
  String description;
  String imageUrl;
  double price;
  bool isFavorite;

  Product(
      {this.title,this.description,this.imageUrl,this.price,this.isFavorite});

  factory Product.fromJson(Map<String,dynamic> json) => Product(
        title: json['title'] as String,description: json['description'] as String,imageUrl: json['imageUrl'] as String,price: json['price'] as double,isFavorite: json['isFavorite'] as bool,);
}



 //viewmodel class
 final String url = "test.com";
 Future<List<Product> fetchProducts() async {
 List<Product> products = List<Product>();
 try {
       final request = await http.get(url);
       if(request.statusCode == 200) {
            products = productsFromJson(request.body.toString());
            notifyListeners();
         } else {
            print(request.statusCode.toString());
         }
    } catch(e) {
      return List<Product>();
    }
    return products;
 } 
 

 //fetch_data.dart
 Create your instance of provider in the page that you wanna fetch the data:
 Under State<yourWidget>

 => FetchDataViewModel _model;
    List<Product> products = [];
 under build method
 => _model = Provider.of<FetchDataViewModel>(context,listen: false);

    Make a http request with FutureBuilder
    FutureBuilder(future:_model.fetchProducts()),builder: (context,snapshot)){
       if(snapshot.connectionState == ConnectionState.done) {
       products = snapshot.data;
       if(products.length > 0) {
           return ListView.builder(
              itemCount: products.length,itemBuilder : (context,index) {
               return _items();
           }
         );
      } else {return _noSavedDataWidget();}
     }
    }

    You can test such a code
,

有时

Provider.of (上下文,听:false )。'providerFunction'

可能会提供帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。