2022년 10월 31일 월요일

[Flutter] android 릴리즈모드 빌드한 apk 를 스마트폰 설치 후, 네트워크 에러 해결방법

 Flutter 개발 하다보면, 릴리즈 모드로 빌드해서 apk 를 직접 스마트폰에 설치하는 일을 해 볼 것이다.


개발 모드 그러니까, 개발 피시에서 직접 폰에 바로 빌드를 하면, 앱에서 사용하는 네트워크동작이 잘 되는데, 

릴리즈 모드로 apk를 빌드해서 직접 스마트폰에 설치해서 실행해 보면,

로그인 페이지는 뜨지만 로그인되지 않고 네트워크오류가 발생 한다.


원인은 Internet 권한 문제 때문이다.




개발 모드에서 네트워크 연결이 잘 되는 이유는,

profile 폴더아래, AndroidManifest.xml 파일에, 이미 권한 허용 구문이 들어있기 때문이다.

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


이 구문이, main 폴더아래, AndroidManifest.xml 파일에 넣어 줘야한다.



해당 권한 구문을 넣으면 네트워크 오류 없이 개발모드와 동일하게 동작하는 것을 볼 수 있을 것이다.

2022년 10월 27일 목요일

[Flutter] Dart 문법설명 (try catch, rethrow, finally, assert, forEach, for in, continue, break)

Dart 문법설명을 해보자

중요해보이는 문법 위주로 정리한다.


try catch

//try catch
try {
var num = null;
num ++;
} catch (e) {
print('e : ${e.toString()}');
}
print('finish!');
=>
e : NoSuchMethodError: method not found: '$add' on null
finish!

catch 에 잡힌후, 다음 구문 실행 됨.


try catch rethrow

//try catch rethrow
try {
var num = null;
num ++;
} catch (e) {
print('e : ${e.toString()}');
rethrow;
}
print('finish!');
=>
e : NoSuchMethodError: method not found: '$add' on null
Uncaught TypeError: Cannot read properties of null (reading '$add')Error: TypeError: Cannot read properties of null (reading '$add')

rethrow를 만나고 프로그램 정지됨.


try catch finally

//try catch finally
try {
var num = null;
num ++;
} catch (e) {
print('e : ${e.toString()}');
rethrow;
} finally {
print('print finally!');
}
print('finish!');
=>
e : NoSuchMethodError: method not found: '$add' on null
print finally!
Uncaught TypeError: Cannot read properties of null (reading '$add')Error: TypeError: Cannot read properties of null (reading '$add')

rethrow를 만나도, finally 는 무조건 실행됨.


assert

//assert
var count1 = 10;
assert(count1 == 1); // => Exception has occurred

=>

Uncaught Error: Assertion failed

코드실행 하다 assert 문구 false 면 정지.


var count2 = 10;
assert(count2 == 10); // => pass
print('finish');
=>
finish

assert 문구  true이면 코드 통과


forEach

//forEach()
var collection1 = ['bmw', 'hyundai', 'kia', 'honda'];
collection1.forEach((element) {print('collection1 : ${element}');});
=>
collection1 : bmw
collection1 : hyundai
collection1 : kia
collection1 : honda


for in

//for in
var collection2 = ['bmw', 'hyundai', 'kia', 'honda'];
for(var element in collection2) {
print('collection2 : ${element}');
}


=>
collection2 : bmw
collection2 : hyundai
collection2 : kia
collection2 : honda


continue

//continue
var collection3 = ['bmw', 'hyundai', 'kia', 'honda'];
for(var i=0; i<collection3.length; i++) {
if(i>=2) continue; // => exit to for
print('i=${i}');
}
=>
i=0
i=1

for문에서 continue 를 만나면 for문을 빠져나감.


break

//break
var i = 0;
while(true) {
i++;
print('i=$i');
if(i>=2) break; // => exit to while
}
=>
i=1
i=2

while문에서 break를 만나면 while문을 빠져나감.



---- English ----



Let's explain Dart grammar

Focus on the most important grammar.


try catch

//try catch
try {
var num = null;
num ++;
} catch (e) {
print('e : ${e.toString()}');
}
print('finish!');
=>
e : NoSuchMethodError: method not found: '$add' on null
finish!

After being caught by catch , the next statement is executed.


try catch rethrow

//try catch rethrow
try {
var num = null;
num ++;
} catch (e) {
print('e : ${e.toString()}');
rethrow;
}
print('finish!');
=>
e : NoSuchMethodError: method not found: '$add' on null
Uncaught TypeError: Cannot read properties of null (reading '$add')Error: TypeError: Cannot read properties of null (reading '$add')

Meets rethrow and program halts.

try catch finally

//try catch finally
try {
var num = null;
num ++;
} catch (e) {
print('e : ${e.toString()}');
rethrow;
} finally {
print('print finally!');
}
print('finish!');
=>
e : NoSuchMethodError: method not found: '$add' on null
print finally!
Uncaught TypeError: Cannot read properties of null (reading '$add')Error: TypeError: Cannot read properties of null (reading '$add')

Even if rethrow is encountered, finally is executed unconditionally.


assert

//assert
var count1 = 10;
assert(count1 == 1); // => Exception has occurred

=>

Uncaught Error: Assertion failed

Execute the code and stop if the assert statement is false.


var count2 = 10;
assert(count2 == 10); // => pass
print('finish');
=>
finish

If the assert statement is true, the code will pass.


forEach

//forEach()
var collection1 = ['bmw', 'hyundai', 'kia', 'honda'];
collection1.forEach((element) {print('collection1 : ${element}');});
=>
collection1 : bmw
collection1 : hyundai
collection1 : kia
collection1 : honda


for in

//for in
var collection2 = ['bmw', 'hyundai', 'kia', 'honda'];
for(var element in collection2) {
print('collection2 : ${element}');
}


=>
collection2 : bmw
collection2 : hyundai
collection2 : kia
collection2 : honda


continue

//continue
var collection3 = ['bmw', 'hyundai', 'kia', 'honda'];
for(var i=0; i<collection3.length; i++) {
if(i>=2) continue; // => exit to for
print('i=${i}');
}
=>
i=0
i=1

If continue is encountered in the for statement, the for statement is exited.


break

//break
var i = 0;
while(true) {
i++;
print('i=$i');
if(i>=2) break; // => exit to while
}
=>
i=1
i=2

When a break is encountered in a while statement, the while statement is exited.




2022년 10월 26일 수요일

[Flutter] mutable 과 immutable 에 대해서

 Dart 언어에서 뿐만 아니라, 다른 개발 언어에서도, 마찬가지로 알고 가야 할 개념인

mutable과 immutable 개념을 알아보자


mutable  : 변경가능, immutable : 변경 불가능


예를 들어, 

class Car {
int? id;
String? model;

Car({this.id, this.model});
}

void main() {
Car car = Car(id:1, model:'m1');
Car car2 = car;

print('car, id=${car.id}, model=${car.model}');

car2.model = 'm2';

print('car2, id=${car.id}, model=${car.model}');
print('car, id=${car.id}, model=${car.model}');
}

결과값

car, id=1, model=m1
car2, id=1, model=m2
car, id=1, model=m2

car2 에 car를 받아서, car2의 변수를 변경했는데, car2 의 model 변수 뿐아니라,

car의 model 변수 값도 변경되었다.

이경우, car의 주소값이 car2에게 복사 된것이므로 같은 주소값을 가지고 있기때문이다, 그래서 car 객체 변수는 mutable 한 것 이다.


car2를 다른 주소값으로 갖게하게끔 Car객체를 생성하고 싶다면,

Car car2 = car;

대신, Car car2 = Car(id:1, model:'m2'); 라고 새로 객체를 생성해주면, car와 별개의 주소값

을 갖게된다.


다른예로,

final name1 = 'David';

const name2 = 'Paul';

둘다, immutable에 해당하는 변수 이다.

값을 할당하면, 다시 변경 불가능한 변수가 되게 final과 const를 붙였기 때문이다.ㅏ

final과 const는 값이 할당되는 시점의 차이가 있다.

final은 컴파일 이후, 런타임 에서 값을 할당 할 수 있다.

그러나 한번 할당 된 값은 변수가 초기화 되기전에는 다시 다른 값으로 변경불가하다.

const는 컴파일 될 때, 값을 할당되는 것이다. 따라서, 런타임 시에는 더이상 값을 할당할수 가 없다.


---- English ----


Not only in Dart language, but in other development languages as well, it is a concept that you need to know.


Learn the concepts of mutable and immutable


mutable : mutable, immutable : immutable


for example,

class Car {
int? id;
String? model;

Car({this.id, this.model});
}

void main() {
Car car = Car(id:1, model:'m1');
Car car2 = car;

print('car, id=${car.id}, model=${car.model}');

car2.model = 'm2';

print('car2, id=${car.id}, model=${car.model}');
print('car, id=${car.id}, model=${car.model}');
}

result

car, id=1, model=m1
car2, id=1, model=m2
car, id=1, model=m2


I received car in car2 and changed the variable of car2, not only the model variable of car2,

The value of the model variable of car has also been changed.

In this case, the car's address value is copied to car2, so it has the same address value, so the car object variable is mutable.


If you want to create a Car object so that car2 has a different address value,

Car car2 = car;

Instead, Car car2 = Car(id:1, model:'m2'); If you create a new object with

will have


In another example,


final name1 = 'David';

const name2 = 'Paul';

Both are variables that are immutable.


This is because when you assign a value, it becomes final and const so that it becomes an immutable variable again.


The difference between final and const is when a value is assigned.


final can be assigned a value at runtime after compilation.


However, the value assigned once cannot be changed to another value until the variable is initialized.


const is a value assigned at compile time. Therefore, it is no longer possible to assign a value at runtime.





2022년 10월 25일 화요일

[Flutter] 함수 파라미터 설명



Dart언어에서 함수 파라미터 사용방법을 설명한다.


Positional parameters : 함수 인자 위치대로, 파라미터를 입력해줘야한다.

Named parameters : 함수 인자를 위치에 상관없이, 파라미터명 이름으로 지정하여 값을 입력해 줘야한다.

optional : 함수에서 받는 파라미터와 상관없이 입력할 값을 생략할 수 있는 사항인 경우이다.

required : 반드시 해당 파라미터는 값을 입력해 줘야한다.



아래 예제 코드에서,
Positional parameters 의 경우에서, 파라미터가 optional 인지, required 인지 예제이며,

또는

Named parameters 의 경우에서, 파라미터가 optional 인지, required 인지 예제를 설명한

사항이다.


class Car {
int? id;
String? model;

// Positional parameters
// int id, String model => required
Car(int id, String model) {
this.id = id;
this.model = model;
}
}

class Train {
int? id;
String? model;

// Named parameters
// int? id, String? model => optional
Train({int? id, String? model}) {
this.id = id;
this.model = model;
}
}

class Ship {
int? id;
String? model;

// Named parameters
// required int id, required String model => required
Ship({required int id, required String model}) {
this.id = id;
this.model = model;
}
}

class Bike {
int? id;
String? model;
int? price;

// Positional parameters
// int id => required, [String? model, int? price = 1000] => optional
Bike(int id, [String? model, int? price = 1000]) {
this.id = id;
this.model = model;
this.price = price;
}
}

class Airplan {
int? id;
String? model;
int? price;

// Named parameters
// int id => required, {String? model, int? price = 3000} => optional
Airplan(int id, {String? model, int? price = 3000}) {
this.id = id;
this.model = model;
this.price = price;
}
}

void main() {
Car car = Car(1, 'm1');
print('Car, id=${car.id}, model=${car.model}');

Train train = Train(model: 't1');
print('Train, id=${train.id}, model=${train.model}');

Ship ship = Ship(model: 's1', id: 3);
print('Ship, id=${ship.id}, model=${ship.model}');

Bike bike1 = Bike(4, 'b1');
print('Bike1, id=${bike1.id}, model=${bike1.model}, price=${bike1.price}');
Bike bike2 = Bike(4, 'b1', 5000);
print('Bike2, id=${bike2.id}, model=${bike2.model}, price=${bike2.price}');

Airplan airplan = Airplan(4, model: 'b1', price: 4000);
print('Airplan, id=${airplan.id}, model=${airplan.model}, price=${airplan.price}');
}


--- English ---

Describes how to use function parameters in Dart language.




Positional parameters: You must input parameters according to the position of the function argument.


Named parameters: Regardless of the location of the function argument, you must specify the parameter name and input the value.


optional : This is a case where the value to be input can be omitted regardless of the parameters received from the function.


required : The parameter must be entered with a value.



In the example code below,

In the case of Positional parameters, an example of whether the parameter is optional or required,


or


In the case of named parameters, this is an example of whether a parameter is optional or required.

2022년 10월 24일 월요일

[Flutter] factory 싱글톤 클래스 테스트

 factory를 사용해서 싱글톤 클래스를 만들어 테스트 해보기.


생성자에 factory 문구 추가

class DB {
static final Map<String, dynamic> _table = <String, dynamic>{};
static final DB _db = DB._internal();

DB._internal();

factory DB() {
return _db;
}

set(String key, dynamic value) => _table[key] = value;
get(String key) => _table[key];
}

main 함수에서 위 DB클래스를 테스트

DB db1 = DB();
db1.set('id', '1');
db1.set('lang', 'korean');

print('db1_id: ${db1.get('id')}, db1_lnag: ${db1.get('lang')}');

DB db2 = DB();

print('db2_id: ${db2.get('id')}, db2_lnag: ${db2.get('lang')}');

실행 결과

I/flutter ( 7655): db1_id: 1, db1_lnag: korean

I/flutter ( 7655): db2_id: 1, db2_lnag: korean



_table은 DB 클래스에서 데이터 저장하는 static final Map 변수입니다.

_db는 DB 객체화 하며 내부 함수 호출하여 실행

main() 에서 DB객체 1번을 만들어 set 으로 데이터 넣어주고, 

print출력하고

DB 객체 2번을 만들어서 다시한번 print출력으로 DB 2번객체의 get 값을 확인하면,

DB객체1 번에 저장한 값이 가져옮을 알수 있다.

DB객체를 계속 생성해도, 처음 사용된 객체를 사용하고 있음을 확인할 수 있다.



--- english ---


Create and test a singleton class using factory.


Add factory statement to constructor

class DB {
static final Map<String, dynamic> _table = <String, dynamic>{};
static final DB _db = DB._internal();

DB._internal();

factory DB() {
return _db;
}

set(String key, dynamic value) => _table[key] = value;
get(String key) => _table[key];
}

Test the above DB class in the main function

DB db1 = DB();
db1.set('id', '1');
db1.set('lang', 'korean');

print('db1_id: ${db1.get('id')}, db1_lnag: ${db1.get('lang')}');

DB db2 = DB();

print('db2_id: ${db2.get('id')}, db2_lnag: ${db2.get('lang')}');

Execution result

I/flutter ( 7655): db1_id: 1, db1_lnag: korean

I/flutter ( 7655): db2_id: 1, db2_lnag: korean



_table is a static final map variable that stores data in DB class.

_db is a DB object and executed by calling an internal function

Create DB object No. 1 in main() and put data as set,

print out

If you create DB object 2 and check the get value of DB 2 object again with print output,

You can see that the value stored in DB object 1 can be moved.

Even if you continue to create DB objects, you can confirm that the first used object is being used.


2022년 10월 23일 일요일

[Flutter] Future, async, await 사용방법

 async 는 비동기 명령사용시 함수에 사용합니다.

비동기 명령은 네트워크통신으로 서버에서 데이터를 요청할 때 또는 delay time이 필요한 명령을 사용할때 사용합니다.

비동기 명령을 사용하는 이유는, 처리시간이 걸리는 명령(코드)에서 메인쓰레드를 잡아 기다리게하지 않고, 별도 쓰레드에서 처리하기 위해서 입니다. 비동기 명령을 통해, 메인쓰레드에서 하는 터치또는 화면표시 렌더링은 비동기명령 처리 시 기다리지 않고 처리됩니다.

await을 설명하겠습니다. 비동기 명령을 사용하는 함수에서 async를 붙여 사용하는데, 비동기명령을 실행 할 때 await를 명령 또는 함수 앞에 붙여주면, 해당 async 붙인 함수 내에서는 await 를 붙인 함수가 실행될때 까지 기다린 후, 다음 명령을 실행합니다.

Future 는 async 를 붙인 함수의 return값을 반환할때 반환형에 함께 붙여 사용합니다.


---- test code ----

void main() {

    print('test 1');

    print('name: ${getName()}');

    print('test 2');

}

Future<String> getName() async {

    print('test 3');

    var name = await requestName();

    print('test 4');    

    return name;

}

---- run result ----

test1

test2

test3

test4






---- english ----


async is used for functions when using asynchronous commands.


Asynchronous commands are used when requesting data from the server through network communication or when using commands that require delay time.


The reason for using asynchronous commands is to process them in a separate thread, rather than waiting for the main thread to wait for commands (code) that take time to process. With an asynchronous command, the touch or display rendering done in the main thread is processed without waiting when processing the asynchronous command.


Let me explain await. Async is used in a function that uses an asynchronous command. When executing an asynchronous command, if await is added before a command or function, the async function waits until the awaited function is executed and then executes the next command. .


Future is used together with the return type when returning the return value of a function to which async is attached.





2022년 10월 21일 금요일

[Flutter] 서버에 데이터 보내는 방법과 서버에서 데이터 받는 방법

✌ 안녕하세요.

Flutter는 Http post 방식으로 서버에 API 요청 할 때, 파라미터를 넘기는 방법에 대해 설명 합니다. 

http.post 함수를 사용할 때, body 에, 파라미터를 담아서 보냅니다.

json 형태로 작성하되, jsonEncode() 함수에 담아서 보냅니다.

예시) jsonEncode([{'id':1, 'date':'2022-10-21'}, {'id':1, 'date':'2022-10-21'}])


위 와 같이 Flutter에서 서버에 있는 API를 파라미터를 담아 요청 시, 파라미터가 API에 제대로 전달이 되어야만 합니다.

이때 서버의 API 수신 파라미터 수신객체가 Object 로 받으면, 파라미터의 포맷은 Map 형태가 됩니다.

서버의 언어가 JAVA인 경우, 그리고 Spring boot 에서 API를 만들고, 파라미터를 Object로 받으면, LinkedHashMap 형태로 들어옵니다. 

그러면 Map에서 각 항목을 key값으로 꺼내서, 사용해야합니다.

매번 Map에서  각 항목을 key값으로 꺼내와야 하는 번거로움이 생깁니다.

불편하다면, 다른 방법으로, 수신파라미터 형태를 Object로 받지말고 특정객체로 생성하는 걸로 받으면 됩니다.

예를들어, Searched.java파일을 만들어, 변수로, 수신 받을 key값으로 정의합니다.

그러면, Map 형태 대신, Searched 형태로 받을 수 있습니다.

다른 API함수에 대해서도 같은 Searched.dart 파일을 사용하기 위해, 요청하는 Flutter의 요청 함수에서 사용하는 모든 파라미터 를 Searched.java파일에 넣어놓으면 Searched.java 파일하나로 Flutter의 API 요청을 모두 받아서 처리할 수 있습니다.

😄 감사합니다.


2022년 10월 20일 목요일

[Flutter] Android Studio에서 에뮬레이터 사용 시 알면 도움이 되는 정보

😃 안녕하세요. 10.0.2.2 ip를 이야기 해 봅니다.

Android Studio 를 사용하면서, 에뮬레이터를 통해 빌드한 화면테스트를 합니다.

대부분의 앱은 서버와의 통신으로 데이터를 화면에 표시하는 모습입니다.

서버개발자가와 협업을 하면, 서버 개발자가 API를 개발하고 개발한 API를 앱 개발자가 사용합니다.

그러나, 서버개발과 앱개발을 혼자서 하는 경우도 많습니다. 

서버에서 동작하는 API를 개발 할때, 앱개발 요청 시 API 동작의 정상여부 및 필요사항을 모두 확인해서 수정 및 완료하고, 앱개발을 들어 갈 수 있다면, 정말 좋은 개발 프로세스 일 것입니다.

하지만, 모두 테스트하고 제대로 동작하는 API를 개발했더라도, 앱개발 시, API 요청 시에, 앱에서의 요청시 좀더 부드럽고 편리한 뷰가 보이게 될 수 있습니다.

그래서 앱과 서버의 서비스 동시에 디버깅하며 서버 서비스 개발도 보완하고, 앱도 더 잘 만들고 싶을 것입니다.

그러면 서버의 서비스하는 API를 localhost에서 디버깅모드로 피시내에서 실행 할 수 있다면, 편리하게 앱도 피시에서 실행하며 서버의 API 개발 디버깅과 앱 디버깅을 동시에 할 수 있을 것 입니다. 

그 때 로컬 피시에서의 디버깅 하는 앱에서 동일한 로컬 피시에서의 디버깅 하며 실행 시킨 서버의 API 호출 요청 시, 앱에서는 http://localhost:8080 또는 http://127.0.0.1:8080 과 같은 URL을 이용하고 싶을 것 입니다.

하지만, 앱에서 http://localhost:8080 또는 http://127.0.0.1:8080 이렇게 요청을 하면(하위 경로는 편의상 생략하여 설명중) 서버의 API로부터 호출 거절 수신을 앱에서 받게 될 것 입니다.

앱에서는 localhost 또는 127.0.0.1  대신, 10.0.2.2를 사용 해야 localhost또는 127.0.0.1 로 호출 하는 것 처럼 호출이 가능합니다.

따라서, 앱에서는 http://10.0.2.2:8080 이런 식 으로 로컬 피시내의 API를 호출 해야 합니다.

앱 개발에 사용해 보세요. 이상입니다.😉