
Dart 는 ui에 최적화된 개발환경을 가지고있으며 상당히 빠르다는 장점이있다.
Dart 의 종류에는 dart web , dart native 가 존재한다.
Dart 를 사용하여 IOS,Android,Windows,Linux,Mac 으로 컴파일이 가능하다.
기존의 C++,C,GO 와 같은 언어들로 코딩할때는 AOT 방법으로 입력된 언어를 기계어로 컴파일 해야 했지만 이는 UI 에 대한 작업을 할때 하나의 버튼 수정만해도 계속해서 처음부터 컴파일해야 하기 때문에 Dart 는 JIT 방법 또한 채택 하였다.
이는 Dart Vm을 사용해 개발자가 쓴 코드의 결과를 바로 화면에 보여준다.하지만 결국 개발이후 배포 과정에선 AOT를 이용한 컴파일을 해야한다.
Dart 를 체험하기 위해 dartpad.dev 링크로 들어가 시작해본다.
🚨🚨 주의사항 🚨🚨
1.Dart는 main mathod 를 중점으로 내부코드를 작성해 동작한다.
2.Dart 에서 세미콜론을 안쓰는 기능이 있기때문에 세미콜론이 자동적으로
찍히지않아 유의해야한다.
메소드 내부에서 사용할때 var를 통해 자동적으로 명시되는 경우
var name = '이름' ;
name = '김김김';
class 에서 변수나 property를 선언할때 타입을 명시하는 경우
String name = '이름';
name = '김김김' ;
var와 동일하게 동작되는 동적타입 변수이다.
외부에서 데이터를 받았을때 그 데이터를 필터링 할수있는 느낌이다.
dynamic name;
if(name is String){
name.메소드
}
if(name is boolean){
name.메소드
}
null이 될수도 있는 값을 뜻한다.
첫번째의 경우 에러가 발생해 String 뒤에 ? 를 명시하여 nullable 를 나타낸다.
두번째와 세번째의 코드는 동일하게 작동하며 세번째의 코드가 Dart의 장점을 활용해
단축 시켰다 볼수있다.
String name ='김김김';
name = null;
String? name ='김김김';
name = null;
if (name != null){
nico.isNotEmpty;
}
String? name ='김김김';
name = null;
name?.isNotEmpty
var 대신 final 로 변수를 지정해주면 변수타입을 변경할수 없게 설정이 가능하다.
final name ='김김김';
name = null;
late 는 final 이나 var 앞에 붙을수있는 수식어이다.
late는 초기 데이터 없이 변수를 선언할 수 있게 해준다.
예를들어 변수를 미리 선언하고 후에 api등으로 데이터를 가져올때 유용하다.
데이터가 들어오지 않은 상태에서 변수 사용이 불가능 하기때문에 실수 방지가 가능하다.
late final String name ;
// do something. go to api
print(name); // 에러!!
name = '김김김';
print(name); // 정상작동!
Dart 의 const 는 Js 의 const등과 다르다 오히려 final 이 유사하다.
사용자의 동작에 따라 api값을 받아 변수에 저장할때는 final 을 사용하고
const는 앱을 배포하기전 컴파일 할때 알고있는 값에 사용하는 것이다.
여러 데이터 타입이 존재한다.(num 은 double과 int의 부모 타입)
String name="김김김";
int age = 12;
double money = 22.22;
num x =12;
x = 1.1;
List 는 배열이고 Colliection If는 Dart에서 사용되는 간단한 조건문이다.원래는 numbers.add(5)와 같은 if안에 문법을 사용해야하는것을 간단히 작성 가능하다.
var numbers = [1,2,3,4,5,];
List<int> numbers2 =[1,2,3,4,5,]; // 동일한 동작
var giveFive = true;
var numbers = [1,2,3,4,if(giveFive) 5,]; //결과 1,2,3,4,5
변수값 을 보여주기 위해서 $를 사용한다.만약 변수값의 계산을 하고싶으면
${} 를 사용하여 나타내준다.
var age = 24;
var name='김김김';
var write= "Hello $name I\'m ${age+3}" ;
print(write); //결과 Hello 김김김 I'm 27
Collection if 와 마찬가지로 list와 같은 변수내용에 직접적으로 간단하게
변화를 줄수있는 방법이다.for(var ooo in xxx)"😃 $ooo", 이 한줄의
코드로 인해 newBro의 리스트에 oldBro를 포함 시키고 구분짓는다.
var oldBro=['이이이','감감감'];
var newBro=['니니니','누누누','나나나',
for(var bro in oldBro) "😃 $bro",
];
print(newBro);
map을 사용해 object형태의 변수틀 을 만들수있다.
var 타입으로 생성하면 역시 자동으로 key,value 형태의 타입을 설정하고
map 을사용해 직접적으로 데이터 타입을 명시할수도 있다.(List 와 혼합가능)
var player = {
'name': '김김김',
'xp': 12.22,
'seqw': false,
};
Map<String,Object> player = {
ddd:'nico',
eee: 1234,
ccc: false,
};
player.containsKey() //메소드
set은 List와 비슷하지만 요소가 하나씩 있어야 되는 경우에 사용한다 이유는 unique한 속성을 가지기 때문에 중복이 어렵기 때문이다.
{} 를 사용한다면 set 이고 [ ] 를 사용한다면 List이다.
set<int> numbers ={1,2,3,4,};
var numbers2 ={1,2,3,4,};
// 위 set 아래 list
List<int> numbers3 = [1,2,3,4,];
var numbers4 =[1,2,3,4,];
다른 함수에서 parameter값을 가져올때 보통의경우 아래와 같이 사용한다.
String sayHello(String name) {
return "Hello $name nice to meet you!";
}
void main() {
print(sayHello('김김김'));
}
하지만 만약 복잡하지 않은 간단한 함수라면 fat arrow syntax방법으로 표현이 가능
num sayNum(num a,num b ) => a+b;
void main() {
print(sayNum(1,2));
}
Parameters의 값이 여러개일때 코드가 복잡해질수 있기 때문에 정리를하여
나열 하는 name argument를 사용한다. (🚨🚨 반드시 매개변수에 {})
String SayHello({String name, int age, String country}){
return"$name , $age, $country";
}
void main() {
print(sayHello(
age:12,
country:'korea',
name:'김김김',)
);}
하지만 이런 경우 요소에 값을 넣지 않으면 nullSafety 가 발생한다.때문에 아래 Default Value나 required modifier의 방법중 하나를 선택해야한다.
required modifier방법은 요소중의 하나라도 선언하지 않으면 에러가 발생하기 떄문에 null값을 방지 할수있다,
/// Default Value : 기본값 설정
String SayHello(
String name='good',
int age=14,
String country='korean'
) {
return"$name , $age, $country";
}
void main() {
print(sayHello());}
/// required modifier 방법
String SayHello(
required String name,
required int age,
required String country'
) {
return"$name , $age, $country";
}
void main() {
print(sayHello());} // error 발생!!!
생략
영상
이유: 가시성이 떨어지고 name arguments 보다 이점이 없음
기존의 코드에서 매개변수에 대한 조건연산은 삼항연산자 조건문으로 많이 사용되었다.
String capitalizeName(String? name) =>
name!= null ? name.toUpperCase(): 'NON';
//null값이 아닐경우 대문자변환 맞을경우 NON
void main(){
capitalizeName('ddd');
capitalizeName(null);
}
여기서 ?의 특성을 활용하면 null값과 null이 아닌값을 구분할수있다.
String capitalizeName(String? name) =>
name?.toUpperCase() ?? 'NON';
//null값이 아닐경우 대문자변환 맞을경우 NON
void main(){
capitalizeName('ddd');
capitalizeName(null);
}
fat arrow 와 ?의 특성 그리고 QQ Operator를 이용해 기존의 다른 언어들의
코드보다 간결하게 표현이 가능하며 아래와 같은 코드도 가능해진다.
void main() {
String? name; //null = x
name ??= 'qqq'; //if(name=null)=> name='qqq'
name = null; //if(name!=null)=> name= null
name ??= 'ddd'; //if(name=null)=> name='ddd'
print(name);
}
자료형을 간편하게 작성할수있게 alias 쉽게말해 자료형의 별명을 만들어준다.
예를들어 아래와 같이 list 값을 받아 반대로 저장하는 함수가 있을때 새로운
Typdef 의 alias 를 만들어 대체하여 사용이 가능하다.
List<int> reverse(List<int> list) {
var reversed = list.reversed;
return reversed.toList();
}
void main() { print(reverse([1,2,3,]));}
Typedef Lint = List<int>; // Lint 로 대체
Lint reverse(Lint list) {
var reversed = list.reversed;
return reversed.toList();
}
void main() { print(reverse([1,2,3,]));}
Flutter 에서 중요하게 사용되는 Dart 의 Class 부분을 살펴보자
Class 에서 Property 를 선언할 때는 var 가아닌 꼭 타입을 사용해서 정의한다.
함수에서 Class 를 호출할때 new를 사용하지 않아도 된다.
Class 내에서의 함수에서 변수를 호출할때 this 를 사용하지 않아도된다.
class People{
final String name = '김김김';
int age = 14;
void DD() {
print('hi $name i\'m $age');
}
}
void main() {
var people = People();
print(people.name);
people.age = 16;
}
위의 코드에서 argument 로 people의 이름과 나이를 전달해서 새로운 People을
만드는 코드를 짜보자 이를 위해선 Constructors 가 필요하다.
class People{
late final String name;
late int age;
People(String name, int age){
this.name = name;
this.age = age
}
void DD () {
print('hi $name i\'m $age');
}
}
void main() {
var people = People('이이이이',15);
people.DD();
}
이와 같이 코드를 짜면 일단 초기값이 없기때문에 late 를 사용해야하고 생성자의
크기가 길어지며 이미 선언한 이름과 나이의 자료형을 한번 더 선언해 코드가 길어진다. 이를 방지 하기위해 아래와 같이 코드를 간단하게 작성 가능하다.
class People{
final String name;
int age;
People(this.name, this.age)
void DD () {
print('hi $name i\'m $age');
}
}
void main() {
var people = People('이이이이',15);
people.DD();
}
🚨🚨 이때 주의 해야할 점은 argument의 위치다.
위와 같은 Positional Parameters는 결국 arguments의 위치가 바뀌면 에러가 발생하고
또한 Class의 크기가 커지면 통제가 어렵다는 큰 단점이 존재한다. 따라서 우리는 Flutter
를 사용할때 앞서 배웠던 Named Parameters를 생성자에 적용 시킬것이다.
class People{
final String name;
int age;
String team,country;
People({
required this.name,
required this.age,
required this.team,
required this.country,}) //null값 방지를 위한 required와 {} 사용
void DD () {
print('hi $name i\'m $age');
}
}
void main() {
var people = People(
name : '이이이이',
team : 'red',
country :'korea',
age : 15,
);
people.DD();
}
비슷하지만 조금 다르게 동작하는생성자를 가지고싶은 경우에 사용하는 방법이다.
두가지의 경우에 대해서 알아볼것이다. 첫번째 blueTeam 이라는 이름의 생성자는
Named parameter를 사용해 만들것이고 두번째 redTeam생성자는 Position
parameter를 사용해 만들것이다.
class People{
String name;
int age;
String team,country;
People({
required this.name,
required this.age,
required this.team,
required this.country,}) //null값 방지를 위한 required와 {} 사용
People.blueTeam({required String name,required int age,})
: // : 콜론 매우 중요!!!
this.age = age,
this.name = name,
this.country = 'korea',
this.team = 'blue'; //name parameter
People.redTeam(String name, int age)
: // : 콜론 매우 중요!!!
this.age = age,
this.name = name,
this.team = 'red',
this.countty = 'china',
void DD () {
print('hi $name i\'m $age');
}
} //position parmeter
void main() {
var bluepeople = People.blueTeam(
name : '이이이이',
age : 15, //named parmeter
);
var redpeople = People.redTeam('김김김김',15); //position parmeter
bluepeople.DD();
redpeople.DD();
}
코드가 계속 길어져 알아보기 힘들지만 결국 중요한 내용은 name parmeter로 만든 생성자든 position으로 만든 생성자 이든 초기화를 위한 :을 써줘야하고 name 은
required 를 position은 arguments 의 위치를 신경써야한다.
Cascade 는 생성한 변수의 arguments 값이나 이후 선언할때 변수명을 ..으로 대체할수있는 장점을 가진다. 사용법은 class 바로 뒤에 ;을 붙이지 않고 사용한다.
var redpeople = People.redTeam('김김김김',15,'blue',)
..name = '이이이' // redpeople.name ='이이이';
..age = 16 // redpeople.age = 16;
..team = 'red';
..sayHello();
Enums는 개발자가 실수를 줄일수있도록 만들어주는 일종의 틀 이다.
enum으로 설정한 변수에는 오직 선언한 값만 들어갈수있다.
enum Team {red,blue} ;
//생략
..team = Team.blue;
추상화 클래스는 다른 클래스들이 직접 구현 해야하는 메소드들을 모아놓은 일종의 '청사진' 이라 보면 된다. 여러 클래스들에 대해서 공통적으로 상속받는 메소드들을
추상화 클래스로 사용이 가능하다.
abstract class Human {
void walk();
}
class Player extedns Human{
void walk(){
print("working!");
}
}
부모 class 에서 Human({required this.name}); 와같이 객체 형태의 파라미터로 선언된 경우에 상속받은 자식 Class 에서 super를 사용해 생성자를 구성할때 (name:name)와 같은 형태로 만들어 main함수에서 값을 받을수있다. 또한 override를 통해 부모 클래스의 객체를 받아올수 있는데 이때도 super를 사용한다
class Human {
final String name;
Human({required this.name});
void sayHello() {
print ('Hi $name ');
}}
enum Team {blue,red};
class Player extends Human {
final Team team;
Player({
required this.team,
required String. name
}) : super(name: name); //super 사용해서 값 보내기
viod sayHello(){
super.sayHello();
print('and I play for ${team}');
}
}
void main() {
var player = Player(team :Team.red,name:'nnn');
}
Mixin은 생성자가 없는 클래스를 의미하며 extends가 아닌 with를 통해
여러 클래스의 Mixin 내부의 프로퍼티와 메소드 들을 가져온다. 이는 상속이아닌
빌리는 개념이며 또한 여러 클래스에 재사용이 가능하다.
class Strong{
final int power = 100;
}
class Karisma{
void karis(){
print("ouuuuuuu");
}
}
class Bigman with Strong,Karisma (){
}