
반환타입 함수이름(매개변수들){
코드작성
return 반환값
}
개념은 코틀린과 동일하기 때문에 문법적인 부분만 살펴본다.
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);
}
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");
}
}
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");
}
}
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}입니다");
}
}
// 다른 파일에 있는 것을 사용할 때는 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에 있는 클래스");
}
}
// 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}
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"); 에러
}
}
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();
}
Future<String> name; // 미래에 받을 String 값
Future<int> number; // 미래에 받을 int 값
Future<bool> isOpend; // 미래에 받을 boolean 값
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
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
플러터에서 소개되는 기능이 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가 정상적으로 나온다


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);
}
}
void main(){
// 애플리케이션 실행
runApp(const MyApp());
}
Widget build(BuildContext context) {
// 여기서 반환하는 객체의 구성을 보고 화면을 만들어준다.
return MaterialApp(
);
}
Widget build(BuildContext context) {
// 여기서 반환하는 객체의 구성을 보고 화면을 만들어준다.
return MaterialApp(
// 어플의 타이틀
// 앱바를 따로 설정하지 않으면 title 문자열이 보여진다.
title: "멋쟁이 사자",
// 테마
// 어플 전체에 적용될 테마
theme: ThemeData(
// 컬러 시스템 설정
// 여기서 설정한 색을 기준으로 상단바, 하단바, 버튼 등의 색상이 셋팅된다.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
// Material3를 적용할 것인지 설정
useMaterial3: true
),
);
}
// 눈에 보이는 화면을 구성하는 부분
home: Scaffold(
),
// 눈에 보이는 화면을 구성하는 부분
home: Scaffold(
// 상단 바
appBar: AppBar(
// 배경색
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// 상단 바의 타이틀을 문자열 요소로 지정한다.
title: Text("멋쟁이 사자"),
),
),
// 화면 본문 부분
body: Center(
),
// 화면 본문 부분
body: Center(
// column은 위에서 아래 방향으로 배치
// Row : 좌측에서 우측으로 배치
// Stack : 겹쳐서 배치한다.
child: Column(
// 화면 정중앙으로 정렬한다.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("첫 번째 Flutter 애플리케이션 입니다"),
Text(
"100",
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
// FloatingActionButton
floatingActionButton: FloatingActionButton(
// 버튼을 눌렀을 때
onPressed: () { },
// 버튼 내부에 보여줄 것
child: Icon(Icons.add),
),
class _MyAppState extends State<MyApp> {
// FloatingActionButton을 눌렀을 때 1씩 증가시킨 값을 담을 변수
int number = 0;
Text(
"$number",
style: Theme.of(context).textTheme.headlineMedium,
),
// FloatingActionButton
floatingActionButton: FloatingActionButton(
// 버튼을 눌렀을 때
onPressed: () {
// setState 함수에서 변수의 값을 변경시키는 작업을 하게되면
// 이 변수를 사용하는 모든 부분에 적용된다.
setState(() {
// number 변수 값을 증가시킨다.
number++;
});
},
// 버튼 내부에 보여줄 것
child: Icon(Icons.add),
),
