
{
	"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))
        );
      },
    );
  }
}
