Is there a way to rebuild only an item of a list within stream builder (flutter)?

73 Views Asked by At

I'm using StreamBuilder to get documents of a firestore collection. Each document has many fields which are used in a StatefulWidget named JobWidget. The list (ListView.custom) of JobWidgets is what my StreamBuilder shows.

When there is an update in a single job, complete list gets rebuild. Is there anyway to build just that particular item in the list? Thanks.

Below is the minimal reproduction of my code.

class JobsDetailView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(10.0),
      margin: EdgeInsets.all(15.0),
      decoration: BoxDecoration(borderRadius: BorderRadius.circular(4.0), color: Colors.white),
      child: StreamBuilder<List<JobDetailWithID>>(
        stream: readJobs(),
        builder: (context, snapshot) {
          List<JobWidget> jobItems = [];
          if (snapshot.hasData) {
            final jobs = snapshot.data;

            /// Sending jobs to parent widget
            this.jobsCallback.call(jobs!);

            for (var x in jobs) {
              jobItems.add(JobWidget(jobDetailsWithID: x));
            }
          } else if (snapshot.hasError) {
            return Center(
                // TODO: Show reload/try again button
                child: Text(
              'Could not load data\n ${snapshot.error}',
              style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.red),
            ));
          } else {
            return Center(child: CircularProgressIndicator());
          }
          return ListView.custom(
            primary: false,
            childrenDelegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return KeepAlive(
                    key: ValueKey<JobWidget>(jobItems[index]),
                    data: jobItems[index],
                  );
                },
                childCount: jobItems.length,
                findChildIndexCallback: (Key key) {
                  final ValueKey<JobWidget> valueKey = key as ValueKey<JobWidget>;
                  final JobWidget data = valueKey.value;
                  final idx = jobItems.indexWhere((element) => element == data);
                  if (idx > 0)
                    return jobItems.indexOf(data);
                  else
                    return null;
                }),
          );
        },
      ),
    );
  }

  static Stream<List<JobDetailWithID>> readJobs() async* {
    var stream = FirebaseFirestore.instance
        .collection('jobs')
        .where("timeline.start_dt",
            isGreaterThan: DateTime.now().subtract(Duration(days: 122)))
        .snapshots()
        .map((snapshot) => snapshot.docs.map((doc) => JobDetailWithID.fromMap(doc)).toList());
    await for (final snapshot in stream) {
      yield snapshot;
    }
  }
}

class JobWidget extends StatefulWidget {
  final JobDetailWithID jobDetailsWithID;

  JobWidget({Key? key, required this.jobDetailsWithID}) : super(key: key);

  @override
  State<JobWidget> createState() => _JobWidgetState();
}

class _JobWidgetState extends State<JobWidget> {
  /// Creating a StatefulWidget UI showing job details (900+ lines of code so omitted here)
}
0

There are 0 best solutions below