Skip to content
This repository has been archived by the owner. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 54 additions & 27 deletions flutter_base/lib/network/api_service.dart
Original file line number Diff line number Diff line change
@@ -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<ApiModel<List<PostModel>, Failure>> getPosts();
Future<ApiModel<PostModel, Failure>> getPost(postId);
Future<ApiModel<List<CommentModel>, 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<ApiModel<List<PostModel>, 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<dynamic> json = jsonDecode(response.body);
final models = json.map((e) => PostModel.fromJson(e));

print(DateTime.now().difference(now).inMilliseconds);
// @override
// Future<ApiModel<List<PostModel>, 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<dynamic> 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._();
Expand All @@ -63,4 +63,31 @@ class ApiServiceDio implements ApiService {
return ApiModel.error(Failure());
}
}

@override
Future<ApiModel<PostModel, Failure>> 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<ApiModel<List<CommentModel>, Failure>> getComments(postId) async {
try {
final response = await dio.get('/comments?postId=$postId');

final list = response.data as List<dynamic>;

final models = list.map((e) => CommentModel.fromJson(e));

return ApiModel.success(models.toList());
} catch (e) {
return ApiModel.error(Failure());
}
}
}
5 changes: 4 additions & 1 deletion flutter_base/lib/network/homework.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@
- URL загрузки комментариев поста — /comments?postId=N
- Необходимо продумать состояние загрузки, результата, пустого результата и ошибки

[API](https://jsonplaceholder.typicode.com/)
[API](https://jsonplaceholder.typicode.com/)

// PostDetails

102 changes: 102 additions & 0 deletions flutter_base/lib/network/homework/comments.dart
Original file line number Diff line number Diff line change
@@ -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<Comments> createState() => _CommentsState();
}

class _CommentsState extends State<Comments> {
final ApiService service = ApiServiceDio.instance;
var state = ContentState.initial;
final comments = <CommentModel>[];

Future<void> 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<CommentModel> 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('Данные не загружены'),
);
}
}
}
53 changes: 53 additions & 0 deletions flutter_base/lib/network/homework/post.dart
Original file line number Diff line number Diff line change
@@ -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,
))),
]),
);
}
}
125 changes: 125 additions & 0 deletions flutter_base/lib/network/homework/post_details.dart
Original file line number Diff line number Diff line change
@@ -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<PostDetails> createState() => _PostDetailsState();
}

class _PostDetailsState extends State<PostDetails> {
final ApiService service = ApiServiceDio.instance;
var state = ContentState.initial;
late PostModel post;

Future<void> 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),
),
),
);
}
}
Loading