31일차에서는 여러 키워드들과 새로운 페이지 이동 방법에 대해 학습했다.
학습한 내용
- const, final
- static, getter, setter
- GetX Pages - Get.toNamed
const
와 final
에 대해서는 [Flutter] 스나이퍼팩토리 6일차에서 정리한 적이 있지만 좀 더 자세히 정리해 보았다.
const
와 final
모두 값을 한 번 지정하면 바꿀 수 없다는 공통적인 속성을 가지고 있다. 하지만 생성 시점이 다르다는 차이가 있다.
const
로 선언된 변수 또는 위젯의 경우 생성을 앱이 시작될 때 메모리에 같이 등록하여 해당 시점에 새로 생성되는 것이 아닌 기존의 메모리에 올라가 있는 인스턴스를 계속 재사용 하게 된다.
즉, const
를 통한 재사용 시 사용하지 않는 인스턴스가 메모리에 남게되는 메모리 낭비를 효율적으로 관리할 수 있다. 또한 const
는 속도도 빠르다.
따라서 효율적인 코딩을 위해 const
를 잘 작성하면 앱의 성능을 높일 수 있다.
const
는 선언부에서 무조건 값을 부여해야 하지만 final
은 선언 시 값을 부여하지 않아도 된다. final
선언 시 값을 부여하지 않은 경우 이후에 최초 1번만 값을 부여할 수 있다. 그렇기 때문에 선언부의 코드만으로 컴파일러가 해당 변수의 값을 알 수 없어서 const
처럼 앱 실행 시 메모리에 넣어 관리를 할 수는 없다.
정리하면 const
는 컴파일 타임에 데이터를 미리 정의하고, final
은 런타임에 데이터를 정의한다.
static
은 생성한 클래스 자체에 데이터를 넣어줄 때 사용한다. 따라서 해당 생성자로 여러 객체가 만들어져도 static
은 전부 같은 값을 가진다.
자주 사용되는 예시로는 페이지의 라우트를 넣어 관리하는 경우이다. 아래 코드는 LoginPage
에 라우트를 static
으로 설정한 코드이다.
class LoginPage extends StatelessWidget{
cost LoginPage({super.key});
static const String route = '/login';
Widget build(BuildContext context) {
return Scaffold(...);
}
}
다른 예시로는 정적 데이터를 보관하고 관리할 때 사용하며 아래와 같이 앱의 페이지 라우트를 따로 클래스를 만들어 관리할 수 있다.
class AppRoutes {
static const String login = LoginPage.route;
static const String main = MainPage.route;
static const String signup = SignupPage.route;
}
또한 API의 라우트들을 저장해 놓고 사용해도 코딩을 할 때 편리하게 사용 가능하다.
class BackendApiRoutes {
static const String readPosts = '/api/v1/posts'
static const String readMemos = '/api/v1/memos'
}
이외에도 앱에서 사용하는 색상이나, 텍스트 스타일을 미리 static
으로 저장해 놓고 사용하면 코드를 훨씬 깔끔하게 작성할 수 있다.
Getter와 Setter는 이번에 학습을 진행했지만 [Flutter] 스나이퍼팩토리 22일차의 과제 4-1에 정리를 했기 때문에 생략했다.
Getx Pages로 앱 내에서 사용할 페이지를 미리 등록하고, URL 방식으로 이동할 수 있다.
먼저 기존의 Get.to
를 사용하는 방식은 아래와 같은 코드로 페이지를 이동했다.
Get.to(() => MainPage());
Get.to(() => LoginPage());
Get.to(() => SignupPage());
URL 방식을 사용하면 아래와 같은 코드로 변경할 수 있다.
Get.toNamed('/main');
Get.toNamed('/login');
Get.toNamed('/signup');
하지만 URL 방식을 사용하려면 우선 GetMaterialApp
에 미리 등록을 해야한다.
GetMaterialApp(
...
getPages: [
GetPage(name: '/main', page: () => MainPage()),
GetPage(name: '/login', page: () => LoginPage()),
GetPage(name: '/signup', page: () => SignupPage()),
],
)
페이지 라우트를 모두 등록한 후에는 앞의 URL 코드 예시 처럼 Get.toNamed()
를 사용하면 된다.
여기서 더 효과적으로 코드를 작성하고 싶다면 getPages
를 따로 관리하는 방법이 있다. 이 때 코드 작성의 편의성을 높이기 위해 라우트들은 또 다른 파일로 작성해 관리할 수 있다.
먼저 util
폴더를 만들고 아래와 같이 AppRoutes
를 작성한다.
class AppRoutes {
static const main = MainPage.route;
static const login = LoginPage.route;
static const signup = SignupPage.route;
}
다음으로 getPages
를 작성할 AppPages
클래스를 아래와 같이 작성한다.
class AppPages {
static final pages = [
GetPage(name: AppRoutes.main, page: () => const MainPage());
GetPage(name: AppRoutes.login, page: () => const LoginPage());
GetPage(name: AppRoutes.signup, page: () => const SignupPage());
];
}
이렇게 작성한 pages
를 GetMaterialAPP
의 getPages
에 연결시키면 된다.
GetMaterialApp(
...
getPages: AppPages.pages
...
)
- private
- super
- Get.parameter와 Get.arguments
private
은 멤버 변수나 멤버 함수에 사용할 수 있으며, 해당 변수나 함수를 클래스 내부에서만 참조하여 사용할 수 있게 한다.
하지만 클래스 안에서만 private가 사용 가능한 다른 언어와 달리 Dart에서는 클래스가 달라도 같은 파일에서는 접근이 가능하다.
Flutter에서 private
를 선언하는 방법은 [Flutter] 스나이퍼팩토리 22일차의 Getter와 Setter에서 간단히 설명한 적이 있는데 변수명이나 함수명 앞에 언더스코어 _
를 붙이면 된다.
아래의 예시 코드에서 User
클래스의 _name
변수와 _printName()
함수는 User
클래스 내부에서만 접근 가능하다. (단, 같은 파일에서는 클래스가 달라도 접근 가능!)
class User {
final String _name;
User(this._name);
_printName() {
print(_name);
}
}
이러한 private
변수를 사용하는 이유는 캡슐화와 자료 보호 때문이다.
클래스의 멤버 변수를 public
으로 선언하면 어디서든지 접근이 가능해 편리하긴 하지만, 의도하지 않은 곳에서 접근하여 값을 변경하는 일이 발생할 수 있다.
이러한 경우에는 어디서 잘못된 값으로 변경되었는지 모두 확인해야 한다. 따라서 수정이 쉽지 않다.
private
를 사용해 정보 은닉을 하면 이러한 일을 방지할 수 있다. 그렇기 때문에 혼자 진행하는 프로젝트가 아닌 다수가 함께 참여하는 프로젝트의 경우에는 private
의 사용이 매우 중요하다.
Flutter에서 여러 클래스들을 생성해 보면서 super
라는 키워드를 많이 접했을 것이다. super
키워드를 예시 코드를 통해 정리해보고자 한다.
우선 아래와 같은 Person
클래스를 생성했다.
class Person {
final String name;
final int age;
Person(
this.name,
this.age,
);
}
Person
클래스를 상속받는 Student
클래스를 아래와 같이 작성했다.
class Student extends Person {
int grade;
Student(
int grade,
String name,
int age,
):this.grade = grade,
super(
name = name,
age = age,
);
printInfo() {
print('이름: ${super.name}');
print('나이: ${this.age}');
print('학년: ${this.grade}');
}
}
두 가지 클래스를 만들고, 아래와 같이 main()
함수를 작성하여 실행하면 이름과 나이, 학년이 정상적으로 출력된다.
void main() {
var student = Student(1, 'Kim', 17);
student.printInfo();
//결과
//이름: Kim
//나이: 17
//학년: 1
}
이를 통해 super
키워드를 사용하면 상속받은 자식 클래스에서 부모 클래스에 접근할 수 있다는 것을 알 수 있다.
또한 this
키워드는 자기 자신의 클래스를 지칭한다. 하지만 위의 예시 코드에서 Student
클래스에 age
변수는 존재하지 않는데 this.age
로 접근해도 정상적인 값이 출력된다.
그 이유는 자식 클래스가 부모 클래스의 모든 것을 상속 받기 때문에 this
키워드를 사용해도 부모의 age
변수에 접근할 수 있었던 것이다.
만약 age
변수를 Student
클래스에 추가한다면 자식 클래스에서 해당 변수를 재정의 하게 된다. 이 경우에는 this
를 사용하면 재정의된 자기 자신의 변수에 접근하고, super
를 사용했을 때 부모의 변수에 접근할 수 있을 것이다.
GetPage의 URL 방식 네비게이션을 활용한 페이지 접근은 원하는 데이터를 포함하여 다음 페이지로 이동할 수 있다.
이러한 방식에는 두 가지가 존재하는데 Get.parameters
와 Get.arguments
이다.
Get.paramters
는 전달받은 paramters
를 사용할 수 있게 해준다.
사용 방식은 Get.toNamed()
의 page
의 URL의 뒤에 전달할 데이터를 추가하면 된다. 아래의 예시는 1234를 전달하는 코드이다.
Get.toNamed('/page/1234');
전달된 값을 받으려먼 Get.parameters
를 사용하면 된다.
Get.parameters['param'];
또한 쿼리를 사용해 데이터를 전달할 수도 있다.
Get.toNamed('/page/value?id=123&pw=456');
쿼리로 전달 받은 값도 Get.parameters
를 사용해 받을 수 있다.
Get.parameters['param']; // value 값
Get.parameters['id']; // id 값, 123
Get.parameters['pw']; // pw 값, 456
arguments
를 페이지 이동과 함께 전달하고 싶다면 Get.toNamed()
의 arguments
를 사용하면 된다. (Get.to()
에서도 사용 가능)
Get.toNamed('/page', arguments: value);
value
에 원하는 값을 넣으면 되며, Get.arguments
를 통해 값을 사용할 수 있다.
Get.arguments;
만약 뒤로 페이지를 돌아갈 때 값을 전달하고 싶다면
Get.back
의result
를 설정하면 된다.Get.back(result: value);
Get.toNamed
를 활용하여 데이터를 넘기는 두 가지 방법을 구현해 보고자 한다.
아래는 데이터를 전달받을 DetailPage
의 예시 코드이다.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
static const String route = '/detail';
Widget build(BuildContext context) {
var params = Get.parameters;
var arguments = Get.arguments;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$params'),
Text('$arguments'),
],
),
),
);
}
}
만들 페이지의 결과물은 아래와 같다.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:new_app/page/main_page.dart';
import 'package:new_app/util/app_pages.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return GetMaterialApp(
getPages: AppPages.pages,//페이지 라우팅 설정
home: const MainPage(), //MainPage 호출
);
}
}
main.dart
에서는 getPages
로 페이지 라우팅을 설정하는데 앱의 라우트는 AppRoutes
로 앱의 페이지 이동은 AppPages
클래스로 따로 작성해 보았다.
import 'package:new_app/page/detail_page.dart';
import 'package:new_app/page/main_page.dart';
class AppRoutes {
static const main = MainPage.route;
static const detail = DetailPage.route;
}
AppRoutes
는 앱의 페이지들의 라우트를 가지고 있다.
import 'package:get/get.dart';
import 'package:new_app/page/detail_page.dart';
import 'package:new_app/page/main_page.dart';
import 'package:new_app/util/app_routes.dart';
class AppPages {
static final pages = [
GetPage(name: AppRoutes.main, page: () => const MainPage()),
GetPage(name: AppRoutes.detail, page: () => const DetailPage()),
];
}
AppPages
는 저장한 라우트를 사용해 페이지를 라우팅한 pages
를 가지고 있다.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:new_app/util/app_routes.dart';
class MainPage extends StatelessWidget {
const MainPage({super.key});
static const String route = '/main';
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextButton(
onPressed: () => Get.toNamed(
'${AppRoutes.detail}/value?a=3', //parameters를 전달
arguments: 'Hello world!', //arguments를 전달
),
child: const Text('Detail Page로 이동'),
),
),
);
}
}
DetailPaage
가 Get.parameters
와 Get.arguments
로 모두 데이터를 받아 출력하기 때문에 Get.toNamed
의 page
에 parameter
를 보낼 수 있도록 /value?a=3
을 더해주었고, arguments
는 Hello world!
를 보냈다.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
static const String route = '/detail';
Widget build(BuildContext context) {
var params = Get.parameters;
var arguments = Get.arguments;
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$params'),
Text('$arguments'),
],
),
),
);
}
}
DetailPage
는 주어진 예시 코드를 그대로 사용했다. 해당 페이지는 전달받은 parameters
와 arguments
를 출력한다.
어제에 이어서 오늘도 GetX를 학습했다. 새로운 방식으로 이동하는 것을 배웠고, 추가로 몇 가지 키워드들도 알아보았다. 오늘따라 후기에 쓸 말이 생각이 안나네...ㅋㅋㅋㅋ 오늘은 짧게 마무리 하겠습니다!!