курсовая тоже готова
This commit is contained in:
parent
a3cf2f9282
commit
322d1676ef
@ -1,9 +1,12 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'events.dart';
|
import 'events.dart';
|
||||||
import 'state.dart';
|
import 'state.dart';
|
||||||
import '../utils/character_service.dart';
|
import '../domain/models/character.dart';
|
||||||
import '../models/character.dart';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:identity/data/repositories/character_repository.dart';
|
||||||
|
import 'package:identity/data/dtos/character_dto.dart';
|
||||||
|
import 'package:identity/data/mappers/character_mapper.dart';
|
||||||
|
|
||||||
|
|
||||||
class DebouncedSearchCubit extends Cubit<String> {
|
class DebouncedSearchCubit extends Cubit<String> {
|
||||||
DebouncedSearchCubit() : super('');
|
DebouncedSearchCubit() : super('');
|
||||||
@ -26,11 +29,11 @@ class DebouncedSearchCubit extends Cubit<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||||
final CharacterService characterService;
|
final CharacterRepository characterService;
|
||||||
|
|
||||||
HomeBloc(this.characterService) : super(const HomeState()) {
|
HomeBloc(this.characterService) : super(const HomeState()) {
|
||||||
on<HomeLoadDataEvent>(_onLoadData);
|
on<HomeLoadDataEvent>(_onLoadData);
|
||||||
on<HomeSearchEvent>(_onSearchCharacters); // Добавлен новый обработчик для поиска
|
on<HomeSearchEvent>(_onSearchCharacters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка данных
|
// Загрузка данных
|
||||||
@ -38,26 +41,34 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
emit(state.copyWith(status: HomeStatus.loading));
|
emit(state.copyWith(status: HomeStatus.loading));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Получаем список объектов CharacterDTO
|
// Получаем список объектов Character
|
||||||
final charactersDTO = await characterService.getCharacters(search: event.searchQuery);
|
final characters = await characterService.loadData(searchQuery: event.searchQuery);
|
||||||
|
if (characters != null) {
|
||||||
|
// Преобразуем список Character в список CharacterDTO с помощью маппера
|
||||||
|
final charactersAsDTO = characters.map((character) => CharacterMapper.toDTO(character)).toList();
|
||||||
|
|
||||||
// Преобразуем список Character в список CharacterDTO
|
// Преобразуем обратно в List<Character>
|
||||||
final charactersAsDTO = charactersDTO.map((character) => character.toDTO()).toList();
|
final charactersAsModel = charactersAsDTO.map((dto) => CharacterMapper.fromDTO(dto)).toList();
|
||||||
|
|
||||||
emit(state.copyWith(status: HomeStatus.loaded, characters: charactersAsDTO)); // Передаем в emit
|
emit(state.copyWith(status: HomeStatus.loaded, characters: charactersAsModel));
|
||||||
|
} else {
|
||||||
|
emit(state.copyWith(status: HomeStatus.error, errorMessage: "Failed to load characters."));
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error: $e');
|
print('Error: $e');
|
||||||
emit(state.copyWith(status: HomeStatus.error, errorMessage: e.toString()));
|
emit(state.copyWith(status: HomeStatus.error, errorMessage: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Логика поиска
|
// Логика поиска
|
||||||
void _onSearchCharacters(HomeSearchEvent event, Emitter<HomeState> emit) {
|
void _onSearchCharacters(HomeSearchEvent event, Emitter<HomeState> emit) {
|
||||||
final query = event.query.toLowerCase();
|
final query = event.query.toLowerCase();
|
||||||
|
|
||||||
// Фильтруем данные по запросу
|
// Фильтруем данные по запросу
|
||||||
final filteredCharacters = state.characters
|
final filteredCharacters = state.characters
|
||||||
.where((character) => character.name.toLowerCase().contains(query))
|
.where((character) => character.name?.toLowerCase().contains(query) ?? false)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// Обновляем состояние с отфильтрованными данными
|
// Обновляем состояние с отфильтрованными данными
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../models/character.dart';
|
import '../domain/models/character.dart';
|
||||||
|
import 'package:identity/data/dtos/character_dto.dart';
|
||||||
|
|
||||||
enum HomeStatus { initial, loading, loaded, error }
|
enum HomeStatus { initial, loading, loaded, error }
|
||||||
class HomeState extends Equatable {
|
class HomeState extends Equatable {
|
||||||
final HomeStatus status;
|
final HomeStatus status;
|
||||||
final List<CharacterDTO> characters;
|
final List<Character> characters; // Используйте Character, а не CharacterDTO
|
||||||
final String errorMessage;
|
final String errorMessage;
|
||||||
|
|
||||||
const HomeState({
|
const HomeState({
|
||||||
@ -16,7 +17,7 @@ class HomeState extends Equatable {
|
|||||||
|
|
||||||
HomeState copyWith({
|
HomeState copyWith({
|
||||||
HomeStatus? status,
|
HomeStatus? status,
|
||||||
List<CharacterDTO>? characters,
|
List<Character>? characters,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) {
|
||||||
return HomeState(
|
return HomeState(
|
||||||
|
57
lib/character_search_delegate.dart
Normal file
57
lib/character_search_delegate.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CharacterSearchDelegate extends SearchDelegate<String> {
|
||||||
|
final List<String> suggestions;
|
||||||
|
|
||||||
|
CharacterSearchDelegate(this.suggestions);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Widget>? buildActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.clear),
|
||||||
|
onPressed: () {
|
||||||
|
query = '';
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget? buildLeading(BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(Icons.arrow_back),
|
||||||
|
onPressed: () {
|
||||||
|
close(context, '');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildResults(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Text('Search result for: $query'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildSuggestions(BuildContext context) {
|
||||||
|
final filteredSuggestions = suggestions
|
||||||
|
.where((suggestion) => suggestion.toLowerCase().contains(query.toLowerCase()))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: filteredSuggestions.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final suggestion = filteredSuggestions[index];
|
||||||
|
return ListTile(
|
||||||
|
title: Text(suggestion),
|
||||||
|
onTap: () {
|
||||||
|
query = suggestion;
|
||||||
|
showResults(context);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
23
lib/data/dtos/character_dto.dart
Normal file
23
lib/data/dtos/character_dto.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
part 'character_dto.g.dart';
|
||||||
|
|
||||||
|
@JsonSerializable(createToJson: false)
|
||||||
|
class CharacterDto {
|
||||||
|
final String name;
|
||||||
|
final String type;
|
||||||
|
final String backstory;
|
||||||
|
@JsonKey(name: 'image_url')
|
||||||
|
final String imageUrl;
|
||||||
|
bool isLiked;
|
||||||
|
|
||||||
|
CharacterDto({
|
||||||
|
required this.name,
|
||||||
|
required this.type,
|
||||||
|
required this.backstory,
|
||||||
|
required this.imageUrl,
|
||||||
|
this.isLiked = false,
|
||||||
|
});
|
||||||
|
String get typeString => type ?? 'Unknown Type';
|
||||||
|
factory CharacterDto.fromJson(Map<String, dynamic> json) => _$CharacterDtoFromJson(json);
|
||||||
|
}
|
15
lib/data/dtos/character_dto.g.dart
Normal file
15
lib/data/dtos/character_dto.g.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'character_dto.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
CharacterDto _$CharacterDtoFromJson(Map<String, dynamic> json) => CharacterDto(
|
||||||
|
name: json['name'] as String,
|
||||||
|
type: json['type'] as String,
|
||||||
|
backstory: json['backstory'] as String,
|
||||||
|
imageUrl: json['image_url'] as String,
|
||||||
|
isLiked: json['isLiked'] as bool? ?? false,
|
||||||
|
);
|
40
lib/data/mappers/character_mapper.dart
Normal file
40
lib/data/mappers/character_mapper.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:identity/data/dtos/character_dto.dart';
|
||||||
|
import 'package:identity/domain/models/character.dart';
|
||||||
|
|
||||||
|
const _imagePlaceholder = 'https://example.com/placeholder.png';
|
||||||
|
|
||||||
|
extension CharacterDtoToModel on CharacterDto {
|
||||||
|
Character toDomain() {
|
||||||
|
return Character(
|
||||||
|
name: name,
|
||||||
|
type: type == 'Survivor' ? CharacterType.Survivor : CharacterType.Hunter,
|
||||||
|
backstory: backstory,
|
||||||
|
imageUrl: imageUrl.isNotEmpty ? imageUrl : _imagePlaceholder,
|
||||||
|
isLiked: isLiked,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class CharacterMapper {
|
||||||
|
static CharacterDto toDTO(Character character) {
|
||||||
|
return CharacterDto(
|
||||||
|
name: character.name,
|
||||||
|
type: character.type.toString().split('.').last, // Преобразуем CharacterType в строку
|
||||||
|
backstory: character.backstory,
|
||||||
|
imageUrl: character.imageUrl,
|
||||||
|
isLiked: character.isLiked,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Character fromDTO(CharacterDto dto) {
|
||||||
|
// Преобразуем строку обратно в CharacterType
|
||||||
|
final characterType = dto.type == 'Survivor' ? CharacterType.Survivor : CharacterType.Hunter;
|
||||||
|
|
||||||
|
return Character(
|
||||||
|
name: dto.name,
|
||||||
|
type: characterType,
|
||||||
|
backstory: dto.backstory,
|
||||||
|
imageUrl: dto.imageUrl,
|
||||||
|
isLiked: dto.isLiked,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
10
lib/data/repositories/api_interface.dart
Normal file
10
lib/data/repositories/api_interface.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:identity/domain/models/character.dart';
|
||||||
|
|
||||||
|
// Колбэк для обработки ошибок
|
||||||
|
typedef OnErrorCallback = void Function(String? error);
|
||||||
|
|
||||||
|
// Абстрактный класс для интерфейса API
|
||||||
|
abstract class ApiInterface {
|
||||||
|
// Метод для загрузки данных, возвращающий список объектов Character
|
||||||
|
Future<List<Character>?> loadData({String? searchQuery, OnErrorCallback? onError});
|
||||||
|
}
|
26
lib/data/repositories/character_repository.dart
Normal file
26
lib/data/repositories/character_repository.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:identity/data/dtos/character_dto.dart';
|
||||||
|
import 'package:identity/data/mappers/character_mapper.dart';
|
||||||
|
import 'package:identity/domain/models/character.dart';
|
||||||
|
import 'package:identity/data/repositories/api_interface.dart';
|
||||||
|
|
||||||
|
class CharacterRepository extends ApiInterface {
|
||||||
|
final Dio _dio;
|
||||||
|
|
||||||
|
CharacterRepository(this._dio);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Character>?> loadData({String? searchQuery, OnErrorCallback? onError}) async {
|
||||||
|
try {
|
||||||
|
final String url = 'http://192.168.1.83:5000/characters';
|
||||||
|
final response = await _dio.get(url, queryParameters: searchQuery != null ? {'search': searchQuery} : null);
|
||||||
|
|
||||||
|
final List<dynamic> data = response.data as List<dynamic>;
|
||||||
|
final characterDtos = data.map((e) => CharacterDto.fromJson(e as Map<String, dynamic>)).toList();
|
||||||
|
return characterDtos.map((dto) => dto.toDomain()).toList();
|
||||||
|
} catch (e) {
|
||||||
|
onError?.call(e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -85,76 +85,3 @@ class Hunter extends Character {
|
|||||||
type: CharacterType.Hunter,
|
type: CharacterType.Hunter,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CharacterDTO {
|
|
||||||
final String name;
|
|
||||||
final String characterType;
|
|
||||||
final String backstory;
|
|
||||||
final String imageUrl;
|
|
||||||
bool isLiked; // Добавлено поле для состояния лайка
|
|
||||||
|
|
||||||
CharacterDTO({
|
|
||||||
required this.name,
|
|
||||||
required this.characterType,
|
|
||||||
required this.backstory,
|
|
||||||
required this.imageUrl,
|
|
||||||
this.isLiked = false, // Значение по умолчанию - false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Метод для преобразования в DTO (можно удалить, так как он уже возвращает текущий объект)
|
|
||||||
CharacterDTO toDTO() {
|
|
||||||
return CharacterDTO(
|
|
||||||
name: name,
|
|
||||||
characterType: typeString,
|
|
||||||
backstory: backstory,
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
isLiked: isLiked,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Метод fromJson для создания объекта из Map
|
|
||||||
factory CharacterDTO.fromJson(Map<String, dynamic> json) {
|
|
||||||
return CharacterDTO(
|
|
||||||
name: json['name'],
|
|
||||||
characterType: json['type'],
|
|
||||||
backstory: json['backstory'],
|
|
||||||
imageUrl: json['image_url'],
|
|
||||||
isLiked: json['isLiked'] ?? false, // Поддержка поля isLiked
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Геттер для типа в строковом виде
|
|
||||||
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,
|
|
||||||
isLiked: isLiked,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Метод для преобразования объекта в Map
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return {
|
|
||||||
'name': name,
|
|
||||||
'type': characterType,
|
|
||||||
'backstory': backstory,
|
|
||||||
'image_url': imageUrl,
|
|
||||||
'isLiked': isLiked, // Добавляем поле isLiked в Map
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,10 +4,13 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'bloc/bloc.dart';
|
import 'bloc/bloc.dart';
|
||||||
import 'bloc/events.dart';
|
import 'bloc/events.dart';
|
||||||
import 'bloc/state.dart';
|
import 'bloc/state.dart';
|
||||||
import 'utils/character_service.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'components/locale/l10n/app_locale.dart';
|
import 'components/locale/l10n/app_locale.dart';
|
||||||
import 'pages/character_detail_page.dart';
|
import 'pages/character_detail_page.dart';
|
||||||
import 'utils/character_search_delegate.dart';
|
import 'package:identity/data/repositories/character_repository.dart';
|
||||||
|
import 'character_search_delegate.dart';
|
||||||
|
import 'package:identity/data/mappers/character_mapper.dart';
|
||||||
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -20,7 +23,7 @@ void main() async {
|
|||||||
MultiBlocProvider(
|
MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider<HomeBloc>(
|
BlocProvider<HomeBloc>(
|
||||||
create: (_) => HomeBloc(CharacterService()),
|
create: (_) => HomeBloc(CharacterRepository(Dio())),
|
||||||
),
|
),
|
||||||
BlocProvider<DebouncedSearchCubit>(
|
BlocProvider<DebouncedSearchCubit>(
|
||||||
create: (_) => DebouncedSearchCubit(),
|
create: (_) => DebouncedSearchCubit(),
|
||||||
@ -112,9 +115,12 @@ class MyHomePage extends StatelessWidget {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
final characters = context.read<HomeBloc>().state.characters;
|
||||||
|
final suggestions = characters.map((e) => e.name).toList();
|
||||||
|
|
||||||
showSearch(
|
showSearch(
|
||||||
context: context,
|
context: context,
|
||||||
delegate: CharacterSearchDelegate(),
|
delegate: CharacterSearchDelegate(suggestions),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -169,7 +175,9 @@ class MyHomePage extends StatelessWidget {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => CharacterDetailPage(characterDTO: characterDTO),
|
builder: (context) => CharacterDetailPage(
|
||||||
|
characterDTO: CharacterMapper.toDTO(characterDTO), // Используем метод toDTO()
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import '../models/character.dart';
|
import 'package:identity/data/dtos/character_dto.dart';
|
||||||
|
import '../domain/models/character.dart';
|
||||||
import '../components/locale/l10n/app_locale.dart';
|
import '../components/locale/l10n/app_locale.dart';
|
||||||
|
|
||||||
class CharacterDetailPage extends StatelessWidget {
|
class CharacterDetailPage extends StatelessWidget {
|
||||||
final CharacterDTO characterDTO;
|
final CharacterDto characterDTO;
|
||||||
|
|
||||||
const CharacterDetailPage({Key? key, required this.characterDTO}) : super(key: key);
|
const CharacterDetailPage({Key? key, required this.characterDTO}) : super(key: key);
|
||||||
|
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import '../bloc/bloc.dart';
|
|
||||||
import '../bloc/events.dart';
|
|
||||||
import '../bloc/state.dart';
|
|
||||||
import '../components/locale/l10n/app_locale.dart';
|
|
||||||
import '../pages/character_detail_page.dart';
|
|
||||||
|
|
||||||
class CharacterSearchDelegate extends SearchDelegate {
|
|
||||||
@override
|
|
||||||
List<Widget>? buildActions(BuildContext context) {
|
|
||||||
return [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: () {
|
|
||||||
query = '';
|
|
||||||
},
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget? buildLeading(BuildContext context) {
|
|
||||||
return IconButton(
|
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
onPressed: () {
|
|
||||||
close(context, null);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildResults(BuildContext context) {
|
|
||||||
context.read<HomeBloc>().add(HomeSearchEvent(query));
|
|
||||||
|
|
||||||
return BlocBuilder<HomeBloc, HomeState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
if (state.status == HomeStatus.loading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
} else if (state.status == HomeStatus.error) {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
AppLocale.of(context)?.errorMessage(state.errorMessage) ?? 'Error',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (state.status == HomeStatus.loaded) {
|
|
||||||
final characters = state.characters;
|
|
||||||
|
|
||||||
if (characters.isEmpty) {
|
|
||||||
return Center(
|
|
||||||
child: Text(AppLocale.of(context)?.noData ?? 'No characters found.'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: characters.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final characterDTO = characters[index];
|
|
||||||
return ListTile(
|
|
||||||
leading: SizedBox(
|
|
||||||
width: 40,
|
|
||||||
child: Image.network(characterDTO.imageUrl, width: 50, height: 50),
|
|
||||||
),
|
|
||||||
title: Text(characterDTO.name),
|
|
||||||
subtitle: Text(characterDTO.typeString),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => CharacterDetailPage(characterDTO: characterDTO),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildSuggestions(BuildContext context) {
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import '../models/character.dart';
|
|
||||||
|
|
||||||
const String baseUrl = 'http://192.168.1.83:5000';
|
|
||||||
|
|
||||||
class CharacterService {
|
|
||||||
// Метод теперь возвращает список CharacterDTO
|
|
||||||
Future<List<CharacterDTO>> getCharacters({String search = ''}) async {
|
|
||||||
try {
|
|
||||||
// Формируем URL с параметром поиска
|
|
||||||
final uri = Uri.parse('$baseUrl/characters?search=$search');
|
|
||||||
|
|
||||||
// Выполняем HTTP-запрос
|
|
||||||
final response = await http.get(uri);
|
|
||||||
|
|
||||||
// Логирование данных для отладки
|
|
||||||
print('Response status: ${response.statusCode}');
|
|
||||||
print('Response body: ${response.body}'); // Выводим тело ответа для анализа
|
|
||||||
|
|
||||||
// Проверяем успешный ответ от сервера
|
|
||||||
if (response.statusCode == 200) {
|
|
||||||
final List<dynamic> data = json.decode(response.body);
|
|
||||||
print('Characters received: $data'); // Печать данных
|
|
||||||
|
|
||||||
// Возвращаем список объектов CharacterDTO
|
|
||||||
return data.map((item) => CharacterDTO.fromJson(item)).toList();
|
|
||||||
} else {
|
|
||||||
// Ошибка, если сервер вернул не 200 статус
|
|
||||||
print('Error: Server responded with status ${response.statusCode}');
|
|
||||||
throw Exception('Ошибка загрузки данных с сервера');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Обработка ошибок при выполнении запроса
|
|
||||||
print('Error fetching characters: $e');
|
|
||||||
throw Exception('Не удалось загрузить персонажей');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
242
pubspec.lock
242
pubspec.lock
@ -62,6 +62,70 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
build:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build
|
||||||
|
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
build_config:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_config
|
||||||
|
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
build_daemon:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_daemon
|
||||||
|
sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.3"
|
||||||
|
build_resolvers:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_resolvers
|
||||||
|
sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.3"
|
||||||
|
build_runner:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: build_runner
|
||||||
|
sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.14"
|
||||||
|
build_runner_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: build_runner_core
|
||||||
|
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.0.0"
|
||||||
|
built_collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_collection
|
||||||
|
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.1.1"
|
||||||
|
built_value:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: built_value
|
||||||
|
sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.9.3"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -94,6 +158,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
code_builder:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_builder
|
||||||
|
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.10.1"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -134,6 +206,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.7"
|
version: "2.3.7"
|
||||||
|
dio:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dio
|
||||||
|
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.7.0"
|
||||||
|
dio_web_adapter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dio_web_adapter
|
||||||
|
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
equatable:
|
equatable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -166,6 +254,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -210,6 +306,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -218,6 +322,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
graphs:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: graphs
|
||||||
|
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
http:
|
http:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -226,6 +338,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.2"
|
version: "1.2.2"
|
||||||
|
http_multi_server:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_multi_server
|
||||||
|
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -258,6 +378,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.20.1"
|
version: "0.20.1"
|
||||||
|
io:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: io
|
||||||
|
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.1"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -266,6 +402,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.9.0"
|
version: "4.9.0"
|
||||||
|
json_serializable:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: json_serializable
|
||||||
|
sha256: "8f52361c07497a7f2c16c13aac159f9be6fb12b1d67719eac98a21d9a205d571"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.9.2"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -298,6 +442,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
logging:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: logging
|
||||||
|
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
macros:
|
macros:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -330,6 +482,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.15.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -402,6 +562,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
|
pool:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pool
|
||||||
|
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.5.1"
|
||||||
posix:
|
posix:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -426,6 +594,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "2.1.5"
|
||||||
|
pubspec_parse:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: pubspec_parse
|
||||||
|
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -482,11 +658,43 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.1"
|
||||||
|
shelf:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf
|
||||||
|
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.2"
|
||||||
|
shelf_web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shelf_web_socket
|
||||||
|
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
source_gen:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_gen
|
||||||
|
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
source_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_helper
|
||||||
|
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.5"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -511,6 +719,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
stream_transform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_transform
|
||||||
|
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -535,6 +751,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.3"
|
version: "0.7.3"
|
||||||
|
timing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timing
|
||||||
|
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -575,6 +799,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
web_socket:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket
|
||||||
|
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.6"
|
||||||
|
web_socket_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web_socket_channel
|
||||||
|
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -600,5 +840,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.6.0 <4.0.0"
|
||||||
flutter: ">=3.24.0"
|
flutter: ">=3.24.0"
|
||||||
|
@ -2,7 +2,7 @@ name: identity
|
|||||||
|
|
||||||
description: A new Flutter project.
|
description: A new Flutter project.
|
||||||
|
|
||||||
publish_to: 'none' # Указывает, что проект не публикуется в pub.dev.
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
|
|
||||||
@ -16,6 +16,7 @@ dependencies:
|
|||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
|
dio: ^5.0.0
|
||||||
|
|
||||||
http: ^1.2.2
|
http: ^1.2.2
|
||||||
equatable: ^2.0.5
|
equatable: ^2.0.5
|
||||||
@ -31,6 +32,10 @@ dev_dependencies:
|
|||||||
|
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
flutter_launcher_icons: ^0.13.1
|
flutter_launcher_icons: ^0.13.1
|
||||||
|
build_runner: ^2.4.6
|
||||||
|
json_serializable: ^6.7.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
flutter_icons:
|
flutter_icons:
|
||||||
android: true
|
android: true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user