아래와 같이 자주 쓰는 색을 변수로 저장하여, 재사용성이 높아져 코드의 유지보수성이 증가한다.
import 'package:flutter/material.dart';
// Basic Color
const Color kWhiteColor = Color(0xFFFFFFFF);
const Color kBlackColor = Color(0xFF000000);
// System Color
const Color kSystemErrorColor = Color(0xFFFC444F);
const Color kSystemSuccessColor = Color(0xFF30E181);
const Color kSystemWarningColor = Color(0xFFFFD362);
// Color Pallette
const Color kPrimaryColor = Color(0xFF4880EE);
Color kPrimaryColorOp = const Color(0xFF4880EE).withOpacity(0.35);
const Color kIconBackgroundColor = Color(0xFFFAFAFC);
const Color kKakaoBackgroundColor = Color(0xFFFEE500);
const Color kFacebookBackgroundColor = Color(0xFF1E70E9);
// 텍스트 폼
const Color kTextLeftBarColor = Color(0xFF4B6AEA);
const Color kTextBottomBarColor = Color(0xFF5991F4);
const Color kGrayF2 = Color(0xFFF2F3F5);
const Color kGrayF5 = Color(0xFFF5F5F5);
const Color kGrayF8 = Color(0xFFF8F8FA);
const Color kGrayAF = Color(0xFFAFB7BF);
const Color kGray87 = Color(0xFF878B95);
const Color kBlue37 = Color(0xFF3767D3);
// nav
const Color kNavBackground = Color(0xFF1F1F1F);
const Color kPapsText = Color(0xFF3F4656);
linter:
rules:
avoid_print: true // release모드에서 출력 안되게 변경
prefer_single_quotes: true // single quote 통일
require_trailing_comma: true
always_use_package_imports: true // 절대경로 사용
prefer_final_locals: true // final 사용 (컴파일러 성능 향상)
camel_case_types: true // camel_case_types
위젯 트리에서 개별 UI 요소를 찾으려면도구 모음에서위젯 모드 선택버튼을클릭합니다.이렇게 하면 기기의 앱이 "위젯 선택" 모드로 전환됩니다.앱 UI에서 위젯을 클릭합니다.이것은 앱 화면에서 위젯을 선택하고 위젯 트리를 해당 노드로 스크롤합니다.위젯 모드 선택버튼을 다시토글하여위젯 선택 모드를 종료합니다.
레이아웃 문제를 디버깅할 때 살펴봐야 할 핵심 필드는size및constraints필드입니다.제약 조건은 트리 아래로 흐르고 크기는 다시 위로 흐릅니다.
Track widget creation
Flutter 관리자가 소스 코드에서 UI가 정의된 방식과 유사한 방식으로 위젯 트리를 표시할 수 있습니다.
flutter run --track-widget-creation
--track-widget-creation 을 사용할 경우 다음과 같이 보기 편하게 구성됩니다.
--track-widget-creation 을 사용하지 않을 경우 위젯 트리의 깊이가 깊어져 보기 불편합니다.
3) Timeline view
기능
프레임 렌더링 차트
프레임 이벤트 차트
CPU 프로파일러
프레임 렌더링 차트
프레임 이벤트 차트
프레임 이벤트 차트는 단일 프레임에 대한 이벤트 추적을 보여줍니다.최상위 이벤트는 그 아래에 이벤트를 생성하는 식입니다.UI 및 GPU 이벤트는 별도의 이벤트 흐름이지만 공통 타임라인을 공유합니다(프레임 차트 상단에 표시됨).이 타임라인은 주어진 프레임에만 적용됩니다.모든 프레임이 공유하는 시계는 반영하지 않습니다.
CPU profiler
프로파일러의 이 탭에는 선택한 프레임 이벤트(예: 다음 예의 레이아웃)에 대한 CPU 샘플이 표시됩니다.이 차트는 맨 위의 스택 프레임이 아래의 스택 프레임을 호출하는 하향식 스택 추적으로 보아야 합니다.각 스택 프레임의 너비는 CPU를 사용한 시간을 나타냅니다.CPU 시간을 많이 소비하는 스택 프레임은 가능한 성능 향상을 찾기에 좋은 위치일 수 있습니다.
call tree top-down call tree view는 CPUprofile에 대한 메서드 trace를 보여줍니다. 이 테이블은profile을 top-down 으로 표현한 것입니다. 즉, callees 를 표시하도록 메서드를 확장할 수 있습니다 .
Total Time 메서드가 자체 코드와 호출 수신자에 대한 코드를 실행하는 데 소요한 시간입니다.
Self Time 메서드가 자체 코드만 실행하는 데 소요된 시간입니다.
Method 호출된 메서드의 이름입니다.
Source 메서드 호출 사이트의 파일 경로입니다.
call tree bottom-up
bottom up view는 CPUprofile에대한 메서드 추적을 보여주지만 이름에서 알 수 있듯이 profile의 bottom-up 표현입니다.즉, 테이블의 각 최상위 메서드는 실제로 주어진 CPU 샘플에 대한 호출 스택의 마지막 메서드입니다(즉, 샘플의 리프 노드).
이 표에서 메서드를 확장하여호출자를 표시할 수 있습니다.
Total Time
메서드가 자체 코드와 호출 수신자에 대한 코드를 실행하는 데 소요한 시간입니다.
Self Time
상향식 트리의 최상위 메서드(profile의 리프 스택 프레임)의 경우 메서드가 자체 코드만 실행하는 데 소요된 시간입니다.하위 노드(CPUprofile의 callees)의 경우 호출자가 호출할 때 호출 수신자의 자체 시간입니다.다음 예에서 호출자의 자체 시간은 호출자Element.updateSlotForChild.visit( )가 호출[Stub] OneArgCheckInLineCache할 때수신자의 자체 시간과 같습니다.
Method
호출된 메서드의 이름입니다.
Source
메서드 호출 사이트의 파일 경로입니다.
4) Memory view
클래스 생성자를 사용하여 생성된 할당된 Dart 개체(예:new MyClass()또는사용MyClass())는힙이라고 하는 메모리 부분에 있습니다.
DevTools 메모리 창을 사용하면 특정 순간에 Isolate가 메모리를 어떻게 사용하는지 확인할 수 있습니다.(Isolate는 모든 Dart 코드가 실행되는 곳. 단일 스레드가 이벤트 루프를 실행하고 있다. )이 창은 Snapshot및 reset을 사용하여 accumulator count를 표시할 수 있습니다.어플리케이션에서 memory leak이 의심되거나 memory allocation과 관련된 다른 버그가 있는 것으로 의심되는 경우 accumulator를 사용하여 메모리 할당 속도를 연구할 수 있습니다.
Memory profiling은 네 부분으로 구성되며 각 부분은 세분화됩니다.
Memory overview chart
Event timeline
Snapshot classes
Class instances
Memory anatomy
시계열 그래프를 사용하여 연속적인 시간 간격에서 Flutter 메모리의 상태를 시각화합니다.차트의 각 데이터 포인트는usage,capacity,external,garbage collection및resident set size와 같은 힙의 측정된 양(y축)과 timestamp(x축)에 해당합니다.
Legend
메모리와 관련하여 수집된 모든 측정값입니다.범례 이름을 클릭하면 해당 데이터가 숨겨지거나 표시됩니다.
Range selector
수집된 모든 메모리 데이터(시계열).선택기에서 가장 왼쪽 또는 첫 번째 시간/데이터(메모리 정보)는 어플리케이션이 시작된 때입니다.가장 오른쪽 또는 마지막 시간/데이터는 어플리케이션이 중지될 때까지 수신(라이브)되는 연속 메모리 정보입니다.
Range selector view
이 시계열 범위(비회색 영역)에 대해 수집된 데이터의 상세 보기입니다.
X-axis timestamp
수집된 메모리 정보(capacity,used,external, RSS(resident set size), GC(garbage collection))의 시간.
Dart 힙에 없지만 여전히 전체 메모리 사용 공간의 일부인 메모리입니다. external memory의 object는 native object입니다(예: 파일에서 읽은 메모리 또는 디코딩된 이미지). native object는 Dart embedder를 사용하여 native OS(Android, Linux, Windows, iOS 등)에서 Dart VM에 노출됩니다.embedder는종료자가 있는 Dart 래퍼를 생성하여 Dart 코드가 이러한 기본 리소스와 통신할 수 있도록 합니다.Flutter에는 Android 및 iOS용 임베더가 있습니다.
이 차트는 메모리 차트 타임라인과 관련된 DevTools 이벤트(예: snapshot 및 reset 버튼 클릭)를 표시합니다.이벤트 타임라인의 마커 위로 마우스를 가져가면 이벤트가 발생한 시간이 표시됩니다.이렇게 하면 타임라인(x축)에서 메모리 누수가 발생한 시기를 식별하는 데 도움이 됩니다.
snapshot 버튼을 클릭하면 모든 active class 및 해당 인스턴스와 관련된 힙의 현재 status가 표시됩니다. 이때 reset 버튼을 누르면 0으로 모든 클래스의 accumulator가 0으로 reset됩니다. reset은 파란색 수평 막대를 사용하여 이전 snapshot에 일시적으로 연결됩니다. reset 버튼을 다시 클릭하면 마지막 reset 이후 accumulator가 재설정되고 최근 reset이 이전 reset에 일시적으로 연결됩니다.
Snapshot classes
이 창에는 힙에 할당된 클래스,total instances,total bytes allocated및 마지막 reset 이후accumulator of allocations가 표시됩니다.
Size
힙의 현재 개체가 사용하는 총 메모리 양입니다.
Count
힙에 있는 현재 개체의 총 수입니다.
Accumulator
마지막 재설정 이후 힙에 있는 총 개체 수입니다.
Class
이 클래스에 할당된 개체의 집계입니다.클래스 이름을 클릭하면 클래스 인스턴스 목록이 표시됩니다.
Shared Preferences 사용법에 대해서 알아본다. Shared Preferences는 key-value 형태의 데이터를 디스크에 저장해서 사용하는 방법으로 기존의 안드로이드 앱 개발에서도 자주 사용되어 왔다. 로그인이 필요한 앱을 개발할 때 사용자의 ID와 패스워드 등을 기억하는 기능을 구현할 때 이용할 수 있다.
유저가 실수로 체크하지 못한곳에서 발생하는 NullPointerException같은 에러를 조기에 방지하고 실제로 돌아가는 어셈블리상에서 null체크를 다시 안하기 때문에 성능적 이득도 있다.
자세히 말하면, null safety는 변수가 null을 허용하는지 구분하기 위한 개념인데 기본적으로 null을 허용하지 않는다. dart 언어는 null safety를 지원하지 않았기 때문에 변수 값이 초기화 되지 않았거나 null 값을 전달하면 컴파일 에러는 발생하지 않고 런타임 과정에서 오류가 발생했다. 이러한 이유로 코드를 작성할 땐 오류가 발생할 것을 예상하지 못하기 때문에 코드를 수정하고 다시 빌드하기까지의 개발 시간이 추가된다.
이를 해결하기 위해 null safety를 적용하여 개발자가 런타임 과정이 아닌 코드를 편집하는 과정에서 IDE로 부터 빠른 피드백을 받도록 하였다.
첫 번째 그림처럼, 기존 null safety를 적용하기 전에는 null이 모든 유형의 하위유형으로 취급되었다. 이런 이유로 여러 자료형들이 null 값을 가질 수 있지만 여러 메서드가 정의되어 있는 다른 자료형과 달리 null는 메서드가 정의되지 않다. 따라서 상위 자료형에 null값이 대입되고 메서드를 적용하려 하면, null은 상위 자료형이 가진 메서드가 없기 때문에 런타임 에러가 발생하는 것이다.
두 번째 그림이 null safety가 적용된 Non-nullable 타입 구조이다. Non-nullable에서는 null 자료형을 분리하여 다른 자료형들의 null을 허용하지 않는다. 하지만 자료형 뒤에 ?를 붙여 null 값을 허용하는 nullable 자료형을 제공하기 때문에 첫 번째 사진의 오류를 방지한다.
// 사용 불가
class ExampleData{
String title;
Widget route;
ExampleData({this.title, this.route});
}
이 코드는 null safety가 적용된 이후 사용할 수 없습니다.
// 먼저 required를 사용하는 방법으로
class ExampleData {
String title;
Widget route;
ExampleData({required this.title, required this.route});
// @required가 아닌 required입니다
}
// Null값이 들어갈 수 있을 땐 ?를 붙여줍니다
class ExampleData {
String? title;
Widget? route;
ExampleData({this.title, this.route});
}
// 초기값 부여
class ExampleData {
String title;
Widget route;
ExampleData({this.title = '', this.route = const Spacer()});
}
대신 required를 사용하거나 null값이 들어갈 수 있다고 표시를 해주거나 default값을 줘야됩니다
기존에 사용하던 @required는 우리가 import하는 material.dart 혹은 coupertino.dart에 있었던 속성이고 앞으로 사용할 required는 언어 수준에서 제공하는 속성입니다
3) null이 들어가지 않는다고 확실히 말하고 싶다면 ! 키워드를 붙여준다
color? 형식의 BorderSide color에, null이 아닌 Color 자료형의 값을 넣어주니 Color? 자료형은 Color를 assign할 수 없다는 에러가 났다. border 라이브러리 코드에서 color가 null이 아닐 때만 this.color를 내보내기 때문에 에러가 나는듯 하다. null값이 들어가지 않다고 확실하게 말해주는 '!' 키워드를 붙여주니 해결되었다.