[Android/Flutter 교육] 81,82일차

MSU·2024년 5월 1일

Android-Flutter

목록 보기
75/85
post-thumbnail

Dart

함수

함수의 기본 문법

반환타입 함수이름(매개변수들){

코드작성

return 반환값

}

포지셔널 매개변수

  • 함수 호출 시 전달할 값을 매개변수의 순서와 맞춰서 전달해야 한다.
  • 기본값을 설정할 경우 [ ] 로 묶어서 작성해준다.

네임드 매개변수

  • 함수 호출시 전달할 값을 어떠한 매개변수에 담을 것인지 이름으로 지정한다.
  • 매개변수의 순서에 관계없이 이름을 통해 값을 지정할 수 있다.
  • 네임드 매개변수는 { } 안에 작성하며 required를 붙여준다
  • 기본값이 있을 경우에는 required를 삭제한다

포지셔널 매개변수와 네임드 매개변수가 섞여 있을 경우

  • 포지셔널 매개변수를 앞쪽에 작성해준다.

익명함수

  • 이름이 없는 함수
  • 매개변수로 함수를 받는 함수를 정의할 때 주로 사용한다.

클래스

개념은 코틀린과 동일하기 때문에 문법적인 부분만 살펴본다.

클래스 문법

class 클래스이름 {

}

객체 생성

객체의 주소값을 담을 변수 = new 클래스명()

new는 생략 가능하다.

객체의 주소값을 담을 변수 = 클래스명()

객체를 통해 멤버 사용

  • 멤버 변수나 메서드 사용시에는 . 연산자를 통해 접근한다.

생성자

  • 객체 생성시 자동으로 동작하는 부분
  • 생성자 작성시 매개변수 작성 부분에서 맴버 변수를 지정하면 전달되는 값이 맴버 변수에 담기게 된다.
class TestClass500 {
  late int memberA;
  late int memberB;

  //  TestClass500(memberA, memberB){
  //    this.memberA = memberA;
  //    this.memberB = memberB;
  //  }

  // 생성자로 전달되는 값이 멤버 변수에 바로 담기게 된다.
  TestClass500(this.memberA, this.memberB);
}

private 멤버

  • 변수나 메서드 이름 앞에 _ 를 붙여주면 private 멤버가 된다.
  • private 멤버는 같은 클래스 내부가 아니더라도 파일이 같으면 자유롭게 사용이 가능하다
  • private 멤버는 파일이 다르면 사용이 불가능하다.

getter, setter

  • private 변수를 사용하는 이유는 변수에 대한 직접적인 접근을 막아 데이터의 무결성을 보장하기 위함이다.
  • 변수의 직접적인 접근을 차단하고 메서드를 통해 변수에 값을 담거나 가져올 수 있도록 만들어서 사용하는 것이 일반적이다(프로퍼티)
  • dart는 getter나 setter를 만들어주면 변수 사용하듯이 사용할 수 있다.

상속

  • 다른 클래스가 가지고 있는 요소를 물려받아 사용하는 개념.
  • dart는 extends 키워드로 상속 관계를 정의한다.
  • 부모가 가지고 있는 메서드를 자식에서 재구현 하는 것도 가능하다(overriding)
  • overridng한 메서드는 @override 키워드를 붙혀주는 것이 관례이다.
void main(){
  // 자식 클래스를 통해 객체를 생성한다.
  var dog1 = Dog(100, 10, "몰티즈");
  dog1.showDogRunLength();
  dog1.showAge();
  dog1.showName();

  var dog2 = Dog(200, 12, "비숑");
  dog2.showDogRunLength();
  dog2.showAge();
  dog2.showName();
}

// 부모 클래스
class Animal{
  late int age;
  late String name;

  Animal(this.age, this.name);

  void showAge(){
    print("나이는 ${age}살 입니다");
  }

  void showName(){
    print("이름은 ${name}입니다");
  }
}

// 자식 클래스
class Dog extends Animal{
  late int dogRunLength;

  // 클래스의 객체를 생성할 때 생성자에서
  // 부모가 가진 매개변수가 없는 생성자를 자동으로 호출하려고 한다.
  // 만약 부모클래스에 매개변수가 없는 생성자가 없다면
  // 부모의 생성자를 명시적으로 호출해줘야 한다.
  Dog(this.dogRunLength, super.age, super.name);

  void showDogRunLength(){
    print("강아지가 뛴 거리는 ${dogRunLength}m 입니다");
  }

  // 부모가 가지고 있는 메서드를 재구현하는 것도 가능하다.
  
  void showName(){
    super.showName();
    print("이름은 ${name}입니다2");
  }
}

인터페이스

  • dart는 인터페이스가 따로 존재하지 않는다.
  • class를 인터페이스 처럼 사용하게 된다.
  • dart는 extends로 상속을 받을 때는 단일 상속에 해당한다.
  • implements로 클래스를 구현하는 방식을 사용하면 다중 상속처럼 사용할 수 있다.
  • extends 키워드를 이용할 때는 필요한 메서드들만 재구현 할 수 있다.
  • implements로 클래스를 구현하면 모든 메서드들을 재구현 해야 한다.
void main(){
  var dog1 = Dog1(100, 10, "몰티즈");
  dog1.showName();
  dog1.showAge();
  dog1.showDogRunLength();

  var dog2 = Dog2(200, 20, "비숑");
  dog2.showName();
  dog2.showAge();
  dog2.showDogRunLength();

  Animal dog10 = Dog1(100, 10, "몰티즈");
  dog10.showName();
  dog10.showAge();

  Animal dog20 = Dog2(200, 20, "비숑");
  dog20.showName();
  dog20.showAge();
}

// 부모 클래스
class Animal{
  late int age;
  late String name;

  Animal(this.age, this.name);

  void showAge(){
    print("나이는 ${age}살 입니다");
  }

  void showName(){
    print("이름은 ${name}입니다");
  }
}

// 상속
class Dog1 extends Animal{

  late int dogRunLength;

  Dog1(this.dogRunLength, super.age, super.name);

  void showDogRunLength(){
    print("강아지가 뛴 거리는 ${dogRunLength}m 입니다");
  }
}

// 클래스를 인터페이스로 사용한다.
// 쉼표로 구분하여 다수를 작성해도 된다.
// 클래스를 인터페이스로 구현할 경우 클래스가 가진
// 모든 변수와 모든 메서드들을 재구현 해줘야 한다.
// 즉, 부모가 가지고 있는 것을 물려받는 개념이 아니고
// 전부 다시 구현하는 개념이 된다.
class Dog2 implements Animal{
  late int dogRunLength;
  // Animal이 가지고 있는 모든 변수들을 다시 정의한다.
  
  int age;

  
  String name;

  Dog2(this.dogRunLength, this.age, this.name);

  void showDogRunLength(){
    print("강아지가 뛴 거리는 ${dogRunLength}m 입니다");
  }
  // Animal이 가지고 있는 모든 메서드들을 다시 구현해야 한다.
  
  void showAge() {
    print("Dog2의 showAge : $age");
  }

  
  void showName() {
    print("Dog2의 showName : $name");
  }
}

Mixin

  • 특정 클래스에 기능을 추가하여 사용할 수 있는 개념
  • 상속 관계를 정의할 때 부모클래스의 기능을 추가하고 싶을 때 사용한다.
  • 부모클래스의 코드를 수정할 수 없는 상황일 때 사용한다.
  • Mixin은 지정된 클래스의 멤버들을 자유롭게 사용할 수 있다.
void main(){
  // Dog 객체를 생성한다.
  var dog = Dog(100, 10, "비숑");
  dog.showAge();
  dog.showName();
  dog.showDogFeeSize();
  dog.showAnimalRunLength();
  dog.showAnimalInfo();
}

class Animal{
  int age;
  String name;

  Animal(this.age, this.name);

  void showAge(){
    print("age : $age");
  }

  void showName(){
    print("name : $name");
  }
}

// 믹스인 정의
// on을 사용하여 믹스인 타입 사용을 제한한다.
// Animal 클래스에 기능을 추가하는 mixin을 정의한다.
mixin AnimalMixin on Animal{

  int animalRunLength = 0;

  void showAnimalRunLength(){
    print(animalRunLength);
  }

  void showAnimalInfo(){
    print("animalRunLength : $animalRunLength");
    print("age : $age");
    print("name : $name");
  }
}

// 강아지 클래스
class Dog extends Animal with AnimalMixin{
  int dogFeeSize;

  Dog(this.dogFeeSize, super.age, super.name);

  void showDogFeeSize(){
    print("강아지가 먹은 양은 ${dogFeeSize}g 입니다");
  }
}

추상 클래스

  • 구현되어 있지 않는 메서드를 가지고 있는 클래스를 추상클래스라고 부른다.
  • 완성된 클래스가 아니기 때문에 객체를 생성할 수 없다
  • 추상한 클래스를 구현한 클래스가 있어야 하며 이는 메서드 재구현에 대한 강제성을 주기 위해 사용한다.
void main(){
  // Anima은 추상클래스이므로 객체를 생성할 수 없다.
  // Animal animal = Animal(10, "비숑");
  // showInfo(animal);

  // Animal을 구현할 클래스의 객체를 생성한다.
  Dog dog = Dog(100, 10, "비숑");
  // Dog는 Animal을 상속받았기 때문에 Animal 타입
  // 변수에 담을 수 있다.
  showInfo(dog);
  dog.showRunSpeed();
}

// 개발자가 만든 메서드를 호출하는 함수
// 이럴 경우에는 함수를 받는 매개변수를 정의하면 된다.
// 만약 함수의 이름을 정했거나 다수의 함수를 받아서 사용할 경우에는
// 추상 클래스를 사용하는 것이 더 깔끔하다.
void showInfo(Animal animal){
  animal.showName();
  animal.showAge();
}

// 추상 클래스 Animal을 정의한다.
abstract class Animal{
  int age;
  String name;

  Animal(this.age, this.name);

  // showAge와 showName을 구현하지 않으면
  // Animal을 사용하는 클래스에서 반드시 재구현 해줘야 한다.
  void showAge();
  void showName();
}

// 추상클래스를 사용할 때는 implements를 사용한다.
class Dog implements Animal{
  int runSpeed;
  // Animal이 가지고 있는 변수들 재정의
  
  int age;

  
  String name;

  Dog(this.runSpeed, this.age, this.name);

  void showRunSpeed(){
    print("강아지의 속도는 : $runSpeed 입니다");
  }

  // Animal이 가지고 있는 메서드 재구현
  
  void showAge() {
    print("강아지의 나이는 ${age}살 입니다");
  }

  
  void showName() {
    print("강아지의 이름은 ${name}입니다");
  }

}

모듈

  • 같은 파일에 있는 모든 요소들은 자유롭게 사용이 가능하다.
  • 다른 파일에 있는 요소들 중 private를 제외하고 모든 요소들을 사용할 수 있다.
  • 이 때, 사용하는 곳 상단에 import '파일명'을 해줘야 한다.
  • 만약 import한 파일들에 중복된 요소들이 각각 존재한다면 이를 구분하기 위해 import 되는 파일에 별칭을 부여하여 별칭을 통해 구분할 수 있다.
// 다른 파일에 있는 것을 사용할 때는 import를 해줘야 한다.
import 'test1.dart' as test1;
// import 하는 파일에 별칭을 부여할 수 있다.
// 별칭을 통해 파일에 정의되어 있는 요소들에 접근할 수 있다.
// 만약 import 파일들 내에 중복된 것들이 있다면
// 별칭을 부여하여 이를 통해 구분해서 사용할 수 있다.
import 'test2.dart' as test2;

// 하위 폴더에 있는 dart 파일 import
import 'sub/test3.dart';


void main(){
  // 같은 파일에 있는 것은 자유롭게 사용이 가능하다.
  print("mainA1 : $mainA1");
  mainFunction();
  var mainClass = MainClass();
  mainClass.showInfo();

  // 다른 파일에 있는 것은 private 멤버를 제외하고 모두 사용가능하다
  print("test1A1 : ${test1.test1A1}");
  test1.test1Function();
  var test1Class = test1.Test1Class();
  test1Class.showInfo();

  // 별칭을 통해 접근한다.
  print("test2.testA1 : ${test2.test1A1}");
  test2.test1Function();
  var test1Class2 = test2.Test1Class();
  test1Class2.showInfo();

  // 하위 폴더에 있는 dart 파일에 정의된 요소
  print("test3A1 : $test3A1");
  test3Function();
  var test3Class = Test3Class();
  test3Class.showInfo();
}

int mainA1 = 100;

void mainFunction(){
  print("main.dart에 있는 함수");
}

class MainClass{
  void showInfo(){
    print("main.dart에 있는 클래스");
  }
}

제네릭

  • 클래스, 함수, 컬렉션 등을 작성할 때 데이터 타입을 정하지 않고 나중에 사용할 때 정할 수 있는 개념
  • 자료형 대신 영문자를 작성해두고 사용할 때 영문자에 해당하는 자료형을 결정해 사용할 수 있도록 한다.
  • 일반적으로 영어 대문자 한 글자를 사용한다.

흔히 사용되는 문자들

  • T : 변수 타입을 표현할 때 사용한다.
  • E : 리스트 내부 요소들의 타입을 표현할 때 사용한다.
  • K : 키를 표현할 때 사용한다.
  • V : 값을 표현할 때 사용한다.
  • 위에 정리한 것을 지킬 필요는 없다.
// A와 B라는 제네릭 타입을 지정했다.
class TestClass<A, B>{
  A data1;
  B data2;

  TestClass(this.data1, this.data2);
}


void main(){
  // 객체를 생성할 때 타입을 정해준다.
  var t1 = TestClass<int, double>(100, 11.11);
  print(t1.data1);
  print(t1.data2);

  var t2 = TestClass<String, bool>("문자열", true);
  print(t2.data1);
  print(t2.data2);
}
100
11.11
문자열
true
// 리스트 생성시에 리스트를 관리할 값의 타입을 제네릭을 통해 정해줘야 한다.
List<int> list1 = [10, 20, 30];
var list2 = <int>[10, 20, 30];
print(list1);
print(list2);
[10, 20, 30]
[10, 20, 30]
// 맵 생성시에 맵이 관리할 값의 타입과 이름의 타입을 제네릭을 통해 정해줘야 한다.
Map<String, int> map1 = {"A":100, "B":200};
var map2 = <int, bool>{0:true, 1:false};
print(map1);
print(map2);
{A: 100, B: 200}
{0: true, 1: false}

Static

  • 객체 생성없이 사용할 수 있는 멤버
  • 변수의 경우에는 프로그램 실행 중에 딱 하나만 존재한다.
  • 클래스 이름을 통해 접근해 사용한다.
void main(){
  var t1 = TestClass();

  // a1은 static 멤버이기 때문에 클래스 이름을 통해 접근한다.
  TestClass.a1++;
  t1.a2++;

  print('TestClass.a1 : ${TestClass.a1}');
  print('t1.a2 : ${t1.a2}');

  var t2 = TestClass();

  TestClass.a1++;
  t2.a2++;

  print('TestClass.a1 : ${TestClass.a1}');
  print('t2.a2 : ${t2.a2}');
}

class TestClass{

  static int a1 = 0;
  int a2 = 0;

}
TestClass.a1 : 1
t1.a2 : 1
TestClass.a1 : 2
t2.a2 : 1

class TestClass{

  static int a1 = 0;
  int a2 = 0;

  // 일반 메서드는 객체를 생성해야지만 사용할 수 있는 메서드이므로
  // static 멤버와 일반 멤버 변수를 모두 사용할 수 있다.
  void test1(){
    print("a1 : $a1");
    print("a2 : $a2");
  }

  // static 메서드는 객체를 생성하지 않고 사용할 수 있는 메서드이므로
  // 객체를 생성해야지만 생성되는 일반 멤버 변수는 사용할 수 없다.
  static void test2(){
    print("a1 : $a1");
    // print("a2 : $a2"); 에러
  }

}

Cascade Operator 캐스케이드 연산자

  • 객체가 가지고 있는 멤버들을 사용할 때 변수.멤버 형태가 아닌 ..멤버 형태로 사용할 수 있는 문법
class TestClass{

  int data1;
  String data2;
  bool data3;

  TestClass(this.data1, this.data2, this.data3);

  void showData(){
    print("data1 : $data1}");
    print("data2 : $data2}");
    print("data3 : $data3}");
  }

  void showData2(){
    print('데이터1 : $data1');
    print('데이터2 : $data2');
    print('데이터3 : $data3');
  }

}

void main(){
  // Cascade 연산자를 사용하지 않았을 때
  var t1 = TestClass(100, "문자열", true);
  t1.showData();
  t1.showData2();

  t1.data1 = 200;
  t1.data2 = "문자열2";
  t1.data3 = false;

  t1.showData();
  t1.showData2();

  print("---------------------------------------");

  // 객체를 생성할 때 Cascade 연산자를 사용한다.
  var t2 = TestClass(100, "문자열1", true)
    ..showData()
    ..showData2()
    ..data1 = 200
    ..data2 = "문자열2"
    ..data3 = false
    ..showData()
    ..showData2();
    
  print("---------------------------------------");

  var t3 = TestClass(100, "문자열1", true);

  t3
    ..showData()
    ..showData2()
    ..data1 = 200
    ..data2 = "문자열2"
    ..data3 = false
    ..showData()
    ..showData2();
}

Async 비동기

  • 개발자가 만든 작업은 위에서 아래로 혹은 호출한 순서대로 동작한다.(동기)
  • 동시에 여러 작업을 수행해야 하는 경우가 있을 수 있다(비동기)
  • 동기적 작업이라는 것은 수행시킨 작업의 순서대로 처리한다는 의미이다.
  • 비동기적 작업이라는 것은 동시에 작업을 처리한다는 의미이다.

비동기 작업을 해야하는 경우

  • 동시에 여러 작업을 수행해야 하는 경우
  • 작업이 쉬는 동안 다른 작업이 수행되어야 하는 경우
  • 작업이 끝날 때까지 대기하는 경우
  • 안드로이드나 iOS와 같은 모바일 애플리케이션은 네트워크와 관련된 모든 작업은 비동기 처리해야 한다.

Future

  • Future 클래스는 미래에 받아올 값이라는 의미의 클래스
  • 미래에 받을 값을 제네릭을 이용하여 다양한 타입으로 받을 수 있다.
  • 비동기 프로그래밍은 오래 걸리는 작업을 기다린 후에 값을 받아와야 하기 때문에 미래값을 표현하는 Future클래스가 필요하다.
Future<String> name; // 미래에 받을 String 값
Future<int> number; // 미래에 받을 int 값
Future<bool> isOpend; // 미래에 받을 boolean 값

Future.delayed

void addNumber(int n1, int n2){
  print('$n1 + $n2 계산 시작');

  print('$n1 + $n2 = ${n1 + n2}');

  print('$n1 + $n2 코드 실행 끝');
}

void addNumber2(int n1, int n2){
  print('$n1 + $n2 계산 시작');

  // 일정 시간 후에 지정된 작업을 수행하라는 비동기 처리 가동
  Future.delayed(Duration(seconds: 3), () {
    print('$n1 + $n2 = ${n1 + n2}');
  });

  Future.delayed(Duration(seconds: 2), (){
    print('$n1 - $n2 = ${n1 - n2}');
  });

  Future.delayed(Duration(seconds: 0), (){
    print('$n1 * $n2 = ${n1 * n2}');
  });

  print('$n1 + $n2 코드 실행 끝');
}

void main(){
  addNumber2(100, 200);
}
100 + 200 계산 시작
100 + 200 코드 실행 끝
100 * 200 = 20000
100 - 200 = -100
100 + 200 = 300

비동기 작업에 대한 동기화

  • 비동기 작업을 동기화 한다는 이야기는 작업을 순서대로 처리하겠다는 이야기임
  • 그러면 비동기 처리를 하지 않으면 순서대로 작업이 처리되게 할 수 있다.
  • 하지만 안드로이드나 iOS같은 모바일 애플리케이션은 네트워크와 관련된 작업은 비동기 처리로 작업하도록 강제되어있다(오류 발생에 대비하기 위해)
  • 그런데 서버로 부터 데이터를 받아온 후 그 데이터를 가지고 작업을 해야하는 경우가 많기 때문에 이런 경우에는 비동기 작업을 동기화 처리 해줘야 한다.
  • dart는 await을 이용해 비동기 처리를 동기 처리로 바꿀 수 있다.
  • await을 사용하면 함수의 반환 타입의 Future 타입이어야 하고 뒤에 async를 붙여줘야 한다.

Future<void> addNumber3(int n1, int n2) async {
  print('$n1 + $n2 계산 시작');

  // 비동기 처리 부분에 await을 넣어주면
  // 동기처리, 즉 작업이 끝날 때 까지 대기했다가 작업이 끝나면
  // 다음 작업이 수행될 수 있도록 해준다.
  // 동기처리를 한 작업이 있는 함수의 경우 반환 타입은 Future 타입으로
  // 해줘야 하고 함수 뒤에 async를 붙여줘야 한다.
  await Future.delayed(Duration(seconds: 3),(){
    print('$n1 + $n2 = ${n1 + n2}');
  });

  print('$n1 + $n2 코드 실행 끝');
}

void main(){
  addNumber3(100, 200);
}
100 + 200 계산 시작
100 + 200 = 300
100 + 200 코드 실행 끝
// 동기처리된 비동기 작업을 통해 구한 값을 반환한다.
Future<int> addNumber4(int n1, int n2) async {
  var result = 0;

  print('$n1 + $n2 계산 시작');

  await Future.delayed(Duration(seconds: 3),(){
    result = n1 + n2;
  });

  print('$n1 + $n2 코드 실행 끝');

  return result;
}

Future<void> main() async {
  var result = await addNumber4(100, 200);
  print('result : $result');
}
100 + 200 계산 시작
100 + 200 코드 실행 끝
result : 300

Flutter

플러터에서 소개되는 기능이 iOS에서도 되는 기능인지 확인한다

개발 환경 구축

sdk를 받는다
https://flutter.dev/

Get started -> 본인 OS 선택 -> Android 선택 -> Download then install Flutter

안드로이드 스튜디오에서 Plugins에 flutter 검색해서 install

안드로이드 스튜디오 재시작을 하면 New Flutter Project 버튼이 생김

SDK경로를 내려받은 SDK 폴더 위치로 설정

프로젝트 이름과 위치 등을 설정해준다.

플러터 작업시 디렉토리 구조를 Project로 봐주면 된다.

Preview 탭을 추가하려면 아래의 과정대로 셋팅하면 된다.

Help -> Find Action 클릭

choose 검색하면 나오는 첫번째 메뉴 클릭 'Choose Boot Java Runtime for the IDE...'

New 클릭해서 나오는 첫번째 선택 후 확인

다시 Help -> Find Action에서 registry 검색 후
'ide.browser.jcef.sandbox.enable' 검색해서 value 체크 해제한 다음 다시 안드로이드 스튜디오를 재시작하면

아래와 같이 안드로이드 스튜디오에서 Preview가 정상적으로 나온다

Flutter 중요 내용 정리

상태

  • UI 요소의 속성에 적용되는 값들

StatelessWidget

  • 상태를 관리하는 요소가 없는 위젯
  • 상태 관리를 하는 요소가 없기 때문에 눈에 보이는 UI 요소에 대해 값을 지정하는 작업을 직접 해줘야 한다.
  • 비교적 구조가 간단하다.
  • 어플 실행 후 종료될 때까지 절대 변하는 부분이 없을 경우 사용한다.
  • 눈에 보이는 요소를 위젯이 직접 관리한다.

StatefulWidget

  • 상태를 관리하는 요소가 있는 위젯
  • 상태 관리 요소가 있기 때문에 상태 값을 변경시키는 것만으로 UI 요소에 변화를 줄 수 있다.
  • 구조가 StatelessWidget에 비해 조금 복잡하다.
  • 눈에 보이는 요소를 상태 관리 요소가 관리한다.
  • 어플 실행 후 종료될 때까지 변하는 부분이 있는 경우 사용한다.

Container

  • 눈에 보이는 UI 요소들을 배치하는 요소
  • Container는 배치되는 모양이나 기능에 따라 여러 가지가 제공된다.
  • 안드로이드 네이티브에서 layout에 해당하는 부분이다.

Scaffold

  • 눈에 보이는 화면의 전체적인 구조를 관리하는 요소이다.
  • 상단 앱바, 하단 네비게이션바, 플로팅 버튼 등등을 설정할 수 있다.

프로젝트 설명

1. 프로젝트 기본 코드 작성


Stateful 위젯과 Stateless 위젯을 자동완성으로 만들 수 있다.

Material3 테마는 아래의 링크에서 참고
https://flutter.github.io/samples/web/material_3_demo/

위젯 클래스를 만들 때 cupertino대신 material 라이브러리를 import하면 된다.

import 'package:flutter/material.dart';

void main(){
  // 애플리케이션 실행
  runApp(const MyApp());
}

// 애플리케이션 전체 클래스
class MyApp extends StatefulWidget {
  const MyApp({super.key});

  
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  
  Widget build(BuildContext context) {
    return const Placeholder(3);
  }
}

2. MyApp 실행

void main(){
// 애플리케이션 실행
runApp(const MyApp());
}

3. 상태 관리 객체의 build 메서드에 MaterialApp 설정

  
  Widget build(BuildContext context) {
    // 여기서 반환하는 객체의 구성을 보고 화면을 만들어준다.
    return MaterialApp(
      
    );
  }

4. 어플 타이틀과 테마 설정을 해준다.

  
  Widget build(BuildContext context) {
    // 여기서 반환하는 객체의 구성을 보고 화면을 만들어준다.
    return MaterialApp(
      // 어플의 타이틀
      // 앱바를 따로 설정하지 않으면 title 문자열이 보여진다.
      title: "멋쟁이 사자",
      // 테마
      // 어플 전체에 적용될 테마
      theme: ThemeData(
        // 컬러 시스템 설정
        // 여기서 설정한 색을 기준으로 상단바, 하단바, 버튼 등의 색상이 셋팅된다.
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        // Material3를 적용할 것인지 설정
        useMaterial3: true
      ),
    );
  }

5. 눈에 보이는 화면을 구성하는 부분을 만들어준다.


      // 눈에 보이는 화면을 구성하는 부분
      home: Scaffold(
        
      ),

6. 상단 앱바를 구성해준다.

      // 눈에 보이는 화면을 구성하는 부분
      home: Scaffold(
          // 상단 바
          appBar: AppBar(
              // 배경색
              backgroundColor: Theme.of(context).colorScheme.inversePrimary,
              // 상단 바의 타이틀을 문자열 요소로 지정한다.
              title: Text("멋쟁이 사자"),
          ),
      ),

7. 화면 본문 부분을 작성해준다.

            // 화면 본문 부분
            body: Center(

            ),

8. 화면 중앙에 문자열 요소 두 개를 배치해준다.

            // 화면 본문 부분
body: Center(
    // column은 위에서 아래 방향으로 배치
    // Row : 좌측에서 우측으로 배치
    // Stack : 겹쳐서 배치한다.
    child: Column(
        // 화면 정중앙으로 정렬한다.
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
            Text("첫 번째 Flutter 애플리케이션 입니다"),
            Text(
                "100",
                style: Theme.of(context).textTheme.headlineMedium,
            ),
        ],
    ),
),

9. FloatingActionButton

        // FloatingActionButton
        floatingActionButton: FloatingActionButton(
          // 버튼을 눌렀을 때
          onPressed: () {  },
          // 버튼 내부에 보여줄 것
          child: Icon(Icons.add),
        ),

10. 숫자 값을 담을 변수를 선언한다

class _MyAppState extends State<MyApp> {

  // FloatingActionButton을 눌렀을 때 1씩 증가시킨 값을 담을 변수
  int number = 0;

11. 두 번째 Text 요소 수정

  • 문자열 값을 변수 출력으로 변경한다.
  • 부모에 const가 붙어있는 경우에는 지워줘야 한다.
              Text(
                "$number",
                style: Theme.of(context).textTheme.headlineMedium,
              ),

12. FloatingActionButton 이벤트

  • 버튼을 눌렀을 때 변수의 값을 1증가시킨다.
        // FloatingActionButton
        floatingActionButton: FloatingActionButton(
          // 버튼을 눌렀을 때
          onPressed: () {
            // setState 함수에서 변수의 값을 변경시키는 작업을 하게되면
            // 이 변수를 사용하는 모든 부분에 적용된다.
            setState(() {
              // number 변수 값을 증가시킨다.
              number++;
            });
          },
          // 버튼 내부에 보여줄 것
          child: Icon(Icons.add),
        ),

profile
안드로이드공부

0개의 댓글