簡體   English   中英

Flutter - Providers 和 Future 調用,如何共享同一個實例?

[英]Flutter - Providers and Future calls, how to share the same instance?

我正在學習 Flutter 並且有些東西我無法掌握。 I implemented a Infinite scroll pagination, with a package ( infine_scroll_pagination ), it works fine, but the data this Package is getting, comes from a Future call, which takes data from the WEB , and parses it in my Provider Class .

我的問題是, Infinite Scroll widget加載的data無法在其state中的其他任何地方訪問。

示例:讓我們獲取一個contact list ,一次加載 10 個聯系人:

class ContactsBody extends StatefulWidget {
  @override
  _ContactsBodyState createState() => _ContactsBodyState();
}

class _ContactsBodyState extends State<ContactsBody> {
  static const _pageSize = 10;
  final PagingController<int, Contact> pagingController =
      PagingController(firstPageKey: 0);

  @override
  void initState() {
    super.initState();
    pagingController.addPageRequestListener((pageKey) {
      _fetchPage(pageKey);
    });
  }

  Future<void> _fetchPage(int pageKey) async {
    try {
      final newItems = await ContactsService().fetchContactsPaged(pageKey, _pageSize);
      final isLastPage = newItems.length < _pageSize;

      if (isLastPage) {
        pagingController.appendLastPage(newItems.contacts);
      } else {
        final nextPageKey = pageKey + 1;
        pagingController.appendPage(newItems.contacts, nextPageKey);
      }
    } catch (error) {
      pagingController.error = error;
    }
  }
  @override
  Widget build(BuildContext context) {
    return ContactsList(pagingController);
  }

  @override
  void dispose() {
    pagingController.dispose();
    super.dispose();
  }

所以基本上這個無限滾動 package 會一次獲取我的聯系人,10 個,這里是我的ContactsService調用:

 Future<Contacts> fetchContactsPaged(int pageKey, int pageSize) async {
    final response = await http.get(.....);

    if (response.statusCode == 200) {
    return Contacts.fromJson(jsonDecode(response.body));
    } else {
      throw Exception('Failed to load contacts');
    }
  }

最后,正如您在上面看到的,它使用其工廠方法“ fromJson() ”初始化我的Provider class (聯系人),並返回parsed data

現在我的Provider class

class Contacts extends ChangeNotifier {
      List<Contact> _contacts = <Contact>[];
    
      Contacts();
    
      factory Contacts.fromJson(final Map<String, dynamic> json) {
        final Contacts contacts = Contacts();
        if (json['data'] != null) {
          json['data'].forEach((contact) {
            contacts.add(Contact.fromJson(contact));
          });
        }
        return contacts;
      }

  void add(final Contact contact) {
    this._contacts.add(contact);
    this.notifyListeners();
  }

我在這里遇到的問題是,當加載Inifinite Scroll listView時,例如我更改了單個聯系人的state (例如,可以將聯系人設置為收藏夾),

如何訪問已初始化FUTURE調用的Contacts() classSAME instanc ,以便我可以訪問該 ZA2F2ED4F8EBC2CBB4C21A29D40AB6 中數據的current state state?

當然,如果我將我的更改發布到POST ,並在我需要的地方重新獲取新值,我將獲得更新后的 state 數據,但我想了解如何在此處訪問同一個實例並制作當前數據在應用程序內隨處可用

編輯:我刪除了原始答案,以便更好地了解 OP 想要實現的目標。

我在 GitHub 上做了一個回購,試圖向您展示您想要實現的目標: https://github.com/Kobatsu/stackoverflow_66578191

您的代碼中有一些令人困惑的事情:

  • 何時創建對象實例(ContactsService、Contacts)
  • 提供者使用
  • (訪問 pagingController 的列表?)
  • 解析 JSON / 使用工廠方法

存儲庫產生以下結果: 在此處輸入圖像描述

當您更新列表(通過向下滾動)時,黃色容器會更新為聯系人數量和收藏夾數量。 如果您單擊聯系人,它將成為收藏夾,並且黃色容器也會更新。

我評論了存儲庫以向您解釋每個部分。

注意:您代碼中的 Contacts class 在我的代碼中變成了 ContactProvider。

ContactsService class 撥打 API 電話:

class ContactsService {
  static Future<List<Contact>> fetchContactsPaged(
      int pageKey, int pageSize) async {
    // Here, you should get your data from your API

    // final response = await http.get(.....);
    // if (response.statusCode == 200) {
    //   return Contacts.fromJson(jsonDecode(response.body));
    // } else {
    //   throw Exception('Failed to load contacts');
    // }

    // I didn't do the backend part, so here is an example
    // with what I understand you get from your API:
    var responseBody =
        "{\"data\":[{\"name\":\"John\", \"isFavorite\":false},{\"name\":\"Rose\", \"isFavorite\":false}]}";
    Map<String, dynamic> decoded = json.decode(responseBody);
    List<dynamic> contactsDynamic = decoded["data"];

    List<Contact> listOfContacts =
        contactsDynamic.map((c) => Contact.fromJson(c)).toList();

    // you can return listOfContacts, for this example, I will add 
    // more Contacts for the Pagination plugin since my json only has 2 contacts
    for (int i = pageKey + listOfContacts.length; i < pageKey + pageSize; i++) {
      listOfContacts.add(Contact(name: "Name $i"));
    }
    return listOfContacts;
  }
}

提供者的用法:

Consumer<ContactProvider>(
        builder: (_, foo, __) => Container(
              child: Text(
                  "${foo.contacts.length} contacts - ${foo.contacts.where((c) => c.isFavorite).length} favorites"),
              padding: EdgeInsets.symmetric(
                  horizontal: 20, vertical: 10),
              color: Colors.amber,
            )),
    Expanded(child: ContactsBody())
  ]),
)

ContactsBody class 中的 Fetch page 方法,我們將聯系人添加到我們的 ContactProvider 中:

  Future<void> _fetchPage(int pageKey) async {
    try {
      // Note : no need to make a ContactsService, this can be a static method if you only need what's done in the fetchContactsPaged method
      final newItems =
          await ContactsService.fetchContactsPaged(pageKey, _pageSize);
      final isLastPage = newItems.length < _pageSize;
      if (isLastPage) {
        _pagingController.appendLastPage(newItems);
      } else {
        final nextPageKey = pageKey + newItems.length;
        _pagingController.appendPage(newItems, nextPageKey);
      }

      // Important : we add the contacts to our provider so we can get
      // them in other parts of our app
      context.read<ContactProvider>().addContacts(newItems);
    } catch (error) {
      print(error);
      _pagingController.error = error;
    }
  }

ContactItem 小部件,我們在其中更新收藏狀態並通知聽眾:

class ContactItem extends StatefulWidget {
  final Contact contact;
  ContactItem({this.contact});

  @override
  _ContactItemState createState() => _ContactItemState();
}

class _ContactItemState extends State<ContactItem> {
  @override
  Widget build(BuildContext context) {
    return InkWell(
        child: Padding(child: Row(children: [
          Expanded(child: Text(widget.contact.name)),
          if (widget.contact.isFavorite) Icon(Icons.favorite)
        ]), padding: EdgeInsets.symmetric(vertical: 8, horizontal: 10),),
        onTap: () {
          // the below code updates the item
          // BUT others parts of our app won't get updated because
          // we are not notifying the listeners of our ContactProvider !
          setState(() {
            widget.contact.isFavorite = !widget.contact.isFavorite;
          });

          // To update other parts, we need to use the provider
          context.read<ContactProvider>().notifyContactUpdated(widget.contact);
        });
  }
}

和 ContactProvider:

class ContactProvider extends ChangeNotifier {
  final List<Contact> _contacts = [];
  List<Contact> get contacts => _contacts;

  void addContacts(List<Contact> newContacts) {
    _contacts.addAll(newContacts);
    notifyListeners();
  }

  void notifyContactUpdated(Contact contact) {
    // You might want to update the contact in your database,
    // send it to your backend, etc...
    // Here we don't have these so we just notify our listeners :
    notifyListeners();
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM