23/03/27 flutter

서지우·2023년 3월 27일
0

Flutter

목록 보기
3/7

Go Router 패키지

flutter에서 페이지 간 이동 시 URL기반의 API를 이용해서 쉽게 이동할 수 있도록 도와주는 패키지이다.

Go Router 패키지 참고(1)
Go Router 패키지 참고(2)


flutter 복습

main.dart

화면이 출력되는 부분!
(주석으로 설명이 되어 있음)

import 'package:flutter/material.dart';
import 'package:fultter_basic_1/first_page.dart';
import 'package:fultter_basic_1/second_page.dart';
import 'package:fultter_basic_1/third_page.dart';

//메인함수
void main(){
  //플러터 프레임워크에 실행요청
  runApp(MyApp());
}

//위젯은 화면을 구성하는 요소
class MyApp extends StatelessWidget {
  //같은 타입의 위젯이 여러개 있을 경우
  //context가 구분할 수 있게 해줌 -> key
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home:FirstPage(),
      //SecondPage(),
      //ThirdPage(),
      //각 페이지 마다 다름.
    );
  }
}

first_page

first_page.dart를 만들어 main.dart에 build안에 home에서 출력해본다.(주석으로 설명)

// frist_page.dart

import 'package:flutter/material.dart';

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

  
  Widget build(BuildContext context) {
    // 통신을 받습니다.
    // 통신 데이터는 -> Json String
    // 통신에서 받아온 데이터를 파싱했다고 가정
    // 파싱 -> 데이터를 다른 형식으로 변경하는 것
    List<String> strList = ["첫번째", "두번째"];

    // 해당 데이터를 위젯으로 바꿔서 칠드런에 넣고 싶다
    // 방법 1. 직접 리스트를 만든다.
    // List<Widget> widgetList = [
    //   Text("첫번째"),
    //   Text("두번째"),
    // ];

    // 요소의 타입을 String에서 Widget으로 바꿀 함수
    final change = (String str) {
      return Text(str);
    };
    // 방법 2. 함수를 각각 요소에 적용한다.
    // List<Widget> widgetList =[
    //   change("첫번째"),
    //   change("두번째"),
    // ];

    // 방법 3.
    // list메소드 map은 리스트의 요소를 다른 타입으로 변경할 수 있다
    // map이 요소 각각을 change함수를 사용해서 Widget으로 변경
    // 리스트로 다시 만들어줌
    List<Widget> widgetList = strList.map(change).toList();


    return Scaffold(
      body: SafeArea(
        child: Column(
          // children은 위젯 리스트를 받는다.
          children: widgetList,
        ),
      ),
    );
  }
}

실행하면 아래와 같이 출력됨.


second_page

second_page.dart를 만들어 main.dart에 build안에 home에서 출력해본다.(주석으로 설명)

import 'package:flutter/material.dart';

//받아온 데이터를 필터링해서 변겅
//1 필터링(where) / 2 변경(map)
class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    //받아온 데이터
    List<String> strList = ["apple", "google", "naver"];

    //a라는 글자가 들어간 str만 필터링 하고 싶음
    //Text위젯 리스트로 만들고 싶음

    //String을 받아서 a가 들어있는지 확인해서
    //있으면 true / 없으면 false를 리턴하는 함수
    final filter = (String str){
      return str.contains("a");
    };

    //Stringㅇ르 Widget으로 변경하는 함수
    //같은 Widget을 리스트로 사용해서 출력할 경우
    //key를 넣어 줘야 한다 -> 같은 애들끼리 구분하기 위해서
    final change = (String str) {
      return Text(str, key: UniqueKey(),);
    };

    List<Widget> widgetList = strList.where(filter).map(change).toList();

    return Scaffold(
      body: SafeArea(
        child: Column(
          children: widgetList,
        ),
      ),
    );
  }
}

실행하면 'a'가 포함되어 있는 단어들이 출력됨.


third_page

Second_page.dart를 만들어 main.dart에 build안에 home에서 출력해본다.(주석으로 설명)

import 'package:flutter/material.dart';

//객체 리스트를 받아서 화면 구성
class ThirdPage extends StatelessWidget {
  const ThirdPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    //파싱된 데이터
    List<Post> postList = [
      Post(userId: 0, id: 0, title: "제목1", body: "내용1"),
      Post(userId: 0, id: 0, title: "제목2", body: "내용2"),
    ];

    //map에서 이용할 요소를 바꾸는 함수
    final change = (Post post){
      return Row(
        children: [
          Text("유저번호 : ${post.userId} | "),
          Text("글번호 : ${post.id} | "),
          Text("제목 : ${post.title} | "),
          Text("내용 : ${post.body}"),
        ],
      );
    };

    return Scaffold(
      body: SafeArea(
        child: Column(
          children: postList.map(change).toList(),
        ),
      ),
    );
  }
}

class Post {
  int userId;
  int id;
  String title;
  String body;

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

void main(){
  Post post1 = new Post(
    userId: 0,
    id: 0,
    title: "제목",
    body: "내용"
  );
}

실행하면 파싱한 데이터들이 저장된 change가 출력됨.


http 통신

http 통신 설명 참고


map 함수

key-value가 한 쌍으로 여러 쌍이 배열로 이루어진다.

Map<String, dynamic> map = {"a":1,"abc":"aaa"};

기본형은 위와 같이 선언되며 좌측에는 key값, 우측에는 value값이 된다.
즉, key값을 호출해서 value를 받아올 수 있다.

map 함수 참고

자료구조

Map
(key:value 형식의 배열)

List가 가지고 있는 메소드
map
(기존 데이터를 다른 타입으로 변경하는 함수)

둘은 다름

map함수가 그래도 이해안되면 아래 코드 참고

class Company {
  String name;

  Company({required this.name});
}

void main(List<String> args) {
  List arr = ["apple", "naver", "google"];

  // 요청사항이 들어옴
  // Company 타입의 배열로 만들어주세요

  // 1번 방식 - 수작업 (요소가 많을 경우 너무 오래걸림)
  List<Company> newArr1 = [
    Company(name: "apple"),
    Company(name: "naver"),
    Company(name: "google"),
  ];

  // // 2번 방식 - 새 리스트 만들고 반복문으로 넣어주기
  List<Company> newArr2 = List.empty(growable: true);

  for (var i = 0; i < arr.length; i++) {
    newArr2.add(Company(name: arr[i]));
  }

  // 3번 방식 - map 사용
  // map은 매개변수로 받은 함수 (item) => Company(name: item) 를 이용해서
  // 각 요소들을 String -> Company 타입으로 변경
  List<Company> newArr3 = arr.map((item) => Company(name: item)).toList();
}

통신 단계별로 만들어보기

jsonplaceholder의 통신웹페이지처럼 userId와 id, title, body를 flutter로 구현해 본다.


프로젝트 및 파일 생성

flutter_http_1의 이름으로 새로운 flutter_project를 만든다.
(해당설정은 그림과 같이)

test폴더아래의 생성되는 파일은 삭제해 주고, pubspec.yaml파일로 들어가 sdk: flutter가 적혀있는 아래에 줄을 맞추어 해당 코드를 적어준다.
마지막 Pub get을 꼭 해준다.

  http:
  flutter_hooks:
  provider:
  go_router:

lib폴더 아래에 list_page.dart와 detail_page.dart 파일을 생성해 주고, main.dart의 있는 내용은 삭제해준다.


main.dart

Stateless로 만들어 준다.
(주석으로 설명)

import 'package:flutter/material.dart';
import 'package:flutter_http_1/list_page.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ListPage(),
      //home:은 불러와서 실행할 class명을 적으면 됨.
    );
  }
}

post_dto.dart

게시판처럼 만들기 위해 작성해 준다.

//post_dto.dart

//테이블 용
class PostDTOTable {
  int userId; //유저 번호
  int id; //글번호
  String title; //제목
  //{}로 감싸면 required가 적혀짐.
  PostDTOTable({required this.userId, required this.id, required this.title});
}

//상세 페이지 용
class PostDTODetail {
  int userId;
  int id;
  String title;
  String body;

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

alt + insert를 눌리면 생성자를 자동 만들어준다.


list_page.dart

설명은 주석으로 되어 있다.

//list_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_http_1/post_dto.dart';
import 'package:http/http.dart' as http; //useEffect사용하기전에 import함.
//as -> 패키지를 http이름으로 사용하겠다는 말

class ListPage extends HookWidget {//HookWidget으로 바꿔줌
  const ListPage({Key? key}) : super(key: key);

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

    //실제 데이터
    //통신은 실패할 수 있다 = nullable
    final listState = useState<List<PostDTOTable>?>(null);
    //?를 넣어줘야 null이 될 수 있음(java랑 다름)
    final jsonState = useState<String?>(null);

    //useEffect(작동함수, 관찰할 상태 리스트);
    //빌드가 완료되면 작동한다
    //관찰하는 상태가 변경되면 작동한다
    //관찰하는 상태가 없으면 빌드 시 1번 작동한다
    useEffect((){
      String url = "https://jsonplaceholder.typicode.com/posts";
      http.get(Uri.parse(url)).then((response) {
        jsonState.value = response.body;
      });
    },[]);

    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: [
            Text(jsonState.value ?? "값이 없습니다"),//null이면 값이 없다고
          ],
        ),
      ),
    );
  }
}

class ListItem extends StatelessWidget {
  PostDTOTable postDTOTable;

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

  
  Widget build(BuildContext context) {
    return 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}"),
          Divider(),
        ],
      ),
    );
  }
}

웹으로 실행하면 아래의 화면과 같이 출력된다.

통신(코드해석)

->문자열이 날아옴
->문자열을 파싱
(내가 필요한 타입으로 변경)
->jsonDecode
->dynamic
(dynamic 모든  타입을 받을 수 있음)
(dynamic 모든 타입으로 변할 수 있음)
(개발자 알아서 파싱해서 쓰라고)
->리스트로 캐스팅(형변환)
->DTO객체 생성 json데이터의 값을 넣어줌
->DTO를 편하게 쓰기 위해 factory 사용

list_page.dart(수정)

json과 관련된 것은 다 주석처리했다.
(설명은 역시나 주석으로 함)

//list_page.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_http_1/post_dto.dart';
import 'package:http/http.dart' as http; //useEffect사용하기전에 import함.
//as -> 패키지를 http이름으로 사용하겠다는 말

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

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

    //실제 데이터
    //통신은 실패할 수 있다 = nullable
    final listState = useState<List<PostDTOTable>?>(null);
    //?를 넣어줘야 null이 될 수 있음(java랑 다름)
    // final jsonState = useState<String?>(null);

    //useEffect(작동함수, 관찰할 상태 리스트);
    //빌드가 완료되면 작동한다
    //관찰하는 상태가 변경되면 작동한다
    //관찰하는 상태가 없으면 빌드 시 1번 작동한다
    useEffect((){
      String url = "https://jsonplaceholder.typicode.com/posts";
      http.get(Uri.parse(url)).then((response) {

        //추가(
        //정사적으로 받아왔는지 체크
        //200은 정상 응답
        if(response.statusCode == 200) {
          //string을 json형식으로 파싱
          dynamic decodedBody = jsonDecode(
              response.body); //아무거나 받을 때 dynamic으로 함.
          //json을 Map List로 캐스팅
          List jsonList = decodedBody as List;
          //list가 됐긴 때문에 아래에 list map을 할 수 있음
          //List를 map 함수로 풀어서 요소를 PostDTOTable로 변경
          //state에 입력
          listState.value = jsonList.map((data) {
            return PostDTOTable(
                userId: data["userId"], id: data["id"], title: data["title"]);
          }).toList();
        }

        //jsonState.value = response.body;
      });
    },[]);

    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: listState.value?.map((e) => ListItem(postDTOTable: e)).toList() ?? [],
            // Text(jsonState.value ?? "값이 없습니다"),//null이면 값이 없다고
        ),
      ),
    );
  }
}

class ListItem extends StatelessWidget {
  PostDTOTable postDTOTable;

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

  
  Widget build(BuildContext context) {
    return 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}"),
          Divider(),
        ],
      ),
    );
  }
}

실행을 하게 되면 아래와 같이 출력된다.

위와 다른 점이라면 여기는 게시판 형식으로 출력되는 것이다.


스스로 만들어 보기

다음 jsonplaceholder에서 id,name,email만 가져와서 만들어 출력해본다.

flutter_http_myself로 project를 만든다.
(나머지 파일들의 설정과 이름은 위와 똑같이 만든다.)


main.dart

import 'package:flutter/material.dart';
import 'package:flutter_http_myself/list_page.dart';

void main(){
  runApp(MyApp());
  //실행할 수 있게 runApp을 해줌.
}

//StatelessWidget으로 한다
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

post_dto.dart

class PostDTOTable{
  //id, name, email의 타입을 지정해 생성자 만들어 줌
  int id;
  String name;
  String email;

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

list_page.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_http_myself/post_dto.dart';
import 'package:http/http.dart' as http;

class ListPage extends HookWidget {//HookWidget을 바꿈
  const ListPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    final listState = useState<List<PostDTOTable>?>(null);
    //실패하면 null이 나오게

    useEffect((){
      String url = "https://jsonplaceholder.typicode.com/users";
      //id,name,email 예시의 json링크를 넣음
      http.get(Uri.parse(url)).then((response){
        if(response.statusCode == 200){//200은 정상응답
          dynamic decodeBody = jsonDecode(response.body);
          List jsonList = decodeBody as List;
          listState.value = jsonList.map((data){
            return PostDTOTable(id: data["id"], name: data["name"], email: data["email"]);
          }).toList();
        }
      });
    },[]);

    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: listState.value?.map((e) => ListItem(postDTOTable: e)).toList() ?? [],
          //children 안에 적어줘야 나옴.
        ),
      ),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    return 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}"),
          Divider(),
        ],
      ),
    );
  }
}

아래의 화면과 같이 출력된다(성공!!!!!!!!)

git

git 주소

0개의 댓글