Slide 1

Slide 1 text

맛픽 (ma’at pick) Flutter 개발기 loplat. sanghun.lee ([email protected])

Slide 2

Slide 2 text

안녕하세요! 저는 로플랫이란 실내위치 인식 기술을 보유한 스타트업에서 Android / iOS 개발자로 일하고있는 21살 개발자 이상훈입니다. “

Slide 3

Slide 3 text

작년에 출시한 맛픽 이라는 서비스가 있습니다.

Slide 4

Slide 4 text

작년에 출시한 맛픽 이라는 서비스가 있습니다.

Slide 5

Slide 5 text

최근에 1.0 정식 출시했습니다! 안드로이드만 지원합니다!

Slide 6

Slide 6 text

아이폰도 만들어주세요… 안드로이드랑 아이폰이랑 기능차이는 없으니 하이브리드로 가보자! 라는 이야기가 나왔습니다

Slide 7

Slide 7 text

가 유력 가 있다네..? 검색하다보니

Slide 8

Slide 8 text

일단 비교해보자!

Slide 9

Slide 9 text

React Native vs Flutter 그 때 당시를 기준으로 라이브러리가 많다 / 라이브러리가 많이 없다 레퍼런스가 많다 / 레퍼런스가 많이 없다 정식출시가 됐다 / 알파버전이다

Slide 10

Slide 10 text

출처 : https://developers.googleblog.com/2018/12/flutter-10-googles-portable-ui-toolkit.html 12/4 정식 출시

Slide 11

Slide 11 text

언어 사용

Slide 12

Slide 12 text

import 'dart:math' show Random; main() async { print('Compute π using the Monte Carlo method.'); await for (var estimate in computePi().take(500)) { print('π ≅ $estimate'); } } /// Generates a stream of increasingly accurate estimates of π. Stream computePi({int batch = 100000}) async* { var total = 0; var count = 0; while (true) { var points = generateRandom().take(batch); var inside = points.where((p) => p.isInsideUnitCircle); total += batch; count += inside.length; var ratio = count / total; // Area of a circle is A = π⋅r², therefore π = A/r². // So, when given random points with x ∈ <0,1>, // y ∈ <0,1>, the ratio of those inside a unit circle // should approach π / 4. Therefore, the value of π // should be: yield ratio * 4; } } Iterable generateRandom([int seed]) sync* { final random = Random(seed); while (true) { yield Point(random.nextDouble(), random.nextDouble()); } } class Point { final double x, y; const Point(this.x, this.y); bool get isInsideUnitCircle => x * x + y * y <= 1; }

Slide 13

Slide 13 text

import 'dart:math' show Random; main() async { print('Compute π using the Monte Carlo method.'); await for (var estimate in computePi().take(500)) { print('π ≅ $estimate'); } } /// Generates a stream of increasingly accurate estimates of π. Stream computePi({int batch = 100000}) async* { var total = 0; var count = 0; while (true) { var points = generateRandom().take(batch); var inside = points.where((p) => p.isInsideUnitCircle); total += batch; count += inside.length; var ratio = count / total; // Area of a circle is A = π⋅r², therefore π = A/r². // So, when given random points with x ∈ <0,1>, // y ∈ <0,1>, the ratio of those inside a unit circle // should approach π / 4. Therefore, the value of π // should be: yield ratio * 4; } } Iterable generateRandom([int seed]) sync* { final random = Random(seed); while (true) { yield Point(random.nextDouble(), random.nextDouble()); } } class Point { final double x, y; const Point(this.x, this.y); bool get isInsideUnitCircle => x * x + y * y <= 1; } 이정도 문법이면, 할만한걸?

Slide 14

Slide 14 text

@override Widget build(BuildContext context) { final bottomPadding = MediaQuery.of(context).padding.bottom; return new Scaffold( key: _scaffoldKey, backgroundColor: Colors.white, body: StoreConnector( converter: (store) => store.state.forceTouchAction, builder: (context, action) { return new Stack( children: [ Transform.scale( scale: 1.0 + (action.pressure == 0.0 ? 0 : action.pressure / 20), child: Stack(children: [ NotificationListener( child: PageView( controller: _pagerController, physics: NeverScrollableScrollPhysics(), children: [MainMyMaatPage(), Container()], ), onNotification: (overscroll) { overscroll.disallowGlow(); }, ), Container( alignment: Alignment.bottomLeft, child: ClipRect( child: BackdropFilter( filter: ImageFilter.blur( sigmaX: Platform.isIOS ? 20 : 0, sigmaY: Platform.isIOS ? 20 : 0), child: Container( height: 64 + MediaQuery.of(context).padding.bottom, decoration: new BoxDecoration( color: Colors.black.withOpacity(0.2)), child: Container( child: Stack( alignment: Alignment.topLeft, children: [ StoreConnector( converter: (store) => store .state.foodReviewedLoadingState, builder: (context, loadingState) { if (loadingState != null && loadingState.isLoading) { return new SizedBox( child: LinearProgressIndicator( backgroundColor: Colors.transparent, valueColor: 방금 한말 취소.

Slide 15

Slide 15 text

맛픽 (ma’at pick) Flutter 개발기 Flutter React Native 처럼 리엑티브 스타일 뷰 (Reactive Style View) 를 지원하는데, React Native의 JavaScript 브릿지 (브릿지)가 필요없고, Dart 라는 언어를 사용하여, 안드로이드 / 크롬 등에서 사용하는 Skia 엔진을 사 용합니다. Dart는 플랫폼에 따라서 네이티브 코드로 최적화되며, 퍼포먼스도 좋아집니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 16

Slide 16 text

맛픽 (ma’at pick) Flutter 개발기 안드로이드에서는 Activity, iOS에서는 ViewController, 그럼 Flutter 에서는? Flutter에서는 모두 ‘Widget’ 으로 통일합니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 17

Slide 17 text

맛픽 (ma’at pick) Flutter 개발기 안드로이드에서는 Activity, iOS에서는 ViewController, 그럼 Flutter 에서는? MaatPickSplashPageWidget ContainerWidget - BoxDecoration - LinearGradient ColumnWidget ImageWidget TextWidget LinearProgressBarWidget 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 18

Slide 18 text

맛픽 (ma’at pick) Flutter 개발기 StatelessWidget과 StatefulWidget StatelessWidget은 Flutter가 처음 위젯을 그리고 난 후에, 다시 그리지 않음을 의미합니다. StatefulWidget은 Flutter가 처음 위젯을 그리고 난 후에, 사용자의 반응, 특정 상태에 따라서 다시 그린다는 것을 의미합니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 19

Slide 19 text

맛픽 (ma’at pick) Flutter 개발기 StatelessWidget과 StatefulWidget의 구현방식 class MaatPickApp extends StatelessWidget { @override Widget build(BuildContext context) { // 여기에 위젯 배치
 }
 } class GettingStartMaatPickButton extends StatefulWidget { @override State createState() => new _GettingStartMaatPickButton(onPressed); } class _GettingStartMaatPickButton extends State { @override Widget build(BuildContext context) { // 여기에 위젯 배치
 } } 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 20

Slide 20 text

맛픽 (ma’at pick) Flutter 개발기 StatefulWidget의
 setState() setState() setState() 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 21

Slide 21 text

맛픽 (ma’at pick) Flutter 개발기 @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( onTap: this.onPressed, splashColor: const Color(0x40000000), highlightColor: const Color(0x20000000), child: Listener( onPointerDown: (status) { setState(() { _isTapped = true; }); }, onPointerUp: (status) { setState(() { _isTapped = false; }); }, behavior: HitTestBehavior.translucent, child: Container( height: 60, padding: EdgeInsets.only(top: 12, bottom: 12), decoration: BoxDecoration( border: Border.all( color: _isTapped ? Colors.orange : Colors.white), borderRadius: BorderRadius.circular(4)), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ new Image.asset( 'assets/icons/google_g_logo.png', width: 34, ), Padding( padding: EdgeInsets.only(left: 24), child: Text( 'Join with google', style: TextStyle( fontFamily: 'Oswald', fontWeight: FontWeight.normal, fontSize: 22, color: Colors.white), )) ], ), ))), ); } setState(() { _isTapped = true; }); setState(() { _isTapped = false; }); _isTapped ? Colors.orange : Colors.white) StatefulWidget의
 setState() 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 22

Slide 22 text

맛픽 (ma’at pick) Flutter 개발기 Hot Reload와 Hot Restart 코드를 작성하고 Ctrl+S 를 하는순간 바로 변경사항이 반영됨 Hot Reload 현재까지 작성된 코드를 기반으로 앱 재시작 Hot Restart 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 23

Slide 23 text

맛픽 (ma’at pick) Flutter 개발기 Material vs Cupertino Flutter에는 2가지의 UI 타입이 있습니다. Material은 안드로이드 (혹은 웹 등)에서 사용하고 있는 UI 스타일이며, 흔히 말하면 “안드로이드 스타일” 입니다. Cupertino는 iOS UI 스타일이며, 흔히 말하면 “iOS 스타일” 입니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 24

Slide 24 text

맛픽 (ma’at pick) Flutter 개발기 Material vs Cupertino 이미지 출처 : https://pub.dartlang.org/packages/flutter_platform_widgets 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 25

Slide 25 text

맛픽 (ma’at pick) Flutter 개발기 Material 사용 MaterialAppBarWidget CustomTabContainerWidget MaterialListViewWidget 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 26

Slide 26 text

맛픽 (ma’at pick) Flutter 개발기 상태 관리 글로벌하게 상태를 저장하고, 저장한걸 불러오고 해야할 때가 있습니다. 예를들어 API 호출을 한 후, 응답받은 다음 리스트를 보여줘야할 때 등이 있죠. 이럴 때 사용하는 것이 ‘상태관리’ 입니다. 상태관리에는 여러가지가 있지만, 제가 맛픽을 만들 때에 두가지 중 하나 (Redux / BLoC) 에서 고민했습니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 27

Slide 27 text

맛픽 (ma’at pick) Flutter 개발기 Redux 자바스크립트에서 앱에서 ‘상태(State)’ 를 관리해주는 하나의 도구이며, Flutter에서도 비록 자바스크립트는 아니지만, 같은 역할을 합니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 28

Slide 28 text

맛픽 (ma’at pick) Flutter 개발기 Redux Redux 에는 3가지의 원칙이 있습니다. 1. 앱에는 ‘단 하나의 Store’ 만 사용함 2. Store 안에 있는 상태값은 모두 읽기전용 (Read-Only) 임 3. Store 안에 State 를 바꾸려면 action > dispatcher 를 사용해야하고, Reducer만 State 를 변경할 수 있음 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 29

Slide 29 text

맛픽 (ma’at pick) Flutter 개발기 BLoC (Business Logic Components) 구글에서 발표한 Dart 앱 비즈니스 로직 패턴입니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 30

Slide 30 text

맛픽 (ma’at pick) Flutter 개발기 BLoC (Business Logic Components) MVVM 패턴에서 ViewModel 이 BLoC 로 대체될 수 있습니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 31

Slide 31 text

맛픽 (ma’at pick) Flutter 개발기 BLoC (Business Logic Components) 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter ੉޷૑ 출처 : https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1

Slide 32

Slide 32 text

맛픽 (ma’at pick) Flutter 개발기 Redux 사용 매우 주관적인 의견이지만, ‘상태값’ 을 한 곳에 저장해서 쓰고, Action > Dispatch 관계로 호출하고, API는 미들웨어 (Middleware)에서만 호출하도록 하고, 상태값에 따라 위젯을 다르게 렌더링하는게 좋다고 판단하여 Redux 를 사용하기로 했습니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 33

Slide 33 text

맛픽 (ma’at pick) Flutter 개발기 Redux의 구조 ੉޷૑ 출처 : https://blog.novoda.com/introduction-to-redux-in-flutter/ 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 34

Slide 34 text

맛픽 (ma’at pick) Flutter 개발기 Redux의 구조 사용자가 닉네임을 입력함 (Action) 닉네임 중복검사 API 호출 (Middleware) 닉네임 중복체크 결과 Store에 저장 (Reducer) 닉네임 중복체크 상태 (Store) 닉네임 중복체크 상태 업데이트 됐으니 View 업데이트 (View) 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 35

Slide 35 text

맛픽 (ma’at pick) Flutter 개발기 사용자가 닉네임을 입력함 (Action) 닉네임 중복검사 API 호출 (Middleware) 닉네임 중복체크 결과 Store에 저장 (Reducer) 닉네임 중복체크 상태 (Store) 닉네임 중복체크 상태 업데이트 됐으니 View 업데이트 (View) Redux의 구조 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 36

Slide 36 text

맛픽 (ma’at pick) Flutter 개발기 flutter_redux : https://pub.dartlang.org/packages/flutter_redux new StoreConnector>( converter: (store) => apiResult.result, builder: (context, list) { return new ListView.builder( itemCount: list.length, itemBuilder: (context, position) { return _listItem(list[position]); }, ); }, ) 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 37

Slide 37 text

맛픽 (ma’at pick) Flutter 개발기 코드 구조 라고 거창하게 써놓긴 했지만.. 1. 아까 말씀드렸다시피 Flutter 에서 UI를 그리는 부분이 많이 가독성이 떨어 지기 때문에 이를 나름 해결했던 방법과 2. API 요청할 때에 API 모델을 만들어서 어떻게 보냈는지 를 다룹니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 38

Slide 38 text

맛픽 (ma’at pick) Flutter 개발기 Widget build() 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 39

Slide 39 text

맛픽 (ma’at pick) Flutter 개발기 Widget build() @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( onTap: this.onPressed, splashColor: const Color(0x40000000), highlightColor: const Color(0x20000000), child: Listener( onPointerDown: (status) { setState(() { _isTapped = true; }); }, onPointerUp: (status) { setState(() { _isTapped = false; }); }, behavior: HitTestBehavior.translucent, child: Container( height: 60, padding: EdgeInsets.only(top: 12, bottom: 12), decoration: BoxDecoration( border: Border.all( color: _isTapped ? Colors.orange : Colors.white), borderRadius: BorderRadius.circular(4)), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ new Image.asset( 'assets/icons/google_g_logo.png', width: 34, ), Padding( padding: EdgeInsets.only(left: 24), child: Text( 'Join with google', style: TextStyle( fontFamily: 'Oswald', fontWeight: FontWeight.normal, fontSize: 22, color: Colors.white), )) ], ), ))), ); } InkWell PointerListener Container Row Image Padding Text 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 40

Slide 40 text

맛픽 (ma’at pick) Flutter 개발기 다시 한번 보시죠 @override Widget build(BuildContext context) { return Material( color: Colors.transparent, child: InkWell( onTap: this.onPressed, splashColor: const Color(0x40000000), highlightColor: const Color(0x20000000), child: Listener( onPointerDown: (status) { setState(() { _isTapped = true; }); }, onPointerUp: (status) { setState(() { _isTapped = false; }); }, behavior: HitTestBehavior.translucent, child: Container( height: 60, padding: EdgeInsets.only(top: 12, bottom: 12), decoration: BoxDecoration( border: Border.all( color: _isTapped ? Colors.orange : Colors.white), borderRadius: BorderRadius.circular(4)), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ new Image.asset( 'assets/icons/google_g_logo.png', width: 34, ), Padding( padding: EdgeInsets.only(left: 24), child: Text( 'Join with google', style: TextStyle( fontFamily: 'Oswald', fontWeight: FontWeight.normal, fontSize: 22, color: Colors.white), )) ], ), ))), ); } 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 끔-찍 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 41

Slide 41 text

맛픽 (ma’at pick) Flutter 개발기 위젯을 그릴 때 컴포넌트 단위로 끊자! @child Widget _closeButton() { return new Container( // 이하 생략
 ); } @child Widget _inputField() { return new Container( // 이하 생략 ); } @child Widget _nextButton() { return new Container( // 이하 생략 ); } @override Widget build(BuildContext context) { return new Scaffold( body: Stack( children: [ _closeButton(), _inputField(), _nextButton() ], ), ); } 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 42

Slide 42 text

맛픽 (ma’at pick) Flutter 개발기 API 모델 { “nickname”: “sanghun.lee”, “is_duplicate”: false } 아래와 같이 JSON 형태로 API 서버에서 응답이 온다고 해봅시다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 43

Slide 43 text

맛픽 (ma’at pick) Flutter 개발기 API 모델 import 'package:json_annotation/json_annotation.dart'; part 'nickname-duplicate-check-response.g.dart'; @JsonSerializable() class NicknameDuplicateCheckResponse { final String nickname; final bool is_duplicate; NicknameDuplicateCheckResponse({ this.nickname, this.is_duplicate }); factory NicknameDuplicateCheckResponse.fromJson(Map json) => _$NicknameDuplicateCheckResponseFromJson(json); Map toJSON() => _$NicknameDuplicateCheckResponse(this); } 아래와 같이 JSON 형태로 API 서버에서 응답이 온다고 해봅시다. 자동 생성 파일 해당 클래스는 JSON Serializable 하도록 함 서버에서 온 JSON을 객체로 만들어줌 (자동 생성된 코드 사용) 해당 객체를 JSON 형태로 변환해줌 (자동 생성된 코드 사용) 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter { “nickname”: “sanghun.lee”, “is_duplicate”: false }

Slide 44

Slide 44 text

맛픽 (ma’at pick) Flutter 개발기 API 모델 해당 코드를 flutter 빌드 명령어를 통해 빌드하면, 아래와 같은 파일이 자동 생성됩니다. // GENERATED CODE - DO NOT MODIFY BY HAND part of 'nickname-duplicate-check-response.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** NicknameDuplicateCheckResponse _$NicknameDuplicateCheckResponseFromJson( Map json) { return NicknameDuplicateCheckResponse( nickname: json['nickname'] as String, is_duplicate: json['is_duplicate'] as bool); } Map _$NicknameDuplicateCheckResponseToJson( NicknameDuplicateCheckResponse instance) => { 'nickname': instance.nickname, 'is_duplicate': instance.is_duplicate }; 원래 파일과 직접적인 링크 서버에서 온 JSON을 객체로 만들어줌 해당 객체를 JSON 형태로 변환해줌 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 45

Slide 45 text

맛픽 (ma’at pick) Flutter 개발기 Native Component Flutter 에서 지원하지 않은 네이티브 뷰 (비디오 플레이어 등), 네이티브 기능 (환경설정에 저장, 권한 요청 등)이 필요한 경우가 있습니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 46

Slide 46 text

맛픽 (ma’at pick) Flutter 개발기 Native Component Flutter 에서는 네이티브 코드와 통신을 위해 MethodChannel API 와, PlatformView API, FlutterTexture API 등을 지원하고 있습니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 47

Slide 47 text

맛픽 (ma’at pick) Flutter 개발기 Native Component 네이티브 단에서 안드로이드에서는 ExoPlayer 를, iOS 에서는 AVPlayer 를 사용합니다. VideoPlayer 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 48

Slide 48 text

맛픽 (ma’at pick) Flutter 개발기 Native Component VideoPlayer Android iOS FlutterSurface FlutterTexture ExoPlayer AVPlayer Flutter TextureWidget 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 49

Slide 49 text

맛픽 (ma’at pick) Flutter 개발기 Native Component VideoPlayer Android iOS .invokeMethod(‘play’) .play() MethodChannel ExoPlayer.play() AVPlayer.play() 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 50

Slide 50 text

맛픽 (ma’at pick) Flutter 개발기 Native Component Camera 카메라도 마찬가지로, 앞서 말씀드린대로 구현됐습니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 51

Slide 51 text

맛픽 (ma’at pick) Flutter 개발기 Native Component 맛픽에서 사용된 / 만든 네이티브 컴포넌트 비디오 플레이어 카메라 뷰 이미지 캐시 / 동영상 캐시 SharedPreferences (shared_preferences 라이브러리) 구글 지도 (google_maps_flutter 라이브러리) FCM (firebase_messaging 라이브러리) 권한 요청 (permission_handler 라이브러리) 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 52

Slide 52 text

맛픽 (ma’at pick) Flutter 개발기 Native Component 라이브러리에 따라 안드로이드는 Java / Kotlin 으로, iOS는 Objective-C / Swift 로 구현되어있음 자체구현한 네이티브 컴포넌트는 Kotlin / Swift 로 작성. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 53

Slide 53 text

맛픽 (ma’at pick) Flutter 개발기 Performance Flutter에서는 디버그 빌드와 릴리즈 빌드가 많이 다릅니다. 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter

Slide 54

Slide 54 text

맛픽 (ma’at pick) Flutter 개발기 릴리즈 빌드 디버그 빌드 01. UI Type 02. State 03. Code Structure 04. Native Component 05. Performance 00. Flutter Android : 38.1MB iOS : 64.6MB Android : 11.7MB iOS : 22.3MB Hot Reload / Hot Restart 하기 위한 웹소켓 생성 코드도 있고… 디버깅을 위한 여러가지 코드 포함 앱 실행하는데 초점, 디버그에 필요한 코드 없음

Slide 55

Slide 55 text

3월중으로 iOS도 베타테스트를 시작할 예정이에요!

Slide 56

Slide 56 text

맛픽 많이 기대해주시고, 안드로이드 사용자분들께서는 플레이스토어에서 받으실 수 있어요!

Slide 57

Slide 57 text

맛픽 굿즈를 가져왔습니다. “ 스티커랑 병따개 챙겨왔어욤! 이따 스태프 분들께서 주실거에요! 꼭 가져가주시고 잘 사용 부탁드립니다. 감사합니다 “