Firebase stream generic

In this post learn firebase stream (query, sort)generic.

Get the data stream using generic

Stream<List<T>> collectionStream<T>({
  @required String path,
  @required T Function(Map<String, dynamic> data, String documentId) builder,
  Query Function(Query query) queryBuilder,
  int Function(T lhs, T rhs) sort,
}) {
  Query query = FirebaseFirestore.instance.collection(path);
  if (queryBuilder != null) {
    query = queryBuilder(query);
  }
  final snapshots = query.snapshots();
  return snapshots.map((snapshot) {
    final result = snapshot.docs
        .map((snapshot) => builder(snapshot.data(), snapshot.id))
        .where((value) => value != null)
        .toList();
    if (sort != null) {
      result.sort(sort);
    }
    return result;
  });
}

Update data using generic

Future<void> updateDocument<T>(String path, T object) async {
    try {
      await _firestore.doc(path).update(JsonMapper.toMap(object)!);
    } catch (e) {
      // TODO: Hanlde AppException
      rethrow;
    }
  }

calling generic function get the data

static String entries(String uid) => 'users/$uid/entries';

Stream<List<Entry>> entriesStream({Job job}) =>
      _service.collectionStream<Entry>(
        path: APIPath.entries(uid),
        queryBuilder: job != null
            ? (query) => query.where('jobId', isEqualTo: job.id)
            : null,
        builder: (data, documentID) => Entry.fromMap(data, documentID),
        sort: (lhs, rhs) => rhs.start.compareTo(lhs.start),
      );

Model class

import 'package:flutter/material.dart';

class Job {
  Job({@required this.name, @required this.ratePerHour, @required this.id});

  final int ratePerHour;
  final String name;
  final String id;

  factory Job.fromMap(Map<String, dynamic> data, String docmentId) {
    if (data == null) {
      return null;
    }
    final String name = data['name'];
    final int ratePerHour = data['ratePerHour'];
    return Job(
      name: name,
      ratePerHour: ratePerHour,
      id: docmentId,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'name': name,
      'ratePerHour': ratePerHour,
    };
  }
}

Get the data model class

import 'package:flutter/foundation.dart';

class Entry {
  Entry({
    @required this.id,
    @required this.jobId,
    @required this.start,
    @required this.end,
    this.comment,
  });

  String id;
  String jobId;
  DateTime start;
  DateTime end;
  String comment;

  double get durationInHours =>
      end.difference(start).inMinutes.toDouble() / 60.0;

  factory Entry.fromMap(Map<dynamic, dynamic> value, String id) {
    final int startMilliseconds = value['start'];
    final int endMilliseconds = value['end'];
    return Entry(
      id: id,
      jobId: value['jobId'],
      start: DateTime.fromMillisecondsSinceEpoch(startMilliseconds),
      end: DateTime.fromMillisecondsSinceEpoch(endMilliseconds),
      comment: value['comment'],
    );
  }

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'jobId': jobId,
      'start': start.millisecondsSinceEpoch,
      'end': end.millisecondsSinceEpoch,
      'comment': comment,
    };
  }
}

https://github.com/fluttertutorialin/getxcleanarchitecturefirebase