212 lines
7.5 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:mobiles_labs_5th_semester/components/extensions/context_x.dart';
import 'package:mobiles_labs_5th_semester/domain/models/game.dart';
import 'package:mobiles_labs_5th_semester/presentation/common/svg_objects.dart';
import 'package:mobiles_labs_5th_semester/presentation/details_page/details_page.dart';
import 'package:mobiles_labs_5th_semester/presentation/home_page/bloc/bloc.dart';
import 'package:mobiles_labs_5th_semester/presentation/home_page/bloc/events.dart';
import 'package:mobiles_labs_5th_semester/presentation/home_page/bloc/state.dart';
import 'package:mobiles_labs_5th_semester/presentation/like_bloc/like_bloc.dart';
import 'package:mobiles_labs_5th_semester/presentation/locale_bloc/locale_events.dart';
import '../../components/utils/debounce.dart';
import '../like_bloc/like_event.dart';
import '../like_bloc/like_state.dart';
import '../locale_bloc/locale_bloc.dart';
import '../locale_bloc/locale_state.dart';
part 'game_card.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key, required this.title});
final String title;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 46, 65, 80),
appBar: AppBar(
backgroundColor: Color.fromARGB(255, 56, 90, 128),
title: Text(
widget.title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
),
body: const Body());
}
}
class Body extends StatefulWidget {
const Body({super.key});
@override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
late Future<List<Game>?> data;
final searchController = TextEditingController();
final scrollController = ScrollController();
@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) {
final bloc = context.read<HomeBloc>();
if (!bloc.state.isPaginationLoading) {
bloc.add(HomeLoadDataEvent(
search: searchController.text,
nextPage: bloc.state.data?.nextPage,
));
}
}
}
@override
void dispose() {
searchController.dispose();
scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 12, left: 12, right: 12),
child: Row(
children: [
Expanded(
flex: 3,
child: CupertinoSearchTextField(
placeholder: context.locale.search,
backgroundColor: Colors.white,
style: TextStyle(color: Color.fromARGB(255, 46, 65, 80)),
controller: searchController,
onChanged: (search) {
Debounce.run(() => context.read<HomeBloc>().add(HomeLoadDataEvent(search: search)));
},
),
),
//значок svg с языком
Padding(
padding: const EdgeInsets.only(left: 12.0),
child: GestureDetector(
//при нажатии на значок отправка события в bloc
onTap: () => context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
child: SizedBox.square(
dimension: 50,
child: Padding(
padding: EdgeInsets.only(right: 12),
child: BlocBuilder<LocaleBloc, LocaleState>(builder: (context, state) {
return state.currentLocale.languageCode == 'ru' ? const SvgRu() : const SvgUk();
}),
),
)),
)
],
),
),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.error != null
? Text(
state.error ?? '',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.red),
)
: state.isLoading
? const CircularProgressIndicator()
: BlocBuilder<LikeBloc, LikeState>(
builder: (context, likeState) {
return Expanded(
child: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.builder(
controller: scrollController,
itemCount: state.data?.data?.length ?? 0,
itemBuilder: (context, index) {
final data = state.data?.data?[index];
return data != null
? _GameCard.fromData(
data,
onLike: _onLike,
isLiked: likeState.likedIds?.contains(data.id.toString()) == true,
onTap: () => _navToDetails(context, data),
)
: const SizedBox.shrink();
},
),
),
);
},
)),
//значок загрузки при пагинации
BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.isPaginationLoading ? const CircularProgressIndicator() : const SizedBox.shrink(),
)
],
);
}
Future<void> _onRefresh() {
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text));
return Future.value(null);
}
void _onLike(String? id, String name, bool isLiked) {
if (id != null) {
context.read<LikeBloc>().add(ChangeLikeEvent(id));
_showSnackBar(context, name, !isLiked);
}
}
void _showSnackBar(BuildContext context, String name, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
isLiked ? context.locale.liked + '"$name"' : context.locale.disliked + '"$name"',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Colors.pink, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
backgroundColor: Colors.white,
duration: const Duration(seconds: 1),
));
});
}
void _navToDetails(BuildContext context, Game data) {
if (data.id != null) {
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => DetailsPage(data.id!)),
);
}
}
}