готовая 6 лаба
This commit is contained in:
parent
03dc457dd8
commit
935f2784bf
@ -25,7 +25,6 @@ class DebouncedSearchCubit extends Cubit<String> {
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
final CharacterService characterService;
|
||||
|
||||
@ -33,17 +32,24 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
on<HomeLoadDataEvent>(_onLoadData);
|
||||
}
|
||||
|
||||
// Внутри HomeBloc, когда получаем данные
|
||||
Future<void> _onLoadData(HomeLoadDataEvent event, Emitter<HomeState> emit) async {
|
||||
emit(state.copyWith(status: HomeStatus.loading));
|
||||
|
||||
try {
|
||||
final characters = await characterService.getCharacters(search: event.searchQuery);
|
||||
print('Characters loaded: $characters'); // Отладочный вывод
|
||||
emit(state.copyWith(status: HomeStatus.loaded, characters: characters));
|
||||
// Получаем список объектов CharacterDTO
|
||||
final charactersDTO = await characterService.getCharacters(search: event.searchQuery);
|
||||
|
||||
// Преобразуем список Character в список CharacterDTO
|
||||
final charactersAsDTO = charactersDTO.map((character) => character.toDTO()).toList();
|
||||
|
||||
emit(state.copyWith(status: HomeStatus.loaded, characters: charactersAsDTO)); // Передаем в emit
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
emit(state.copyWith(status: HomeStatus.error, errorMessage: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -3,10 +3,9 @@ import 'package:flutter/material.dart';
|
||||
import '../models/character.dart'; // Добавим импорт для Character
|
||||
|
||||
enum HomeStatus { initial, loading, loaded, error }
|
||||
|
||||
class HomeState extends Equatable {
|
||||
final HomeStatus status;
|
||||
final List<Character> characters; // Список персонажей
|
||||
final List<CharacterDTO> characters; // Теперь используем DTO
|
||||
final String errorMessage;
|
||||
|
||||
const HomeState({
|
||||
@ -15,10 +14,9 @@ class HomeState extends Equatable {
|
||||
this.errorMessage = '',
|
||||
});
|
||||
|
||||
// Метод для обновления состояния
|
||||
HomeState copyWith({
|
||||
HomeStatus? status,
|
||||
List<Character>? characters,
|
||||
List<CharacterDTO>? characters,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return HomeState(
|
||||
|
@ -41,7 +41,6 @@ class MyHomePage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Загружаем данные сразу после инициализации страницы
|
||||
context.read<HomeBloc>().add(HomeLoadDataEvent());
|
||||
|
||||
return Scaffold(
|
||||
@ -71,20 +70,20 @@ class MyHomePage extends StatelessWidget {
|
||||
return ListView.builder(
|
||||
itemCount: characters.length,
|
||||
itemBuilder: (context, index) {
|
||||
final character = characters[index];
|
||||
final characterDTO = characters[index]; // Убедитесь, что это CharacterDTO
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 40,
|
||||
child: Image.network(character.imageUrl, width: 50, height: 50),
|
||||
child: Image.network(characterDTO.imageUrl, width: 50, height: 50),
|
||||
),
|
||||
title: Text(character.name),
|
||||
subtitle: Text(character.typeString),
|
||||
title: Text(characterDTO.name),
|
||||
subtitle: Text(characterDTO.typeString), // Используем typeString из CharacterDTO
|
||||
onTap: () {
|
||||
// Переход на страницу с деталями персонажа
|
||||
// Переход на страницу с деталями персонажа, передаем CharacterDTO
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CharacterDetailPage(character: character),
|
||||
builder: (context) => CharacterDetailPage(characterDTO: characterDTO), // Передаем CharacterDTO
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -103,7 +102,6 @@ class MyHomePage extends StatelessWidget {
|
||||
class CharacterSearchDelegate extends SearchDelegate {
|
||||
@override
|
||||
Widget buildSuggestions(BuildContext context) {
|
||||
// Показываем предложения на основе текущего запроса
|
||||
return BlocProvider<HomeBloc>(
|
||||
create: (context) => HomeBloc(CharacterService()),
|
||||
child: BlocBuilder<HomeBloc, HomeState>(
|
||||
@ -115,15 +113,16 @@ class CharacterSearchDelegate extends SearchDelegate {
|
||||
return ListView.builder(
|
||||
itemCount: suggestions.length,
|
||||
itemBuilder: (context, index) {
|
||||
final character = suggestions[index];
|
||||
final character = suggestions[index]; // Используем правильный тип
|
||||
return ListTile(
|
||||
title: Text(character.name),
|
||||
subtitle: Text(character.typeString),
|
||||
onTap: () {
|
||||
// Переход на страницу с деталями персонажа
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CharacterDetailPage(character: character),
|
||||
builder: (context) => CharacterDetailPage(characterDTO: character), // Передаем правильный параметр
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -137,13 +136,11 @@ class CharacterSearchDelegate extends SearchDelegate {
|
||||
|
||||
@override
|
||||
Widget buildResults(BuildContext context) {
|
||||
// Отправляем запрос с задержкой
|
||||
final debouncedSearchCubit = context.read<DebouncedSearchCubit>();
|
||||
debouncedSearchCubit.search(query);
|
||||
|
||||
return BlocBuilder<DebouncedSearchCubit, String>(
|
||||
builder: (context, searchQuery) {
|
||||
// Отправляем запрос на сервер через HomeBloc
|
||||
if (searchQuery.isNotEmpty) {
|
||||
context.read<HomeBloc>().add(HomeLoadDataEvent(searchQuery: searchQuery));
|
||||
}
|
||||
@ -161,21 +158,26 @@ class CharacterSearchDelegate extends SearchDelegate {
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: characters.length,
|
||||
itemBuilder: (context, index) {
|
||||
final character = characters[index];
|
||||
return ListTile(
|
||||
title: Text(character.name),
|
||||
subtitle: Text(character.typeString),
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CharacterDetailPage(character: character),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
final characterDTO = characters[index]; // Здесь определяем characterDTO
|
||||
return ListTile(
|
||||
leading: SizedBox(
|
||||
width: 40,
|
||||
child: Image.network(characterDTO.imageUrl, width: 50, height: 50),
|
||||
),
|
||||
title: Text(characterDTO.name),
|
||||
subtitle: Text(characterDTO.typeString), // Используем typeString
|
||||
onTap: () {
|
||||
// Переход на страницу с деталями персонажа
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => CharacterDetailPage(characterDTO: characterDTO), // Передаем characterDTO
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return Center(child: Text('Нет результатов'));
|
||||
@ -192,8 +194,8 @@ class CharacterSearchDelegate extends SearchDelegate {
|
||||
IconButton(
|
||||
icon: Icon(Icons.clear),
|
||||
onPressed: () {
|
||||
query = ''; // Очистить запрос
|
||||
showSuggestions(context); // Показать предложения
|
||||
query = '';
|
||||
showSuggestions(context);
|
||||
},
|
||||
),
|
||||
];
|
||||
@ -204,7 +206,7 @@ class CharacterSearchDelegate extends SearchDelegate {
|
||||
return IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
close(context, null); // Закрыть поиск
|
||||
close(context, null);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -54,8 +54,9 @@ class Character {
|
||||
return '$typeString - $backstory';
|
||||
}
|
||||
|
||||
static empty() {
|
||||
return null;
|
||||
// Для пустого объекта
|
||||
static Character? empty() {
|
||||
return null; // Возвращаем null для пустого объекта
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,3 +85,68 @@ class Hunter extends Character {
|
||||
type: CharacterType.Hunter,
|
||||
);
|
||||
}
|
||||
|
||||
class CharacterDTO {
|
||||
final String name;
|
||||
final String characterType;
|
||||
final String backstory;
|
||||
final String imageUrl;
|
||||
|
||||
CharacterDTO({
|
||||
required this.name,
|
||||
required this.characterType,
|
||||
required this.backstory,
|
||||
required this.imageUrl,
|
||||
});
|
||||
|
||||
CharacterDTO toDTO() {
|
||||
return CharacterDTO(
|
||||
name: name,
|
||||
characterType: typeString,
|
||||
backstory: backstory,
|
||||
imageUrl: imageUrl,
|
||||
);
|
||||
}
|
||||
|
||||
// Добавим метод fromJson
|
||||
factory CharacterDTO.fromJson(Map<String, dynamic> json) {
|
||||
return CharacterDTO(
|
||||
name: json['name'],
|
||||
characterType: json['type'], // 'type' - это то, что мы ожидаем от API
|
||||
backstory: json['backstory'],
|
||||
imageUrl: json['image_url'], // Используем корректное имя поля для URL
|
||||
);
|
||||
}
|
||||
|
||||
// Геттер для typeString
|
||||
String get typeString {
|
||||
switch (characterType) {
|
||||
case 'Выживший':
|
||||
return 'Выживший';
|
||||
case 'Охотник':
|
||||
return 'Охотник';
|
||||
default:
|
||||
return 'Неизвестно';
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразуем DTO обратно в объект Character
|
||||
Character toCharacter() {
|
||||
return Character(
|
||||
name: name,
|
||||
type: characterType == 'Выживший' ? CharacterType.Survivor : CharacterType.Hunter,
|
||||
backstory: backstory,
|
||||
imageUrl: imageUrl,
|
||||
);
|
||||
}
|
||||
|
||||
// Метод для преобразования в Map
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'name': name,
|
||||
'type': characterType,
|
||||
'backstory': backstory,
|
||||
'image_url': imageUrl,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,47 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../models/character.dart'; // Убедитесь, что путь правильный
|
||||
|
||||
class CharacterDetailPage extends StatelessWidget {
|
||||
final Character character;
|
||||
final CharacterDTO characterDTO;
|
||||
|
||||
// Конструктор с обязательным параметром
|
||||
const CharacterDetailPage({Key? key, required this.character}) : super(key: key);
|
||||
const CharacterDetailPage({Key? key, required this.characterDTO}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(character.name),
|
||||
title: Text(characterDTO.name),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Изображение персонажа
|
||||
Center(
|
||||
child: Image.network(
|
||||
character.imageUrl,
|
||||
characterDTO.imageUrl,
|
||||
height: 200,
|
||||
width: 200,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
|
||||
Text(
|
||||
character.name, // Имя персонажа
|
||||
characterDTO.name,
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
character.typeString, // Тип персонажа (Survivor или Hunter)
|
||||
characterDTO.typeString,
|
||||
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
|
||||
// Предыстория персонажа
|
||||
Text(
|
||||
'Полное описание:',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// Текст предыстории
|
||||
Text(
|
||||
character.backstory,
|
||||
characterDTO.backstory,
|
||||
style: TextStyle(fontSize: 14),
|
||||
maxLines: null,
|
||||
softWrap: true,
|
||||
|
@ -5,7 +5,8 @@ import '../models/character.dart';
|
||||
const String baseUrl = 'http://192.168.1.83:5000'; // IP-адрес вместо localhost
|
||||
|
||||
class CharacterService {
|
||||
Future<List<Character>> getCharacters({String search = ''}) async {
|
||||
// Метод теперь возвращает список CharacterDTO
|
||||
Future<List<CharacterDTO>> getCharacters({String search = ''}) async {
|
||||
try {
|
||||
// Формируем URL с параметром поиска
|
||||
final uri = Uri.parse('$baseUrl/characters?search=$search');
|
||||
@ -22,8 +23,8 @@ class CharacterService {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
print('Characters received: $data'); // Печать данных
|
||||
|
||||
// Возвращаем список объектов Character
|
||||
return data.map((item) => Character.fromJson(item)).toList();
|
||||
// Возвращаем список объектов CharacterDTO
|
||||
return data.map((item) => CharacterDTO.fromJson(item)).toList();
|
||||
} else {
|
||||
// Ошибка, если сервер вернул не 200 статус
|
||||
print('Error: Server responded with status ${response.statusCode}');
|
||||
|
Loading…
Reference in New Issue
Block a user