27일차 과제 - rlatkddbs99/Flutter GitHub Wiki

Android Emulator - flutter_emulator_5554 2023-03-02 23-06-39

main.dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:user/page/mainPage.dart';

void main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: mainPage(),
    );
  }
}

page/mainPage.dart

import 'package:animate_do/animate_do.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:user/model/profile.dart';
import 'package:user/page/detailPage.dart';

class mainPage extends StatefulWidget {
  const mainPage({super.key});

  @override
  State<mainPage> createState() => _mainPageState();
}

class _mainPageState extends State<mainPage> {
  List<Profile> profiles = []; //dio 결과 담을 리스트
  readUsers() {
    var url = "https://jsonplaceholder.typicode.com/users";
    Dio dio = Dio();
    dio.get(url).then((value) {
      //결과가 얻어지면 value에는 future를 통해 얻어진 결과가 들어감
      if (value.statusCode == 200) {
        var data = List<Map<String, dynamic>>.from(
            value.data); //List<dynamic>을 Map형식으로 바꿈
        //print(value.data.runtimeType);  List<dynamic>
        profiles = data.map((e) => Profile.fromMap(e)).toList(); //하나하나 새로운값을 넣음
        setState(() {}); //새로고침
      }
    });
  }

  @override
  void initState() {
    //생성될 때 한번 실행
    super.initState();
    readUsers();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("My Contacts"),
          backgroundColor: Colors.transparent,
          foregroundColor: Colors.black,
          elevation: 0,
        ),
        body: ListView.builder(
          itemCount: profiles.length,
          itemBuilder: (context, index) => FadeInRight(
            delay: Duration(milliseconds: 100 * index), //스르륵 애니메이션
            child: ListTile(
              leading: CircleAvatar(
                backgroundImage: NetworkImage(
                    "https://xsgames.co/randomusers/assets/avatars/male/${profiles[index].id}.jpg"), //프로바이더 형태, userId를 이용함
              ),
              title: Text(profiles[index].name),
              subtitle: Text(profiles[index].email), //email내용
              trailing: IconButton(
                  onPressed: () {
                    Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (context) => DetailPage(
                                profile: profiles[
                                    index]))); //눌렀을 때 해당 페이지로 이동, 해당 유저 데이터 가지고감
                  },
                  icon: Icon(Icons.navigate_next)),
            ),
          ),
        ));
  }
}

page/detailPage.dart

import 'package:animate_do/animate_do.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:user/model/profile.dart';

class DetailPage extends StatelessWidget {
  const DetailPage({super.key, required this.profile});
  final Profile profile;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true, //사진이 앱바영역 까지 먹음
      appBar: AppBar(
        title: Text(profile.name), //현재 넘어온 사람의 name 앱바에 표시
        elevation: 0,
        backgroundColor: Colors.transparent,
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Stack(
            //위젯 겹치게 보이기
            clipBehavior: Clip.none, //circleAvatar안잘리게
            children: [
              Image.network(
                "https://xsgames.co/randomusers/assets/avatars/male/${profile.id}.jpg",
                fit: BoxFit.cover,
                colorBlendMode: BlendMode.darken, //이미지 살짝 어둡게
                color: Colors.black45, //색깔 지정 필요
                width: double.infinity, //이미지 가로 길이가 다 차지할 수 있도록
                height: 320,
              ), //이미지 번호에 맞춰 가져오기
              Positioned(
                bottom: -48,
                child: Padding(
                  padding: const EdgeInsets.only(left: 16), //왼쪽에 공백
                  child: CircleAvatar(
                    radius: 48,
                    backgroundImage: NetworkImage(
                        "https://xsgames.co/randomusers/assets/avatars/male/${profile.id}.jpg"),
                  ),
                ),
              )
            ],
          ),
          SizedBox(
            height: 56,
          ),
          FadeInRight(
            duration: Duration(milliseconds: 500),
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    profile.name,
                    style: TextStyle(fontSize: 32),
                  ),
                  Divider(
                    thickness: 2,
                  ),
                  Text(
                    "Information",
                    style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
                  ),
                  FadeInRight(
                    duration: Duration(milliseconds: 900),
                    child: Row(
                      children: [
                        Icon(Icons.email),
                        SizedBox(
                          width: 15,
                        ),
                        Text(
                          profile.email,
                          style: TextStyle(fontSize: 13),
                        ),
                      ],
                    ),
                  ),
                  FadeInRight(
                    duration: Duration(milliseconds: 900),
                    child: Row(
                      children: [
                        Icon(Icons.phone),
                        SizedBox(
                          width: 15,
                        ),
                        Text(
                          profile.phone,
                          style: TextStyle(fontSize: 13),
                        ),
                      ],
                    ),
                  ),
                  FadeInRight(
                    duration: Duration(milliseconds: 900),
                    child: Row(
                      children: [
                        Icon(Icons.pin_drop),
                        SizedBox(
                          width: 15,
                        ),
                        Text(
                          //바로출력 못해 DataType이라 .으로 접근
                          profile.address.city +
                              profile.address.street +
                              profile.address.suite,
                          style: TextStyle(fontSize: 13),
                        ),
                      ],
                    ),
                  ),
                  SizedBox(height: 7),
                  FadeInRight(
                    duration: Duration(milliseconds: 1100),
                    child: Divider(
                      thickness: 2,
                    ),
                  ),
                  SizedBox(
                    height: 5,
                  ),
                  Text("Company",
                      style:
                          TextStyle(fontWeight: FontWeight.bold, fontSize: 15)),
                  SizedBox(
                    height: 3,
                  ),
                  FadeInRight(
                      duration: Duration(milliseconds: 1100),
                      child: Text(profile.company.name)),
                  FadeInRight(
                      duration: Duration(milliseconds: 1100),
                      child: Text(profile.company.catchPhrase)),
                  FadeInRight(
                      duration: Duration(milliseconds: 1100),
                      child: Text(profile.company.bs)),
                ],
              ),
            ),
          )
        ],
      ),
    );
  }
}

model/profile.dart

import 'package:user/model/address.dart';
import 'package:user/model/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']), //Address라는 클래스를 넘겨주기로 했으니까
      phone: map['phone'],
      website: map['website'],
      company: Company.fromMap(map['company']),
    );
  }
}

model/address.dart

import 'package:user/model/geo.dart';

class Address {
  String street;
  String suite;
  String city;
  String zipcode;
  Geo 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: map['geo'] 안돼. 지금 Geo라는 클래스를 줘야 하는데 map이 넘어감
        //map을 연결 시켜주면 돼
        geo: Geo.fromMap(
            map['geo']) //map의 형태인 map['geo']를 가지고 Geo라는 클래스를 만들고 일치하게 만듦
        );
  }
}

model/company.dart

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']);
  }
}

model/geo.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']);
  }
}
⚠️ **GitHub.com Fallback** ⚠️