사실 설치 과정이 꽤 복잡하여 정리를 해보았다.
필자는 macOS를 사용중이라 mac에 집중하여 설치 방법을 소개하려고 한다.

Flutter 설치에는 두 가지 단계가 있다.

  1. SDK 설치
    • 먼저 flutter.dev 사이트에 접속 후 Get Started 버튼을 찾아 들어가면 운영체제마다 Install을 할 수 있도록 안내되어 있다.
      => 해당 방법은 .zip 파일을 설치해 경로를 설정하는 등등의 과정이 필요하다.
    • 더 쉽게 설치하려면 homebrew를 이용하자 (mac)
    • 터미널을 실행한 후 $brew install --cask flutter
    • 별다른 에러가 없다면 잘 설치된 것이다.
  2. 시뮬레이터 설치
    • 시뮬레이터 설치가 굉장히 까다롭다.
    • IOS, Android 등 OS마다 전부 설치 방법이 다르다…
    • 그냥 공식문서 보고 잘 따라해보자. Install 페이지를 찾았다면 우측 목차에 [iOS setup / Android setup] 두 목차가 있을 것이다.
    • 필자는 iOS, Android 둘 다 셋팅해보려 한다.
    • 설치가 완료되면 $flutter doctor 명령어로 플러터를 시작할 수 있는 상태인지 확인해보자.
    • 모든 준비사항이 완료되어야 시작 가능하다.

 

'Mobile' 카테고리의 다른 글

[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

Google에서 개발한 오픈 소스 UI 소프트웨어 개발 키트
Dart의 프레임워크이며 모바일 앱, 웹, Linux 등 여러 플랫폼에 대해 개발이 가능하다.

동작 원리

네이티브 앱은 운영체제가 렌더링에 관여한다.
그러나

Flutter 혹은 Dart는 운영체제와 직접적으로 소통하지 않는다.

Flutter에서는 실제로 버튼을 만들어내는 등과 같은 행위는 할 수 없다.
그렇다면 어떻게 UI를 만들어내는 것일까?

위 구조를 살펴보면 Framework 하단에 C/C++로 이루어진 엔진이 존재한다.
네이티브 앱은 운영체제(IOS, AOS 등)와 직접적인 상호작용을 통해 모바일 앱 상의 화면을 그려내지만 Flutter는 엔진이 렌더링을 한다.

그렇다. Dart/Flutter로 작성된 코드를 해당 화면을 그려주는 것은 바로 엔진이다.

그리고 운영체제와 엔진 사이에서 인터페이스 역할을 해주는 것이 Embedder이다.

Embedder는 각 플랫폼에 맞게 애플리케이션이 원활하게 실행될 수 있도록 지원하며,
플러터의 크로스 플랫폼 개발을 가능하게 한다.

이제 예시로 애플리케이션 동작 Flow를 살펴보면 아래와 같다.
https://flutter-ko.dev/resources/faq#run-android

  1. 사용자가 앱을 실행함
  2. C / C++로 구현된 엔진이 각각의 운영체제에서 이해할 수 있는 표현으로 컴파일됨
  3. 엔진이 Dart 코드를 실행시킴

단점

Native 자원을 사용하지 못해 실제 운영체제로부터 화면이 그려지는게 아니라 Flutter 엔진에 의해 렌더링됨.
즉, 네이티브에서 사용 가능한 위젯을 사용할 수 없음


마무리

포스팅이 단점으로 끝나는 것 같지만, Flutter 엔진은 실로 강력한 기능을 지원해서 네이티브 위젯의 필요성을 못느낄 정도라고 한다.

또한 각 운영체제에서 앱이 실행될 수 있게 해주는 Embedder의 존재로 인해 크로스 플랫폼이 가능하다는 것은 정말 강력한 장점이라고 생각한다.

현재 시점에 제대로 된 플러터 개발을 시작하지 않아서 느낌만 가지고 말할 수는 없지만 플러터로 개발된 다양한 웹앱을 보니 정말 자연스러운 것 같다.

이제부터 세부적인 사항에 대해 차차 알아보려고 한다.

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

Dart에서 클래스는 어떻게 사용할까?
기본적인 클래스 및 생성자 구조와 다양한 속성들에 대해 기록하였다.


Basic Class

class Player {
    // Class에서 property를 정의할 때는 Type을 지정해준다.
    String name = "JP"; // 외부에서 이름 변경 가능
    final String name = "JP"; // 이러면 외부에서 이름 못바꿈
    int xp = 1500;

    void sayHello() {
        var name = "JPJP";
        // Dart의 클래스에서는 this를 사용하지 않는다. $this.name이 아닌 그냥 $name을 사용
        print("Hi my name is $name"); 
        // 만약 함수 내부에 새로운 name이 정의되었고 이를 사용하려면 아래와 같이 사용한다.
        print("Hi my name is ${this.name}")
    }
}

void main() {
    var player = Player();
    player.name; // JP
    player.xp;  // 1500
}

Constructors

class Player {
    late final String name;
    int xp;

    // Dart에서 Constructor는 아래와 같이 정의
    Player(String name, int xp) {
        this.name = name;
        this.xp = xp;
    }

    // 위 Player Constructor와 같음
    Player(this.name, this.xp);
}

void main() {
    var player = Player("JP", 31);
}

Named Constructor Parameters

class Player {
    late final String n ame;
    int xp, age;
    String team;

    // 위 Player Constructor와 같음
    // 이러면 각 인자가 null로 들어올 수 있다고 에러가 난다.
    // default 값을 정해주거나, required 수식어를 붙여준다.
    Player({this.name = "Default", required this.xp, required this.team, required this.age});
}

void main() {
    // 아래 방법은 positional 이므로 함수처럼 optional로 설정 가능하다.
    var player = Player("JP", 31, "red", 12);

    // optional
    var player = Player(name:"JP", age:12, team:"red", xp:31);
}

Named Constructor

class Player {
    late final String name;
    int xp, age;
    String team;

    Player({
        required this.name, 
        required this.xp, 
        required this.team, 
        required this.age
    });

    // 아래와 같은 코드로 용도에 맞는 생성자를 구성할 수 있다.
    // ':'을 사용함으로써 Player 객체를 원하는 설정에 맞게 초기화하는 것으로 이해하자.
    Player.createBluePlayer({required String name, required int age
    }) : 
    this.age = age, 
    this.name = name,
    this.team = 'blue',
    this.xp = 0;

    Player.createRedPlayer(String name, int age) : 
    this.age = age, 
    this.name = name,
    this.team = 'red',
    this.xp = 0;
}

void main() {
    var bluePlayer = Player.createBluePlayer(name:"JP", age:21);
    var redPlayer = Player.createRedPlayer("EL", 23);
}

Cascade Notation

class Player {
    late final String name;
    int xp, age;
    String team;

    Player({
        required this.name, 
        required this.xp, 
        required this.team, 
        required this.age
    });
}

void main() {
    // 생성자 인스턴스를 만들고 맨 마지막에 세미콜론을 생략한 채 .. + 속성에 접근하여 수정할 수 있다.
    // 이것이 바로 Cascade Notation이다
    var user = Player(name:"JP", xp:0, team:"blue", age:21)
    ..name = 'EL'
    ..xp = 9999
    ..team = 'red';
}

Enums

// Enum을 지정해줌으로써 파라미터를 잘못 넘기는 것을 방지해준다.
// 마치 TypeScript에서 인자로 받을 수 있는 파라미터를 지정해 주는 것과 같다.
enum Team {red, blue}

class Player {
    late final String name;
    int xp, age;
    // enum으로 정의된 Team을 타입으로 설정했기 때문에 team은 red or blue만 받을 수 있다.
    Team team;

    Player({
        required this.name, 
        required this.xp, 
        required this.team, 
        required this.age
    });
}

void main() {    
    var user = Player(name:"JP", xp:0, team:"blue", age:21)
    ..name = 'EL'
    ..xp = 9999
    ..team = Team.blue; // 변경도 enum 범위 안에서 변경해야 한다.
}

Abstract Classes

// Human 추상 클래스는 walk라는 메서드를 가지고 있다.
// 기능이 정의되지는 않았지만 일반 클래스에서 Human 추상메서드를 구현하면 walk 메서드를 꼭 구현해야한다.
abstract class Human {
    void walk();
}

class Player extends Human {
    // extends는 상속이기 때문에 walk 함수의 내용이 채워져 있다면 재정의를 하지 않아도 된다.
    // 그러나 이번 케이스에서는 Human -> walk 메서드가 정의만 되어있어서 재정의 필요
    void walk() {
        print('im walking');
    }
}

class Coach implements Human {
    // implements는 인터페이스 구현이기 때문에 무조건 walk 메서드 재정의가 필요하다.
    void walk() {
        print('the coach is walking');
    }
}

Inheritance

class Human {
    final String name;
    Human(this.name);
    void sayHellao() {
        print('Hi my name is $name');
    }
}

enum Team {red, blue}

class Player extends Human {
    final Team team;

    // 생성자 뒤에 : super 키워드를 통해 부모 클래스와 상호작용 할 수 있다.
    Player({
        required this.team,
        required String name
    }) : super(name);

    // override 키워드 안써줘도됌.
    @override
    void sayHello() {
        super.sayHello();
        print("and I play for ${team}");
    }
}

void main() {
    var player = Player(team:Team.red, name:'JP');
    player.sayHello();
}

Mixins

enum Team {red, blue}

// mixin은 아래와 같이 선언한다.
// 생성자가 존재하지 않아야 한다.
mixin Strong {
  final double strengthLevel = 1500.99;
}

mixin QuickRunner {
  void runQuick() {
    print("runnnnnn!");
  }
}

mixin Tall {
  final double height = 1.99;
}

// mixin 클래스를 사용하려면 with 키워드를 사용한다.
class Player with Strong, QuickRunner, Tall {
  final Team team;

    Player({
        required this.team,
        required String name
    });
}

class Horse with Strong, QuickRunner {
  //...
}

void main() {
  var player = Player(name:"JP", team:Team.red);
  //player는 Player 클래스 인스턴스이면서 QuickRunner mixin 클래스를 사용하므로 runQuick 함수 사용이 가능하다.
  player.runQuick();
}

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

Dart에서 Function을 정의하고 사용하는 방법에 대해 알아보았다.


Function

String sayHello(String name) {
	return "Hello $name nice to meet you!";
}

// 위 함수와 같음
// 함수가 한 줄 짜리일 때 사용
String sayHello(String name) => "Hello $name nice to meet you!";

void main() {
	sayHello("JP");
}

Named Parameters

String sayHello(String name, int age, String country) {
	return "Hello $name, you are $age, and you come form $country";
}

// Named Parameter를 사용할 때는 매개변수 받을 때 {} 추가
// default 값 설정 가능
// 실제로 값을 받아야 할 때에는 required 키워드를 타입 앞에 적어준다.
String sayHello2({String name = "Default Value", int age = 20, required String country}) {
	return "Hello $name, you are $age, and you come form $country";
}

void main() {
	// not good
	sayHello("JP", 31, "YongIn");
	
	//using named argument - good
	sayHello(age:12, country: "YongIn", name: "JP");
}

Optional Positional Parameters => 잘 안씀

// Optional 파라미터를 쓰려면 대괄호 안에 '?'로 인자가 있을 수도 없을 수도 있다는 것을 명시하고, Default Value를 설정해준다.
String sayHello(
	String name, 
	int age, 
	[String? country = 'Dongtan']
) => 'Hello $name, you are $age years old from $country';

void main() {
	sayHello("JP", 31);
}

QQ Operator

// 인자로 null이 들어올 경우 Optional('?')로만 해결할 수 없다.
// if문으로 name이 있을 경우에만 uppercase 해주거나 3항연산자로도 처리할 수 있다.
String capitalizeName(String? name) => name != null ? name.toUpperCase() : 'ANNO';

// 위 함수를 더 짧게 하면 아래와 같다.
// ??를 쓸 경우 좌항이 null이면 우항을 설정한다.
// JS에서 || 연산자랑 비슷한듯?
String captializeName(String? name) => name?.toUpperCase() ?? 'ANNO';

// ??= 연산자도 있다.
String captializeName(String? name) {
	name ??= "People";  // name이 null이면 People 값을 설정해준다. ??랑 똑같은듯
}

void main() {
	capitalizeName("jp");
	capitalizeName(null);
}

TypeDef

// TypeDef란 자료형이 헷갈릴 때 alias를 만드는 것
// reverseListOfNumbers 함수는 List<int> 타입을 리턴받으려고 할 때 정의해둔 ListOfInts를 사용할 수 있다.
// 함수 정의시 리턴 타입 뿐만 아니라 매개변수로도 사용할 수 있는 등 활용이 무궁무진하다.
typedef ListOfInts = List<int>;

ListOfInts reverseListOfNumbers(List<int> list) {
	var reversed = list.reversed;
	return reversed.toList();
}

void main() {
	reverseListOfNumbers([1, 2, 3]);
}

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09
[Dart] Dart의 변수 정의  (3) 2023.10.09

이번에는 DataType을 공부해보았다.

Dart에서 지원하는 여러가지 메서드 및 유용한 기능들이 많았다.

생소하지만 자주 쓰일 것 같다.


DataType

void main() { 	
	// 아래는 변수의 자료형을 나타낸다. 	
    String name = 'JP'; 	
    bool alive = true; 	
    int age = 12; 	
    double money = 69.99; 	 	
    // num type은 정수, 실수 전부 가능하다. 	
    num x = 12; 	
    num x = 1.1; 
 }

Lists and Collection if

void main() {
  var giveMeFive = true;
  List<int> numbers = [1, 2, 3, 4];  // 리스트 사용
  numbers.add(5);
  numbers.first;
  numbers.last;

  // collection if
  if(giveMeFive) 5 else 6,
  // 아래와 동일
  if(giveMeFive) {
    numbers.add(5);
  }else {
    numbers.add(6);
  }
}

String Interpolation

void main() {
	var name = 'JP';
	// 작은따옴표, 큰따옴표 상관 없이 가능하다.
	// 1. 변수가 이미 존재할 때 사용하는 방법
	var greeting = 'Hello everyone my name is $name, nice to meet you';

	// 3. 계산이 필요할 때
	// age + 2를 하고 싶다면 ${} 안에 수식을 작성한다.
	var age = 10;
	var greeting = "Hello everyone my name is $name, and I'm ${age + 2} years old";
}

Collection if

void main() {
	var oldFriends = ['a', 'b'];
	
	// 아래와 같이 할 경우 newFriends에 oldFriends를 추가한다.
	// 이모티콘 + oldFriend로 추가된다.
	var newFriends = [
		'c', 
		'd', 
		'e',
		for(var friend in oldFriends) "😀 $friend",
	];
	// 위에 작성한 collection For을 기본 for로 하면 아래와 같다.
	for(var friend in oldFriends) {
		newFriends.add(friend);
	}
}

Maps

void main() {
	// 아래와 같이 작성하면 컴파일러가 자동으로 자료형을 지정함.
	// 아래 player는 Map<string, Objec>라는 자료형을 가짐
	var player = {
		'name':'JP',
		'xp':19.99,
		'superpower':false,
	};
	// 자료형을 직접 지정하여 생성 가능
	Map<int, bool> player = {
		1:true,
		2:false,
		3:false,
	};
}

Sets

void main() {
	var numbers = {1, 2, 3, 4, 5};
	Set<int> numbers = {1, 2, 3, 4, 5};
	numbers.add(1);  // numbers에 1이 있기 때문에 변화가 없다. 즉, 중복이 안된다.
}

 

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 변수 정의  (3) 2023.10.09

Dart의 변수를 정의하는 방법에 대해 공부한 내용.
변수 정의에 앞서 알아야 할 것은 Dart는 main 함수로부터 시작이 된다는 것이다.
필자는 현재 C#으로 개발을 오래 해왔기 때문에 변수의 타입을 지정하는 것에 익숙하다. (그러나 C#과는 또 다르다.)

이번에 게시할 내용은 NomadCoder에 무료 강의 기반이다.


변수 타입

void main() {
    //1. 변수 타입 or var
    //변수의 타입 혹은 var를 사용하는 경우는 나중에 변경 가능하다.
    //말 그대로 변수이다.
    int i = 12;
    var name = 'la';
    i = 123123123123;
    name = 'lalalala';
}

Final

//2. final - 한번만 변수 값을 할당하고 싶을 때 사용한다.
void main() {
    final name = 'JP';
    name = 'JPP'; //에러 발생
}

dynamic

void main() {
    //3. dynamic - 타입에 상관없이 변수 값을 할당 가능하게 한다.
    //아래 세 가지 변수 할당 전부 가능
    dynamic name;
    name = 'JP';
    name = 1212;
    name = true;

    //dynamic으로 정의된 변수는 아래와 같이 조건문으로 타입을 검사하면
    //해당 타입에 맞는 메서드를 호출해 사용할 수 있다.
    if(name is String) {
        name.length
    }
}

const

void main () {
    //4. const - 알고 있는 값에 대해 상수 값을 정의한다.
    const name = 'JP';
    name = 'JPP'; //상수 값은 변경될 수 없으므로 에러 발생

    //fetchAPI 함수의 값을 알 수 없으므로 에러 발생
    const name2 = fetchAPI(); 
}

null safety

void main() {
    //5. null safety - null 값을 참조하지 못하게 해준다.
    String name = 'JP';
    // 에러 발생
    name = null; 

    String? name2 = 'JP';
    // 정상 동작
    name = null; 
    // name 값이 null이 될 수 있어서 에러 발생
    name.isEmpty; 
    // 정상 동작 - name != null이 아닐 경우에만 isEmpty 동작
    name?.isEmpty;
    // name?.isEmpty와 같은 동작 결과 
    if(name != null) {
        name.isEmpty
    }
}

late

void main() {
    //6. late - 변수의 값을 나중에 초기화한다.
    // late는 API 호출 후 리턴 값을 정의하는데 주로 사용한다.
    late name;
    name = 'JP';

    late final String name;
    name = 'JP';
    name = 'JPJP'; //에러 발생
}

'Mobile' 카테고리의 다른 글

[Flutter] Flutter 설치방법  (0) 2023.10.12
[Flutter] Flutter의 동작 방식  (0) 2023.10.10
[Dart] Class 및 다양한 Class 속성에 대해  (0) 2023.10.10
[Dart] Dart의 Function 사용법  (0) 2023.10.10
[Dart] Dart의 Data Types  (0) 2023.10.09

+ Recent posts