{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
import 'package:dio/dio.dart';
class Geo {
String lat;
String lng;
Geo({required this.lat, required this.lng});
factory Geo.fromMap(Map<String, dynamic> map) {
return Geo(
lat: map['lat'],
lng: map['lng']
);
}
}
class Address {
String street;
String suite;
String city;
String zipcode;
Geo geo;
Address({
required this.street,
required this.suite,
required this.city,
required this.zipcode,
required this.geo,
});
factory Address.fromMap(Map<String, dynamic> map) {
return Address(
street: map['street'],
suite: map['suite'],
city: map['city'],
zipcode: map['zipcode'],
geo: Geo.fromMap(map['geo']),
);
}
}
class Company {
String name;
String catchPhrase;
String bs;
Company({
required this.name,
required this.catchPhrase,
required this.bs,
});
factory Company.fromMap(Map<String, dynamic> map) {
return Company(
name: map['name'],
catchPhrase: map['catchPhrase'],
bs: map['bs']
);
}
}
class User {
int id;
String name;
String username;
String email;
Address address;
String phone;
String website;
Company company;
User({
required this.id,
required this.name,
required this.username,
required this.email,
required this.address,
required this.phone,
required this.website,
required this.company,
});
factory User.fromMap(Map<String, dynamic> map) {
return User(
id: map['id'],
name: map['name'],
username: map['username'],
email: map['email'],
address: Address.fromMap(map['address']),
phone: map['phone'],
website: map['website'],
company: Company.fromMap(map['company']),
);
}
}
void main() async {
Dio dio = Dio();
var url = 'https://jsonplaceholder.typicode.com/users/1';
var res = await dio.get(url);
if (res.statusCode == 200) {
var user1 = User.fromMap(res.data);
print(user1);
}
}
[
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
},
{
"id": 2,
"name": "Ervin Howell",
"username": "Antonette",
"email": "Shanna@melissa.tv",
"address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
"geo": {
"lat": "-43.9509",
"lng": "-34.4618"
}
},
"phone": "010-692-6593 x09125",
"website": "anastasia.net",
"company": {
"name": "Deckow-Crist",
"catchPhrase": "Proactive didactic contingency",
"bs": "synergize scalable supply-chains"
}
},
... 10개
]
void main() async {
Dio dio = Dio();
var url = 'https://jsonplaceholder.typicode.com/users/1';
var res = await dio.get(url);
if (res.statusCode == 200) {
var user1 = User.fromMap(res.data);
print(user1);
}
}
void main() async {
Dio dio = Dio();
var url = 'https://jsonplaceholder.typicode.com/users';
var res = await dio.get(url);
List<User> users = [];
if (res.statusCode == 200) {
var data = List<Map<String, dynamic>>.from(res.data);
for (var userMap in data) {
users.add(User.fromMap(userMap));
}
print(users);
}
}
프로필 앱 만들기
- 다음의 공개된 API를 분석하고, 클래스를 활용하여 적용 후
연락처를 보여주는 앱을 다음과 같이 만드시오.
- https://jsonplaceholder.typicode.com/users
- 반드시 Profile 클래스를 만들고 Serialization을 진행할 수 있도록 하시오.
- 각 사람별 이미지를 CircleAvatar를 통해 보여주도록 한다.
- 이 때, 해당 API에는 프로필이미지가 없으므로 다음의 이미지 URL을 활용한다.
- https://xsgames.co/randomusers/assets/avatars/male/{번호}.jpg
- 위 URL에 들어가는 {번호}에는 유저ID를 넣어 사용할 수 있도록 한다.- 애니메이션 효과를 적절히 사용하여 최대한 위 결과물과 비슷하도록 만드시오.
- 네트워크에 통신하여 데이터를 가져오는 것은 첫 페이지(리스트 보여주는 페이지)에만 할 수 있도록 한다.
import 'package:flutter/material.dart';
import 'page/main_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
import 'address.dart';
import 'company.dart';
class Profile {
int id;
String name;
String username;
String email;
Address address;
String phone;
String website;
Company company;
Profile({
required this.id,
required this.name,
required this.username,
required this.email,
required this.address,
required this.phone,
required this.website,
required this.company,
});
factory Profile.fromMap(Map<String, dynamic> map) {
return Profile(
id: map['id'],
name: map['name'],
username: map['username'],
email: map['email'],
address: Address.fromMap(map['address']),
phone: map['phone'], website: map['website'],
company: Company.fromMap(map['company']),
);
}
}
import 'geo.dart';
class Address {
String street;
String suite;
String city;
String zipcode;
Geo geo;
Address({
required this.street,
required this.suite,
required this.city,
required this.zipcode,
required this.geo,
});
factory Address.fromMap(Map<String, dynamic> map) {
return Address(
street: map['street'],
suite: map['suite'],
city: map['city'],
zipcode: map['zipcode'],
geo: Geo.fromMap(map['geo']),
);
}
String toString() => '$city, $street, $suite';
}
class Geo {
String lat;
String lng;
Geo({required this.lat, required this.lng});
factory Geo.fromMap(Map<String, dynamic> map) {
return Geo(lat: map['lat'], lng: map['lng']);
}
}
class Company {
String name;
String catchPhrase;
String bs;
Company({
required this.name,
required this.catchPhrase,
required this.bs,
});
factory Company.fromMap(Map<String, dynamic> map) {
return Company(
name: map['name'],
catchPhrase: map['catchPhrase'],
bs: map['bs']
);
}
}
import 'package:animate_do/animate_do.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../model/profile.dart';
import '../widget/profile_item.dart';
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Future<List<Profile>> getData() async {
Dio dio = Dio();
String url = 'https://jsonplaceholder.typicode.com/users';
var res = await dio.get(url);
if (res.statusCode == 200) {
var data = List<Map<String, dynamic>>.from(res.data);
return data.map((e) => Profile.fromMap(e)).toList();
}
return [];
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.black,
elevation: 0,
centerTitle: true,
title: Text('My Contacts'),
),
body: FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
var profiles = snapshot.data!;
return ListView.builder(
itemCount: profiles.length,
itemBuilder: (context, index) {
var profile = profiles[index];
return FadeInRight(
delay: Duration(milliseconds: 500*index),
child: ProfileItem(profile: profile),
);
}
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
import 'package:animate_do/animate_do.dart';
import 'package:flutter/material.dart';
import '../model/profile.dart';
class ProfilePage extends StatelessWidget {
const ProfilePage({Key? key, required this.profile, required this.imgUrl}) : super(key: key);
final Profile profile;
final String imgUrl;
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text(profile.name),
),
body: Stack(
children: [
Column(
children: [
Container(
width: double.infinity,
height: 250,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(imgUrl),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black54, BlendMode.darken
),
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FadeInRight(
delay: Duration(milliseconds: 500),
child: Padding(
padding: const EdgeInsets.only(top: 45, bottom: 20),
child: Text(profile.name, style: TextStyle(fontSize: 30)),
),
),
Divider(),
FadeInRight(
delay: Duration(milliseconds: 1500),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text('Information', style: TextStyle(fontSize: 24)),
),
Row(
children: [
Icon(Icons.mail),
SizedBox(width: 8),
Text(profile.email),
],
),
Row(
children: [
Icon(Icons.call),
SizedBox(width: 8),
Text(profile.phone),
],
),
Row(
children: [
Icon(Icons.location_on),
SizedBox(width: 8),
Text('${profile.address}'),
],
),
],
),
),
Divider(),
FadeInRight(
delay: Duration(milliseconds: 2500),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Text('Company', style: TextStyle(fontSize: 24)),
),
Text(profile.company.name),
Text(profile.company.catchPhrase),
Text(profile.company.bs),
],
),
),
],
),
),
],
),
Positioned(
top: 200,
left: 16,
child: Container(
width: 100,
height: 100,
child: ClipRRect(
borderRadius: BorderRadius.circular(100),
child: Image.network(imgUrl),
),
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import '../model/profile.dart';
import '../page/profile_page.dart';
class ProfileItem extends StatelessWidget {
const ProfileItem({Key? key, required this.profile}) : super(key: key);
final Profile profile;
Widget build(BuildContext context) {
String imgUrl = 'https://xsgames.co/randomusers/assets/avatars/male/${profile.id}.jpg';
return ListTile(
leading: ClipRRect(
borderRadius: BorderRadius.circular(30),
child: Image.network(imgUrl),
),
title: Text(profile.name),
subtitle: Text(profile.email),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfilePage(profile: profile, imgUrl: imgUrl))
);
},
);
}
}