簡體   English   中英

Flutter - MultiProvider 如何與相同類型的提供者一起工作?

[英]Flutter - How does MultiProvider work with providers of the same type?

例如,我試圖一次獲取為多個流發出的數據,但是這些流中的 2 個或更多發出相同類型的數據,比如說一個字符串。

我的問題是,是否可以使用MultiProvider並使用相同類型的多個StreamProvider (或任何提供者,但我對這種情況感興趣),同時仍然能夠訪問每個提供者發出的數據?

一個解決方案是在使用常見數據類型時使用StreamBuilder ,但我真的很喜歡MultiProvider在更清晰的代碼方面提供的功能。

例子:

class MyScreen extends StatelessWidget {
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        StreamProvider<String>(stream: Observable.just("stream1")),
        StreamProvider<String>(stream: Observable.just("stream2")),
        StreamProvider<String>(stream: Observable.just("stream3"))
      ],
      child: Builder(
        builder: (BuildContext context) {
          AsyncSnapshot<String> snapshot =
              Provider.of<AsyncSnapshot<String>>(context);
          String data = snapshot.data;
          return Text(data); 
        },
      ),
    );
  }
}

MultiProvider與否不會改變任何東西。 如果兩個提供程序共享相同的類型,則最深的一個會覆蓋該值。

不可能從不是給定類型最接近祖先的提供者那里獲取值。

如果您需要獨立訪問所有這些值,則每個值都應具有唯一的類型。

例如,而不是:

Provider<int>(
  value: 42,
  child: Provider<int>(
    value: 84,
    child: <something>
  ),
)

你可以做:

class Root {
  Root(this.value);

  final int value;
}

class Leaf {
  Leaf(this.value);

  final int value;
}


Provider<Root>(
  value: Root(42),
  child: Provider<Leaf>(
    value: Leaf(84),
    child: <something>
  ),
)

這允許使用以下方法獨立獲取每個值:

Provider.of<Root>(context)
Provider.of<Leaf>(context);

更新:我最近發現的這個解決方案似乎更干凈,工作得更好。 下面的解決方案是另一種方式,但需要更多的編碼。


我一直在尋找類似的解決方案,但找不到任何解決方案,因此我使用 MultiProvider、StreamGroup 和 ChangeNotifier 實現了自己的解決方案。 我使用StreamGroup來保存我需要通過添加和刪除流來跟蹤的所有流。 我不想使用一堆額外的庫和/或插件。

ChangeNotifierProxyProvider ,只要Family流從其上方的StreamProvider<Family>獲得更新,它就會運行update函數。

// main.dart
@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      StreamProvider<Family>(
        initialData: Family(),
        create: (context) => FirebaseFireStoreService().streamFamilyInfo(),
      ),
      ChangeNotifierProxyProvider<Family, FamilyStore>(
        create: (context) => FamilyStore(),
        update: (context, family, previousFamilyStore) {
          // Manually calling the function to update the 
          // FamilyStore store with the new Family
          previousFamilyStore!.updateFamily(family);
          return previousFamilyStore;
        },
      )
    ],
    builder: (context, child) => MaterialApp(),
  );
}

Family只保存了一個AdultProfile uid數組,所以我可以跟蹤一個家庭中的成年人。 它基本上只是一個流接收器。

// family.dart
class Family {
  List<String> adults;

  Family({
    this.adults = const [],
  });

  factory Family.fromMap(Map<String, dynamic>? data) {
    if (data == null) {
      return Family(adults: []);
    }

    return Family(
      adults: [...data['adults']],
    );
  }
}

在我的 firestore class ,我有必要的函數可以為我需要的class返回一個Stream 我只粘貼了Family的函數,但為AdultProfile粘貼了相同的代碼,但路徑變化很小。

// firebase_firestore.dart
Stream<Family> streamFamilyInfo() {
  try {
    return familyInfo(FirebaseAuthService().currentUser!.uid).snapshots().map(
      (snapshot) {
        return Family.fromMap(snapshot.data() as Map<String, dynamic>);
      },
    );
  } catch (e) {
    FirebaseAuthService().signOut();
    rethrow;
  }
}

這是大部分工作發生的地方:

// family_store.dart
class FamilyStore extends ChangeNotifier {
  List<AdultProfile>? adults = [];
  StreamGroup? streamGroup = StreamGroup();

  FamilyStore() {
    // Handle the stream as they come in
    streamGroup!.stream.listen((event) {
      _handleStream(event);
    });
  }

  void handleStream(streamEvent) {
    // Deal with the stream as they come in 
    // for the different instances of the class
    // you may have in the data structure
    if (streamEvent is AdultProfile) {
      int index = adults!.indexWhere((element) => element.uid == streamEvent.uid);
      if (index >= 0) {
        adults![index] = streamEvent;
      } else {
        adults!.add(streamEvent);
      }
      notifyListeners();
  }

  // This is the function called from the
  // ChangeNotifierProxyProvider in main.dart
  void updateFamily(Family newFamily) {
    _updateAdults(newFamily.adults);
  }

  void _updateAdults(List<String> newAdults) async {
    if (newAdults.isEmpty) return;

    // Generate list of comparisons so you can add/remove
    // streams from StreamGroup and the array of classes
    Map<String, List<String>> updateLists =
        _createAddRemoveLists(adults!.map((profile) => profile.uid).toList(), newAdults);

    for (String uid in updateLists['add']!) {
      // Add the stream for the instance of the 
      // AdultProfile to the StreamGroup
      (streamGroup!.add(
        FirebaseFireStoreService().streamAdultProfile(uid),
      ));
    }

    for (String uid in updateLists['remove']!) {
      // Remove the stream for the instance of the 
      // AdultProfile from the StreamGroup
      streamGroup!.remove(
        FirebaseFireStoreService().streamAdultProfile(uid),
      );
      // Also remove it from the array
      adults!.removeWhere((element) => element.uid == uid);
      notifyListeners();
    }
  }
}

暫無
暫無

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

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