рабочая 8 лаба - это курсовая
This commit is contained in:
parent
d9df87a7d9
commit
a6f44be5b2
@ -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);
|
||||
}
|
||||
|
@ -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?,
|
||||
);
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
@ -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});
|
||||
}
|
@ -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;
|
||||
|
@ -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{
|
||||
}
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
7
lib/domain/models/home.dart
Normal file
7
lib/domain/models/home.dart
Normal file
@ -0,0 +1,7 @@
|
||||
import 'package:pmu/domain/models/card.dart';
|
||||
|
||||
class HomeData{
|
||||
final List<MarketData>? data;
|
||||
|
||||
HomeData({this.data});
|
||||
}
|
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -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,
|
||||
}) =>
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user