From 5109ee925e3b156f152960c7d38b67bd226d23db Mon Sep 17 00:00:00 2001 From: Evgan Date: Tue, 5 Jul 2022 17:38:59 +0300 Subject: [PATCH] hw-lessons12/Evgan --- flutter_base/lib/network/api_service.dart | 81 ++++++++---- flutter_base/lib/network/homework.md | 5 +- .../lib/network/homework/comments.dart | 102 ++++++++++++++ flutter_base/lib/network/homework/post.dart | 53 ++++++++ .../lib/network/homework/post_details.dart | 125 ++++++++++++++++++ flutter_base/lib/network/main.dart | 21 ++- .../lib/network/models/comment_model.dart | 22 +++ 7 files changed, 380 insertions(+), 29 deletions(-) create mode 100644 flutter_base/lib/network/homework/comments.dart create mode 100644 flutter_base/lib/network/homework/post.dart create mode 100644 flutter_base/lib/network/homework/post_details.dart create mode 100644 flutter_base/lib/network/models/comment_model.dart diff --git a/flutter_base/lib/network/api_service.dart b/flutter_base/lib/network/api_service.dart index 0e98d22..041d5b7 100644 --- a/flutter_base/lib/network/api_service.dart +++ b/flutter_base/lib/network/api_service.dart @@ -1,44 +1,44 @@ -import 'dart:convert'; - import 'package:dio/dio.dart'; import 'package:flutter_base/network/models/api_model.dart'; +import 'package:flutter_base/network/models/comment_model.dart'; import 'package:flutter_base/network/models/post_model.dart'; -import 'package:http/http.dart' as http; const _baseUrl = 'https://jsonplaceholder.typicode.com'; abstract class ApiService { Future, Failure>> getPosts(); + Future> getPost(postId); + Future, Failure>> getComments(postId); } -class ApiServiceImpl implements ApiService { - static final ApiServiceImpl _instance = ApiServiceImpl._(); +// class ApiServiceImpl implements ApiService { +// static final ApiServiceImpl _instance = ApiServiceImpl._(); - ApiServiceImpl._(); +// ApiServiceImpl._(); - static ApiServiceImpl get instance => _instance; +// static ApiServiceImpl get instance => _instance; - @override - Future, Failure>> getPosts() async { - try { - final url = Uri.parse(_baseUrl + '/posts'); - final response = await http.get(url); - if (response.statusCode != 200) { - ApiModel.error(Failure()); - } - await Future.delayed(const Duration(seconds: 1)); - final now = DateTime.now(); - final List json = jsonDecode(response.body); - final models = json.map((e) => PostModel.fromJson(e)); - - print(DateTime.now().difference(now).inMilliseconds); +// @override +// Future, Failure>> getPosts() async { +// try { +// final url = Uri.parse(_baseUrl + '/posts'); +// final response = await http.get(url); +// if (response.statusCode != 200) { +// ApiModel.error(Failure()); +// } +// await Future.delayed(const Duration(seconds: 1)); +// final now = DateTime.now(); +// final List json = jsonDecode(response.body); +// final models = json.map((e) => PostModel.fromJson(e)); - return ApiModel.success(models.toList()); - } catch (e) { - return ApiModel.error(Failure()); - } - } -} +// print(DateTime.now().difference(now).inMilliseconds); + +// return ApiModel.success(models.toList()); +// } catch (e) { +// return ApiModel.error(Failure()); +// } +// } +// } class ApiServiceDio implements ApiService { static final ApiServiceDio _instance = ApiServiceDio._(); @@ -63,4 +63,31 @@ class ApiServiceDio implements ApiService { return ApiModel.error(Failure()); } } + + @override + Future> getPost(postId) async { + try { + final response = await dio.get('/posts/$postId'); + final data = response.data; + final model = PostModel.fromJson(data); + return ApiModel.success(model); + } catch (e) { + return ApiModel.error(Failure()); + } + } + + @override + Future, Failure>> getComments(postId) async { + try { + final response = await dio.get('/comments?postId=$postId'); + + final list = response.data as List; + + final models = list.map((e) => CommentModel.fromJson(e)); + + return ApiModel.success(models.toList()); + } catch (e) { + return ApiModel.error(Failure()); + } + } } diff --git a/flutter_base/lib/network/homework.md b/flutter_base/lib/network/homework.md index e4ff257..9b5c4f1 100644 --- a/flutter_base/lib/network/homework.md +++ b/flutter_base/lib/network/homework.md @@ -8,4 +8,7 @@ - URL загрузки комментариев поста — /comments?postId=N - Необходимо продумать состояние загрузки, результата, пустого результата и ошибки -[API](https://jsonplaceholder.typicode.com/) \ No newline at end of file +[API](https://jsonplaceholder.typicode.com/) + +// PostDetails + diff --git a/flutter_base/lib/network/homework/comments.dart b/flutter_base/lib/network/homework/comments.dart new file mode 100644 index 0000000..561f88f --- /dev/null +++ b/flutter_base/lib/network/homework/comments.dart @@ -0,0 +1,102 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_base/network/api_service.dart'; +import 'package:flutter_base/network/enums.dart'; +import 'package:flutter_base/network/models/comment_model.dart'; + +class Comments extends StatefulWidget { + final int postId; + const Comments({Key? key, required this.postId}) : super(key: key); + + @override + State createState() => _CommentsState(); +} + +class _CommentsState extends State { + final ApiService service = ApiServiceDio.instance; + var state = ContentState.initial; + final comments = []; + + Future load() async { + setState(() { + state = ContentState.loading; + }); + final response = await service.getComments(widget.postId); + if (response.hasError) { + setState(() { + state = ContentState.failure; + comments.clear(); + }); + } else { + setState(() { + state = response.result!.isNotEmpty + ? ContentState.success + : ContentState.empty; + comments + ..clear() + ..addAll(response.result!); + }); + } + } + + @override + void initState() { + load(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column(children: [ + Container( + height: 20, + child: Text('Комментарии'), + ), + Flexible( + child: _CommentsView( + state: state, + comments: comments, + )), + ]); + } +} + +class _CommentsView extends StatelessWidget { + final ContentState state; + final List comments; + const _CommentsView({Key? key, required this.state, this.comments = const []}) + : super(key: key); + + @override + Widget build(BuildContext context) { + switch (state) { + case ContentState.success: + return ListView.builder( + itemCount: comments.length, + itemBuilder: (context, i) => ListTile( + title: Text(comments[i].name), + subtitle: Text(comments[i].body), + ), + ); + case ContentState.loading: + return const Center( + child: CircularProgressIndicator(), + ); + case ContentState.empty: + return const Center( + child: Text('Пустой список постов'), + ); + case ContentState.failure: + return const Center( + child: Text( + 'Ууупс, что-то пошло не так', + style: TextStyle(color: Colors.red), + ), + ); + default: + return const Center( + child: Text('Данные не загружены'), + ); + } + } +} diff --git a/flutter_base/lib/network/homework/post.dart b/flutter_base/lib/network/homework/post.dart new file mode 100644 index 0000000..414cf7e --- /dev/null +++ b/flutter_base/lib/network/homework/post.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_base/network/homework/comments.dart'; +import 'package:flutter_base/network/homework/post_details.dart'; + +class Post extends StatelessWidget { + final int postId; + const Post({Key? key, required this.postId}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Post info $postId'), + ), + body: Column(mainAxisSize: MainAxisSize.max, children: [ + // Flexible( + // child: SizedBox( + // width: double.infinity, + // child: DecoratedBox( + // decoration: const BoxDecoration( + // color: Color.fromARGB(255, 86, 92, 102)), + // child: Padding( + // padding: const EdgeInsets.all(10), + // child: PostDetails(postId: postId)), + // ))), + // Flexible( + // child: SizedBox( + // width: double.infinity, + // height: double.infinity, + // child: DecoratedBox( + // decoration: const BoxDecoration( + // color: Color.fromARGB(255, 171, 189, 221)), + // child: Comments( + // postId: postId, + // )))), + Container( + decoration: + const BoxDecoration(color: Color.fromARGB(255, 86, 92, 102)), + child: Padding( + padding: const EdgeInsets.all(10), + child: PostDetails(postId: postId)), + ), + Flexible( + child: Container( + decoration: const BoxDecoration( + color: Color.fromARGB(255, 200, 203, 207)), + child: Comments( + postId: postId, + ))), + ]), + ); + } +} diff --git a/flutter_base/lib/network/homework/post_details.dart b/flutter_base/lib/network/homework/post_details.dart new file mode 100644 index 0000000..74a09e0 --- /dev/null +++ b/flutter_base/lib/network/homework/post_details.dart @@ -0,0 +1,125 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_base/network/api_service.dart'; +import 'package:flutter_base/network/enums.dart'; +import 'package:flutter_base/network/models/post_model.dart'; + +class PostDetails extends StatefulWidget { + final int postId; + const PostDetails({Key? key, required this.postId}) : super(key: key); + + @override + State createState() => _PostDetailsState(); +} + +class _PostDetailsState extends State { + final ApiService service = ApiServiceDio.instance; + var state = ContentState.initial; + late PostModel post; + + Future load() async { + setState(() { + state = ContentState.loading; + }); + final response = await service.getPost(widget.postId); + if (response.hasError) { + setState(() { + state = ContentState.failure; + }); + } else { + inspect(response.result); + setState(() { + state = + response.result != null ? ContentState.success : ContentState.empty; + post = response.result as PostModel; + }); + } + } + + @override + void initState() { + load(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + switch (state) { + case ContentState.loading: + return const Center( + child: CircularProgressIndicator(), + ); + case ContentState.success: + return _PostInfoView(post: post as PostModel); + case ContentState.failure: + return const Center( + child: Text( + 'Ууупс, что-то пошло не так', + style: TextStyle(color: Colors.red), + ), + ); + case ContentState.empty: + return const Center( + child: Text('Пустой список постов'), + ); + default: + return const Center( + child: Text('Данные не загружены'), + ); + } + } +} + +class _PostInfoView extends StatelessWidget { + final PostModel post; + const _PostInfoView({Key? key, required this.post}) : super(key: key); + @override + Widget build(BuildContext context) { + final id = post.id; + final userId = post.userId; + final title = post.title; + final body = post.body; + return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + _TextView( + label: 'id', + value: id.toString(), + ), + _TextView( + label: 'userId', + value: userId.toString(), + ), + _TextView( + label: 'title', + value: title, + ), + _TextView( + label: 'ibodyd', + value: body, + ) + ]); + } +} + +class _TextView extends StatelessWidget { + final String label; + final String value; + const _TextView({ + Key? key, + required this.label, + required this.value, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only(bottom: 10), + child: Text( + '${label}: $value', + style: const TextStyle( + color: Color.fromARGB(255, 255, 255, 255), + ), + ), + ); + } +} diff --git a/flutter_base/lib/network/main.dart b/flutter_base/lib/network/main.dart index f6d011a..0bf2944 100644 --- a/flutter_base/lib/network/main.dart +++ b/flutter_base/lib/network/main.dart @@ -1,10 +1,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_base/network/api_service.dart'; import 'package:flutter_base/network/enums.dart'; +import 'package:flutter_base/network/homework/post.dart'; import 'package:flutter_base/network/models/post_model.dart'; void main() { - runApp(const App()); + //runApp(const App()); + runApp(MaterialApp( + initialRoute: '/', + onGenerateRoute: (settings) { + switch (settings.name) { + case '/': + return MaterialPageRoute( + builder: (context) => const App(), + ); + } + }, + )); } class App extends StatelessWidget { @@ -92,6 +104,13 @@ class _PostsView extends StatelessWidget { itemBuilder: (context, i) => ListTile( title: Text(posts[i].title), subtitle: Text(posts[i].body), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Post(postId: posts[i].id), + )); + }, ), ); case ContentState.loading: diff --git a/flutter_base/lib/network/models/comment_model.dart b/flutter_base/lib/network/models/comment_model.dart new file mode 100644 index 0000000..872094f --- /dev/null +++ b/flutter_base/lib/network/models/comment_model.dart @@ -0,0 +1,22 @@ +class CommentModel { + final int postId; + final int id; + final String name; + final String email; + final String body; + + CommentModel({ + required this.postId, + required this.id, + required this.name, + required this.email, + required this.body, + }); + + CommentModel.fromJson(Map json) + : postId = json['postId'], + id = json['id'], + name = json['name'], + email = json['email'], + body = json['body']; +}