728x90

이전 포스팅에 이어 Tutorial 2를 진행하면서 gstreamer의 기초 요소인 elements와 pipeline 등의 개념을 살펴봅니다.

 

Basic tutorial 2: GStreamer concepts

Please port this tutorial to javascript! Basic tutorial 2: GStreamer concepts Goal The previous tutorial showed how to build a pipeline automatically. Now we are going to build a pipeline manually by instantiating each element and linking them all together

gstreamer.freedesktop.org

#include <QCoreApplication>
#include <QDebug>
#include "gst/gst.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    GstElement *pipeline, *source, *sink;
    GstBus *bus;
    GstMessage *msg;
    GstStateChangeReturn ret;

    /* Initialize GStreamer */
    gst_init (&argc, &argv);

    /* Create the elements */
    source = gst_element_factory_make ("videotestsrc", "source");
    sink = gst_element_factory_make ("autovideosink", "sink");

    /* Create the empty pipeline */
    pipeline = gst_pipeline_new ("test-pipeline");

    if (!pipeline || !source || !sink) {
        g_printerr ("Not all elements could be created.\n");
        return -1;
    }

    /* Build the pipeline */
    gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
    if (gst_element_link (source, sink) != TRUE) {
        g_printerr ("Elements could not be linked.\n");
        gst_object_unref (pipeline);
        return -1;
    }

    /* Modify the source's properties */
    g_object_set (source, "pattern", 0, NULL);

    /* Start playing */
    ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr ("Unable to set the pipeline to the playing state.\n");
        gst_object_unref (pipeline);
        return -1;
    }

    /* Wait until error or EOS */
    bus = gst_element_get_bus (pipeline);
    msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
                (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));

    /* Parse message */
    if (msg != NULL) {
        GError *err;
        gchar *debug_info;

        switch (GST_MESSAGE_TYPE (msg)) {
        case GST_MESSAGE_ERROR:
            gst_message_parse_error (msg, &err, &debug_info);
            g_printerr ("Error received from element %s: %s\n",
                        GST_OBJECT_NAME (msg->src), err->message);
            g_printerr ("Debugging information: %s\n",
                        debug_info ? debug_info : "none");
            g_clear_error (&err);
            g_free (debug_info);
            break;
        case GST_MESSAGE_EOS:
            g_print ("End-Of-Stream reached.\n");
            break;
        default:
            /* We should not reach here because we only asked for ERRORs and EOS */
            g_printerr ("Unexpected message received.\n");
            break;
        }
        gst_message_unref (msg);
    }

    /* Free resources */
    gst_object_unref (bus);
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (pipeline);

    return a.exec();
}

gstreamer는 elements라는 요소로 이루어지고 있고 source elements로부터 sink elements까지 downstream으로 진행되고 있습니다. 그 중간에는 filter라는 elements도 존재합니다.

elements들은 gst_elements_factory_make라는 함수로 만들고 첫 번째 파라미터에 elements의 종류를 넣고 두 번째에 원하는 이름을 넣어줍니다.

예제에서 생성한 elements들은 videotestsrc, autovideosink로 필터가 없습니다.

/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");

따라서 처음 나온 그림에서 필터가 제거된 모습의 아래와 같은 형태라고 보시면 됩니다.

여기서 video test는 test video pattern을 생성해 줍니다. autovideosink는 받아온 이미지를 띄워줍니다. 이때 여러 videosink 중에 상황에 따라 적절한 sink 타입으로 진행합니다.

test video pattern

pipeline은 elements들을 담을 수 있는 elements입니다. pipeline은 bin을 상속받기 때문에 bin이라고도 합니다.

/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");

if (!pipeline || !source || !sink) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
}

 

 

 

생성된 pipline에 source, sink elements을 포함시켜 줍니다. get_bin_add_many 함수를 통해 여러 elements들을 동시에 추가할 수도 있고 gst_bin_add 함수를 통해 개별로 추가할 수도 있습니다.

/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);

추가 후 gst_elements_link 함수를 통해 source elements와 sink elements를 이어줍니다. 이어줌으로써 downstream을 이루게 됩니다.

if (gst_element_link (source, sink) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
}

생성된 elements들을 g_object_set / get 함수를 통해 property를 수정하거나 얻어올 수 있습니다. 

예를 들어 source elements인 videotestsrc의 property 중 pattern property입니다. elements, property name, 그리고 값을 넘기면 해당 속성에 따라 다른 패턴을 그려주게 됩니다.

/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);

 

728x90
728x90

Qt5, Window 10에서 테스트 할 gstreamer 관련 포스팅 입니다. 튜토리얼을 따라가보면서 진행해볼 예정입니다.

 

Tutorials

Tutorials Welcome to the GStreamer Tutorials! The following sections introduce a series of tutorials designed to help you learn how to use GStreamer, the multi-platform, modular, open-source, media streaming framework. Prerequisites Before following these

gstreamer.freedesktop.org

 

Tutorial 진행에 앞서 gstreamer 패키지를 사용하기 위해 헤더파일과 lib 파일을 다운로드 합니다.

 

Download GStreamer

Download GStreamer If you're on Linux or a BSD variant, you can install GStreamer using your package manager. For other platforms, specifically Windows, macOS, Android, and iOS, we provide binary releases in the form of official installers or tarballs main

gstreamer.freedesktop.org

본인이 사용할 컴파일러에 맞게 설치합니다. MSVC 64비트로 설치해 보겠습니다.

두 파일을 모두 다운로드 후 설치해줍니다.

Qt Creator를 실행 해 Console Project를 만들어줍니다.

include 폴더와 lib 폴더, bin 폴더를 프로젝트의 폴더로 복사해줍니다.

프로젝트 파일을 아래와 같이 작성합니다.

QT -= gui

CONFIG += c++17 console
CONFIG -= app_bundle

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
        main.cpp

DEPENDPATH += \
    $$PWD/include

INCLUDEPATH += \
    $$PWD/include \
    $$PWD/include/gstreamer-1.0 \
    $$PWD/include/glib-2.0/ \
    $$PWD/include/glib-2.0/include \
    $$PWD/lib/glib-2.0/include \

win32: LIBS += -L$$PWD/lib/ -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -lintl

DESTDIR += \
    $$PWD/bin

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
#include <QCoreApplication>
#include "gst/gst.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    GstElement *pipeline;
    GstBus *bus;
    GstMessage *msg;

    /* Initialize GStreamer */
    gst_init (&argc, &argv);

    /* Build the pipeline */
    pipeline = gst_parse_launch("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm",NULL);

    /* Start playing */
    gst_element_set_state (pipeline, GST_STATE_PLAYING);

    /* Wait until error or EOS */
    bus = gst_element_get_bus (pipeline);
    msg =
        gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));

    /* See next tutorial for proper error message handling/parsing */
    if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
      g_error ("An error occurred! Re-run with the GST_DEBUG=*:WARN environment "
          "variable set for more details.");
    }

    /* Free resources */
    gst_message_unref (msg);
    gst_object_unref (bus);
    gst_element_set_state (pipeline, GST_STATE_NULL);
    gst_object_unref (pipeline);

    return a.exec();
}

실행하면 https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm 의 동영상이 재생되는 것을 볼 수 있습니다.

먼저 gstreamer를 사용하기 앞서 초기화를 해줍니다.

gst_init (&argc, &argv);

gstreamer는 source elements에서 sink elemnet로 미디어를 전송하는데 이런식으로 elements들의 집합을 pipeline이라고 합니다. 이 pipeline을 간단하게 구성하기 위해 gst_parse_launch 함수를 호출합니다. 

pipeline = gst_parse_launch("playbin uri=file:/C:/Users/psy10/Downloads/sintel_trailer-480p.mkv",NULL);
pipeline = gst_parse_launch("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm",NULL);

이 파이프라인을 구성하는데 playbin이라는 elements를 넘길 수 있습니다. https://를 통해 웹 상에 있는 동영상을 스트리밍하거나 file://을 통해 내부 파일도 재생할 수 있습니다.

 

파이프 라인을 playing 상태로 설정합니다.

gst_element_set_state (pipeline, GST_STATE_PLAYING);

아래 함수를 통해 stream이 종료(GST_MESSAGE_EOS)되거나 에러(GST_MESSAGE_ERROR)가 발생할 때까지 대기합니다.

bus = gst_element_get_bus (pipeline);
msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));

stream이 종료 후 반환된 Message를 통해 ERROR가 발생했는지 체크합니다.

if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
   g_error ("An error occurred! Re-run with the GST_DEBUG=*:WARN environment "
      "variable set for more details.");
}

작업 종료 후 마지막으로 pipeline state를 정리하고 나머지 객체들을 해제시켜 줍니다.

gst_message_unref (msg);
gst_object_unref (bus);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
728x90
728x90

https://stackoverflow.com/questions/30605949/is-it-possible-to-remove-weston-toolbar

 

Is it possible to remove Weston toolbar?

Is it possible to have weston run without the toolbar at the top and change the background to black. So that when i have a script in init.d it calls weston first than my qt5 app? such as: weston &...

stackoverflow.com

 

728x90
728x90

QAbstractItemModel을 적용하고 있는 View에 보여지는 폰트 변경하는 방법입니다.

 

일단 예시로 QAbstractItemModel을 상속받고 있는 QFileSystemModel을 상속받는 다른 클래스를 만들어 줍니다. 그리고 data 함수와 headerData 함수를 오버라이딩 합니다.

class MyFileSystem : public QFileSystemModel 
{
    QVariant data ( const QModelIndex& index, int role ) const;
    QVariand headerData (int section, Qt::Orientation orientation, int role ) const;
    ...;
};

data 함수를 통해 본문의 폰트를 수정, headerData 함수를 통해 가장 상단 header의 폰트를 수정할 수 있습니다.

QFont font;

QVariant data( const QModelIndex& index, int role )
{
    if ( role == Qt::FontRole ) {
    	return font;
    }
    
    return QFileSystemModel::data(index, role);
}
    
QVariand headerData(int section, Qt::Orientation orientation, int role ) 
{
    if ( role == Qt::FontRole ) {
    	return font;
    }
    
    return QFileSystemModel::headerData(section, orientation, role);
}

위 role에서 폰트 말고도 여러 enum 값을 통해 다양한 설정이 가능합니다.

 

Qt의 Model / View 관련한 다른 내용은 이 링크 참고 부탁드립니다.

 

Qt 4.8: Model/View Tutorial

Model/View Tutorial Every UI developer should know about ModelView programming and the goal of this tutorial is to provide you with an easily understandable introduction to this topic. Table, list and tree widgets are components frequently used in GUIs. Th

het.as.utexas.edu

 

728x90
728x90

dynamic

기존 dart 문법에서 var 자료형을 쓰면 처음 초기화 한 형태에 맞는 자료형을 갖게 됩니다. 하지만 한번 자료형이 정해진 var 변수에 다른 형태의 자료형을 그냥 대입하려 하면 문제가 생기는데 dynamic을 사용하면 대입할 때 마다 값에 맞는 자료형으로 바뀌게 됩니다.

Nullable, Non-Nullable, Null 인식 연산자

NULL이 될수 있는지 없는지 명시해줄 때 사용합니다.

null 인식 연산자(??=)의 경우 값에 null이 들어있을 경우에만 해당 값을 대입합니다.

const, final

두 키워드 모두 값의 불변을 위한 키워드로 사용합니다.

두 키워드의 차이점은 DateTime.now 같이 유동적으로 코드가 실행되는 순간 결정되는 값에는 const를 사용할 수 없습니다.  final은 빌드 시 값을 몰라도 되지만 const는  빌드 시 값을 알아야 하기 때문에 사용이 불가능 합니다.

그리고 이 키워드 사용시 var 없이도 알아서 자료형을 잡아줍니다.  (dynamic이 아니므로 다른 자료형의 값을 넣을 수 없습니다.)

Named Paramter

함수에 인자를 넘길 때 인자를 명시하면서 넘겨주는 방식입니다.

Arrow

함수에서 반환되는 값을 간단히 표현하기 위해 => 연산자를 사용합니다.

위에 작성한 간단한 설명은 제가 잘 몰랐던 문법이라 정리해 두었습니다. 다른 기초 문법의 경우 아래 유튜브 영상을 참고해주시기 바랍니다.

 

728x90

'Programming > Flutter' 카테고리의 다른 글

[Flutter] 레이아웃과 위젯  (0) 2022.11.20
[Flutter] 프로젝트 내 폰으로 실행  (0) 2022.11.20
[Flutter] 기본 프로젝트 설명  (0) 2022.11.18
[Flutter] 비동기 처리  (0) 2022.11.18
[Flutter] 개발 환경 세팅  (0) 2022.11.17
728x90

이전에 했던 기본 프로젝트에 위젯을 몇개 추가해보고 이벤트 실습도 해보겠습니다. 

 

먼저 Button 입니다. Button을 클릭하면 카운터를 초기화 하는 이벤트입니다. 기본 State 객체에 버튼을 하나 추가했습니다.

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: 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.bodyText2
            ),
            TextButton(
                onPressed: () {
                  setState(() {
                    _counter = 0;
                  });
                }                 
                , child: const Text('Clear'))
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
      
    );
  }
}

clear 버튼을 누르면 카운터 변수 값이 초기화되고 화면이 갱신됩니다.

이 버튼의 종류는 여러가지가 있지만 일반적인 버튼은 TextButton, ElevatedButton, OutlineButton 등이 있습니다. 

ElevatedButton
OutlineButton

다음으로 입력창입니다. 입력 위젯은 TextField입니다.

화면을 보니 Clear 버튼의 글자 크기가 너무 작은거 같아서 TextField를 통해 폰트 크기를 전달 받고 Change 버튼을 누르면 Clear 버튼의 글자 바꿔 키워보겠습니다.

TextField로 입력한 값을 받아 활용하기 위해서는 TextEditingController를 사용해야 합니다. 기존 레이아웃은 column으로 font 변경관련은 row로 배치하기 위해 Container로 분리하였습니다.

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  TextEditingController value = TextEditingController();

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              alignment: Alignment.center,
              padding: const EdgeInsets.all(20),
              child: Column(
                children: <Widget>[
                  const Text(
                    'You have pushed the button this many times:',
                  ),
                  Text(
                      '$_counter',
                      style: Theme.of(context).textTheme.bodyText2
                  ),
                  TextButton(
                      onPressed: () {
                        setState(() {
                          _counter = 0;
                        });
                      }
                      , child: const Text('Clear')
                  ),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsets.all(20),
              child: Row(
                children: <Widget>[
                  const Text(
                      'Font Size ='
                  ),
                  TextField(
                    keyboardType: TextInputType.number,
                    controller: value,
                  ),
                  ElevatedButton(
                      onPressed: (){},
                      child: const Text(
                        'Change'
                      ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.

    );
  }
}

그런데 실제 결과를 보면 아래처럼 입력창이 제대로 나오지 않습니다.

Row 배치를 Column 배치로만 바꾸면 제대로 나옵니다.

이때는 TextField를 Flexible로 감싸면 됩니다.

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  TextEditingController value = TextEditingController();

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              alignment: Alignment.center,
              padding: const EdgeInsets.all(20),
              child: Column(
                children: <Widget>[
                  const Text(
                    'You have pushed the button this many times:',
                  ),
                  Text('$_counter',
                      style: Theme.of(context).textTheme.bodyText2),
                  TextButton(
                      onPressed: () {
                        setState(() {
                          _counter = 0;
                        });
                      },
                      child: const Text('Clear')),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsets.all(20),
              child: Row(
                children: <Widget>[
                  const Text('Font Size ='),
                  Flexible(
                    child: TextField(
                      keyboardType: TextInputType.number,
                      controller: value,
                    ),
                  ),
                  ElevatedButton(
                    onPressed: () {},
                    child: const Text('Change'),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

이제 이 입력 창을 클릭하면 숫자를 입력할 수 있는 키패드가 나오게 됩니다.

이제 입력 값을 통해 FontSize를 변경하기 위해 Button onPressd 시 TextEditingController 값을 파싱합니다. 그 후 SetState를 호출하면서 파싱된 값을 Size 변수에 넘기고 Size의 값은 Button의 TextStyle에 넘겨주게 됩니다.

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  TextEditingController value = TextEditingController();
  int size = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              alignment: Alignment.center,
              padding: const EdgeInsets.all(20),
              child: Column(
                children: <Widget>[
                  const Text(
                    'You have pushed the button this many times:',
                  ),
                  Text('$_counter',
                      style: Theme.of(context).textTheme.bodyText2),
                  TextButton(
                      onPressed: () {
                        setState(() {
                          _counter = 0;
                        });
                      },
                      child: Text('Clear',
                        style: TextStyle(
                          fontSize: size.toDouble()
                        ),
                      ),
                  ),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsets.all(20),
              child: Row(
                children: <Widget>[
                  const Text('Font Size ='),
                  Flexible(
                    child: TextField(
                      keyboardType: TextInputType.number,
                      controller: value,
                    ),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      setState(() {
                        size = int.parse(value.value.text);
                      });
                    },
                    child: const Text('Change'),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

728x90

'Programming > Flutter' 카테고리의 다른 글

[Flutter] Dart 문법  (0) 2022.11.23
[Flutter] 프로젝트 내 폰으로 실행  (0) 2022.11.20
[Flutter] 기본 프로젝트 설명  (0) 2022.11.18
[Flutter] 비동기 처리  (0) 2022.11.18
[Flutter] 개발 환경 세팅  (0) 2022.11.17
728x90

프로젝트 만들고 소스 작업을 하고 테스트를 위해서 에뮬을 통해서 결과물을 확인했었습니다. 하지만 이 안드로이드 스토디오에 에뮬레이터에 크롬까지 띄우면 메모리 사용량이 어마어마합니다. 그래서 에뮬을 사용하지 않고 폰에서 직접 실행 / 테스트하려고 합니다.

 

갤럭시 s22 ultra, win 10 노트북 기준 설명입니다. 먼저 각 제조사 핸드폰 usb 드라이버를 설치합니다. 삼성의 경우 이 링크에서 설치가 가능합니다.

 

다운로드 자료실 | 스스로해결 | 삼성전자서비스

다운로드 자료실 원하시는 정보를 못 찾으셨다면 아래 서비스를 이용해보세요. 문제해결이 되지 않거나 어려우시다면 인공지능 채팅 로봇으로 상담을 받아보시기 바랍니다. 무상 보증기간 이

www.samsungsvc.co.kr

다음으로 핸드폰에서 [설정] > [휴대전화 정보]로 들어갑니다.

[소프트웨어 정보]로 들어갑니다.

중간에 있는 [빌드번호]를 여러번 클릭하면 개발 설정 n 단계 전입니다.라고 메시지가 뜹니다.

Pin 번호를 입력하면 아래처럼 개발자 모드를 켰습니다. 라고 메시지가 뜹니다.

그 후 다시 [설정] 화면으로 돌아오면 [개발자 옵션] 메뉴가 생긴 것을 볼 수 있습니다.

[개발자 옵션]에서 쭉 내리다보면 [디버깅]에 [USB 디버깅] 옵션이 있는 것을 볼 수 있는데 이 옵션을 활성화시켜줍니다.

그 후 안드로이드 스튜디오를 실행하면 USB 디버깅을 허용하시겠습니까 라는 알림이 뜨는데 허용을 눌러줍니다.

만약 이 알림이 안 뜬다면 알림바를 내려 Android 시스템에서 USB 액션을 선택 후 [USB를 제어할 기기]에서 [연결된 기기]를 한번 클릭한다면 재연결 되듯이 보이면서 위 알림이 보일 겁니다.

 

그 후 우측 상단 장비 모델 명이 스마트폰의 모델 코드인 것을 확인하고 실행 버튼 혹은 [Shitf] + [F10]으로 실행하면 어플에 실행 결과를 확인할 수 있습니다.

728x90

'Programming > Flutter' 카테고리의 다른 글

[Flutter] Dart 문법  (0) 2022.11.23
[Flutter] 레이아웃과 위젯  (0) 2022.11.20
[Flutter] 기본 프로젝트 설명  (0) 2022.11.18
[Flutter] 비동기 처리  (0) 2022.11.18
[Flutter] 개발 환경 세팅  (0) 2022.11.17
728x90

이전에 만들었던 프로젝트를 열면 왼쪽 [Project]에 여러 파일들이 있습니다.

기본적으로 작업하는 dart 파일은 lib 폴더에 그리고 특이하게도 테스트 용 dart 폴더가 따로 존재합니다. 그 의외에도 여러 파일들이 있지만 추후 설명하겠습니다.

 

dart 파일을 위에서부터 보겠습니다. 먼저 import 입니다. 소스 파일에서 사용하려는 패키지를 불러올 때 사용합니다. 

import 'package:flutter/material.dart';

해당 패키지한 내용은 이 링크를 통해 확인하실 수 있습니다.

 

다음으로 main 함수입니다. runApp을 통해 위젯을 전달해 앱을 시작하게 됩니다.

void main() {
  runApp(const MyApp());
}

다음으로 전달되는 위젯 클래스 MyApp입니다. MyApp은 StatelessWidget이라는 클래스를 상속받았습니다. StatelessWidget을 상속받은 위젯은 변화가 없는 즉 Run 이후 한번 로딩되면 바뀔일이 없는 위젯이 상속받아 사용합니다. 이 StatelessWidget은 build를 한번 호출해 기본 UI를 미리 구축해 놓습니다. 이제 StatelessWidget을 상속받은 MyApp도 build를 재정의하여 MyApp만의 화면을 build하여 MaterialApp을 리턴합니다. 

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',        // 앱 이름
      theme: ThemeData(
        primarySwatch: Colors.blue,     // 테마 색깔
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),    // 앱 실행시 첫 실행할 위젯 지정
    );
  }
}

다음으로 MyApp에서 지정한 첫 실행에 세팅된 MyHomePage 클래스입니다. 이 클래스는 위 클래스와 다르게 StatefulWidget을 상속받았습니다. StatelessWidget과 다르게 화면에 갱신이 필요한 위젯(클래스)는 이 클래스를 상속받습니다. StatefulWidget은 먼저 State를 생성하기 위해 createState를 호출하는데 이때 State 객체를 생성합니다. createState() 옆에 =>는 return 이라고 생각하시면 됩니다. 

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

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

마지막으로 생성한 State에 대한 설명입니다. 아래 build는 위 StatelessWidget과 같이 Widget을 생성해 반환하여 화면에 그려줍니다. 가장 처음에 있는 counter 변수는 언더바가 가장 앞에 적혀있는데 이는 현재 dart 파일에서만 사용한다는 의미입니다. incrementCounter 함수도 내부 함수이며 구현에 setState를 통해 위젯을 갱신합니다.

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: 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.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

build에서 Scaffold라는 것을 return 하는데 기본 어플의 뼈대라고 생각하면 편할거 같습니다. 기본적으로 appbar, body,bottomNavigationbar 등이 있고 상황에 따라 원하는 구조를 추가할 수 있고 Scafforld 내에서 [ctrl] + [space]를 통해 옵션들을 확인할 수 있습니다. 

body에 Center 함수로 화면 가운데 위치시키면서 child 옵션으로 Column을 그 안에 Children을 통해 Text 위젯들을 넣었습니다. 이처럼 하위에 위젯을 놓을 때 하나를 놓기 위해선 child, 여러개를 놓기 위해서는 children을 호출합니다.

 @override
  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.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );

마지막으로 floatingActionButton에 있는 onPressed는 클릭하였을 때 incrementCounter 함수를 호출해주고 그 함수는 위에서 setState를 통해 count 개수를 늘리며 화면을 갱신해주고 있습니다. 

728x90

'Programming > Flutter' 카테고리의 다른 글

[Flutter] Dart 문법  (0) 2022.11.23
[Flutter] 레이아웃과 위젯  (0) 2022.11.20
[Flutter] 프로젝트 내 폰으로 실행  (0) 2022.11.20
[Flutter] 비동기 처리  (0) 2022.11.18
[Flutter] 개발 환경 세팅  (0) 2022.11.17

+ Recent posts