2024-12-20 11:40:46 +04:00

219 lines
9.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:pmu/components/extensions/context_x.dart';
import 'package:pmu/components/utils/debounce.dart';
import 'package:pmu/data/repositories/bbc_repository.dart';
import 'package:pmu/domain/models/card.dart';
import 'package:pmu/presentation/common/svg_objects.dart';
import 'package:pmu/presentation/details_page/details_page.dart';
import 'package:pmu/presentation/dialogs/show_dialog.dart';
import 'package:pmu/presentation/home_page/bloc/bloc.dart';
import 'package:pmu/presentation/home_page/bloc/events.dart';
import 'package:pmu/presentation/home_page/bloc/state.dart';
import 'package:pmu/presentation/likes_bloc/likes_bloc.dart';
import 'package:pmu/presentation/likes_bloc/likes_events.dart';
import 'package:pmu/presentation/likes_bloc/likes_state.dart';
import 'package:pmu/presentation/locale_bloc/locale_bloc.dart';
import 'package:pmu/presentation/locale_bloc/locale_events.dart';
import 'package:pmu/presentation/locale_bloc/locale_state.dart';
part 'card.dart';
//основной виджет со скаффолд и боди внутри
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
// состояние его
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return const Scaffold(
body: _Body(),
);
}
}
// виджет с соновной логикой(на самом деле вся логика в состоянии)
class _Body extends StatefulWidget {
const _Body({super.key});
@override
State<_Body> createState() => _BodyState();
}
// состояние боди с основной лог8икой
class _BodyState extends State<_Body> {
// создание объектов контроллеров для скролла и поиска
final searchController = TextEditingController();
final scrollController = ScrollController();
int page = 1;
// инициализация
@override
void initState() {
//инициализация иконок флагов вроде
SvgObjects.init();
// после завершения сборки виджетов отправляет события загрузки данных и лайков в соответсвующие блоки, дальнейшая работа происходит там
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HomeBloc>().add(const HomeLoadDataEvent());
context.read<LikeBloc>().add(const LoadLikesEvent());
});
// вешает слушатель на скролл контроллер(для пагинации)
scrollController.addListener(_onNextPageListener);
super.initState();
}
//слушатель на скролл контроллер
void _onNextPageListener() {
// проверка на то что список прокручен не до конца
if (scrollController.offset >= scrollController.position.maxScrollExtent) {
// preventing multiple pagination request on multiple swipes
final homebloc = context.read<HomeBloc>();
// проверка на то что подгрузка пагинации уже не активна
if (!homebloc.state.isPaginationLoading) {
page ++;
// отправляем в хомблок событие на подгрузку данных со следующей страницы
homebloc.add(HomeLoadDataEvent(
search: searchController.text,
nextPage: page,
));
}
}
}
// освобождает контроллеры(чистит)
void dispose() {
searchController.dispose();
scrollController.dispose();
super.initState();
}
@override
Widget build(BuildContext context) {
//final data = BbcRepository().loadData();
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(children: [
Row(
children: [
Expanded(
flex: 4,
child: Padding(
padding: const EdgeInsets.all(12),
// поисковая строка и немного ее обработки: на изменение текста в строке происходит ран дебаунса, я пока не знаю что это. и посылается в хомблок событие на загрузку новых данных
child: CupertinoSearchTextField(
controller: searchController,
placeholder: context.locale.search,
onChanged: (search) {
Debounce.run(() => context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: search)));
},
),
),
),
GestureDetector(
// при нажатии на эту херь отправляется событие на смену локали
onTap: () =>
context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
child: SizedBox.square(
dimension: 50,
child: Padding(
padding: const EdgeInsets.only(right: 12),
// отображение флага в зависимости от состояния локали
child: BlocBuilder<LocaleBloc, LocaleState>(
builder: (context, localeState) {
return localeState.currentLocale.languageCode == 'ru'
? const SvgRu()
: const SvgUk();
},
),
),
),
),
],
),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, homeState) => homeState.error != null
? Text(
homeState.error ?? '',
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: Colors.red),
)
: homeState.isLoading
? const CircularProgressIndicator()
: BlocBuilder<LikeBloc, LikeState>(
builder: (context, likeState) {
return Expanded(
child: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
controller: scrollController,
padding: EdgeInsets.zero,
itemCount: homeState.data?.data?.length ?? 0,
itemBuilder: (context, index) {
final data = homeState.data?.data?[index];
return data != null
? _Card.fromData(
data,
onLike: _onLike,
isLiked: likeState.likedIds
?.contains(data.id) ==
true,
onTap: () =>
_navToDetails(context, data),
)
: const SizedBox.shrink();
},
),
),
);
},
),
),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, homeState) => homeState.isPaginationLoading
? const CircularProgressIndicator()
: const SizedBox.shrink(),
),
]),
);
}
// функция показа снэкбара
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
// в зависимомти от локализациии и от того понравилась или разонравилась новость отображается итоговый снэкбар
'${isLiked ? context.locale.liked : context.locale.disliked}',
style: Theme.of(context).textTheme.bodyLarge,
),
backgroundColor: Colors.lightBlue,
duration: const Duration(seconds: 1),
));
});
}
// функция для перехода к детальной странице
void _navToDetails(BuildContext context, CardData data) {
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => DetailsPage(data)),
);
}
// функция вызывается при нажатии на кнопку лайка и вызывает функцию показа снэкбара, так же, запоминается айди лайкнутой новости
void _onLike(String? id, String title, bool isLiked) {
if (id != null) {
context.read<LikeBloc>().add(ChangeLikeEvent(id));
_showSnackBar(context, title, !isLiked);
}
}
// функция вызывается при тяге вниз основной части для обновления и вызывает событие загрузки данных для homedata
Future<void> _onRefresh() {
page = 1;
context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: searchController.text));
return Future.value(null);
}
}