23/04/03(flutter+post+attr+user+boxoffice과제)

조영문·2023년 4월 3일
0

flutter

목록 보기
8/9

git

https://github.com/youngmoon97/flutter
파일 이름 : flutter_http_1 / flutter_http_2 / flutter_boxoffice / flutter_attr_busan

포스트 만들기

lib 폴더

MultiProvider / gorouter / Singleton / ChangeNotofier / factory / json /

jsonplaceholder

https://jsonplaceholder.typicode.com/posts

MVC 모델

#model

dto

post_dto.dart

//테이블 용
class PostDTOTable {
  //Post가 게시판을 뜻하는것임
  int userId; //유저번호
  int id; //글 번호
  String title; //제목

  PostDTOTable({required this.userId, required this.id, required this.title});

  //팩토리:생성자를 통해서 객체를 만들려고 하는데 처리가 번거로울 경우 사용함
  //어떠한 타입을 받아서 클래스 내부에서 객체를 생성해서 리턴 해주기 위함
  factory PostDTOTable.fromJson(dynamic json) => PostDTOTable(
        userId: json["userId"],
        id: json["id"],
        title: json["title"],
      );

  //모양은 화살표 함수와 비슷, 기능과 역할은 다르다.

  //팩토리 아님, 일반 함수임
  //JSON은 JSON인데 LIST타입의 데이터를 받는 JSON
  static List<PostDTOTable> fromJsonList(List jsonList) {
    return jsonList
        .map((json) => PostDTOTable.fromJson(json))
        .toList(); //괄호안에 e를 그대로 써도되고 json으로 바꿔도되고..
  }
}

//상세 페이지용
class PostDTODetail {
  int userId; //유저번호
  int id; //글 번호
  String title; //제목
  String body; //내용

  PostDTODetail(
      {required this.userId,
      required this.id,
      required this.title,
      required this.body});

  factory PostDTODetail.fromJson(dynamic json) => PostDTODetail(
        userId: json["userId"],
        id: json["id"],
        title: json["title"],
        body: json["body"],
      );
}

#repository

post_repository.dart

//post_repository.dart
import 'dart:convert';

import 'package:flutter_http_1/post/model/dto/post_dto.dart';
import 'package:http/http.dart' as http;

class PostRepository{
  //싱글톤 - 해당 타입의 객체가 프로그램에서 단 한개만 존재
  //스태틱 변수 선언
  static PostRepository? _instance;
  //첨에는 null이니까 ? 붙임. 지금은 인스턴스가 아니라 오브젝트 상태임

  //public 생성자 제거(public 생성자가 있으면 여러개 만들수 있어서 막아야됨)
  //dart에서 private는 맨 앞에 언더바(_)를 붙인다.
  PostRepository._canAnyName(){
    // count++;
    // print(count);
  } //생성자 이름은 아무거나 만들어도 상관없음 앞에다가 언더바 붙이기

  // static int count = 0;

  //변수 ?? 대체값
  //?? 뒤에는 변수가 null일 경우 대체값으로 들어가는(리턴되는) 내용
  //String str = [null일수도 있는 String]; 이면 에러나는데 String str = [null일수도 있는 String] ?? "값없음";은 가능
  //String str = "값없음";
  //계속 null이라서 부를때마다 새 객체가 호출됨
  //--------------------------------------------------------------------
  //변수 ??= 대입값
  //변수가 null일 경우 변수에 대입값을 넣고 리턴한다.
  //String str = [null일수도 있는 String] ??= "값이 없음";
  //String str = ["값이 없음"라는 값이 들어간 null일수도 있는 String];
  //위 단계에서 값이 들어가기 때문에 다음번 호출할때는 null 아님(새 객체는 한번만 생기고 다음부터는 원래 객체 불러져서 싱글톤 동작)

  //싱글톤 객체 getter
  static PostRepository get instance => _instance ??= PostRepository._canAnyName();

  //값을 넘겨주는 함수. 통신은 실패할 수 있으므로 nullable임
  Future<List<PostDTOTable>?> getDTOList() async{
    String url = "https://jsonplaceholder.typicode.com/posts";

    http.Response response = await http.get(Uri.parse(url));

      if(response.statusCode==200){ //http 상태 코드. 200은 정상 진행
       return PostDTOTable.fromJsonList(jsonDecode(response.body));
      }else{
        return null;
      }
    }
  }
  Future<PostDTODetail?> getDTO(int postId) async{
    String url="https://jsonplaceholder.typicode.com/posts/$postId";
    http.Response response = await http.get(Uri.parse(url));
  }

void main() {
  //PostRepository.instance; //생성자를 불러서 1 출력
  //PostRepository.instance; //생성자를 불러서 2 출력
  //PostRepository.instance; //생성자를 불러서 3 출력
  // (??대신 ??=이면 싱글톤이 적용되어서 여러번 불러도 1 한번만 출력됨)
}

#view

##pages

detail_page.dart

import 'package:flutter/material.dart';


class DetailPage extends StatelessWidget {
  const DetailPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Text("디테일 페이지");
  }
}

list_page.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_http_1/post/controller/post_table_controller.dart';
import 'package:flutter_http_1/post/model/dto/post_dto.dart';
import 'package:flutter_http_1/post/model/repository/post_repository.dart';
import 'package:flutter_http_1/routes.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';

class ListPage extends HookWidget {
  const ListPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    //더미 데이터
    //PostDTOTable postDTOTable = PostDTOTable(userId: 0, id: 0, title: "테스트 제목");

    //실제 데이터
    //통신은 실패할 수 있다 = nullable
    //controller 생성으로 주석처리댐
    //final listState = useState<List<PostDTOTable>?>(null);
    final controller = context.watch<PostTableController>();

    //final jsonState = useState<String?>(null);

    //useEffect(작동함수, 관찰할 상태 리스트);
    //빌드가 완료되면 작동한다
    //관찰하는 상태가 변경되면 작동한다
    //관찰하는 상태가 없으면 빌드시 한번 작동한다.
    useEffect((){
      controller.setPostDTOTableList();
    },[]);

    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: controller.postDTOTableList?.map((e) => ListItem(postDTOTable: e)).toList()?? [],
        ),
      ),
    );
  }
}

class ListItem extends StatelessWidget {
  PostDTOTable postDTOTable;

  ListItem({Key? key, required this.postDTOTable}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        context.pushNamed(Routes.detail);
      },
      child: Container(
        padding: EdgeInsets.all(10),
        decoration: BoxDecoration(
            border: Border.all(width: 2, color: Colors.black)),
        child: Column(
          children: [
            Text("유저번호 : ${postDTOTable.userId}"),
            Divider(),
            Text("글 번호 : ${postDTOTable.id}"),
            Divider(),
            Text("글 제목 : ${postDTOTable.title}"),
          ],
        ),
      ),
    );
  }
}

widgets

#controller

post_table_controller.dart

import 'package:flutter/foundation.dart';
import 'package:flutter_http_1/post/model/dto/post_dto.dart';
import 'package:flutter_http_1/post/model/repository/post_repository.dart';

class PostTableController extends ChangeNotifier{
  List<PostDTOTable>? _postDTOTableList;

  List<PostDTOTable>? get postDTOTableList => _postDTOTableList;

  void setPostDTOTableList(){
    PostRepository.instance.getDTOList().then((value){
      _postDTOTableList = value;
      notifyListeners();
    });
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_http_1/post/controller/post_table_controller.dart';
import 'package:flutter_http_1/post/view/pages/list_page.dart';
import 'package:flutter_http_1/routes.dart';
import 'package:provider/provider.dart';

void main(){
  // controller는 사용하기 전에 주입을 해줘야한다.
  // 프로젝트가 커지면 하나의 controller로만 사용하기 어렵다.
  // 주입을 해주는 ChangeNotifierProvider를 여러 개 사용하고 싶다. ->
  // MultiProvider를 사용한다.
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (BuildContext context)=>PostTableController()),
      ],
    child: MyApp(),
    )
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: Routes.goRouter,
      );
  }
}

routes.dart

import 'package:flutter_http_1/post/view/pages/detail_page.dart';
import 'package:flutter_http_1/post/view/pages/list_page.dart';
import 'package:go_router/go_router.dart';

class Routes {
  static const String table = 'table';
  static const String detail = 'detail';

  static final GoRouter goRouter = GoRouter(
    initialLocation: '/table',
    routes: [
      GoRoute(
        name: Routes.table,
        path: '/table',
        builder: (context, state) => ListPage(),
      ),
      GoRoute(
        name: Routes.detail,
        path: '/detail',
        builder: (context, state) => DetailPage(),
      ),
    ],
  );
}

USER


API :
https://jsonplaceholder.typicode.com/users

#controller

user_controller.dart

import 'package:flutter/foundation.dart';
import 'package:flutter_http_2/model/dto/user_dto.dart';
import 'package:flutter_http_2/model/repository/user_repository.dart';

class UserTableController extends ChangeNotifier{
  List<UserDTOTable>? _userDTOTableList;

  List<UserDTOTable>? get userDTOTableList => _userDTOTableList;

  void setUserDTOTableList(){
    UserRepository.instance.getDTOList().then((value){
      _userDTOTableList=value;
      notifyListeners();
    });
  }
}

#model

##dto

user_dto.dart

class UserDTOTable {
  int id;
  String name;
  String email;

  UserDTOTable({required this.id, required this.name, required this.email});

  factory UserDTOTable.fromJson(dynamic json) => UserDTOTable(
        id: json["id"],
        name: json["name"],
        email: json["email"],
  );

  static List<UserDTOTable> fromJsonList(List jsonList){
    return jsonList.map((json) => UserDTOTable.fromJson(json)).toList();
  }
}

class UserDTODetaiil{
  int id;
  String name;
  String email;
  String body;

  UserDTODetaiil({required this.id, required this.name,
    required this.email, required this.body});

}

##repository

user_repository.dart

import 'dart:convert';

import 'package:flutter_http_2/model/dto/user_dto.dart';
import 'package:http/http.dart' as http;

class UserRepository{
  static UserRepository? _instance;

  UserRepository._canAnyName(){

  }// 생성자

  static UserRepository get instance => _instance ??= UserRepository._canAnyName();

  Future<List<UserDTOTable>?> getDTOList() async{
    String url = "https://jsonplaceholder.typicode.com/users";

    http.Response response = await http.get(Uri.parse(url));

    if(response.statusCode == 200){
      return UserDTOTable.fromJsonList(jsonDecode(response.body));
    }else{
      return null;
    }
  }
}

void main(){
  //
}

#view

##pages

user_detail.dart

import 'package:flutter/material.dart';

class UserDetail extends StatelessWidget {
  const UserDetail({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Text("유저 디테일 페이지");
  }
}

user_page.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_http_2/controller/user_controller.dart';
import 'package:flutter_http_2/model/dto/user_dto.dart';
import 'package:flutter_http_2/model/repository/user_repository.dart';
import 'package:flutter_http_2/routes.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';

class UserPage extends HookWidget {
  const UserPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller = context.watch<UserTableController>();

    useEffect(() {
      controller.setUserDTOTableList();
    }, []);

    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: controller.userDTOTableList?.map((e) => UserItem(postDTOTable: e)).toList()?? [],
        ),
      ),
    );
  }
}

class UserItem extends StatelessWidget {
  UserDTOTable postDTOTable;

  UserItem({Key? key, required this.postDTOTable}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        context.pushNamed(Routes.detail);
      },
      child: Container(
        padding: EdgeInsets.all(10),
        decoration: BoxDecoration(
          border: Border.all(width: 2, color: Colors.black),
        ),
        child: Column(
          children: [
            Text("아이디 : ${postDTOTable.id}"),
            Divider(),
            Text("이름 : ${postDTOTable.name}"),
            Divider(),
            Text("이메일 : ${postDTOTable.email}"),
          ],
        ),
      ),
    );
  }
}

##widgets

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_http_2/controller/user_controller.dart';
import 'package:flutter_http_2/routes.dart';
import 'package:flutter_http_2/view/pages/user_page.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
            create: (BuildContext context) => UserTableController()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: Routes.goRouter,
    );
  }
}

routes.dart

import 'package:flutter_http_2/view/pages/user_detail.dart';
import 'package:flutter_http_2/view/pages/user_page.dart';
import 'package:go_router/go_router.dart';

class Routes {
  static const String table = 'table';
  static const String detail = 'detail';

  static final GoRouter goRouter = GoRouter(
    initialLocation: '/table',
    routes: [
      GoRoute(
        name: Routes.table,
        path: '/table',
        builder: (context, state) => UserPage(),
      ),
      GoRoute(
        name: Routes.detail,
        path: '/detail',
        builder: (context, state) => UserDetail(),
      ),
    ],
  );
}

BoxOffice

API :
http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt=20120101

#model

##dto

boxoffice_dto.dart

class BoxofficeDTOTable {
  String rank;
  String audiCnt;
  String movieNm;
  String openDt;

  BoxofficeDTOTable({
    required this.rank,
    required this.audiCnt,
    required this.movieNm,
    required this.openDt,});

  factory BoxofficeDTOTable.fromJson(dynamic json) =>
      BoxofficeDTOTable(
        rank: json["rank"],
        audiCnt: json["audiCnt"],
        movieNm: json["movieNm"],
        openDt: json["openDt"],
      );

  static List<BoxofficeDTOTable> fromJsonList(List jsonList) {
    return jsonList.map((json) => BoxofficeDTOTable.fromJson(json)).toList();
  }
}

class BoxofficeResult {
  String boxofficeType;
  String showRange;
  List<BoxofficeDTOTable> dailyBoxOfficeList;

  BoxofficeResult({
    required this.boxofficeType,
    required this.showRange,
    required this.dailyBoxOfficeList,
  });

  factory BoxofficeResult.fromJson(dynamic json) =>
      BoxofficeResult(
        boxofficeType: json["boxofficeType"],
        showRange: json["showRange"],
        dailyBoxOfficeList: BoxofficeDTOTable.fromJsonList(json["dailyBoxOfficeList"]),
      );

}

##repository

boxoffice_repository.dart

import 'dart:convert';

import 'package:flutter_boxoffiice/post/dto/boxoffice_dto.dart';
import 'package:http/http.dart' as http;

class BoxofficeRepository{
  static BoxofficeRepository? _instance;

  BoxofficeRepository._canAnyName(){

  }

  static BoxofficeRepository get instance => _instance ??= BoxofficeRepository._canAnyName();

  Future<BoxofficeResult?> getDTOList() async{
    String url="http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt=20120101";
    http.Response response = await http.get(Uri.parse(url));
    if(response.statusCode==200){
      var json = jsonDecode(response.body);

      //print(response.body);
      return BoxofficeResult.fromJson(json["boxOfficeResult"]);
    }else{
      return null;
    }
  }
}
void main(){

}

#view

##pages

boxoffice_list_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_boxoffiice/post/dto/boxoffice_dto.dart';
import 'package:flutter_boxoffiice/post/repository/boxoffice_repository.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class ListPage extends HookWidget {
  const ListPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final listState = useState<BoxofficeResult?>(null);

    useEffect(() {
      BoxofficeRepository.instance.getDTOList().then((value) {
        listState.value = value;
      });
    }, []);

    return Scaffold(
      body: SafeArea(
          child: Column(
            children: [
              Text(listState.value?.boxofficeType ?? ""),
              Divider(height: 1.0,color: Colors.black,thickness: 1.0),
              Text(listState.value?.showRange ?? ""),
              Expanded(
                child: ListView(
                  children: listState.value?.dailyBoxOfficeList.map((e) => ListItem(boxofficeDTOTable: e)).toList() ?? [],
                ),
              ),
            ],
          )
      ),
    );
  }
}

class ListItem extends StatelessWidget {
  BoxofficeDTOTable boxofficeDTOTable;
  ListItem({Key? key, required this.boxofficeDTOTable}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(10),
      decoration: BoxDecoration(
          border: Border.all(width: 2, color: Colors.black)),
      child: Column(
        children: [
          Divider(),
          Text("랭킹 : ${boxofficeDTOTable.rank}"),
          Divider(),
          Text("관객 수 : ${boxofficeDTOTable.audiCnt}"),
          Divider(),
          Text("영화 이름 : ${boxofficeDTOTable.movieNm}"),
          Divider(),
          Text("개봉 일 : ${boxofficeDTOTable.openDt}"),
        ],
      ),
    );
  }
}

widgets

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_boxoffiice/view/pages/boxoffice_list_page.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

Attr_Busan

API
https://apis.data.go.kr/6260000/AttractionService/getAttractionKr?serviceKey=L4O6Jd5locofQV0Sa674EwMQ4GyHi380DNlzkWVMQLw8O2LvzNMvBKe1RxTj4jssgmQKPrDvinJFtSOIs9KmbA%3D%3D&pageNo=1&numOfRows=10&resultType=json

attr_dto.dart

class AttrDTO {
  // MAIN_TITLE
  // CNTCT_TEL
  // MAIN_IMG_THUMB

  String mainTitle;
  String cntctTel;
  String mainImgThumb;

  AttrDTO({
    required this.mainTitle,
    required this.cntctTel,
    required this.mainImgThumb,
  });

  factory AttrDTO.fromJson(dynamic json) => AttrDTO(
        mainTitle: json["MAIN_TITLE"],
        cntctTel: json["CNTCT_TEL"],
        mainImgThumb: json["MAIN_IMG_THUMB"],
      );

  static List<AttrDTO> fromJsonList(List jsonList){
    return jsonList.map((e) => AttrDTO.fromJson(e)).toList();
  }
}

attr_item.dart

import 'package:flutter/material.dart';
import 'package:flutter_attr_busan/attr_dto.dart';

class AttrItem extends StatelessWidget {
  final AttrDTO attrDTO;
  const AttrItem({Key? key, required this.attrDTO}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(10),
      child: Column(
        children: [
          Image.network(attrDTO.mainImgThumb),
          Text(attrDTO.mainTitle),
          Text(attrDTO.cntctTel),
        ],
      ),
    );
  }
}

attr_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_attr_busan/attr_dto.dart';
import 'package:flutter_attr_busan/attr_item.dart';
import 'package:flutter_attr_busan/attr_repository.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class AttrPage extends HookWidget {
  const AttrPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final listState = useState<List<AttrDTO>>([]);

    useEffect(() {
      AttrRepository.getDTO().then((value) {
        listState.value = value ?? [];
      });
    }, []);

    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: listState.value.map((e) => AttrItem(attrDTO: e)).toList(),
        ),
      ),
    );
  }
}

attr_repository.dart

import 'dart:convert';

import 'package:flutter_attr_busan/attr_dto.dart';
import 'package:http/http.dart' as http;

class AttrRepository {
  static Future<List<AttrDTO>?> getDTO() async {
    final String url = "https://apis.data.go.kr/6260000/AttractionService/getAttractionKr?serviceKey=L4O6Jd5locofQV0Sa674EwMQ4GyHi380DNlzkWVMQLw8O2LvzNMvBKe1RxTj4jssgmQKPrDvinJFtSOIs9KmbA%3D%3D&pageNo=1&numOfRows=10&resultType=json";
    http.Response response = await http.get(Uri.parse(url));
    if(response.statusCode == 200){
      dynamic json = jsonDecode(response.body);
      return AttrDTO.fromJsonList(json["getAttractionKr"]["item"]);
    } else {
      return null;
    }
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_attr_busan/attr_page.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

0개의 댓글