Flutter(dart)

김지환·2022년 3월 19일
0

Flutter란?

  • 구글에서 2017년 5월 출시된 모바일/웹/데스크톱 크로스 플랫폼 GUI SDK이다. 하나의 코드 베이스로 안드로이드, 아이폰, 리눅스, 윈도우즈, 맥 및 웹 브라우저에서 모두 동작되는 앱을 위해 출시되었다. 사용되는 언어는 역시 구글에 의해 제창된 Dart를 사용한다.
  • 웹 브라우저에서 플러터 앱 실행은 Dart언어를 HTML/CSS/자바스크립트로 자동 Transpilation을 통해 가능하다. 리눅스/윈도우즈/맥 데스크톱 상에서의 플러터 앱 실행은 2021년 3월부터 공식 지원되고 있다. 안드로이드 OS의 차기 운영체제인 퓨시아의 유저 인터페이스 및 퓨시아 애플리케이션들이 플러터로 작성된다.
  • 플러터 프레임웍은 소스코드를 네이티브 CPU 머신코드로 직접 컴파일 하며 UI를 렌더링 엔진 Skia로 직접 렌더링하기 때문에 성능이 뛰어나다. 소프트웨어 디자이너의 선택에 따라 iOS 앱에서 구글의 Material 테마 디자인과 Ripple 애니메이션을 사용하는 것이 가능하고 반대로 안드로이드에서 애플의 Cupertino 테마를 적용하는 것도 가능하다. 즉 플랫폼에 관계없이 커스텀이 가능하다.
  • 크로스플랫폼 환경이면서도 네이티브 성능과 미려한 UI, 각종 확장 기능을 제공하는 프레임워크이다.
  • Hot Reload 기능을 사용하면 디버깅을 중지하지 않고 소스 수정 후 저장만 하면 에뮬레이터나 기기에 바로 반영되어 UI와 로직이 모두 업데이트된다.

dart란?

  • 구글이 JS를 대체하기 위해 개발한 웹프로그래밍 언어로 모바일 앱,웹,앱, 명령어 스크립트, 서버 프로그래밍 등 dart를 사용해 만들수있다.

  • 웹앱이 아닌 네이티브 코드로 안드로이드 / IOS용 앱을 동시에 개발이 가능하다.

  • 모든것들이 전부 Object로 취급된다.
    :var 선언할수있는 것들 전부, function, number, null 전부 Object로 취급된다.
    변수에 넣을 수 있는 모든 것은 객체이며 모든 객체는 클래스의 인스턴스로 취급된다.

  • List , List같이 제너릭 type을 지원한다.

  • Typed언어이지만 자유도를 주며 Dynamic은 any같이 기본 오브젝트를 명시적으로 타입 지정해준다.

  • public private protected의 정의가 없으며, _function()으로 정의된다.

  • 문법적으로는

    print();로 출력이 가능하고
    data type으로는 number , double , String, bool, List, Map(key:value)로 나뉜다.
    그리고 var는 타입을 써주지않고 처음 선언한 값이 데이터타입이 된다.
    그리고 final과 const는 같지만 조금은 다르다.

Flutter설치

  • 1.flutter SDK다운
    2.android studio 설치
    3.환경변수 등록
    4.등등
    /usr/local/Caskroom/flutter/2.10.3/flutter
  • brew를 통해서 설치해준다.
    : 패키지를 통해서 데스크탑에 설치하게되면 버전 업데이트의 번거로움이 생긴다.
  • brew install flutter /
    brew install android-sdk /
    brew install android-ndk /
    brew install android-studio
    brew tap dart-lang/dart
    brew install dart
  • android-studio는 패캐지로 설치한다.
  • android-studio에서 flutter와 dart를 이용하여 앱을 만든다.

Flutter구조

  • ios: iOS 빌드시에 필요한 파일들과 코드들이 생성됩니다.

android: Android 빌드시에 필요한 파일들과 코드들이 생성됩니다.

lib: 스켈레톤 앱의 코드가 들어있으며, 이후의 코드 구현은 거의 모두 이 폴더 내에서 합니다.

pubspec.yaml: 플러터 프로젝트의 중심 같은 파일로, 앱 이름, 버전, 빌드, 의존성 (dependencies), 리소스 (이미지, 폰트 파일 등) 등이 모두 등록되어 있는 파일입니다.

  • StatelessWidget 클래스
    : 상태가 없는 위젯을 정의하는데 사용된다.
    상태를 가지지 않는 위젯을 구성하는 기본 클레스로 한번 그려진다음 다시 그리지 않는다는 뜻이다.
    이러한 클래스는 상수는 가질수 있지만, 프로퍼티로 변수를 가지지않는다.

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Welcome to Flutter',
    theme:ThemeData(primarySwatch:Colors.blue,
         home: Scaffold( ··· );
          ),
        );
      }
    }
  • Stateful 클래스
    : 상태가 있는 위젯을 정의하는데 사용된다.
    생명주기동안 값이 변할수있는 위젯으로 반드시 State인스턴스를 생성하는 statefulWidget클래스를 생성해야된다. 자체 값은 변하지 않지만, 내부의 state클래스가 생명주기동안 값이 변한다.

    class MyHomePage extends StatefulWidget {
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
    
      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: ···,
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      }
    }

여기에서 setState()는 전달된 익명 함수를 실행한 후 화면을 다시 그리게 하는 build()메소드를 다시 실행시키는 역할을 한다. setState()메소드는 상속 받고 있는 state 클래스가 제공하는 메소드이다.

Flutter LifeCycle

  • statefulWidget을 만들때, state객체를 만든다. 해당 위젯의 모든 가변 상태가 유지되는곳이다.
    : state는 재구축할때마다 폐기되지않고 프레임이 재구성 될때 마다 state속성, getter, settet등에서 가져온다.

  • createState()
    : 빌드하도록 지시하면 즉시 호출되고 이 메소드는 반드시 존재해야 한다.

      class MyHomePage extends StatefulWidget {
        @override
        _MyHomePageState createState() => new _MyHomePageState();
      }
  • mounted == true
    : createState가 state클래스를 생성하면 buildContext는 state에 할당된다.
    모든 위젯은 bool형식의 this.mounted속성을 가지고있다.(setState()를 쓸때 유용하다)

  • initState()
    : 위젯이 생성될때 처음으로 호출되는 메소드이다. 오직 한번만 호출된다. super.initState()를 호출해야된다.

    @override
    initState() {
      // 부모의 initState호출
      super.initState();
      // 이 클래스애 리스너 추가
      cartItemStream.listen((data) {
        _updateWidget(data);
      });
    }s
  • didChangeDependencies()
    : 위젯이 최초 생설될때 initState다음에 바로 호출된다.
    의존 데이터 객체가 호출될때마다 호출된다.(API호출의 경우)

  • build()
    : 반드시 위젯을 리턴하고 플루터의 모든 gui는 child / children을 가진 위젯이다.

  • didUpdateWidget()
    : 부모 위젯이 변경되어 이 위젯을 재 구성해야 되는 경우 일부 데이터들과 state들을 재정의 해주거나 초기화 해주는경우에 사용된다.

    @override
    void didUpdateWidget(Widget oldWidget) {
      if (oldWidget.importantProperty != widget.importantProperty) {
        _init();
      }
    }
  • setState()
    : build context의 위젯을 다시 빈드하게되고 비동기적이지 않은 callback을 사용한다.

  • deactivate()
    : 거의 사용되지않지만, 트리에서 state가 제거될때 호출된다.

    void updateProfile(String name) {
     setState(() => this.name = name);
    }
  • dispose()
    : state객체가 영구히 제거된다.

  • mounted == false
    : 이 상태에서 state객체는 다시 mount되지 않고, setState()가 호출되면 에러가 발생한다.

dart 문법

  • Row 안에 컨테이너박스 2개를 넣을수있다.

    body: Row(
     children:[
       Flexible(child: Container( width: )),
       Container()
     ]
    )
    children:[
      Flexible(child: Container(color: Colors.blue  ), flex: 3,),
      Flexible(child: Container(color: Colors.green  ), flex: 7,),
    ]
    //=>이런식으로 css를 줄수있고 박스 2개를 3:7로 나눈다.
    children:[
      Expanded(child: Container(color: Colors.blue  )),
      Container(width: 100, color: Colors.green  ),
    ]
    //=>Expanded는 flex:1을 가진박스랑 똑같다.
  • 변수

     int
      Double
      Num
      Bool
      String
      List
      Set
      Map
      dynamic
    
      dynamic some =9;
      some = 'apple'
      ㄴ> 다이나믹으로 선언해주면 모든 형식타입을 허용한다.
      String name;
    Int age;
    List<int> number = [0,1,2]
    Map<String, int> score = {
        ‘en’: 100,
        ‘math’: 90,
    }
    String greet(){
        print(‘hello’);
        return ‘hello’;
    
    }
  • 연산자

     ~/
     print(55~2) // 27
    
     As
     ㄴ>강제 형변환 키워드로 
     부모 타입 객체를 자식 타입 객체로 다운캐스팅 할떄 사용한다.
     int n = 34;
     double m = n as double;
    
     Is/is!      Age is int.    / age is! Num
     ㄴ> 타입을 확인해 true / false를 반환
    
     Name?.length
     ㄴ> 널인경우 에러를막고 값을 할당시킨다.
    
     Name ?? 20
     ㄴ> name이 null이면 20을 반환 값이 있으면 그 값을 반환.
    
     스프레드 연산자
     Var a = [1,2,3];
     Var b = […a,4,5];
  • 함수(메소드)

     String see(String name){
     return ‘Hi $name’;
     }
      또는
     String see(String name) => ‘hi $name’;
    
     Named parameter
     {{}}파라미터를 중괄호로 감싸면 파라미터를 전달할수있다.
     Void introduce({required String name, required int age}){
    
     }
    
     Optional parameter
     []대괄호로 감싸면 선택형 인자를 설정할수있다.
     Void inroduces(String name, int age, [String? food]) {

     }
    
     Default parameter
     Void inroduce(String name, int age, [String food = ‘whole’]){
    
     }
  • Null Safety(flutter 2.0)

    기본값 초기화 없이 선언하는 변수는 null값에 대한 안정성을 보장하기 위해서
    타입을 nullable과 non-nullable로 구분해줘야한다.
    
    *Nullable
    일반 타입 뒤에 물음표를 붙여주면 해당 변수는 타입 또는 null이 될수있음을 나타낸다. 초기화없이 사용하면 null값으로 할당된다.
    
    void main() {
      String? name;
      int? age;
      bool? student;
    
      print(name)	//null
    }
    
    *non-nullable
    물음표없이 일반 타입으로 사용하면 자동적으로 nul이 될수없는 값으로 인식된다.
    사용전에 반드시 초기값을 할당해줘야한다.
    Required, late

    Required
    함수에서 네임드 파라미터나 옵셔널 파라미터를 설정할때 non-nullable로
    지정하기위해 타입 앞에 붙여준다.
    Void greet({required String name}){
        print(‘hello’);
    }

    Late
    클래스에 non-nullable 프로퍼티가 필요하지만 디폴트값이나 초기화를 시키지 않을 경우 나중에 값을 할당한다는 의미로 붙여준다.
    Class Cat{
        late String name;
        late int age;
    }
  • 중요한 메소드

    Var list = [1,2,3,4];
    Var newList = list;
    print(newList);
    
    Var spreadList = [10, …list, 100];
    
    print(spreadList);
    -map
    map은 iterable(배열그룹)을 대상으로 foreach돌린다.

    Var list = [1,2,3];
    Var newList = list.map((e) => e+1);
    list[0] = 0;


    -asMap
    배열 값에 index키 값을 삽입해줘서 반환해준다.
    키값은 인덱스로 부여된다. 0부터 시작한다.
    Var map = words.asMap();

    -toList
    생성되는 값들을 리스트로 만들어 반환한다.
    List zero = orderList.toList();
    Map.keys.toList() => [0,1,2,3];

    -where
    배열요소를 필터링한다.

    Var list = [1,2,3,4];
    Var filter = list.where((e) => e!=3);
    //3이 아닌값만 필터링한다.
    Print(filter); => [1,2,4];

    -for / foreach
    for(var e in list){
    print(e);
    }

    List.forEach((e) => print(e));

    -add / addAll / length / clear

    Var vege = List();
    생성자 사용
    Var fruits = [‘apple’, ‘orange’];
    문자열 사용하여 list생성

    Fruits.add(‘kiwi’);
    Fruits.addAll([‘grape’, ‘banana’]);
    다수항목 추가하기

    Var appleIndex = fruits.indexOf(‘apple’);
    Fruits.removeAt(appleIndex);
    리스트 단일 항목 삭제
    Fruits.clear();
모든 항목 삭제


    -sort
    Fruits.sort((alb) => a.compareTo(b));
    [‘b’, ‘banana’,a] => [a, b, banana’]이런식으로 순서대로 정렬된다.


    -reduce
    Int sum = number.reduce((total, element) => total + element);
  • 위젯
    • 글자위젯(저 Text부분 대신 넣는다)
      Text('안녕')
    • 이미지위젯
      Image.asset(‘assets/electron.png’)
      pub spec.yaml파일에 들어가서
      중간에 flutter:를 찾고
      Flutter:
      Assets:
      -assets/
      이렇게 써주면 assets 파일에있는걸 가져다가 쓰겟다 등록
    • 아이콘위젯
      Icon(Icons.star) 또는 Icon(Icons.shop)
    • 박스위젯
      Container( width: 50 , height: 50, color : Colors.blue)
      home: Center(
      child: Container( width: 50 , height: 50, color : Colors.blue),
  • MaterialApp 위젯
    (Cupertino 위젯도 있다.
    혹은 내가 커스터마이징해서 만드는 위젯)
  • Scaffold()위젯
    상중하로 나눠주는 위젯
    -헤드 바디 푸터로 나눠준다고 생각하면 된다.
    return MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: Text(‘앱이다.’)),
      body: Container(),
      bottomNavigationBar: BottomAppBar(
      child: Text('asdfdee')),
    )
  • mainAxisAlignment(좌우) / crossAxisAlignment(상하)
    display 같은 요소들을 정렬해줄때 써준다.
      body: Row(
      mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.center,
      children: [ ], 
  • Body(CSS)
body:  SizedBox(
 child: Icon(Icons.star,)
   //아이콘은 컬러와 사이즈가 끝이다.
  child: Text('안녕하세요',
    style: TextStyle(color: Color(0xffd52727), 
       fontSize: 30, fontWeight: FontWeight.w700

    ),
  ),
),
  • Button(CSS)

    : TextButton(child: Text(), onPressed: (){},)
    안에는 2가지 옵션은 꼭 들어가야된다.  
    / IconButton() /  ElevatedButton()
    이렇게 3가지를 쓸수있다.
    
    1)TextButton
    child: TextButton(
      child: Text('글자'), 
      onPressed: (){},)
    2)ElevatedButton
    child: ElevatedButton(
      child: Text('글자'),
      onPressed: (){},
      style: ButtonStyle(),
    )

    3)IconButton
    child: IconButton(
      icon: Icon(Icons.star),
      onPressed: (){},)
  • appBar(Header)(CSS)

    : 옵션
    Title: 왼쪽제목
    Leading: 왼쪽에 넣을 아이콘,(드롭바 같은경우)
    Actions: [우측아이콘들]

    AppBar(
        leading: Icon(Icons.star),
        Title: text(‘앱이다.’),
        actions: [Icon(Icons.star), Icon(Icons.star)]
    
    ),
    
    	

커스텀 위젯 문법

  • 커스텀위젯은 class로 만든다.
    : 밑에다가 그냥 쓰이는 부분을 컴포넌트화 시켜서
    함수처럼 사용해준다.
    재사용 많은 UI나 / 큰 페이지들 위주로 쓴다.

  •  @override
     함수를 쓰면서 중복되었을때 
     내꺼 먼저 써달라는 기호이다.
    
     1)
     return MaterialApp(
             home: Scaffold(
               appBar: AppBar(),
               body: ShopItem()
             )
         );
       }
     }
    
     class ShopItem extends StatelessWidget {
       const ShopItem({Key? key}) : super(key: key);
       @override
       Widget build(BuildContext context) {
         return  SizedBox(
           child: Text('안녕'),
         );
       }
     }
  •  2)변수로 선언해줘도 a 라고 써주면 사용 가능하다.
     But 실시간으로 바뀌는 데이터들은 저렇게 변수 선언해서
     사용하면 버그가 많이 생긴다.
    
     var a = SizedBox(
       child: Text('안녕'),
     );
    
     class MyApp extends StatelessWidget {
       const MyApp({Key? key}) : super(key: key);
    
       @override
       Widget build(BuildContext context) {
         return MaterialApp(
             home: Scaffold(
               appBar: AppBar(),
               body: a
             )
         );
       }
     }

profile
Web Developer

0개의 댓글