From 6eda69fcc5d49f5f2fecdecf9d0bd3a881118511 Mon Sep 17 00:00:00 2001 From: "nikbel2004@outlook.com" Date: Wed, 18 Dec 2024 17:22:07 +0400 Subject: [PATCH] Components: Implementation of Search Debounces --- lib/components/utils/debounce.dart | 22 +++++++++++++ .../bloc/candy_bloc/candy_bloc.dart | 31 +++++++++++++++++++ .../bloc/candy_bloc/candy_event.dart | 10 ++++++ .../bloc/candy_bloc/candy_state.dart | 25 +++++++++++++++ .../pages/home_page/candy_page.dart | 1 - 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/components/utils/debounce.dart create mode 100644 lib/presentation/bloc/candy_bloc/candy_bloc.dart create mode 100644 lib/presentation/bloc/candy_bloc/candy_event.dart create mode 100644 lib/presentation/bloc/candy_bloc/candy_state.dart diff --git a/lib/components/utils/debounce.dart b/lib/components/utils/debounce.dart new file mode 100644 index 0000000..40f6ac0 --- /dev/null +++ b/lib/components/utils/debounce.dart @@ -0,0 +1,22 @@ +import 'dart:async'; +import 'dart:ui'; + +class Debounce { + // Фабричный конструктор, возвращающий экземпляр класса Debounce. Паттерн Singleton + factory Debounce() => _instance; + + // Приватный конструктор (Не даёт создание класса извне) + Debounce._(); + + // Статический экземпляр класса + static final Debounce _instance = Debounce._(); + + static Timer? _timer; + + // Статический метод, где action функция, совершающая действие, а delay совершает задержку + static void run( + {required VoidCallback action, Duration delay = const Duration(milliseconds: 500)}) { + _timer?.cancel(); + _timer = Timer(delay, action); + } +} diff --git a/lib/presentation/bloc/candy_bloc/candy_bloc.dart b/lib/presentation/bloc/candy_bloc/candy_bloc.dart new file mode 100644 index 0000000..d042026 --- /dev/null +++ b/lib/presentation/bloc/candy_bloc/candy_bloc.dart @@ -0,0 +1,31 @@ +import 'package:candystore/data/repositories/candy_repository.dart'; +import 'package:candystore/presentation/bloc/candy_bloc/candy_event.dart'; +import 'package:candystore/presentation/bloc/candy_bloc/candy_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class CandyBloc extends Bloc { + final CandyRepository repository; + + CandyBloc(this.repository) : super(const CandyState()) { + on(_onLoadData); + } + + Future _onLoadData(CandyLoadDataEvent event, Emitter emit) async { + if (event.nextPage == null) { + emit(state.copyWith(isLoading: true)); + } else { + emit(state.copyWith(isPaginationLoading: true)); + } + + String? error; + + final data = await repository.loadData( + q: event.search, page: event.nextPage ?? 1, onError: (e) => error = e); + + if (event.nextPage != null) { + data?.data?.insertAll(0, state.data?.data ?? []); + } + + emit(state.copyWith(isLoading: false, isPaginationLoading: false, data: data, error: error)); + } +} diff --git a/lib/presentation/bloc/candy_bloc/candy_event.dart b/lib/presentation/bloc/candy_bloc/candy_event.dart new file mode 100644 index 0000000..6ed4fd8 --- /dev/null +++ b/lib/presentation/bloc/candy_bloc/candy_event.dart @@ -0,0 +1,10 @@ +abstract class CandyEvent { + const CandyEvent(); +} + +class CandyLoadDataEvent extends CandyEvent { + final String? search; + final int? nextPage; + + const CandyLoadDataEvent({this.search, this.nextPage}); +} diff --git a/lib/presentation/bloc/candy_bloc/candy_state.dart b/lib/presentation/bloc/candy_bloc/candy_state.dart new file mode 100644 index 0000000..8f9a89b --- /dev/null +++ b/lib/presentation/bloc/candy_bloc/candy_state.dart @@ -0,0 +1,25 @@ +import 'package:candystore/models/candy_data.dart'; +import 'package:equatable/equatable.dart'; + +class CandyState extends Equatable { + final CandyData? data; + final bool isLoading; + final bool isPaginationLoading; + final String? error; + + const CandyState( + {this.data, this.isLoading = false, this.isPaginationLoading = false, this.error}); + + // Получение нового экземпляра состояния + CandyState copyWith( + {CandyData? data, bool? isLoading, bool? isPaginationLoading, String? error}) => + CandyState( + data: data ?? this.data, + isLoading: isLoading ?? this.isLoading, + isPaginationLoading: isPaginationLoading ?? this.isPaginationLoading, + error: error ?? this.error); + + // Поля, по которым Equtable сравнивает состояния + @override + List get props => [data, isLoading, isPaginationLoading, error]; +} diff --git a/lib/presentation/pages/home_page/candy_page.dart b/lib/presentation/pages/home_page/candy_page.dart index 85ad7e7..ddb92d4 100644 --- a/lib/presentation/pages/home_page/candy_page.dart +++ b/lib/presentation/pages/home_page/candy_page.dart @@ -1,5 +1,4 @@ import 'dart:math'; -import 'package:candystore/components/l10n/app_locale.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart';