Flutter 를 배우기위한 Dart 기초

이준호·2023년 2월 5일
post-thumbnail

Dart?

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 에서 세미콜론을 안쓰는 기능이 있기때문에 세미콜론이 자동적으로
찍히지않아 유의해야한다.

VARIABLES

변수생성

메소드 내부에서 사용할때 var를 통해 자동적으로 명시되는 경우

var name = '이름' ;
name = '김김김';

class 에서 변수나 property를 선언할때 타입을 명시하는 경우

String name = '이름';
name = '김김김' ;

dynamic

var와 동일하게 동작되는 동적타입 변수이다.
외부에서 데이터를 받았을때 그 데이터를 필터링 할수있는 느낌이다.

dynamic name;
if(name is String){
 name.메소드
}
if(name is boolean){
 name.메소드
}

nullable

null이 될수도 있는 값을 뜻한다.
첫번째의 경우 에러가 발생해 String 뒤에 ? 를 명시하여 nullable 를 나타낸다.
두번째와 세번째의 코드는 동일하게 작동하며 세번째의 코드가 Dart의 장점을 활용해
단축 시켰다 볼수있다.

String name ='김김김';
name = null;
String? name ='김김김';
name = null;
if (name != null){
	nico.isNotEmpty;
}
String? name ='김김김';
name = null;
name?.isNotEmpty

Final Variables

var 대신 final 로 변수를 지정해주면 변수타입을 변경할수 없게 설정이 가능하다.

final name ='김김김';
name = null;

Late Variables

late 는 final 이나 var 앞에 붙을수있는 수식어이다.
late는 초기 데이터 없이 변수를 선언할 수 있게 해준다.
예를들어 변수를 미리 선언하고 후에 api등으로 데이터를 가져올때 유용하다.
데이터가 들어오지 않은 상태에서 변수 사용이 불가능 하기때문에 실수 방지가 가능하다.

late final String name ;
// do something. go to api
print(name);   // 에러!!
name = '김김김';
print(name);   // 정상작동!

Constant Variables

Dart 의 const 는 Js 의 const등과 다르다 오히려 final 이 유사하다.
사용자의 동작에 따라 api값을 받아 변수에 저장할때는 final 을 사용하고
const는 앱을 배포하기전 컴파일 할때 알고있는 값에 사용하는 것이다.

DATA TYPES

Basic Data Types

여러 데이터 타입이 존재한다.(num 은 double과 int의 부모 타입)

String name="김김김";
int age = 12;
double money = 22.22;
num x =12;
x = 1.1;

List & Collection If

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

String Interpolation

변수값 을 보여주기 위해서 $를 사용한다.만약 변수값의 계산을 하고싶으면
${} 를 사용하여 나타내준다.

var age = 24;
var name='김김김';
var write= "Hello $name I\'m ${age+3}" ;
print(write);			//결과 Hello 김김김 I'm 27

Collection For

Collection if 와 마찬가지로 list와 같은 변수내용에 직접적으로 간단하게
변화를 줄수있는 방법이다.for(var ooo in xxx)"😃 $ooo", 이 한줄의
코드로 인해 newBro의 리스트에 oldBro를 포함 시키고 구분짓는다.

var oldBro=['이이이','감감감'];
var newBro=['니니니','누누누','나나나',
	for(var bro in oldBro) "😃 $bro",
];
print(newBro);

Maps

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() //메소드

Sets

setList와 비슷하지만 요소가 하나씩 있어야 되는 경우에 사용한다 이유는 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,];

FUNCTIONS

Defining a Function

다른 함수에서 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));
}

Name Parameters

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 Valuerequired 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 발생!!!

Optional Positonal Parameters

생략
영상
이유: 가시성이 떨어지고 name arguments 보다 이점이 없음

QQ Operator

기존의 코드에서 매개변수에 대한 조건연산은 삼항연산자 조건문으로 많이 사용되었다.

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);
}

Typedef

자료형을 간편하게 작성할수있게 alias 쉽게말해 자료형의 별명을 만들어준다.
예를들어 아래와 같이 list 값을 받아 반대로 저장하는 함수가 있을때 새로운
Typdefalias 를 만들어 대체하여 사용이 가능하다.

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,]));}

CLASSES

Firsd Dart Class

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;
}

Constructors(생성자)

위의 코드에서 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의 위치다.

Named Constructor Parameters

위와 같은 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();
}

Named Constructor

비슷하지만 조금 다르게 동작하는생성자를 가지고싶은 경우에 사용하는 방법이다.
두가지의 경우에 대해서 알아볼것이다. 첫번째 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 Notation

Cascade 는 생성한 변수의 arguments 값이나 이후 선언할때 변수명을 ..으로 대체할수있는 장점을 가진다. 사용법은 class 바로 뒤에 ;을 붙이지 않고 사용한다.

 var redpeople = People.redTeam('김김김김',15,'blue',)
 ..name = '이이이'					// redpeople.name ='이이이';
 ..age = 16							// redpeople.age = 16;
 ..team = 'red';
 ..sayHello();

Enums

Enums는 개발자가 실수를 줄일수있도록 만들어주는 일종의 틀 이다.
enum으로 설정한 변수에는 오직 선언한 값만 들어갈수있다.

enum Team {red,blue} ;
 //생략
 
..team = Team.blue;

Abstract Classes

추상화 클래스는 다른 클래스들이 직접 구현 해야하는 메소드들을 모아놓은 일종의 '청사진' 이라 보면 된다. 여러 클래스들에 대해서 공통적으로 상속받는 메소드들을
추상화 클래스로 사용이 가능하다.

abstract class Human {
 void walk();
}
class Player extedns Human{
 void walk(){
 print("working!");
 }
}

Inheritance(상속)

부모 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');
 }
 

Mixins

Mixin은 생성자가 없는 클래스를 의미하며 extends가 아닌 with를 통해
여러 클래스의 Mixin 내부의 프로퍼티와 메소드 들을 가져온다. 이는 상속이아닌
빌리는 개념이며 또한 여러 클래스에 재사용이 가능하다.

class Strong{
	final int power = 100;
}
class Karisma{
	void karis(){
    print("ouuuuuuu");
    }
}

class Bigman with Strong,Karisma (){

}
profile
IT 학습과정중 학습내용을 복기하기 위해 사용하는 블로그 입니다.

0개의 댓글