diff --git a/lib/data/dtos/characters_dto.dart b/lib/data/dtos/characters_dto.dart index 99050d9..c9888d3 100644 --- a/lib/data/dtos/characters_dto.dart +++ b/lib/data/dtos/characters_dto.dart @@ -13,23 +13,24 @@ class CharactersDto { @JsonSerializable(createToJson: false) class CharacterDataDto { -final String? id; -final String? type; -final CharacterAttributesDataDto? attributes; + final String? id; + final String? type; + final CharacterAttributesDataDto? attributes; -const CharacterDataDto({this.id, this.type, this.attributes}); + const CharacterDataDto({this.id, this.type, this.attributes}); -factory CharacterDataDto.fromJson(Map json) => _$CharacterDataDtoFromJson(json); + factory CharacterDataDto.fromJson(Map json) => _$CharacterDataDtoFromJson(json); } @JsonSerializable(createToJson: false) class CharacterAttributesDataDto { -final String? name; -final String? born; -final String? died; -final String? image; + final String? name; + final String? born; + final String? died; + final String? image; -const CharacterAttributesDataDto({this.name, this.born, this.died, this.image}); + const CharacterAttributesDataDto({this.name, this.born, this.died, this.image}); -factory CharacterAttributesDataDto.fromJson(Map json) => _$CharacterAttributesDataDtoFromJson(json); -} + factory CharacterAttributesDataDto.fromJson(Map json) => + _$CharacterAttributesDataDtoFromJson(json); +} \ No newline at end of file diff --git a/lib/data/repositories/api_interface.dart b/lib/data/repositories/api_interface.dart index efb32f8..e74083e 100644 --- a/lib/data/repositories/api_interface.dart +++ b/lib/data/repositories/api_interface.dart @@ -1,5 +1,7 @@ import 'package:mobilki_lab1/domain/models/card.dart'; +typedef OnErrorCallback = void Function(String? error); + abstract class ApiInterface { - Future?> loadData (String? q); + Future?> loadData({OnErrorCallback? onError}); } \ No newline at end of file diff --git a/lib/data/repositories/mock_repository.dart b/lib/data/repositories/mock_repository.dart index 03edeb9..8aecf05 100644 --- a/lib/data/repositories/mock_repository.dart +++ b/lib/data/repositories/mock_repository.dart @@ -4,28 +4,27 @@ import 'package:mobilki_lab1/domain/models/card.dart'; class MockRepository extends ApiInterface { @override - Future?> loadData(String? q) async { + Future?> loadData({OnErrorCallback? onError}) async { return [ CardData( 'Freeze', descriptionText: 'so cold..', imageUrl: - 'https://www.skedaddlewildlife.com/wp-content/uploads/2018/09/depositphotos_22425309-stock-photo-a-lonely-raccoon-in-winter.jpg', + 'https://www.skedaddlewildlife.com/wp-content/uploads/2018/09/depositphotos_22425309-stock-photo-a-lonely-raccoon-in-winter.jpg', ), CardData( 'Hi', descriptionText: 'pretty face', icon: Icons.hail, imageUrl: - 'https://www.thesprucepets.com/thmb/nKNaS4I586B_H7sEUw9QAXvWM_0=/2121x0/filters:no_upscale():strip_icc()/GettyImages-135630198-5ba7d225c9e77c0050cff91b.jpg', + 'https://www.thesprucepets.com/thmb/nKNaS4I586B_H7sEUw9QAXvWM_0=/2121x0/filters:no_upscale():strip_icc()/GettyImages-135630198-5ba7d225c9e77c0050cff91b.jpg', ), CardData( 'Orange', descriptionText: 'I like autumn', icon: Icons.warning_amber, - imageUrl: - 'https://furmanagers.com/wp-content/uploads/2019/11/dreamstime_l_22075357.jpg', + imageUrl: 'https://furmanagers.com/wp-content/uploads/2019/11/dreamstime_l_22075357.jpg', ), ]; } -} +} \ No newline at end of file diff --git a/lib/data/repositories/potter_repository.dart b/lib/data/repositories/potter_repository.dart index 21a3287..3817a93 100644 --- a/lib/data/repositories/potter_repository.dart +++ b/lib/data/repositories/potter_repository.dart @@ -14,21 +14,21 @@ class PotterRepository extends ApiInterface { static const String _baseUrl = 'https://api.potterdb.com'; - @override - Future?> loadData(String? q) async{ - try{ + Future?> loadData({String? q, OnErrorCallback? onError}) async { + try { const String url = '$_baseUrl/v1/characters'; final Response response = await _dio.get>( url, - queryParameters: q != null ? {'filter[name_cont]' : q} : null, + queryParameters: q != null ? {'filter[name_cont]': q} : null, ); final CharactersDto dto = CharactersDto.fromJson(response.data as Map); final List? data = dto.data?.map((e) => e.toDomain()).toList(); return data; - } on DioException catch (e){ + } on DioException catch (e) { + onError?.call(e.response?.statusMessage); return null; } } diff --git a/lib/presentation/home_page/home_page.dart b/lib/presentation/home_page/home_page.dart index c65749e..d5f4cc5 100644 --- a/lib/presentation/home_page/home_page.dart +++ b/lib/presentation/home_page/home_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:mobilki_lab1/data/repositories/potter_repository.dart'; import 'package:mobilki_lab1/domain/models/card.dart'; - -import '../details_page/details_page.dart'; +import 'package:mobilki_lab1/presentation/details_page/details_page.dart'; part 'card.dart'; @@ -16,87 +16,77 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - final Color _color = Colors.brown.shade300; + @override + Widget build(BuildContext context) { + return const Scaffold(body: Body()); + } +} + +class Body extends StatefulWidget { + const Body({super.key}); + + @override + State createState() => _BodyState(); +} + +class _BodyState extends State { + final searchController = TextEditingController(); + late Future?> data; + + final repo = PotterRepository(); @override void initState() { + data = repo.loadData(); super.initState(); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: _color, - title: Text(widget.title), - ), - body: const Body(), - ); - } -} - -class Body extends StatelessWidget { - const Body({super.key}); - - @override - Widget build(BuildContext context) { - final data = [ - CardData( - 'кот', - descriptionText: 'глупенький', - imageUrl: - 'https://i.pinimg.com/564x/98/74/75/98747559040c03402a6d9ad4e47152c6.jpg', - ), - CardData( - 'сырный кот', - descriptionText: 'он не виноват', - imageUrl: - 'https://i.pinimg.com/564x/e7/c5/59/e7c559e449cd0bb5370d7740b57daa1f.jpg', - ), - CardData( - 'злой кот', - descriptionText: 'я бы ему не верила', - imageUrl: - 'https://i.pinimg.com/736x/27/cf/5e/27cf5e3abe1795ce04095941902c61ca.jpg', - ), - CardData( - 'умный кот', - descriptionText: 'он только делает вид что умный', - imageUrl: - 'https://i.pinimg.com/736x/e9/24/80/e924801430b813eee2635b40fe6e11d6.jpg', - ), - CardData( - 'кот в шляпе', - descriptionText: '我喜欢橘子!', - imageUrl: - 'https://i.pinimg.com/564x/3e/ed/11/3eed1103c548ed7c46a1e37a2695f53a.jpg', - ), - CardData( - 'котенок', - descriptionText: 'я его очень понимаю', - imageUrl: - 'https://i.pinimg.com/736x/38/9f/e6/389fe61133f036fc980a6b2e7abb2557.jpg', - ), - ]; - - return Center( - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: data.map((data) { - return _Card.fromData( - data, - onLike: (String title, bool isLiked) => - _showSnackBar(context, title, isLiked), - onTap: () => _navToDetails(context, data), - ); - }).toList(), - ), + return Padding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(12), + child: CupertinoSearchTextField( + controller: searchController, + onChanged: (search) { + setState(() { + data = repo.loadData(q: search); + }); + }, + ), + ), + Expanded( + child: Center( + child: FutureBuilder?>( + future: data, + builder: (context, snapshot) => SingleChildScrollView( + child: snapshot.hasData + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: snapshot.data?.map((data) { + return _Card.fromData( + data, + onLike: (String title, bool isLiked) => + _showSnackBar(context, title, isLiked), + onTap: () => _navToDetails(context, data), + ); + }).toList() ?? + [], + ) + : const CircularProgressIndicator(), + ), + ), + ), + ), + ], ), ); } - void _navToDetails(BuildContext context, CardData data){ + void _navToDetails(BuildContext context, CardData data) { Navigator.push( context, CupertinoPageRoute(builder: (context) => DetailsPage(data)), @@ -107,10 +97,10 @@ class Body extends StatelessWidget { WidgetsBinding.instance.addPostFrameCallback((_) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( - 'Вам ${isLiked ? 'понравился' : 'больше не нравится'} $title', + '$title ${isLiked ? 'liked!' : 'disliked :('}', style: Theme.of(context).textTheme.bodyLarge, ), - backgroundColor: Colors.brown.shade400, + backgroundColor: Colors.orangeAccent, duration: const Duration(seconds: 1), )); });