플러터 앱 개발 시 간단한 구성 요소들을 배치하여 동작시키는 법을 알아보겠습니다.
오늘 배워서 완성할 앱은 다음과 같은 간단한 앱입니다.
최종 동작 화면
지금부터 만들어 봅시다.
앱 기본 틀 구성
import 'package:flutter/material.dart' ;
void main () {
runApp ( const MyApp ());
}
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 Tutorial' ,
theme : ThemeData (
colorScheme : ColorScheme . fromSeed ( seedColor : Colors . deepPurple ),
useMaterial3 : true ,
),
home : const MyHomePage ( title : 'Flutter Tutorial Page' ),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage ({ super . key , required this . title });
final String title ;
@ override
State < MyHomePage > createState () => _MyHomePageState ();
}
class _MyHomePageState extends State < MyHomePage > {
String mynamestr = "" ;
TextEditingController namecont = TextEditingController ();
@ override
Widget build ( BuildContext context ) {
return Scaffold (
appBar : AppBar (
backgroundColor : Theme . of ( context ). colorScheme . inversePrimary ,
title : Text ( widget . title ),
),
body : const Center (),
);
}
}
이게 main.dart에서 맨 처음 앱을 화면에 띄우도록 코딩된 내용입니다.
main()함수에서 MyApp()클래스를 호출하고
MyApp클래스에서 MyHomePage클래스를 리턴합니다
MyHomePage클래스는 StatefulWidget이며 override된 _MyHomePageState클래스 안에서 비로소 메인 화면을 구성하게 됩니다.
바로 아래 코드가 핵심인데요
class _MyHomePageState extends State < MyHomePage > {
String mynamestr = "" ;
TextEditingController namecont = TextEditingController ();
@ override
Widget build ( BuildContext context ) {
return Scaffold (
appBar : AppBar (
backgroundColor : Theme . of ( context ). colorScheme . inversePrimary ,
title : Text ( widget . title ),
),
body : const Center (),
);
}
}
화면 구성은 @override Widget build 안에있는 Scaffold 위젯 안에 하나하나 배치되게 됩니다.
해당 화면내에서 쓰이는 변수(ex: mynamestr, namecont)들은 오버라이드된 빌드위젯 바깥쪽에 먼저 정의합니다 (전역변수 느낌).
appBar는 앱의 타이틀 바입니다.
타이틀의 text로는 MyApp클래스에서 넘겨준 인자인 title변수를 씁니다. ("Flutter Tutorial Page")
body안에 위젯들을 배치하기 전에 해당 코드가 정상 동작하는지
device를 Chrome(web)으로 해놓고 Run해보겠습니다. (안드로이드 스튜디오에서)
이렇듯 타이틀과 앱바가 정상적으로 배치된 것을 볼 수 있습니다.
타이틀바를 배치했으면 그밑에 body: 부분에서 비로소 넣고자 하는 각종 위젯들을 배치합니다.
텍스트 위젯
텍스트 위젯은 말그대로 특정한 텍스트 문구를 화면 내에 배치하는 위젯입니다.
@ override
Widget build ( BuildContext context ) {
return Scaffold (
appBar : AppBar (
backgroundColor : Theme . of ( context ). colorScheme . inversePrimary ,
title : Text ( widget . title ),
),
body : const Center (
child : Column (
mainAxisAlignment : MainAxisAlignment . center ,
children : < Widget > [
SizedBox (
height : 20 ,
),
Text (
'안녕하세요' ,
),
],
),
),
);
}
body: 안의 내용물 전체를 Center로 감싸고 child: 내에 배치할 위젯들의 배열을 담습니다.
보통 위젯들은 세로로 쭉 나열되며 배치되기 때문에 Column()안에 children 배열 내에 위젯들을 배치합니다.
SizedBox는 위젯과 위젯 사이의 간격을 주고 싶을때 사용합니다. 지금은 앱바 바로밑에 Text가 바로 배치되는것 보다 일정 간격을 띄어주고 Text를 배치하는게 보기 좋으므로 다음과같이 높이가 20인 SizedBox 밑에 '안녕하세요'라는 Text위젯을 배치합니다.
텍스트폼필드 위젯
텍스트폼필드 위젯은 사용자가 특정 문구를 입력할 수 있게 하는 인풋필드입니다.
TextFormField (
controller : namecont ,
decoration : InputDecoration (
hintText : '이름을 입력하세요' ,
border : OutlineInputBorder (
borderRadius : BorderRadius . circular ( 20 ))),
),
TextFormField에서 controller는 빌드위젯 밖에서 정의했던
TextEditingController namecont = TextEditingController ();
namecont 변수를 사용합니다.
namecont변수는 TextEditingController 위젯인데 변수로 정의해야 나중에 해당 인풋필드 안에 담긴 내용을 가져와서 사용하기 편합니다.
*참고로 controller안에 namecont 변수가 들어가서 동적으로 작동하는 요소이기 때문에 TextFormField안에 const는 삭제되고 동시에 위와같이 body: 에서 const Center라고 되어있던 부분을 Center로 바꿔줘야합니다. (Center앞에 const는 삭제 하나, 이미 만들었던 정적인 위젯들인 Text나 SizedBox위젯 앞에는 const를 추가해줌 - 아래 코드 참조)
body : Center (
child : Column (
mainAxisAlignment : MainAxisAlignment . center ,
children : < Widget > [
const SizedBox (
height : 20 ,
),
const Text (
'안녕하세요' ,
),
const SizedBox (
height : 10 ,
),
TextFormField (
controller : namecont ,
decoration : InputDecoration (
hintText : '이름을 입력하세요' ,
border : OutlineInputBorder (
borderRadius : BorderRadius . circular ( 20 ))),
),
],
),
),
decoration으로 인풋필드 내에 hintText를 적어주고 border등의 요소를 통해 디자인을 입맛에 맞게 다듬습니다.
버튼 위젯
버튼 위젯은 사용자가 해당 버튼을 누르고 해당 버튼 클릭 시 특정 동작을 할수있게 해주는 위젯입니다.
TextButton (
child : const Text ( "제출" ),
onPressed : () {
mynamestr = "제 이름은 ${ namecont . text } 입니다" ;
setState (() {});
},
),
const SizedBox (
height : 10 ,
),
Text (
mynamestr ,
),
TextButton()으로 만들며 버튼 안의 Text는 child: 내에 작성하고,
onPressed: 메소드 안에 해당 버튼 클릭 시 동작할 함수를 적어줍니다.
class _MyHomePageState extends State < MyHomePage > {
String mynamestr = "" ;
TextEditingController namecont = TextEditingController ();
이렇게 빌드위젯 외부에 정의했던 변수 중 mynamestr라는 string변수가 있는데
버튼을 클릭할 시 mynamestr변수가 "제 이름은 {namecont.text} 입니다" 로 변경됩니다.
namecont는 위에 텍스트 폼 위젯에 들어있는 텍스트에디팅컨트롤러 변수(일종의 인풋필드)인데,
해당 인풋필드에 사용자가 입력한 문구를 namecont.text로 가져올 수 있습니다.
즉 사용자가 입력한 이름이 "임꺽정" 이라면 mynamestr에는 "제 이름은 임꺽정 입니다" 라는 문자열이 할당되겠죠.
setState((){}); 함수는 해당 클래스 내의 모든 변수들의 변경사항을 다시 반영해서 화면을 다시그려주는 함수입니다.
그렇다면 TextButton밑에 있었던
이 Text위젯에 있던 mynamestr가 업데이트 된 모습이 반영되어 화면에 갱신되겠죠. 바로 다음과 같이요.
아이콘 위젯
아이콘 위젯은 아이콘을 배치하는 위젯입니다.
간단합니다.
깜찍한 사람얼굴 아이콘이 생겼습니다.
이미지 위젯
이미지 위젯은 배치하고자 하는 이미지 파일을 직접 화면에 보여주는 위젯입니다.
이미지 위젯은 코딩 전에 먼저 해당 프로젝트 폴더 내에 assets이라는 폴더를 만들고
그 assets폴더 내에 넣고자 하는 이미지 파일을 넣어줘야합니다.
그리고 나서 다음과같이 assets안에 넣어진 이미지 파일명으로 이미지 위젯을 생성합니다.
Image . asset ( 'dog.jpg' ),
const SizedBox (
height : 20 ,
),
최종 완성된 화면입니다.
이름 입력 전
이름 입력->제출버튼 클릭
최종 완성된 전체 코드는 다음과 같습니다.
import 'package:flutter/material.dart' ;
void main () {
runApp ( const MyApp ());
}
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 Tutorial' ,
theme : ThemeData (
colorScheme : ColorScheme . fromSeed ( seedColor : Colors . deepPurple ),
useMaterial3 : true ,
),
home : const MyHomePage ( title : 'Flutter Tutorial Page' ),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage ({ super . key , required this . title });
final String title ;
@ override
State < MyHomePage > createState () => _MyHomePageState ();
}
class _MyHomePageState extends State < MyHomePage > {
String mynamestr = "" ;
TextEditingController namecont = TextEditingController ();
@ override
Widget build ( BuildContext context ) {
return Scaffold (
appBar : AppBar (
backgroundColor : Theme . of ( context ). colorScheme . inversePrimary ,
title : Text ( widget . title ),
),
body : Center (
child : Column (
mainAxisAlignment : MainAxisAlignment . center ,
children : < Widget > [
const SizedBox (
height : 20 ,
),
const Text (
'안녕하세요' ,
),
const SizedBox (
height : 10 ,
),
TextFormField (
controller : namecont ,
decoration : InputDecoration (
hintText : '이름을 입력하세요' ,
border : OutlineInputBorder (
borderRadius : BorderRadius . circular ( 20 ))),
),
const SizedBox (
height : 10 ,
),
TextButton (
child : const Text ( "제출" ),
onPressed : () {
mynamestr = "제 이름은 ${ namecont . text } 입니다" ;
setState (() {});
},
),
const SizedBox (
height : 10 ,
),
Text (
mynamestr ,
),
const SizedBox (
height : 10 ,
),
const Icon ( Icons . face ),
const SizedBox (
height : 10 ,
),
Image . asset ( 'dog.jpg' ),
const SizedBox (
height : 20 ,
),
],
),
),
);
}
}
기본 앱 위젯 튜토리얼 요약입니다.
위젯 기본 요약
최종 동작 화면입니다.
최종 완성 동작화면