[Flutter] Flutter

Sooooooah·2023년 4월 26일
0

플러터

목록 보기
3/7

Flutter 문서

앱 개발 방식

1. 네이티브 방식

안드로이드나 iOS같은 플랫폼 자체에서 제공하는 개발 환경으로 개발

안드로이드
개발 도구 : 안드로이드 스튜디오
개발 언어 : 자바, 코틀린

iOS
개발 환경 : 맥OS
개발 도구 : Xcode
개발 언어 : 스위프트, 오브젝티브-C

2. 하이브리드 방식

웹 기술로 앱 화면을 만든 후 네이티브 기술로 감싸서 앱 형태로 포장
기존의 웹 기술을 활용하고 빠르게 앱으로 변환가능
네이티브 성능에 미치지 못하며, 별도의 UI를 만들기 때문에 네이티브 앱 느낌을 내지 못한다.

3. 크로스 플랫폼 방식

한 번 구현하여 안드로이드와 iOS 등 각 플랫폼용 앱을 만든다.
빌드할 때 네이티브 코드로 변환되기 때문에 결과적으로는 네이티브 방식으로 개발했을 때와 거의 같은 성능을 보장한다.

플러터

구글이 출시한 크로스 플랫폼 개발 프레임워크

💡 UI
안드로이드 : 머터리얼 디자인
iOS : 쿠퍼티노 디자인

  • 개발 언어 : 다트(Dart.js)
    문법은 자바, 자바스크립트와 비슷하다.

플러터의 장점

  1. 배포시 Dart언어가 각 플랫폼의 네이티브에 맞는 코드로 컴파일해준다.
  2. 디바이스의 렌더링을 Skia 라는 오픈 소스 그래픽 라이브러리에게 맡긴다. 그로 인해 대부분의 작업에서 GPU를 사용하여 UI가 60fps로 동작, 또한 네이티브 컴포넌트를 사용하지 않게 되어 안드로이드나 iOS에 관계없이 비슷한 화면을 구성할 수 있게 되었다.

    Skia
    Skia는 다양한 하드웨어 및 소프트웨어 플랫폼에서 공통 API를 제공하는 오픈 소스 2D 그래픽 라이브러리입니다.
    구글 크롬, Chrome OS, Android, Flutter, Firefox, Firefox OS 등 많은 다른 제품들의 그래픽 엔진 역할을 합니다.


플러터 프로젝트

안드로이드 에뮬레이터 : Pixel XL, 버전 (Nougat) 7.1.1

프로젝트 구조

  • .idea : 개발 도구에 필요한 설정
  • android : 안드로이드 네이티브 코드를 작성하는 부분
  • build : 빌드시 생성되는 파일
  • iOS : iOS 네이티브 코드를 작성하는 부분
  • lib : 다트 코드를 작성하는 부분
  • test : 테스트 코드를 작성하는 부분
  • .gitignore : Git 설정 파일, 버전 관리시 무시할 파일 규칙 작성
  • .metadata : 프로젝트가 관리하는 파일
  • .packages : 각종 패키지 정보
  • flutter_app.iml : 개발 도구에 필요한 설정 파일
  • pubspec.lock : 패키지 매니저가 이용하는 파일
  • pubspec.yaml : 패키지 매니저가 이용하는 파일
  • README.md : 프로젝트 설명을 작성하는 파일

애뮬레이터 실행

Hot reload 테스트

web > index.html

  • mainfest.json 란?
  • 스크립트에서 앱을 실행? > 플러터 3.7업데이트 Element Embedding
  • 이전에는 플러터 앱이 html의 body 전체를 오버라이딩하는 방식으로만 작동했었는데, 이번 업데이트로 일반 웹 내부에 앱을 컴포넌트처럼 넣어서 보여줄 수 있다

프로젝트 코드

// 머티리얼 디자인 위젯  
import 'package:flutter/material.dart';

// 앱 시작 부분
void main() {
  runApp(const MyApp());
}

// 시작 클래스. 머티리얼 디자인 앱 생성
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'First Flutter',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: const MyHomePage(title: 'Hello World'),
    );
  }
}

// 시작 클래스가 실제로 표시하는 클래스. 카운터 앱 화면
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

// 위 MyHomePage 클래스의 상태를 나타내는 State 클래스
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0; // 화면에 표시할 상탯값인 카운터 숫자

// counter 변수를 1 증가시키고 화면을 다시 그리는 메서드
  void _incrementCounter() {
    // 화면을 다시 그리도록 하는 함수. StatefulWidget만 가능
    setState(() {
      _counter++;
    });
  }

  // 화면에 UI를 그리는 메서드. 그려질 위젯을 반환
  
  Widget build(BuildContext context) {
    return Scaffold(
      // 머티리얼 디자인 기본 뼈대 위젯
      appBar: AppBar(
        // 상단 앱바
        title: Text(widget.title),
      ),
      body: Center(
        // 표시할 내용
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter, // 클릭시 _incrementCounter() 메서드 실행
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

StatelessWidget

  • 상태(State)를 가지지 않는 위젯
  • 위젯이 어떤 변화에 대해 무감각하다는 것을 의미
  • 상태 변화를 감지하지 않기 때문에 화면을 구성할 때 최초 한 번만 build()함수를 호출한 후 다시 호출하지 않는다.
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'First Flutter',
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: const MyHomePage(title: 'Hello World'),
    );
  }
}

StatefulWidget

  • 상태 변경을 감지하고 변경된 사항을 화면 구성에 실제로 반영
  • 주로 createState()함수를 통해서 State객체를 만드는 작업을 한다
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

StatefulWidget 생명주기

createState()

  • statefulWidget을 구축하자마자 호출된다.
  • 위젯 트리에 상태를 만들기 위해 호출된다.

initState()

  • 위젯 트리를 초기화한다.
  • 단 한 번만 호출된다.

didChangeDependencies()

  • state객체의 종속성이 변경될 때 호출된다.
  • initState뒤에 호출되지만 그 이외에도 호출된다.

build()

  • 위젯으로 만든 UI를 구축한다.
  • 다양한 곳에서 반복적으로 호출된다.
  • 변경된 부분 트리를 감지하고 대체한다.

didUpdateWidget()

  • 위젯의 구성이 변경될 때마다 호출된다.
  • 부모 위젯이 변경되고 다시 그려져야 할 때 호출된다.
  • oldWidget 인수를 취득해 비교한다.

setState()

  • 상태가 변경되었을 때 프레임워크에 상태가 변경됨을 알린다.

deactivate()

  • state오브젝트가 트리로부터 삭제될 때마다 호출된다.

dispose()

  • 객체가 트리에서 완전히 삭제되고 두 번 다시 빌드되지 않으면 호출된다.

State

  1. 위젯이 빌드 완료된 후 읽을 수 있다. 따라서 build() 함수 호출 전에 State 설정이 되어야 한다.
  2. 위젯이 유효한 동안에는 State는 변경될 수 있다.
// 위 MyHomePage 클래스의 상태를 나타내는 State 클래스
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0; // 화면에 표시할 상탯값인 카운터 숫자

// counter 변수를 1 증가시키고 화면을 다시 그리는 메서드
  void _incrementCounter() {
    // 화면을 다시 그리도록 하는 함수. StatefulWidget만 가능
    setState(() {
      _counter++;
    });
  }

  // 화면에 UI를 그리는 메서드. 그려질 위젯을 반환
  
  Widget build(BuildContext context) {
    return Scaffold(
      // 머티리얼 디자인 기본 뼈대 위젯
      appBar: AppBar(
        // 상단 앱바
        title: Text(widget.title),
      ),
      body: Center(
        // 표시할 내용
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter, // 클릭시 _incrementCounter() 메서드 실행
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

BuildContext

  • BuildContext는 위젯 트리에서 위젯의 위치에 대한 참조이다.
  • 하나의 BuildContext는 하나의 위젯만 가진다.

위젯 트리

  • 실질적인 UI를 그리기 위한 구성

플러터는 3가지 트리로 구성되어 있다.

네비게이션 동작 방식

  • Navigator.push() 로 새로운 화면, Navigator.pop(context) 로 이전화면
  • 실행되는 화면이 스택 구조로 메모리에 쌓인다.

몇 가지 궁금증

super key

[This method overrides a method annotated as '@mustCallSuper' in 'State', but doesn't invoke the overridden method.][](https://fronquarry.tistory.com/20)

왜 자꾸 const 를 붙이라고하지?

[use 'const' literals as arguments to constructors of '@immutable' classes]에 대하여

By making the list const the list and everything within the list will become compile-time constants. This can only be done if everything inside the list is able to be const. The benefit is that the list will only be created once at compile time rather than every time the surrounding build method is called. 출처

불필요한 리소스 사용을 줄이기 위함이다!

profile
즐거운 서비스를 추구합니다.

2개의 댓글

comment-user-thumbnail
2023년 5월 15일

skia에 문제가 있어 3.10부터 ios는 impeller로 바뀌었습니다. android는 preview 상태이나 금년안에 stable로 전환될듯하고요.
breaking change는 아니니 앱들에 별 변동은 없습니다

답글 달기
comment-user-thumbnail
2023년 5월 15일

const로 말그대로 변하지 않는 객체를 선언한 것이지 때문에 여러곳에 사용해도 한번만 생성됩니다. flutter 보다는 Dart언어의 특징입니다.

Stateless, Stateful 위젯의 constructor가 const로 만들어지는 이유가 바로, 어차피 변할 일 없는 위젯이니 여러곳에 사용되더라도 메모리에는 1개만 만들어 두고 재사용 하겠다는 뜻입니다.

답글 달기