skip to Main Content

I have custom class model called UserModel and it has below fields.

  final String userId;
  final String name;
  final String age;
  final String fullBirthday;
  final String gender;
  final String phone;
  final String fullRegion;
  final String registerDate;
  final int? totalScore;
  final int? stepScore;
  final int? diaryScore;
  final int? commentScore;

And I have list contained several these UserModels.

  1. I add users getting from FireStore to this list
  2. I update stepScore, diaryScore, commentScore
  3. and update totalScore as adding these three scores.

For the second step, I send <List<UserModel?>> as parameter to the second step.

And as iterating this list with for in loop, I get data from collection step, diary, comment from Firstore.

In FamilyAsyncNotifier I send this list as paremeter, and here’s my updateScore function.

class RankingViewModel
    extends FamilyAsyncNotifier<List<UserModel?>, List<UserModel?>> {
  late RankingRepository _rankingRepository;

  @override
  FutureOr<List<UserModel?>> build(List<UserModel?> users) async {
    _rankingRepository = RankingRepository();
    DateTime now = DateTime.now();
    DateTime firstDateOfMonth = DateTime(now.year, now.month, 1);
    final updatedUsers = await updateScore(users, firstDateOfMonth, now);
    return updatedUsers;
  }

  Future<List<UserModel?>> updateScore(
      List<UserModel?> users, DateTime startDate, DateTime endDate) async {
    List<UserModel?> scoreUserList = [];

    for (var index = 0; index < users.length; index++) {
      final String userId = users[index]!.userId;

      int? stepScore =
          await _rankingRepository.getStepScores(userId, startDate, endDate);
      int? diaryScore =
          await _rankingRepository.getDiaryScores(userId, startDate, endDate);
      int? commentScore =
          await _rankingRepository.getCommentScores(userId, startDate, endDate);
      int? totalScore = stepScore + diaryScore + commentScore;

      final updatedUser = users[index]?.copyWith(
        totalScore: totalScore,
        stepScore: stepScore,
        diaryScore: diaryScore,
        commentScore: commentScore,
      );
      scoreUserList.add(updatedUser);
    }
    return scoreUserList;
  }
}

final rankingProvider = AsyncNotifierProvider.family<RankingViewModel,
    List<UserModel?>, List<UserModel?>>(
  () => RankingViewModel(),
);

Of course, upadteScore function has three function to get data from collection step, diary, comment in Firestore.

That is in my repository class.

class RankingRepository {
  final FirebaseFirestore _db = FirebaseFirestore.instance;

  List<DateTime> getBetweenDays(DateTime startDate, DateTime endDate) {
    List<DateTime> dates = [];
    DateTime currentDate = startDate;

    while (currentDate.isBefore(endDate) ||
        currentDate.isAtSameMomentAs(endDate)) {
      dates.add(currentDate);
      currentDate = currentDate.add(const Duration(days: 1));
    }
    return dates;
  }

  Future<int> getStepScores(
      String userId, DateTime startDate, DateTime endDate) async {
    List<DateTime> betweenDates = getBetweenDays(startDate, endDate);
    int stepScores = 0;
    for (var date in betweenDates) {
      final dateString =
          "${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}";
      final query = await _db.collection("user_step_count").doc(userId).get();

      if (query.exists) {
        final dateExists = query.data()?.containsKey(dateString);

        if (dateExists!) {
          dynamic diaryStepString = query.get(dateString);

          final int dailyStepInt = diaryStepString as int;

          final dailyScore = dailyStepInt < 0
              ? 0
              : dailyStepInt > 10000
                  ? 100
                  : ((dailyStepInt / 1000).floor()) * 10;
          stepScores += dailyScore;
        }
      }
    }
    return stepScores;
  }

  Future<int> getDiaryScores(
      String userId, DateTime startDate, DateTime endDate) async {
    final query = await _db
        .collection("diary")
        .where("userId", isEqualTo: userId)
        .where("timestamp", isGreaterThanOrEqualTo: startDate)
        .where("timestamp", isLessThanOrEqualTo: endDate)
        .get();
    int docCount = query.docs.length;
    return docCount * 100;
  }

  Future<int> getCommentScores(
      String userId, DateTime startDate, DateTime endDate) async {
    final query = await _db
        .collectionGroup("comments")
        .where("userId", isEqualTo: userId)
        .where("timestamp", isGreaterThanOrEqualTo: startDate)
        .where("timestamp", isLessThanOrEqualTo: endDate)
        .get();
    int docCount = query.docs.length;
    return docCount * 20;
  }
}

But when I run this, it takes too much time.

I think this is due to below line in updateScore function.

  int? stepScore =
          await _rankingRepository.getStepScores(userId, startDate, endDate);
      int? diaryScore =
          await _rankingRepository.getDiaryScores(userId, startDate, endDate);
      int? commentScore =
          await _rankingRepository.getCommentScores(userId, startDate, endDate);
      int? totalScore = stepScore + diaryScore + commentScore;

I wait for getStepScores function finished, and then wait for getDiaryScores finished..

But when I do this process same in ReactJS, It doens’t take this this much at all as getting data from firestore and compile this data.

I want to review my process is appropriately composed for my purpose and know how to make it faster.

Please help me.

2

Answers


  1. You can use the Future.wait function to achieve concurrency and optimize the code.

    final List<int?> results = await Future.wait([
      _rankingRepository.getStepScores(userId, startDate, endDate),
      _rankingRepository.getDiaryScores(userId, startDate, endDate),
      _rankingRepository.getCommentScores(userId, startDate, endDate),
    ]);
    
    final int? stepScore = results[0];
    final int? diaryScore = results[1];
    final int? commentScore = results[2];
    
    Login or Signup to reply.
  2. With Dart 3 we can use the wait property of the FutureIterableExtension, which catches errors in ParallelWaitError, instead of Future.wait(), which catches only the first error.

    final results = await [
      _rankingRepository.getStepScores(userId, startDate, endDate),
      _rankingRepository.getDiaryScores(userId, startDate, endDate),
      _rankingRepository.getCommentScores(userId, startDate, endDate),
    ].wait;
    

    You can then access the future by the index.

    final stepScore = results[0];
    final diaryScore = results[1];
    final commentScore = results[2];
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search