10-01 TIL(Today I Learned)
Resolving conflicts
개발자 1과 2가 같은 버전에서 기반하여 브랜치를 생성해서 같은 파일을 수정하고 push하려고 할 때 병합 충돌이 발생한다.
병합 충돌이 어떤 브랜치에서 발생했는지 보기 위해 다음 커맨드를 실행한다.
git log --merge
(병합된 커밋에 관한 정보만 표시)
commit 79bca730b68e5045b38b96bec35ad374f44fe4e3 (HEAD -> feature2)
Author: Developer 2
<developer2@demo.com>
Date: Sat Jan 29 16:55:40 2022 +0000
chore: add feature 2
commit 678b0648107b7c53e90682f2eb8103c59f3cb0c0
Author: Developer 1
<developer1@demo.com>
Date: Sat Jan 29 16:53:40 2022 +0000
chore: add feature 1
여기서 feature 1과 2 브랜치에서 충돌이 발생했음을 알 수 있다. 다음으로 변경된 코드 중 어느 부분이 충돌을 일으켰는지 보기 위해 git diff
명령어를 사용한다.
git diff
diff --cc Feature.js
index 1b1136f,c3be92f..0000000
--- a/Feature.js
+++ b/Feature.js
@@@ -1,4 -1,4 +1,8 @@@
let add = (a, b) => {
++<<<<<<< HEAD
+ if(a + b > 10) { return 'way too much'}
++=======
+ if(a + b > 10){ return 'too much' }
++>>>>>>> d3b3cc0d9b6b084eef3e0afe111adf9fe612898e
return a + b;
}
-
index 1b1136f,c3be92f..0000000
이 부분은 파일의 해시(고유 식별자)를 나타낸다. 변경된 버전(1b1136f)과 변경 후 버전(c3be92f)의 해시를 비교한다.
-- a/Feature.js
: 변경 전 파일을 나타내며,a/Feature.js
는 원본 파일의 경로를 나타낸다.+++ b/Feature.js
: 변경 후 파일을 나타내며,b/Feature.js
는 변경된 파일의 경로를 나타낸다.<<<<<<< HEAD
: 이 부분은 현재 작업 중인 브랜치의 변경 내용을 나타냅니다. HEAD는 현재 작업 중인 브랜치를 가리킨다.=======
는 HEAD 브랜치와 다른 브랜치의 변경 내용을 구분하는 구분선이다.-
d3b3cc0d9b6b084eef3e0afe111adf9fe612898e 는 다른 브랜치의 변경 내용을 나타내며, d3b3cc0d9b6b084eef3e0afe111adf9fe612898e는 해당 브랜치의 커밋 해시를 나타낸다.
Mixin
Mixin은 클래스에 추가할 수 있는 재사용 가능한 메서드, 속성 또는 다른 코드 블록들의 집합을 말한다.
코드 예시로는 여러 종류의 동물원을 관리하는 프로그램을 만든다고 가정해보자. 각 동물들은 공통적인 기능을 가지고 있지만, 각자의 특별한 특성도 가지고 있다. 이런 경우에 mixin을 사용하여 코드의 재사용성을 높일 수 있다.
// 기본 동물 클래스
class Animal {
String name;
Animal(this.name);
void makeSound() {
print('동물 소리');
}
}
// 비행 기능을 가진 mixin
mixin Flyable {
void fly() {
print('날기');
}
}
// 수영 기능을 가진 mixin
mixin Swimmable {
void swim() {
print('수영하기');
}
}
// 비둘기 클래스: 동물 클래스를 확장하면서 Flyable mixin을 mixin
class Pigeon extends Animal with Flyable {
Pigeon(String name) : super(name);
}
// 고래 클래스: 동물 클래스를 확장하면서 Swimmable mixin을 mixin
class Whale extends Animal with Swimmable {
Whale(String name) : super(name);
}
기본 동물이라는 객체를 설계할 때 위의 Animal이라는 클래스를 보면 name이라는 속성과 makeSound 라는 메서드를 가지도록 했다.
그리고 비행 기능을 가진 mixin과 수영 기능을 가진 mixin을 만들고 비둘기 클래스와 고래 클래스를 만들 때 Animal이라는 클래스를 상속(비둘기, 고래 둘 다 동물이므로 동물이 가진 속성을 또 선언하지 않아도 사용할 수 있다)하고 비둘기는 Flyable이라는 mixin을 추가하고 고래는 Swimmable이라는 mixin을 추가해 다음과 같이 사용할 수 있다.
void main() {
var pigeon = Pigeon('비둘기');
pigeon.makeSound(); // 동물 소리
pigeon.fly(); // 날기
var whale = Whale('고래');
whale.makeSound(); // 동물 소리
whale.swim(); // 수영하기
}
비둘기와 고래 인스턴스를 생성했고 Animal 클래스의 method 뿐만 아니라 각 동물의 특성에 맞는 mixin을 사용할 수 있음을 알 수 있다.
Flutter Animation
Essential animation concepts and classes
What’s the point?
- Animation, a core class in Flutter’s animation library, interpolates the values used to guide an animation.
- An
Animation
object knows the current state of an animation (for example, whether it’s started, stopped, or moving forward or in reverse), but doesn’t know anything about what appears onscreen. - An AnimationController manages the
Animation
. - A CurvedAnimation defines progression as a non-linear curve.
- A Tween interpolates between the range of data as used by the object being animated. For example, a
Tween
might define an interpolation from red to blue, or from 0 to 255. -
Use
Listener
s andStatusListener
s to monitor animation state changes. -
interpolate(보간하다)는 추정의 한 유형(estimation)으로 알려진 이산 데이터의 범위를 기반으로 새로운 데이터 포인트를 구성(찾는) 방법을 말한다.
애니메이션에서는 애니메이션의 시작지점과 끝지점의 중간 값을 계산하고 시간에 따라 부드럽게 변경함으로써 애니메이션을 만들 수 있다.
애니메이션
객체는 현재 애니메이션 상태는 알고 있지만 화면에 나타나는 내용에 대해서는 아무것도 알지 못한다.-
“
CurvedAnimation
은 진행을 비선형 곡선으로 정의한다”이 문장을 뜯어보면, ‘진행’이란 애니메이션의 진행 상태를 의미하는 것이고 ‘비선형’은 애니메이션의 진행 속도가 일정하지 않게 변하는 것을 의미하며 “곡선”이라는 의미는 애니메이션의 진행 상태와 시간의 관계가 직선이 아니라 곡선 형태로 정의됨을 의미함.
Animation
In Flutter, an Animation
object knows nothing about what is onscreen. An Animation
is an abstract class that understands its current value and its state (completed or dismissed). One of the more commonly used animation types is Animation<double>
.
여기서 Animation
은 현재의 값과 상태(완료 또는 끝)를 이해하는 abstract class이다.
이 때, abstract class
란 직접 인스턴스(객체)를 생성할 수 없으며, 다른 클래스들이 공통으로 가져야 하는 특성과 동작을 정의하는 데 사용된다. 즉, Animation
는 위와 같은 특성을 가지고 있으므로 Animation
를 상속받는 구체적인 클래스를 만들어서 원하는 애니메이션을 정의하고 사용한다.
An Animation
object sequentially generates interpolated numbers between two values over a certain duration. The output of an Animation
object might be linear, a curve, a step function, or any other mapping you can devise. Depending on how the Animation
object is controlled, it could run in reverse, or even switch directions in the middle.
Animation
객체는 일정한 시간동안 두 값 사이의 중간 값을 순차적으로 생성한다.
Animations can also interpolate types other than double, such as Animation<Color>
or Animation<Size>
.
애니메이션은 숫자뿐 아니라 색깔과 크기와 같은 다른 유형도 보간할 수 있다.
An Animation
object has state. Its current value is always available in the .value
member.
애니메이션 객체는 상태를 가지고 있다. 현재 값은 항상 .value
멤버에서 사용할 수 있다.
An Animation
object knows nothing about rendering or build()
functions.
애니메이션 객체는 렌더링이나 build()
함수에 대해서 전혀 알지 못한다.
AnimationController
AnimationController is a special Animation
object that generates a new value whenever the hardware is ready for a new frame. By default, an AnimationController
linearly produces the numbers from 0.0 to 1.0 during a given duration. For example, this code creates an Animation
object, but does not start it running:
애니메이션을 초단위로 나눠서 생각해보면 불연속적인 화면을 시간의 흐름에 따라 이어가면서 부드럽게 구현한다고 생각하면 된다. 따라서 AnimationController
는 애니메이션이 재생되는 시간 동안 잘게 나눠서 시간마다 생성할 화면이 준비될 때마다 새로운 값을 생성하는 특수한 애니메이션 객체라고 생각하면 된다.
예를 들어서, 아래 코드에서 2초 동안 애니메이션을 진행하는데 2초 동안 0.0에서 1.0까지의 숫자를 선형으로 생성한다.
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
AnimationController
derives from Animation<double>
, so it can be used wherever an Animation
object is needed. However, the AnimationController
has additional methods to control the animation. For example, you start an animation with the .forward()
method. The generation of numbers is tied to the screen refresh, so typically 60 numbers are generated per second. After each number is generated, each Animation
object calls the attached Listener
objects. To create a custom display list for each child, see RepaintBoundary.
여기서 숫자의 생성에 대한 이야기가 무슨 말인지 튕길 수 있는데, 화면 주사율을 생각해보면 된다. 주사율은 1초당 화면이 갱신되는 횟수를 뜻하는데, 60Hz라는 말은 1초 동안 화면을 60단계로 쪼개서 보여줄 수 있다는 의미다. 위에서 AnimationController
는 화면이 갱신될 때마다 숫자를 생성한다고 했다. 따라서 숫자가 생성된다는 것은(0.0 ~ 1.0) 화면이 갱신되면서 애니메이션이 진행되고 있다는 것을 뜻한다.
When creating an AnimationController
, you pass it a vsync
argument. The presence of vsync
prevents offscreen animations from consuming unnecessary resources. You can use your stateful object as the vsync by adding SingleTickerProviderStateMixin
to the class definition. You can see an example of this in animate1 on GitHub.
위 코드에서 vsync
키워드가 생소할 텐데, 작은 장난감 자동차 애니메이션을 상상해보자. 내 노트북 스크린은 초당 60번 화면을 갱신하는데, 자동차는 초당 200번 화면을 갱신한다. 즉, 스크린과 자동차의 화면 주사율이 다르기 때문에 스크린의 화면이 갱신되는 동안 화면 밖에서 자동차 애니메이션이 더 많은 갱신을 일으키고 있어 불필요한 리소스를 소비하게 된다.
요약하자면, vsync
는 애니메이션을 화면 주사율에 맞추어 실행하여 화면 밖에서 실행되는 애니메이션의 무분별한 리소스 소비를 방지하는 것이라고 생각할 수 있다.
Tween
AnimationController
객체는 0.0에서 1.0까지의 범위를 가짐. 다른 범위나 다른 데이터 유형이 필요하다면, Tween
객체를 사용하여 애니메이션을 다른 범위나 다른 데이터 유형으로 보간하도록 구성할 수 있음.
tween = Tween<double>(begin: -200, end: 0);
Tween
은 오직 begin
와 end
파라미터를 갖는 상태가 없는 객체임. Tween
의 주요 역할은 입력 범위에 따른 출력 범위로 계산하여 반환하는 것이다.
Tween
은 Animation
대신 Animatable<T>
에서 상속받는다. Animatable
은 Animation
처럼 반드시 double을 출력할 필요는 없다. 예를 들어, ColorTween은 두 개의 색상 사이의 진행을 지정합니다.
colorTween = ColorTween(begin: Colors.transparent, end: Colors.black54);
Tween
객체는 어떠한 상태도 저장하지 않고 대신 애니메이션의 현재 값(0.0 과 1.0 사이)을 실제 애니메이션 값으로 대응하여 반환하는 transform
함수를 사용하는 evaluate
함수를 제공한다.
Animation notifications
An Animation object can have Listener
s and StatusListener
s, defined with addListener()
and addStatusListener()
.
- A
Listener
is called whenever the value of the animation changes. The most common behavior of aListener
is to callsetState()
to cause a rebuild. - A
StatusListener
is called when an animation begins, ends, moves forward, or moves reverse, as defined byAnimationStatus
.
Animation examples
Rendering animations
기본 예제
import 'package:flutter/material.dart';
void main() => runApp(const LogoApp());
class LogoApp extends StatefulWidget {
const LogoApp({super.key});
@override
State<LogoApp> createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10),
height: 300,
width: 300,
child: const FlutterLogo(),
),
);
}
}
애니메이션을 추가한 예제
State<LogoApp> createState() => _LogoAppState();
}
- class _LogoAppState extends State<LogoApp> {
+ class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
+ late Animation<double> animation;
+ late AnimationController controller;
+
+ @override
+ void initState() {
+ super.initState();
+ controller =
+ AnimationController(duration: const Duration(seconds: 2), vsync: this);
+ animation = Tween<double>(begin: 0, end: 300).animate(controller)
+ ..addListener(() {
+ setState(() {
+ // The state that has changed here is the animation object's value.
+ });
+ });
+ controller.forward();
+ }
+
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10),
+ height: animation.value,
+ width: animation.value,
child: const FlutterLogo(),
),
);
}
+
+ @override
+ void dispose() {
+ controller.dispose();
+ super.dispose();
+ }
}
먼저late
키워드는 변수를 나중에 초기화할 것임을 나타내는 키워드이다. 즉, 변수를 선언할 때는 초기값을 제공하지 않고, 변수가 실제 사용되기 전에 값을 제공할 것임을 표시할 때 사용하는 키워드다.
Dart의 연쇄 표기(..
) 는 객체의 메서드를 연속적으로 호출할 때 이를 간결하게 만들어주는 문법이다.
var person = Person()
..setName('John')
..setAge(30)
..setEmail('john@example.com');
addListener()
함수는 애니메이션이 새로운 숫자(화면이 갱신됨, 즉 애니메이션이 진행됨)를 생성할 때마다 setState 함수를 호출하므로 build( )함수가 애니메이션이 끝날 때까지 호출된다.
아래의 build()
에서는Container
위젯에서 animation의 값을 받고 있으므로 애니메이션이 진행되는 동안 Container
의 크기가 변경된다.
Simplifying with AnimatedWidget
AnimatedWidget
기본 클래스를 사용하면 핵심 위젯 코드를 애니메이션 코드에서 분리할 수 있 AnimatedWidget
은 애니메이션을 보유하는 State 객체를 유지할 필요가 없다.
class AnimatedLogo extends AnimatedWidget {
const AnimatedLogo({super.key, required Animation<double> animation})
: super(listenable: animation);
@override
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10),
height: animation.value,
width: animation.value,
child: const FlutterLogo(),
),
);
}
}
즉, 위 코드와 차이를 살펴보면 LogoApp 이라는 핵심 위젯 코드 내에서 addListner 메서드를 사용하여 애니메이션의 상태를 관리했다.(initState( )
내에서 선언)
하지만 위와 같이 AnimatedWidget
를 상속받아서 위젯을 생성하게 되면 LogoApp이라는 더 큰 단위의 위젯에서 애니메이션 관련 위젯을 manage 하지 않을 수 있다.
import 'package:flutter/material.dart';
void main() => runApp(const LogoApp());
+ class AnimatedLogo extends AnimatedWidget {
+ const AnimatedLogo({super.key, required Animation<double> animation})
+ : super(listenable: animation);
+
+ @override
+ Widget build(BuildContext context) {
+ final animation = listenable as Animation<double>;
+ return Center(
+ child: Container(
+ margin: const EdgeInsets.symmetric(vertical: 10),
+ height: animation.value,
+ width: animation.value,
+ child: const FlutterLogo(),
+ ),
+ );
+ }
+ }
+
class LogoApp extends StatefulWidget {
const LogoApp({super.key});
@override
State<LogoApp> createState() => _LogoAppState();
}
@@ -15,32 +33,18 @@
@override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
+ animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
}
+ Widget build(BuildContext context) => AnimatedLogo(animation: animation);
@override
void dispose() {
controller.dispose();
super.dispose();
}
위와 같이 LogoApp 이라는 위젯에서는 AnimatedLogo
라는 위젯으로 extract 해줌으로서 더 간결하게 해당 위젯 내 코드를 관리할 수 있다.
Refactoring with AnimatedBuilder
- 다른 위젯의 build 메서드의 일부로 애니메이션을 보여주려면
AnimatedBuilder
를 사용하고 단순히 재사용 가능한 애니메이션을 가진 위젯을 정의하려면AnimatedWidget
를 사용하는 것이 좋다. -
플러터 .API에서
AnimatedBuilders
의 예시로는 다음과 같다.BottomSheet
,ExpansionTile
,PopupMenu
,ProgressIndicator
,RefreshIndicator
,Scaffold
,SnackBar
,TabBar
,TextField
.
One problem with the code in the animate3 example, is that changing the animation required changing the widget that renders the logo. A better solution is to separate responsibilities into different classes:
- Render the logo
- Define the
Animation
object - Render the transition
You can accomplish this separation with the help of the AnimatedBuilder
class. An AnimatedBuilder
is a separate class in the render tree. Like AnimatedWidget
, AnimatedBuilder
automatically listens to notifications from the Animation
object, and marks the widget tree dirty as necessary, so you don’t need to call addListener()
.
The widget tree for the animate4 example looks like this:
Starting from the bottom of the widget tree, the code for rendering the logo is straightforward:
class LogoWidget extends StatelessWidget {
const LogoWidget({super.key});
// Leave out the height and width so it fills the animating parent
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 10),
child: const FlutterLogo(),
);
}
}
위 그림의 중간 세 블록은 모두 아래에 표시된 GrowTransition
내의 build() 메서드에서 생성된다.
GrowTransition
위젯 자체는 애니메이션 상태를 가지지 않고 아래 생성자에서 볼 수 있듯이, child(애니메이션을 그릴 위젯)과 animation
객체를 전달받고 있다.
이를 활용하여 The build() 함수는 익명함수(Anonymous
builder) 와 LogoWidget
객체를 파라미터로 가지는 AnimatedBuilder
를 생성하고 반환한다. 이 때, 실제로 화면에 애니메이션을 구현하는 것은 익명함수(Anonymous
builder) 가 수행하고 애니메이션을 적용할 객체를 감쌀 Container
위젯을 생성한다.
class GrowTransition extends StatelessWidget {
const GrowTransition(
{required this.child, required this.animation, super.key});
final Widget child;
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: animation,
builder: (context, child) {
return SizedBox(
height: animation.value,
width: animation.value,
child: child,
);
},
child: child,
),
);
}
}
바깥 참조(outer reference)란 변수가 자신이 정의된 스코프가 아닌 다른 스코프에서 사용되는 것을 의미한다. 즉, 위 코드에서 child 라는 변수가 두 번 지정된 것처럼 보이지만 AnimatedBuilder
내부의 익명 함수(builder)에서 child를 참조하고 있는데 이 child 변수는 익명 함수의 바깥에서 선언된 AnimatedBuilder
의 child 인자로 받은 child를 참조하고 있는 것! 이다.
최종적으로iniState()
메서드에서 AnimationController
와 Tween
을 생성하고 이들을 animate()
메서드와 바인딩한다. 실제 애니메이션은 build()
내에서 일어난다.
import 'package:flutter/material.dart';
void main() => runApp(const LogoApp());
-
+ class LogoWidget extends StatelessWidget {
+ const LogoWidget({super.key});
+
+ // Leave out the height and width so it fills the animating parent
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsets.symmetric(vertical: 10),
+ child: const FlutterLogo(),
+ );
+ }
+ }
+
+ class GrowTransition extends StatelessWidget {
+ const GrowTransition(
+ {required this.child, required this.animation, super.key});
+
+ final Widget child;
+ final Animation<double> animation;
@override
Widget build(BuildContext context) {
return Center(
- child: Container(
- margin: const EdgeInsets.symmetric(vertical: 10),
- height: animation.value,
- width: animation.value,
- child: const FlutterLogo(),
+ child: AnimatedBuilder(
+ animation: animation,
+ builder: (context, child) {
+ return SizedBox(
+ height: animation.value,
+ width: animation.value,
+ child: child,
+ );
+ },
+ child: child,
),
);
}
}
class LogoApp extends StatefulWidget {
const LogoApp({super.key});
@override
State<LogoApp> createState() => _LogoAppState();
@@ -34,18 +54,23 @@
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
}
@override
+ Widget build(BuildContext context) {
+ return GrowTransition(
+ animation: animation,
+ child: const LogoWidget(),
+ );
+ }
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
Simultaneous animations
The Curves class defines an array of commonly used curves that you can use with a CurvedAnimation.
Curves
클래스는 CurvedAnimation
과 함께 사용할 수 있는 커브 배열을 정의한다.
이 때 ‘배열’ 이란 동일한 메모리 크기의 요소(값 또는 변수) 컬렉션으로 구성된 데이터 구조를 일컫는 것으로, 각 요소는 적어도 하나의 배열 인덱스나 키로 식별된다.
이때 ‘컬렉션’ 이란 해결 중인 문제에 대해 어느 정도 공유된 의미를 갖는 개수가 변할 수 있는 데이터 항목(0이 될수도 있음)들을 그룹화한 것을 일컫는다.
Each tween manages an aspect of the animation. For example:
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
sizeAnimation = Tween<double>(begin: 0, end: 300).animate(controller);
opacityAnimation = Tween<double>(begin: 0.1, end: 1).animate(controller);
sizeAnimation.value
로 크기를 opacityAnimation.value
로 투명도를 가져올 수 있지만, AnimatedWidget
위젯의 생성자는 오직 하나의 Animation
객체를 사용한다. 이 문제를 해결하기 위해 예시에는 자체 Tween
객체를 생성하고 명시적으로 값을 계산한다.
트윈 객체를 생성할 때 시작값과 끝값을 직접 지정하여 애니메이션이 어떻게 변할지를 명시적으로 정의함.
AnimatedLogo
를 자체 Tween
객체로 캡슐화하도록 변경하고, build()
메서드에서 부모의 애니메이션 객체에 대한 Tween.evaluate()
를 호출하여 필요한 크기 및 투명도 값을 계산한다.
class AnimatedLogo extends AnimatedWidget {
const AnimatedLogo({super.key, required Animation<double> animation})
: super(listenable: animation);
// Make the Tweens static because they don't change.
**static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
static final _sizeTween = Tween<double>(begin: 0, end: 300);**
@override
Widget build(BuildContext context) {
final animation = listenable as Animation<double>;
return Center(
**child: Opacity(
opacity: _opacityTween.evaluate(animation),**
child: Container(
margin: const EdgeInsets.symmetric(vertical: 10),
height: **_sizeTween.evaluate(animation),**
width: **_sizeTween.evaluate(animation),**
child: const FlutterLogo(),
),
),
);
}
}
class LogoApp extends StatefulWidget {
const LogoApp({super.key});
@override
State<LogoApp> createState() => _LogoAppState();
}
class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {
late Animation<double> animation;
late AnimationController controller;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = **CurvedAnimation(parent: controller, curve: Curves.easeIn)**
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
Widget build(BuildContext context) => AnimatedLogo(animation: animation);
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
캡슐화(encapsulate)란 데이터를 그 데이터를 조작하는 메커니즘 또는 메서드와 함께 묶는 것을 의미하기도 하며, 객체의 구성 요소와 같은 데이터 중 일부에 직접 접근하는 것을 제한하는 것을 의미하기도 함. 아래 코드에서 Tween
객체를 클래스 내에서만 공유 가능하도록 Static final private 변수로 선언함으로써 캡슐화를 했다고 이해할 수 있다.
이 때 외부에서 접근할 수 없다는 것은 해당 변수나 메서드가 동일한 클래스나 라이브러리의 외부에서 직접 사용할 수 없다는 것을 의미한다.
class Example {
int _privateValue = 42;
void _privateMethod() {
print("This is a private method");
}
void publicMethod() {
// 외부에서 호출 가능한 공개 메서드
print("This is a public method");
}
}
void main() {
var obj = Example();
// 공개 메서드 호출 가능
obj.publicMethod(); // 출력: This is a public method
// _privateValue 및 _privateMethod에 직접 접근 불가능
print(obj._privateValue); // 컴파일 오류
obj._privateMethod(); // 컴파일 오류
}
static
키워드를 사용하면 클래스의 모든 인스턴스가 공유하는 변수 또는 메서드로 만들 수 있고 이러한 변수 또는 메서드는 객체가 아닌 클래스 자체에 속하며, 클래스 이름을 통해 접근할 수 있다.
댓글남기기