简体   繁体   中英

How to change the stream in StreamProvider in Flutter

I'm consuming a stream from Firebase Firestore with a StreamProvider from the provider -package like the below code snippet shows ( DatabaseService.streamVehicles is just a wrapper function to the actual query to Firestore).

In response to user events, the locationId variable will be set to different ids to filter in the Firestore query. However, whenever locationId changes, the component will rebuild, but the function passed to the create -parameter will not be called again, so the new value of locationId will not be passed to the query and the stream is still the same, unfiltered stream. How can I make sure a new stream is created whenever locationId changes?

class HomePage extends StatefulWidget {
  static final String id = 'home';

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

class _HomePageState extends State<HomePage> {
  final CupertinoTabController _tabController =
      CupertinoTabController(initialIndex: 0);

  String locationId;

  void setLocationId(String newId) {
    this.setState(() {
      locationId = newId;
    });
  }

  @override
  Widget build(BuildContext context) {
    print("Building homepage");
    return Consumer<User>(
      builder: (context, user, _) {
        return StreamProvider<List<Vehicle>>(
          create: (context) => DatabaseService.streamVehicles(
            user.organization.id,
            locationId: locationId,
          ),
          catchError: (ctx, err) {
            print("Error in stream");
            print(err);
            return [];
          },
          child: CupertinoTabScaffold(
            controller: _tabController,
            tabBar: CupertinoTabBar(
              activeColor: kHighlightColor,
              items: <BottomNavigationBarItem>[
                BottomNavigationBarItem(
                  icon: Icon(CupertinoIcons.collections),
                  title: Text('Vehicles'),
                ),
                BottomNavigationBarItem(
                  icon: Icon(CupertinoIcons.settings),
                  title: Text('Settings'),
                ),
              ],
            ),
            tabBuilder: (BuildContext context, int index) {
              switch (index) {
                case 0:
                  return VehiclesPage(
                      locationId: locationId, setLocationId: setLocationId);
                case 1:
                  return SettingsPage();
              }
              return null;
            },
          ),
        );
      },
    );
  }
}

The streamVehicles method of DatabaseService is defined as the following.

  static Stream<List<Vehicle>> streamVehicles(
    String organizationId, {
    String locationId,
  }) {
    final Query ref = _firestore
        .collection('organizations')
        .document(organizationId)
        .collection('vehicles')
        .where(
          "location.id",
          isEqualTo: locationId,
        );

    return ref.snapshots().map((vehicleSnapshots) => vehicleSnapshots.documents
        .map((vehicleSnapshot) => Vehicle.fromFirestore(vehicleSnapshot))
        .toList());
  }

If you want to create a new stream whenever something happens, and then render the contents of that stream, I'd typically store the stream in the state of the widget:

void setLocationId(String newId) {
  this.setState(() {
    locationId = newId;
  });
  // TODO: determine the new stream, and set that in the state too
}

Then in your build method, you use the stream from the state, instead of directly from the service.

Use a StreamProvider.family to create a new stream provider unique to each id's.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM