рабочая 8 лаба - это курсовая

This commit is contained in:
salih 2024-12-21 16:06:06 +04:00
parent d9df87a7d9
commit a6f44be5b2
12 changed files with 132 additions and 85 deletions

View File

@ -16,8 +16,9 @@ class FoodDataDto{
final String? idMeal;
final String? strMeal;
final String? strMealThumb;
final String? strInstructions;
const FoodDataDto({this.idMeal, this.strMeal, this.strMealThumb});
const FoodDataDto({this.idMeal, this.strMeal, this.strMealThumb, this.strInstructions});
factory FoodDataDto.fromJson(Map<String, dynamic> json) => _$FoodDataDtoFromJson(json);
}

View File

@ -16,4 +16,5 @@ FoodDataDto _$FoodDataDtoFromJson(Map<String, dynamic> json) => FoodDataDto(
idMeal: json['idMeal'] as String?,
strMeal: json['strMeal'] as String?,
strMealThumb: json['strMealThumb'] as String?,
strInstructions: json['strInstructions'] as String?,
);

View File

@ -1,11 +1,18 @@
import 'package:pmu/data/dtos/food_dto.dart';
import 'package:pmu/domain/models/card.dart';
import 'package:pmu/domain/models/home.dart';
extension FoodDataDtoToModel on FoodDataDto {
MarketData toDomain() => MarketData(
beanId: idMeal ?? "Неизвестно",
colorGroup: strMeal ?? "Неизвестная категория",
MarketData toDomain() => MarketData(
id: idMeal ?? "Неизвестно",
strMeal: strMeal ?? "Неизвестная категория",
description: strInstructions,
imageUrl: strMealThumb,
id: idMeal,
);
}
extension FoodDtoToModel on FoodsDto{
HomeData toDomain() => HomeData(
data: meals?.map((e) => e.toDomain()).toList(),
);
}

View File

@ -1,7 +1,8 @@
import 'package:pmu/domain/models/card.dart';
import 'package:pmu/domain/models/home.dart';
typedef OnErrorCallback = void Function(String? error);
abstract class ApiInterface{
Future<List<MarketData>?> loadData();
Future<HomeData?> loadData({OnErrorCallback? onError});
}

View File

@ -3,6 +3,7 @@ import 'package:pmu/data/dtos/food_dto.dart';
import 'package:pmu/data/mappers/foods_mapper.dart';
import 'package:pmu/data/repository/api_interface.dart';
import 'package:pmu/domain/models/card.dart';
import 'package:pmu/domain/models/home.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class FoodRepository extends ApiInterface{
@ -14,7 +15,7 @@ class FoodRepository extends ApiInterface{
static const String _baseUrl = 'https://www.themealdb.com/api/json/v1/1';
@override
Future<List<MarketData>?> loadData({OnErrorCallback? onError, String? q}) async {
Future<HomeData?> loadData({OnErrorCallback? onError, String? q}) async {
try {
final String url = q != null ? '$_baseUrl/search.php' : '$_baseUrl/search.php?f=s';
@ -24,8 +25,8 @@ class FoodRepository extends ApiInterface{
);
final FoodsDto dto = FoodsDto.fromJson(response.data as Map<String, dynamic>);
final List<MarketData>? items = dto.meals?.map((e) => e.toDomain()).toList();
return items;
final HomeData data = dto.toDomain();
return data;
} on DioException catch (e) {
onError?.call(e.error?.toString());
return null;

View File

@ -1,28 +1,9 @@
import 'package:pmu/data/repository/api_interface.dart';
import 'package:pmu/domain/models/card.dart';
import 'package:pmu/domain/models/home.dart';
class MockRepository extends ApiInterface {
@override
Future<List<MarketData>?> loadData() async{
return [
MarketData(
beanId: '1',
colorGroup:
'khaki',
imageUrl: 'https://www.themealdb.com/images/media/meals/sbx7n71587673021.jpg'
),
MarketData(
beanId: '2',
colorGroup:
'khaki',
imageUrl: 'https://www.themealdb.com/images/media/meals/sbx7n71587673021.jpg'
),
MarketData(
beanId: '3',
colorGroup:
'khaki',
imageUrl: 'https://www.themealdb.com/images/media/meals/sbx7n71587673021.jpg'
),
];
Future<HomeData?> loadData({OnErrorCallback? onError}) async{
}
}

View File

@ -1,13 +1,13 @@
class MarketData {
final String beanId;
final String colorGroup;
final String id;
final String strMeal;
final String? imageUrl;
final String? id;
final String? description;
MarketData({
required this.beanId,
required this.colorGroup,
required this.id,
required this.strMeal,
required this.imageUrl,
this.id,
required this.description,
});
}

View File

@ -0,0 +1,7 @@
import 'package:pmu/domain/models/card.dart';
class HomeData{
final List<MarketData>? data;
HomeData({this.data});
}

View File

@ -11,22 +11,49 @@ class DetailsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFFFFA400),
backgroundColor: Colors.blueGrey,
centerTitle: true,
title: Text(
meals.strMeal,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
body: SingleChildScrollView(
child: Container(
constraints: BoxConstraints(
minHeight: MediaQuery.sizeOf(context).height - 105),
decoration: const BoxDecoration(
color: Color(0xFFFFA400),
),
color: Colors.white, // Белый фон страницы
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Image.network(meals.imageUrl ?? ''),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
meals.imageUrl ?? '',
fit: BoxFit.cover,
width: double.infinity,
height: 200,
),
),
),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(16),
),
child: Text(
meals.description ?? '',
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: Colors.black87,
),
),
),
],
),

View File

@ -1,9 +1,10 @@
import 'package:equatable/equatable.dart';
import 'package:pmu/domain/models/card.dart';
import 'package:pmu/domain/models/home.dart';
class HomeState extends Equatable {
final List<MarketData>? data;
final HomeData? data;
final bool isLoading;
final String? error;
@ -14,7 +15,7 @@ class HomeState extends Equatable {
});
HomeState copyWith({
List<MarketData>? data,
HomeData? data,
bool? isLoading,
String? error,
}) =>

View File

@ -3,34 +3,31 @@ part of "home_page.dart";
typedef OnLikeCallback = void Function(String? id, String title, bool isLiked)?;
class _MarketCard extends StatelessWidget {
final String beanId;
final String id;
final String colorGroup;
final String? imageUrl;
final OnLikeCallback onLike;
final VoidCallback? onTap;
final String? id;
final bool isLiked;
const _MarketCard({
required this.beanId,
required this.id,
required this.colorGroup,
required this.imageUrl,
this.onLike,
this.onTap,
this.id,
this.isLiked = false,
});
factory _MarketCard.fromData(MarketData items,
{OnLikeCallback? onLike, VoidCallback? onTap, bool isLiked = false}) =>
_MarketCard(
beanId: items.beanId,
colorGroup: items.colorGroup,
id: items.id,
colorGroup: items.strMeal,
imageUrl: items.imageUrl,
onLike: onLike,
onTap: onTap,
isLiked: isLiked,
id: items.id,
);
@override

View File

@ -76,34 +76,40 @@ class _MyHomePageState extends State<MyHomePage> {
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
children: [
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, state) {
return state.currentLocale.languageCode == 'ru'
? const SvgRu()
: const SvgUk();
},
Row(
children: [
Expanded(
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, state) {
return state.currentLocale.languageCode == 'ru'
? const SvgRu()
: const SvgUk();
},
),
),
),
),
],
),
BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) => state.isLoading
@ -115,9 +121,9 @@ class _MyHomePageState extends State<MyHomePage> {
child: ListView.builder(
controller: scrollController,
padding: EdgeInsets.zero,
itemCount: state.data?.length ?? 0,
itemCount: state.data?.data?.length ?? 0,
itemBuilder: (context, index) {
final data = state.data?[index];
final data = state.data?.data?[index];
return data != null
? _MarketCard.fromData(
data,
@ -140,17 +146,34 @@ class _MyHomePageState extends State<MyHomePage> {
void _showSnackBar(BuildContext context, String title, bool isLiked) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'$title ${isLiked ? context.locale.liked : context.locale.disliked}',
style: Theme.of(context).textTheme.bodyLarge,
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.transparent,
elevation: 0,
duration: const Duration(seconds: 1),
content: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.orangeAccent.withOpacity(0.9),
borderRadius: BorderRadius.circular(16),
),
child: Text(
'$title ${isLiked ? context.locale.liked : context.locale.disliked}',
style: Theme.of(context)
.textTheme
.bodyLarge
?.copyWith(color: Colors.white),
),
),
),
),
backgroundColor: Colors.orangeAccent,
duration: const Duration(seconds: 1),
));
);
});
}
void _navToDetails(BuildContext context, MarketData data) {
Navigator.push(
context,