728x90

구글링이나 GitHub 샘플코드를 받아서 보면 Null Safety 이전과 이후의 버전에 따라 동작이 되기도 하고 안되기도 하더라.

라이브러리 버전을 최신버전으로 변경해보면 에러가 발생하는 것이 http, dio 부분에서 걸리는 거 같아서

dio 라이브러리에 대한 이해 부족으로 소스코드 변경에 시간 낭비를 하는 거 같아서 학습해 두고자 한다.

 

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  http: ^0.13.4  # 0.12.2 null safety 이전, 최신 버전 0.13.4
  dio: ^4.0.6   # 3.0.10 null safety 이전, 최신 버전 4.0.6

 

main.dart 파일 import 사항

import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:dio/dio.dart';

 

android/app/src/main/AndroidManifest.xml 파일 추가 사항

- 추가 안해도 개발 모드에서 잘 동작하는 걸 확인했다.

<uses-permission android:name="android.permission.INTERNET" />

 

서버에서 데이터 가져오고 파싱처리하는 코드

class _MyHomePageState extends State<MyHomePage> {
  Future getServerDataWithHttp() async {
    try {
      var jsonString = await http.get(
          Uri.parse('https://www.abc.com/androidSample/getData_Flutter.php'));
      if (jsonString.statusCode == 200) {
        print(jsonString);
        var resp = jsonDecode(jsonString.body);
        print(resp);
        UserResult userResult = UserResult.fromJson(resp);
        for (User user in userResult.result) {
          print(user.userNM);
        }
      }
    } catch (e) {
      print('error : ${e}');
    }
  }
 
  Future getServerDataWithDio() async {
    String url = 'https://www.abc.com/androidSample/getData_Flutter.php';
    BaseOptions options = BaseOptions(
      baseUrl: 'https://www.abc.com',
      connectTimeout: 3000,
      receiveTimeout: 3000,
    );
    Dio dio = Dio(options);
    try {
      Response resp = await dio.get(
        "/androidSample/getData_Flutter.php",
        //queryParameters: {"search": "dio"},
      );
      print("Response:");
      print("Status: ${resp.statusCode}");
      print("Header:\n${resp.headers}");
      print("Data:\n${resp.data}");
 
      UserResult userResult = UserResult.fromJson(resp.data);
      for (User user in userResult.result) {
        print(user.userNM);
      }
 
    } catch (e) {
      print("Exception: $e");
    }
 
 
  }
 
  @override
  void initState() {
    super.initState();
    getServerDataWithHttp();
    getServerDataWithDio();
  }
 
  @override
  Widget build(BuildContext context) {  }

 

 

서버에서 가져온 JSON 데이터 구조

{result: [
    {idx: 1, userNM: 개발자, mobileNO: 01000010001, telNO: 0234560001, photo: 1.jpg}, 
    {idx: 2, userNM: 이정은, mobileNO: 01001230001, telNO: , photo: 2.jpg}, 
    {idx: 3, userNM: 김홍길, mobileNO: 01001230002, telNO: , photo: }
  ]
}

 

 

 

class UserResult {
  final List<User> result;
 
  UserResult({required this.result});
 
  factory UserResult.fromJson(Map<String, dynamic> parsedJson) {
 
    var list = parsedJson['result'] as List;
    List<User> usersList = list.map((i) => User.fromJson(i)).toList();
 
    return UserResult(
      result: usersList,
    );
  }
}
 
class User {
  final int idx;
  final String userNM;
  final String mobileNO;
  final String telNO;
  final String photo;
 
  User(
      {required this.idx,
      required this.userNM,
      required this.mobileNO,
      required this.telNO,
      required this.photo});
 
  factory User.fromJson(Map<String, dynamic> parsedJson) {
    return User(
      idx: parsedJson['idx'],
      userNM: parsedJson['userNM'],
      mobileNO: parsedJson['mobileNO'],
      telNO: parsedJson['telNO'],
      photo: parsedJson['photo'],
    );
  }
 
  Map<String, dynamic> toJson() => {
    "idx": idx,
    "userNM": userNM,
    "moboileNO": mobileNO,
    "telNO": telNO,
    "photo": photo,
  };
}
 

factory는 싱글톤 패턴을 사용할 때 쓰는 예약어이다.

dart 공식문서에 새로운 인스턴스를 생성하지 않는 생성자를 구현할 때 factory 키워드를 사용하라고 명시되어 있다.

factory 의 특징

- 이전에 이미 생성된 인스턴스가 있다면 원래 값을 return하여 재사용한다.

- 하나의 클래스에서 하나의 인스턴스만 사용한다.

- 서브 클래스를 리턴할 때 사용할 수 있다.

- factory 생성자에서는 this에 접근할 수 없다.

 

 

 

서버에서 넘겨주는 데이터 형식을 아래와 같이 JSON 배열 데이터로 변경했다.

[
 {idx: 1, userNM: 개발자, mobileNO: 01000010001, telNO: 0234560001, photo: 1.jpg}, 
 {idx: 2, userNM: 이정은, mobileNO: 01001230001, telNO: , photo: 2.jpg}, 
 {idx: 3, userNM: 김홍길, mobileNO: 01001230002, telNO: , photo: }
]

 

DIO를 이용해 데이터를 변환하는 로직

Future getServerDataWithDio() async {
  BaseOptions options = BaseOptions(
    baseUrl: 'https://www.abc.com',
    connectTimeout: 3000,
    receiveTimeout: 3000,
  );
  Dio dio = Dio(options);
  try {
    Response resp = await dio.get(
      "/androidSample/getData_Flutter.php",
      //queryParameters: {"search": "dio"},
    );
    print("Response:");
    print("Status: ${resp.statusCode}");
    print("Header:\n${resp.headers}");
    print("Data:\n${resp.data}");
 
    List<User> users = resp.data.map<User>((parsedJson) {
      return User.fromJson(parsedJson);
    }).toList();
 
    for (User user in users) {
      print(user.userNM);
    }
 
  } catch (e) {
    print("Exception: $e");
  }
}

 

 

POST 방식으로 데이터 통신을 할 경우 코드 예제이다.

formData 변수 사용법으로 사용하니까 제대로 동작되더라.

Future getServerDataWithDio() async {
  BaseOptions options = BaseOptions(
    baseUrl: 'https://www.abc.com',
    connectTimeout: 3000,
    receiveTimeout: 3000,
  );
  Dio dio = Dio(options);
 
  var formData = FormData.fromMap({
    "search""이정은",
  });
 
  try {
    Response resp = await dio.post(
      "/androidSample/putData_Flutter.php",
      data: formData,
    );
    // print("Response:");
    // print("Status: ${resp.statusCode}");
    // print("Header:\n${resp.headers}");
    print("Data:\n${resp.data}");
 
    // UserResult userResult = UserResult.fromJson(resp.data);
    List<User> users = UserResult.fromJson(resp.data).result;
    for (User user in users) {
      print(user.userNM);
    }
  } catch (e) {
    print("Exception: $e");
  }
}
 

 

이해를 돕기 위해 서버 코드에 사용한 PHP 코드를 첨부한다.

<?php
if(!isset($_SESSION)) {
    session_start();
}
 
//ini_set("display_startup_errors", 1);
//ini_set("display_errors", 1);
//error_reporting(E_ALL);
 
// 파일을 직접 실행하면 동작되지 않도록 하기 위해서
if(isset($_POST) && $_SERVER['REQUEST_METHOD'== "POST"){
    @extract($_POST); // POST 전송으로 전달받은 값 처리
 
    require_once 'phpclass/dbconnect.php';
    require_once 'phpclass/loginClass.php';
    $c = new LoginClass();
    $column ="idx,userNM,mobileNO,telNO,photo";
    $sql = "select idx,userNM,mobileNO,telNO,photo from Person";
    if(!empty($search)) {
        $where = "userNM LIKE '%".$search."%' or mobileNO LIKE '%".$search."%'";
    } else {
        $where = "";
    }
 
    if(strlen($where) > 0){
        $sql .= " where ".$where;
    }
 
    $R = array(); // 결과 담을 변수 생성
    $result = $c->putDbArray($sql);
    while($row = $result->fetch_assoc()) {
        if($row['photo'== NULL) {
            $row['photo'= "";
        } else {
            $path = "./photos/".$row['photo'];
            if(!file_exists($path)) {
                $row['photo'= "";
            }
        }
        array_push($R, $row);
    }
    header("Cache-Control: no-cache, must-revalidate");
    header("Content-type: application/json; charset=UTF-8");
 
    echo json_encode(array('result'=>$R)); //배열-문자열등을 json형식의 '문자열'로 변환
}
?>

 

추가적으로 계속 유사 예제로 테스트하고 적어둘 예정이다.

 

'Flutter 앱 > Network' 카테고리의 다른 글

ListView.separated 예제  (0) 2023.11.20
Flutter Login Example  (0) 2022.07.25
Session vs JWT  (0) 2022.07.22
Flutter Login 로직 구현 예제 (오류 포함)  (0) 2022.07.22
Flutter DIO 라이브러리 예제2  (0) 2022.07.02
블로그 이미지

Link2Me

,