Причесал код

This commit is contained in:
Вячеслав Иванов 2024-09-25 12:55:28 +04:00
parent 16f0f53745
commit decf3d296d
29 changed files with 178 additions and 151 deletions

View File

@ -2,8 +2,8 @@
"@@locale": "en",
"search": "Search",
"liked": "liked!",
"disliked": "disliked :(",
"liked": "liked",
"disliked": "disliked",
"arbEnding": "Чтобы не забыть про отсутствие запятой :)"
}

View File

@ -2,8 +2,8 @@
"@@locale": "ru",
"search": "Поиск",
"liked": "понравился!",
"disliked": "разонравился :(",
"liked": "поставлен лайк",
"disliked": "убран лайк",
"arbEnding": "Чтобы не забыть про отсутствие запятой :)"
}

View File

@ -104,13 +104,13 @@ abstract class AppLocale {
/// No description provided for @liked.
///
/// In ru, this message translates to:
/// **'понравился!'**
/// **'поставлен лайк'**
String get liked;
/// No description provided for @disliked.
///
/// In ru, this message translates to:
/// **'разонравился :('**
/// **'убран лайк'**
String get disliked;
/// No description provided for @arbEnding.

View File

@ -10,10 +10,10 @@ class AppLocaleEn extends AppLocale {
String get search => 'Search';
@override
String get liked => 'liked!';
String get liked => 'liked';
@override
String get disliked => 'disliked :(';
String get disliked => 'disliked';
@override
String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';

View File

@ -10,10 +10,10 @@ class AppLocaleRu extends AppLocale {
String get search => 'Поиск';
@override
String get liked => 'понравился!';
String get liked => 'поставлен лайк';
@override
String get disliked => 'разонравился :(';
String get disliked => 'убран лайк';
@override
String get arbEnding => 'Чтобы не забыть про отсутствие запятой :)';

View File

@ -12,7 +12,8 @@ class CharactersDto {
this.meta,
});
factory CharactersDto.fromJson(Map<String, dynamic> json) => _$CharactersDtoFromJson(json);
factory CharactersDto.fromJson(Map<String, dynamic> json) =>
_$CharactersDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@ -23,7 +24,8 @@ class CharacterDataDto {
const CharacterDataDto({this.id, this.type, this.attributes});
factory CharacterDataDto.fromJson(Map<String, dynamic> json) => _$CharacterDataDtoFromJson(json);
factory CharacterDataDto.fromJson(Map<String, dynamic> json) =>
_$CharacterDataDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@ -33,7 +35,8 @@ class CharacterAttributesDataDto {
final String? species;
final String? image;
const CharacterAttributesDataDto({this.name, this.nationality, this.species, this.image});
const CharacterAttributesDataDto(
{this.name, this.nationality, this.species, this.image});
factory CharacterAttributesDataDto.fromJson(Map<String, dynamic> json) =>
_$CharacterAttributesDataDtoFromJson(json);
@ -45,7 +48,8 @@ class MetaDto {
const MetaDto({this.pagination});
factory MetaDto.fromJson(Map<String, dynamic> json) => _$MetaDtoFromJson(json);
factory MetaDto.fromJson(Map<String, dynamic> json) =>
_$MetaDtoFromJson(json);
}
@JsonSerializable(createToJson: false)
@ -56,5 +60,6 @@ class PaginationDto {
const PaginationDto({this.current, this.next, this.last});
factory PaginationDto.fromJson(Map<String, dynamic> json) => _$PaginationDtoFromJson(json);
factory PaginationDto.fromJson(Map<String, dynamic> json) =>
_$PaginationDtoFromJson(json);
}

View File

@ -14,7 +14,8 @@ extension CharacterDataDtoToModel on CharacterDataDto {
attributes?.name ?? 'UNKNOWN',
image: attributes?.image ??
'https://upload.wikimedia.org/wikipedia/en/archive/b/b1/20210811082420%21Portrait_placeholder.png',
descriptionText: _makeDescriptionText(attributes?.nationality, attributes?.species),
descriptionText:
_makeDescriptionText(attributes?.nationality, attributes?.species),
id: id,
);

View File

@ -25,7 +25,8 @@ class MockRepository extends ApiInterface {
'Orange',
descriptionText: 'I like autumn',
icon: Icons.warning_amber,
image: 'https://furmanagers.com/wp-content/uploads/2019/11/dreamstime_l_22075357.jpg',
image:
'https://furmanagers.com/wp-content/uploads/2019/11/dreamstime_l_22075357.jpg',
),
],
);

View File

@ -33,7 +33,8 @@ class PotterRepository extends ApiInterface {
},
);
final CharactersDto dto = CharactersDto.fromJson(response.data as Map<String, dynamic>);
final CharactersDto dto =
CharactersDto.fromJson(response.data as Map<String, dynamic>);
final HomeData data = dto.toDomain();
return data;
} on DioException catch (e) {

View File

@ -215,7 +215,8 @@ class MyApp extends StatelessWidget {
create: (context) => LikeBloc(),
child: BlocProvider<HomeBloc>(
lazy: false,
create: (context) => HomeBloc(context.read<PotterRepository>()),
create: (context) =>
HomeBloc(context.read<PotterRepository>()),
child: const HomePage(),
),
),

View File

@ -10,7 +10,8 @@ abstract class SvgObjects {
];
for (final String p in pics) {
final loader = SvgAssetLoader(p);
svg.cache.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
svg.cache
.putIfAbsent(loader.cacheKey(null), () => loader.loadBytes(null));
}
}
}

View File

@ -10,7 +10,8 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
on<HomeLoadDataEvent>(_onLoadData);
}
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
Future<void> _onLoadData(
HomeLoadDataEvent event, Emitter<HomeState> emit) async {
if (event.nextPage == null) {
emit(state.copyWith(isLoading: true));
} else {

View File

@ -48,7 +48,7 @@ class _Card extends StatelessWidget {
margin: const EdgeInsets.all(16),
constraints: const BoxConstraints(minHeight: 240),
decoration: BoxDecoration(
color: Colors.white30,
color: Colors.grey,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
@ -61,6 +61,29 @@ class _Card extends StatelessWidget {
),
child: Column(
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
left: 8, right: 16, bottom: 16, top: 10),
child: GestureDetector(
onTap: () => onLike?.call(id, text, isLiked),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: isLiked
? const Icon(
Icons.favorite,
color: Colors.redAccent,
key: ValueKey<int>(0),
)
: const Icon(
Icons.favorite_border,
key: ValueKey<int>(1),
),
),
),
),
),
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
@ -83,7 +106,8 @@ class _Card extends StatelessWidget {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
padding:
const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@ -100,28 +124,6 @@ class _Card extends StatelessWidget {
],
),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(left: 8, right: 16, bottom: 16),
child: GestureDetector(
onTap: () => onLike?.call(id, text, isLiked),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: isLiked
? const Icon(
Icons.favorite,
color: Colors.redAccent,
key: ValueKey<int>(0),
)
: const Icon(
Icons.favorite_border,
key: ValueKey<int>(1),
),
),
),
),
),
],
),
),

View File

@ -93,14 +93,16 @@ class _BodyState extends State<_Body> {
controller: searchController,
placeholder: context.locale.search,
onChanged: (search) {
Debounce.run(
() => context.read<HomeBloc>().add(HomeLoadDataEvent(search: search)));
Debounce.run(() => context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: search)));
},
),
),
),
GestureDetector(
onTap: () => context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
onTap: () =>
context.read<LocaleBloc>().add(const ChangeLocaleEvent()),
child: SizedBox.square(
dimension: 50,
child: Padding(
@ -121,7 +123,10 @@ class _BodyState extends State<_Body> {
builder: (context, state) => state.error != null
? Text(
state.error ?? '',
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.red),
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: Colors.red),
)
: state.isLoading
? const CircularProgressIndicator()
@ -140,8 +145,11 @@ class _BodyState extends State<_Body> {
? _Card.fromData(
data,
onLike: _onLike,
isLiked: likeState.likedIds?.contains(data.id) == true,
onTap: () => _navToDetails(context, data),
isLiked: likeState.likedIds
?.contains(data.id) ==
true,
onTap: () =>
_navToDetails(context, data),
)
: const SizedBox.shrink();
},
@ -162,7 +170,9 @@ class _BodyState extends State<_Body> {
}
Future<void> _onRefresh() {
context.read<HomeBloc>().add(HomeLoadDataEvent(search: searchController.text));
context
.read<HomeBloc>()
.add(HomeLoadDataEvent(search: searchController.text));
return Future.value(null);
}

View File

@ -11,14 +11,16 @@ class LikeBloc extends Bloc<LikeEvent, LikeState> {
on<LoadLikesEvent>(_onLoadLikes);
}
Future<void> _onLoadLikes(LoadLikesEvent event, Emitter<LikeState> emit) async {
Future<void> _onLoadLikes(
LoadLikesEvent event, Emitter<LikeState> emit) async {
final prefs = await SharedPreferences.getInstance();
final data = prefs.getStringList(_likedPrefsKey);
emit(state.copyWith(likedIds: data));
}
Future<void> _onChangeLike(ChangeLikeEvent event, Emitter<LikeState> emit) async {
Future<void> _onChangeLike(
ChangeLikeEvent event, Emitter<LikeState> emit) async {
final updatedList = List<String>.from(state.likedIds ?? []);
if (updatedList.contains(event.id)) {

View File

@ -5,11 +5,13 @@ import 'package:flutter_app/presentation/locale_bloc/locale_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class LocaleBloc extends Bloc<LocaleEvent, LocaleState> {
LocaleBloc(Locale defaultLocale) : super(LocaleState(currentLocale: defaultLocale)) {
LocaleBloc(Locale defaultLocale)
: super(LocaleState(currentLocale: defaultLocale)) {
on<ChangeLocaleEvent>(_onChangeLocale);
}
Future<void> _onChangeLocale(ChangeLocaleEvent event, Emitter<LocaleState> emit) async {
Future<void> _onChangeLocale(
ChangeLocaleEvent event, Emitter<LocaleState> emit) async {
final toChange = AppLocale.supportedLocales
.firstWhere((e) => e.languageCode != state.currentLocale.languageCode);
emit(state.copyWith(currentLocale: toChange));