lab7
This commit is contained in:
parent
5db40e6364
commit
f420330f68
BIN
assets/icon.jpg
BIN
assets/icon.jpg
Binary file not shown.
Before Width: | Height: | Size: 8.0 KiB |
@ -31,21 +31,53 @@ class MoviePaginationDto {
|
|||||||
@JsonSerializable(createToJson: false)
|
@JsonSerializable(createToJson: false)
|
||||||
class MovieDataDto {
|
class MovieDataDto {
|
||||||
@JsonKey(name: 'id')
|
@JsonKey(name: 'id')
|
||||||
final int? id; // Это поле может использоваться для идентификации фильма
|
final int? id;
|
||||||
final String? name; // Название фильма
|
final String? name;
|
||||||
final String? description; // Описание фильма
|
final String? description;
|
||||||
final PosterDto? poster; // Постер фильма
|
final PosterDto? poster;
|
||||||
|
@JsonKey(name: 'year', defaultValue: 0)
|
||||||
|
final int year;
|
||||||
|
final List<GenreDto>? genres;
|
||||||
|
final List<CountryDto>? countries;
|
||||||
|
|
||||||
const MovieDataDto(this.name, this.description, this.poster, {this.id});
|
const MovieDataDto(
|
||||||
|
this.name,
|
||||||
|
this.description,
|
||||||
|
this.poster, {
|
||||||
|
this.id,
|
||||||
|
this.year = 0,
|
||||||
|
this.genres,
|
||||||
|
this.countries,
|
||||||
|
});
|
||||||
|
|
||||||
factory MovieDataDto.fromJson(Map<String, dynamic> json) =>
|
factory MovieDataDto.fromJson(Map<String, dynamic> json) =>
|
||||||
_$MovieDataDtoFromJson(json);
|
_$MovieDataDtoFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(createToJson: false)
|
||||||
|
class GenreDto {
|
||||||
|
final String? name;
|
||||||
|
|
||||||
|
const GenreDto({this.name});
|
||||||
|
|
||||||
|
factory GenreDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GenreDtoFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(createToJson: false)
|
||||||
|
class CountryDto {
|
||||||
|
final String? name;
|
||||||
|
|
||||||
|
const CountryDto({this.name});
|
||||||
|
|
||||||
|
factory CountryDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$CountryDtoFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@JsonSerializable(createToJson: false)
|
@JsonSerializable(createToJson: false)
|
||||||
class PosterDto {
|
class PosterDto {
|
||||||
final String? url; // URL постера
|
final String? url;
|
||||||
final String? previewUrl; // URL миниатюры постера
|
final String? previewUrl;
|
||||||
|
|
||||||
const PosterDto({this.url, this.previewUrl});
|
const PosterDto({this.url, this.previewUrl});
|
||||||
|
|
||||||
|
@ -30,6 +30,21 @@ MovieDataDto _$MovieDataDtoFromJson(Map<String, dynamic> json) => MovieDataDto(
|
|||||||
? null
|
? null
|
||||||
: PosterDto.fromJson(json['poster'] as Map<String, dynamic>),
|
: PosterDto.fromJson(json['poster'] as Map<String, dynamic>),
|
||||||
id: (json['id'] as num?)?.toInt(),
|
id: (json['id'] as num?)?.toInt(),
|
||||||
|
year: (json['year'] as num?)?.toInt() ?? 0,
|
||||||
|
genres: (json['genres'] as List<dynamic>?)
|
||||||
|
?.map((e) => GenreDto.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
countries: (json['countries'] as List<dynamic>?)
|
||||||
|
?.map((e) => CountryDto.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
GenreDto _$GenreDtoFromJson(Map<String, dynamic> json) => GenreDto(
|
||||||
|
name: json['name'] as String?,
|
||||||
|
);
|
||||||
|
|
||||||
|
CountryDto _$CountryDtoFromJson(Map<String, dynamic> json) => CountryDto(
|
||||||
|
name: json['name'] as String?,
|
||||||
);
|
);
|
||||||
|
|
||||||
PosterDto _$PosterDtoFromJson(Map<String, dynamic> json) => PosterDto(
|
PosterDto _$PosterDtoFromJson(Map<String, dynamic> json) => PosterDto(
|
||||||
|
@ -2,18 +2,24 @@ import 'package:pmd_labs/data/dtos/movies_dto.dart';
|
|||||||
import 'package:pmd_labs/domain/models/carddata.dart';
|
import 'package:pmd_labs/domain/models/carddata.dart';
|
||||||
import 'package:pmd_labs/presentation/home_page/home_page.dart';
|
import 'package:pmd_labs/presentation/home_page/home_page.dart';
|
||||||
|
|
||||||
|
const _imagePlaceholder =
|
||||||
|
'https://upload.wikimedia.org/wikipedia/en/archive/b/b1/20210811082420%21Portrait_placeholder.png';
|
||||||
|
|
||||||
extension MovieDataDtoMapper on MovieDataDto {
|
extension MovieDataDtoMapper on MovieDataDto {
|
||||||
CardData toDomain() => CardData(
|
CardData toDomain() => CardData(
|
||||||
name ?? 'UNKNOWN', // Исправлено с title на name
|
name ?? 'UNKNOWN',
|
||||||
imageUrl: poster?.url, // Обратите внимание, что используем правильно поле
|
imageUrl: poster?.url ?? _imagePlaceholder,
|
||||||
id: id?.toString() ?? '0', // Защита от null, если id нет
|
id: id?.toString() ?? '0',
|
||||||
descriptionText: description ?? 'Нет описания', // Используем реальное описание
|
descriptionText: description ?? 'Нет описания',
|
||||||
|
year: year,
|
||||||
|
genres: genres?.map((genre) => genre.name ?? 'UNKNOWN').toList() ?? [],
|
||||||
|
countries: countries?.map((country) => country.name ?? 'UNKNOWN').toList() ?? [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MoviesDtoToModel on MoviesDto {
|
extension MoviesDtoToModel on MoviesDto {
|
||||||
HomeData toDomain() => HomeData(
|
HomeData toDomain() => HomeData(
|
||||||
data: docs?.map((e) => e.toDomain()).toList(), // Изменено с data на docs
|
data: docs?.map((e) => e.toDomain()).toList(),
|
||||||
nextPage: (pagination?.hasNextPage ?? false)
|
nextPage: (pagination?.hasNextPage ?? false)
|
||||||
? ((pagination?.currentPage ?? 0) + 1)
|
? ((pagination?.currentPage ?? 0) + 1)
|
||||||
: null
|
: null
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import 'package:pmd_labs/components/utils/error_callback.dart';
|
|
||||||
import 'package:pmd_labs/data/repositories/api_interface.dart';
|
|
||||||
import 'package:pmd_labs/domain/models/carddata.dart';
|
|
||||||
import 'package:pmd_labs/presentation/home_page/home_page.dart';
|
|
||||||
|
|
||||||
class MockRepository extends ApiInterface {
|
|
||||||
@override
|
|
||||||
Future<HomeData?> loadData({OnErrorCallback? onError}) async {
|
|
||||||
return HomeData(
|
|
||||||
data: [
|
|
||||||
CardData('JoJo’s Bizarre Adventure', descriptionText: 'kono dio da', imageUrl: 'https://i1.sndcdn.com/avatars-253MmMf9QZzxVBJi-rvlyeg-t1080x1080.jpg'),
|
|
||||||
CardData('Example', descriptionText: 'what is this?', imageUrl: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQvaBQ6nAedlqvXsh-dLXZi2Gexy1RkDbTUKQ&s'),
|
|
||||||
CardData('Mock data', descriptionText: 'Mock data description', imageUrl: 'https://cdn-user30887.skyeng.ru/uploads/6692a339c6989979804399.png'),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -14,14 +14,14 @@ class MovieRepository extends ApiInterface {
|
|||||||
));
|
));
|
||||||
|
|
||||||
static const String _baseUrl = 'https://api.kinopoisk.dev';
|
static const String _baseUrl = 'https://api.kinopoisk.dev';
|
||||||
static const String _apiKey = 'HQTFY5N-8D34FT0-HXQQQ1S-KPREHDX'; // Ваш API-ключ
|
static const String _apiKey = 'HQTFY5N-8D34FT0-HXQQQ1S-KPREHDX';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<HomeData?> loadData({
|
Future<HomeData?> loadData({
|
||||||
OnErrorCallback? onError,
|
OnErrorCallback? onError,
|
||||||
String? q,
|
String? q,
|
||||||
int page = 1,
|
int page = 1,
|
||||||
int pageSize = 15,
|
int pageSize = 10,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
const String url = '$_baseUrl/v1.4/movie/search';
|
const String url = '$_baseUrl/v1.4/movie/search';
|
||||||
@ -30,7 +30,7 @@ class MovieRepository extends ApiInterface {
|
|||||||
url,
|
url,
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'page': page,
|
'page': page,
|
||||||
'limit': pageSize,
|
'pageSize': pageSize,
|
||||||
'query': q,
|
'query': q,
|
||||||
},
|
},
|
||||||
options: Options(
|
options: Options(
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
|
||||||
class CardData {
|
class CardData {
|
||||||
final String text;
|
final String text;
|
||||||
final String descriptionText;
|
final String descriptionText;
|
||||||
final String? imageUrl;
|
final String? imageUrl;
|
||||||
final String? id;
|
final String? id;
|
||||||
|
final int? year;
|
||||||
CardData(this.text,
|
final List<String>? genres;
|
||||||
{required this.descriptionText,
|
final List<String>? countries;
|
||||||
|
CardData(this.text, {
|
||||||
|
required this.descriptionText,
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
this.id});
|
this.id,
|
||||||
|
this.year,
|
||||||
|
this.genres,
|
||||||
|
this.countries,
|
||||||
|
});
|
||||||
}
|
}
|
@ -21,16 +21,16 @@ class MyApp extends StatelessWidget {
|
|||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<LocaleBloc>(
|
return BlocProvider<LocaleBloc>( //
|
||||||
lazy: false,
|
lazy: false,
|
||||||
create: (context) => LocaleBloc(Locale(Platform.localeName)),
|
create: (context) => LocaleBloc(Locale(Platform.localeName)),
|
||||||
child: BlocBuilder<LocaleBloc, LocaleState>(
|
child: BlocBuilder<LocaleBloc, LocaleState>( //
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Flutter Demo',
|
title: 'Flutter Demo',
|
||||||
locale: state.currentLocale,
|
locale: state.currentLocale, // передаем текущую локаль
|
||||||
localizationsDelegates: AppLocale.localizationsDelegates,
|
localizationsDelegates: AppLocale.localizationsDelegates, // делегат (подключение локали)
|
||||||
supportedLocales: AppLocale.supportedLocales,
|
supportedLocales: AppLocale.supportedLocales, // список доступных локалей (подключение локали)
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme:
|
colorScheme:
|
||||||
@ -40,7 +40,7 @@ class MyApp extends StatelessWidget {
|
|||||||
home: RepositoryProvider<MovieRepository>(
|
home: RepositoryProvider<MovieRepository>(
|
||||||
lazy: true,
|
lazy: true,
|
||||||
create: (_) => MovieRepository(),
|
create: (_) => MovieRepository(),
|
||||||
child: BlocProvider<LikeBloc>(
|
child: BlocProvider<LikeBloc>( // добавили BlocProvider
|
||||||
lazy: false,
|
lazy: false,
|
||||||
create: (context) => LikeBloc(),
|
create: (context) => LikeBloc(),
|
||||||
child: BlocProvider<HomeBloc>(
|
child: BlocProvider<HomeBloc>(
|
||||||
|
@ -11,8 +11,11 @@ class DetailsPage extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text("Детали"),
|
title: Text("Детали"),
|
||||||
|
backgroundColor: Colors.purpleAccent, // Фиолетовый цвет для AppBar
|
||||||
),
|
),
|
||||||
body: SingleChildScrollView(
|
body: Container(
|
||||||
|
color: Colors.purple[100], // Светло-сиреневый фон для всей страницы
|
||||||
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -39,12 +42,49 @@ class DetailsPage extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(bottom: 4.0),
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
data.text,
|
data.text,
|
||||||
style: Theme.of(context).textTheme.headlineLarge,
|
style: Theme.of(context).textTheme.headlineLarge?.copyWith(
|
||||||
|
color: Colors.purple, // Простой сиреневый цвет текста заголовка
|
||||||
|
fontWeight: FontWeight.bold, // Жирный шрифт
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
|
child: Text(
|
||||||
|
'Год: ${data.year}', // Отображение года
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
|
color: Colors.purple[700], // Темный сиреневый цвет текста года
|
||||||
|
fontStyle: FontStyle.italic, // Курсив для выделения года
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
data.descriptionText,
|
data.descriptionText ?? '', // Обработаем случай, если описания нет
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
|
color: Colors.purple[600], // Темный сиреневый цвет текста описания
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Отображение жанров
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
'Жанры: ${data.genres?.join(', ') ?? 'Нет жанров'}', // Проверка на null
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
|
color: Colors.purple[600], // Темный сиреневый цвет текста жанров
|
||||||
|
fontStyle: FontStyle.italic, // Курсив для выделения жанров
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Отображение стран
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
'Страны: ${data.countries?.join(', ') ?? 'Нет стран'}', // Проверка на null
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||||
|
color: Colors.purple[600], // Темный сиреневый цвет текста стран
|
||||||
|
fontStyle: FontStyle.italic, // Курсив для выделения стран
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -53,6 +93,7 @@ class DetailsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ part of 'home_page.dart';
|
|||||||
|
|
||||||
typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
|
typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
|
||||||
|
|
||||||
class _Card extends StatelessWidget {
|
class _Card extends StatelessWidget { // состояние карточки регулируется извне; виджеты с точкой убираем
|
||||||
final String text;
|
final String text;
|
||||||
final String descriptionText;
|
final String descriptionText;
|
||||||
final String? imageUrl;
|
final String? imageUrl;
|
||||||
@ -11,8 +11,7 @@ class _Card extends StatelessWidget {
|
|||||||
final String? id;
|
final String? id;
|
||||||
final bool isLiked;
|
final bool isLiked;
|
||||||
|
|
||||||
const _Card(
|
const _Card(this.text, {
|
||||||
this.text, {
|
|
||||||
required this.descriptionText,
|
required this.descriptionText,
|
||||||
this.imageUrl,
|
this.imageUrl,
|
||||||
this.onLike,
|
this.onLike,
|
||||||
@ -21,8 +20,7 @@ class _Card extends StatelessWidget {
|
|||||||
this.isLiked = false,
|
this.isLiked = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory _Card.fromData(
|
factory _Card.fromData(CardData data, {
|
||||||
CardData data, {
|
|
||||||
OnLikeCallback onLike,
|
OnLikeCallback onLike,
|
||||||
VoidCallback? onTap,
|
VoidCallback? onTap,
|
||||||
bool isLiked = false,
|
bool isLiked = false,
|
||||||
@ -43,13 +41,14 @@ class _Card extends StatelessWidget {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.all(16),
|
margin: const EdgeInsets.all(16),
|
||||||
constraints: const BoxConstraints(minHeight: 160),
|
constraints: const BoxConstraints(minHeight: 130),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white70,
|
color: Colors.white.withOpacity(0.9),
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(color: Colors.purpleAccent, width: 2),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.grey.withOpacity(.5),
|
color: Colors.grey.withOpacity(0.5),
|
||||||
spreadRadius: 4,
|
spreadRadius: 4,
|
||||||
offset: const Offset(0, 5),
|
offset: const Offset(0, 5),
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
@ -68,33 +67,32 @@ class _Card extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
width: 120,
|
width: 120,
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
Positioned.fill(
|
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
imageUrl ?? '',
|
imageUrl ?? '',
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
|
||||||
errorBuilder: (_, __, ___) => const Placeholder(),
|
errorBuilder: (_, __, ___) => const Placeholder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Center(
|
||||||
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
style: Theme
|
||||||
|
.of(context)
|
||||||
|
.textTheme
|
||||||
|
.headlineSmall
|
||||||
|
?.apply(color: Colors.purple),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
Text(
|
),
|
||||||
descriptionText,
|
SizedBox(height: 4),
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -102,25 +100,40 @@ class _Card extends StatelessWidget {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8, right: 16, bottom: 16),
|
padding: const EdgeInsets.only(
|
||||||
|
left: 8, right: 16, bottom: 16),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => onLike?.call(id, text, isLiked),
|
onTap: () => onLike?.call(id, text, isLiked),
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: isLiked ? Colors.transparent : Colors.purple,
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
child: isLiked
|
child: isLiked
|
||||||
? const Icon(
|
? const Icon(
|
||||||
Icons.favorite,
|
Icons.favorite,
|
||||||
color: Colors.redAccent,
|
color: Colors.purple,
|
||||||
key: ValueKey<int>(0),
|
key: ValueKey<int>(0),
|
||||||
)
|
)
|
||||||
: const Icon(
|
: const Icon(
|
||||||
Icons.favorite_border,
|
Icons.favorite_border,
|
||||||
|
color: Colors.purple,
|
||||||
key: ValueKey<int>(1),
|
key: ValueKey<int>(1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -32,7 +32,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
backgroundColor: Colors.purpleAccent,
|
||||||
),
|
),
|
||||||
body: const Body(),
|
body: const Body(),
|
||||||
);
|
);
|
||||||
@ -56,7 +56,7 @@ class _BodyState extends State<Body> {
|
|||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
context.read<HomeBloc>().add(const HomeLoadDataEvent());
|
context.read<HomeBloc>().add(const HomeLoadDataEvent());
|
||||||
context.read<LikeBloc>().add(const LoadLikesEvent());
|
context.read<LikeBloc>().add(const LoadLikesEvent()); // событие на изменение лайка
|
||||||
});
|
});
|
||||||
scrollController.addListener(_onNextPageListener);
|
scrollController.addListener(_onNextPageListener);
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ class _BodyState extends State<Body> {
|
|||||||
final MovieRepository repo = MovieRepository();
|
final MovieRepository repo = MovieRepository();
|
||||||
var data = MovieRepository().loadData();
|
var data = MovieRepository().loadData();
|
||||||
|
|
||||||
void _onLike(String? id, String title, bool isLiked) {
|
void _onLike(String? id, String title, bool isLiked) { // обработчик лайков
|
||||||
print("$id $title, $isLiked");
|
print("$id $title, $isLiked");
|
||||||
if (id != null) {
|
if (id != null) {
|
||||||
context.read<LikeBloc>().add(ChangeLikeEvent(id));
|
context.read<LikeBloc>().add(ChangeLikeEvent(id));
|
||||||
@ -96,7 +96,7 @@ class _BodyState extends State<Body> {
|
|||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
' ${isLiked ? context.locale.liked : context.locale.disliked} $title',
|
' ${isLiked ? context.locale.liked : context.locale.disliked} $title', //переписали константные строки под локаль
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.orangeAccent,
|
backgroundColor: Colors.orangeAccent,
|
||||||
@ -125,20 +125,24 @@ class _BodyState extends State<Body> {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
child: CupertinoSearchTextField(
|
child: CupertinoSearchTextField(
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
onChanged: (search) {
|
onChanged: (search) {
|
||||||
Debounce.run(() => context
|
Debounce.run(() => context
|
||||||
.read<HomeBloc>()
|
.read<HomeBloc>()
|
||||||
.add(HomeLoadDataEvent(search: search)));
|
.add(HomeLoadDataEvent(search: search)));
|
||||||
})),
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12), // Отступ между полем поиска и иконкой
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
|
context.read<LocaleBloc>().add(const ChangeLocaleEvent()), // смена иконки локализации
|
||||||
child: SizedBox.square(
|
child: SizedBox.square(
|
||||||
dimension: 50,
|
dimension: 50,
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 12),
|
|
||||||
child: BlocBuilder<LocaleBloc, LocaleState>(
|
child: BlocBuilder<LocaleBloc, LocaleState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return state.currentLocale.languageCode == 'ru'
|
return state.currentLocale.languageCode == 'ru'
|
||||||
@ -148,8 +152,10 @@ class _BodyState extends State<Body> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
BlocBuilder<HomeBloc, HomeState>(
|
),
|
||||||
|
BlocBuilder<HomeBloc, HomeState>( // обертка списка с карточками
|
||||||
builder: (context, state) => state.isLoading
|
builder: (context, state) => state.isLoading
|
||||||
? CircularProgressIndicator()
|
? CircularProgressIndicator()
|
||||||
: BlocBuilder<LikeBloc, LikeState>(
|
: BlocBuilder<LikeBloc, LikeState>(
|
||||||
|
@ -43,7 +43,7 @@ dev_dependencies:
|
|||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: "ic_launcher"
|
android: "ic_launcher"
|
||||||
ios: true
|
ios: true
|
||||||
image_path: "assets/launcher.jpg"
|
image_path: "assets/icon1.jpg"
|
||||||
min_sdk_android: 21
|
min_sdk_android: 21
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user