DART <-> PHP
//Member data class
class MemberModel {
int id;
String email;
String pw;
String ip;
String datetimeadd;
MemberModel(
{ required this.id,
required this.email,
required this.pw,
required this.ip,
required this.datetimeadd});
factory MemberModel.fromJson(Map<String, dynamic> json) {
return MemberModel(
id: json['id'],
email: json['email'],
pw: json['pw'],
ip: json['ip'],
datetimeadd: json['datetimeadd'],
);
}
}
[기존 php 웹서버에서 질의결과 Row 데이타가 없을때 {} 또는 nodata 문자열이 리턴 되는 조건을 가정]
서버 API SELECT에서 리스트데이타 받을 때
1) 여러행이 List(memberModer) 형태로 수신되는 경우
질의데이타가 리턴된다는 전제로
return (json.decode(response.body) as List).map((i) =>MemberModel.fromJson(i)).toList();
또는 같은형태의
var jsonResponse = json.decode(response.body) as List<dynamic>;
return jsonResponse
.map<MemberModel>((data) => MemberModel.fromJson(data))
.toList();
이렇게 받을수도 있을것 같으나,
2) 해당되는 자료가 없을때 서버API에서 {} 또는 nodate 문자열이 리턴되는 경우라면 위 방법 불가
{}은 문자열로 List가 아님에도 강제 캐스팅(as List) 되고 있음 -> '자료없음' 체크도 불가능하고 오류 유발
[php 웹서버에서 데이타가 없을때 {} 또는 nodata 문자열이 리턴 되는 조건을 가정]
header('Content-Type: application/json; charset=UTF-8');
$json = json_encode('{}', JSON_PRETTY_PRINT); //{} 문자열 리턴 -.-
echo $json;
※ 보통(정상)의 경우는 웹서버에서 비어있는 [] <- array를 리턴하면 됨.
header('Content-Type: application/json; charset=UTF-8');
$json = json_encode([], JSON_PRETTY_PRINT); //빈 JSON 리턴
echo $json;
3) 해결(list 인지 문자열 인지 판단)
단 1개행이라도 수신이되면 List 타입이고, 해당 행이 없어 {} 공백이 수신되면 문자열 타입임.
List 타입이면 List add 해서 전체행 다음로직에 리턴, {}공백이면 List add 없이 List<MemberModel> memberList = []; 리턴
즉, List<MemberModel> 자료형 변형 없이 다음 로직에 전달.
[질의 데이타 리턴 된 경우]
[ {} 리턴 된 경우]
Dart Type 확인 예제
main() {
var parsed = "nodate";
if (parsed is List) {
print("parsed is List!!");
} else {
print("parsed is String!");
}
//parsed is String!
var parsed2 = [1, 2, 3];
if (parsed2 is List<dynamic>) {
print("parsed is List!!");
} else {
print("parsed is String!");
}
//parsed is List!!
}
▼ 위와 같이 Type 확인 적용
List<MemberModel> memberList = [];
....
....
if (response.statusCode == 200) { //서버요청 성공:200
var parsed = json.decode(response.body);
if (parsed is List<dynamic>) {
for (var member in parsed) {
memberList.add(MemberModel.fromJson(member));
}
}
return memberList;
[웹에서 JSON 문자열 -> DATA로 받기]
class FutureFetchService {
Future<List<MemberModel>> fetchMember() async {
List<MemberModel> memberList = [];
Map formdata = {
'email': 'love2@daum.net',
'pw': '12345',
'token': 'eyJhbGciOiJI....5.aWwuY29t....d300fa99660e763056e54dd'
};
var body = json.encode(formdata);
var response = await http.post(url,
headers: {"Content-Type": "application/json"}, body: body);
if (response.statusCode == 200) { //서버요청 성공:200
var parsed = json.decode(response.body);
if (parsed is List<dynamic>) {
for (var member in parsed) {
memberList.add(MemberModel.fromJson(member));
}
}
return memberList;
//return (json.decode(response.body) as List).map((i) =>MemberModel.fromJson(i)).toList();
} else {
//Exception
throw Exception('Failed to load');
}
}
}
//_ListViewPageState
class _ListViewPageState extends State<ListViewPage> {
late Future<List<MemberModel>> _members;
@override
void initState() {
super.initState();
_members = FutureFetchService().fetchMember();
}
...
[일반적인 보통(정상)의 조건일 경우]
웹서버(php)에서 200(정상처리) 상태값과 함께 List json 리턴(질의결과가 없을 경우 빈array 인 [] 리턴함)
$result = $stmt->fetchAll(PDO::FETCH_OBJ); //질의결과가 없으면 array(0)
$response->getBody()->write(json_encode($result));
return $response
->withHeader('content-type', 'application/json')
->withStatus(200);
▼
플러터(Dart)에서 200(정상처리) 응답을 받으면, List 질의결과가 있든지 없든지 상관없이 받음.
class FutureFetchService {
Future<List<MemberModel>> fetchMember() async {
final queryParameters = {
'email': '11@daum.net',
'pw': '11111',
'tk': '111111....dX9Hs'
};
var url = Uri.http('localhost', '/restapi/public/s/100', queryParameters);
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = json.decode(response.body) as List<dynamic>;
return jsonResponse
.map<MemberModel>((data) => MemberModel.fromJson(data))
.toList();
} else {
throw Exception('Failed to load');
}
}
}
[다른이야기]
//Member data class
int id;
String email;
String pw;
String ip;
String datetimeadd;
을 아래 처럼 final 로 선언하게 되면...
final int id;
final String email;
final String pw;
final String ip;
final String datetimeadd;
서버에서 받아온 데이타를 플래터 FutureBuilder(..) 에서 snapshot 으로 받아와 ListView.builder(...) 에 적용 한 후
return FutureBuilder(
future: _members,
builder: (context, AsyncSnapshot<List<MemberModel>> snapshot) {
아래 snapshot.data![index].email = "change"; 처럼 텍스트 변경할 때 이미 초기화(initialized) 되어 있다고 나오며 변경이 불가해짐.
child: GestureDetector(
onTap: () {
setState(() {
//snapshot.data?.removeAt(index);
snapshot.data![index].email = "change";
});
}, // Image tapped
child: Image.asset(imageList[index]),
)