Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2019 GDG Android - super.init() - ma'at pick for Flutter

Dora Lee
January 25, 2019

2019 GDG Android - super.init() - ma'at pick for Flutter

안녕하세요! 로플랫이란 실내위치 인식 기술을 보유한 회사에서 Android / iOS 개발자로 일하고 있는 이상훈이라고 합니다!

이번에 GDG Android에서 맛픽이란 서비스를 Flutter로 다시 개발한 개발기를 발표했습니다 :)

Dora Lee

January 25, 2019
Tweet

More Decks by Dora Lee

Other Decks in Technology

Transcript

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. 일단 비교해보자!

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. 언어 사용

    View full-size slide

  12. 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;
    }

    View full-size slide

  13. 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;
    }
    이정도 문법이면,
    할만한걸?

    View full-size slide

  14. @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: [
    StoreConnectorFoodReviewedLoadingState>(
    converter: (store) => store
    .state.foodReviewedLoadingState,
    builder: (context, loadingState) {
    if (loadingState != null &&
    loadingState.isLoading) {
    return new SizedBox(
    child:
    LinearProgressIndicator(
    backgroundColor:
    Colors.transparent,
    valueColor:
    방금 한말 취소.

    View full-size slide

  15. 맛픽 (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

    View full-size slide

  16. 맛픽 (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

    View full-size slide

  17. 맛픽 (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

    View full-size slide

  18. 맛픽 (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

    View full-size slide

  19. 맛픽 (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

    View full-size slide

  20. 맛픽 (ma’at pick)
    Flutter 개발기 StatefulWidget의

    setState()
    setState() setState()
    01. UI Type
    02. State
    03. Code Structure
    04. Native Component
    05. Performance
    00. Flutter

    View full-size slide

  21. 맛픽 (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

    View full-size slide

  22. 맛픽 (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

    View full-size slide

  23. 맛픽 (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

    View full-size slide

  24. 맛픽 (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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  28. 맛픽 (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

    View full-size slide

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

    View full-size slide

  30. 맛픽 (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

    View full-size slide

  31. 맛픽 (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

    View full-size slide

  32. 맛픽 (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

    View full-size slide

  33. 맛픽 (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

    View full-size slide

  34. 맛픽 (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

    View full-size slide

  35. 맛픽 (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

    View full-size slide

  36. 맛픽 (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

    View full-size slide

  37. 맛픽 (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

    View full-size slide

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

    View full-size slide

  39. 맛픽 (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

    View full-size slide

  40. 맛픽 (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

    View full-size slide

  41. 맛픽 (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

    View full-size slide

  42. 맛픽 (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

    View full-size slide

  43. 맛픽 (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
    }

    View full-size slide

  44. 맛픽 (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

    View full-size slide

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

    View full-size slide

  46. 맛픽 (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

    View full-size slide

  47. 맛픽 (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

    View full-size slide

  48. 맛픽 (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

    View full-size slide

  49. 맛픽 (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

    View full-size slide

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

    View full-size slide

  51. 맛픽 (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

    View full-size slide

  52. 맛픽 (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

    View full-size slide

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

    View full-size slide

  54. 맛픽 (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 하기 위한 웹소켓 생성
    코드도 있고… 디버깅을 위한 여러가지 코드 포함
    앱 실행하는데 초점,
    디버그에 필요한 코드 없음

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  57. 맛픽 굿즈를 가져왔습니다.

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

    View full-size slide