StatefulWidget 구조
Navigator.push 로 context와 다음 넘어갈 화면 정보(app 혹은 화면 widget)를 넣고, 필요한 경우 key를 전달하여 이동한다.
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MyHomePage2(title: "hello")));
},
child: Text("next page")),
다시 되돌아 오기 위해서는
pop을 해오면 된다.
Navigator.pop(context);
아무것도 없는 위젯
수직, 수평 방향 위젯 배치
mainAxis : 위젯의 기본 방향 (위아래, 좌우)
crossAxis : 위젯의 수직방향(좌우, 위아래)
mainAxisSize : mainAxis 상수값 크기
children의 위젯들을 순서대로 겹쳐줌
가장 먼저 작성된 위젯이 가장 아랫쪽에 배치됨
body: Stack(
children: <Widget>[
Container(
width: 200,
height: 200,
color: Colors.amber,
),
Container(
width: 150,
height: 150,
color: Colors.red,
),
Container(
width: 100,
height: 100,
color: Colors.blue,
),
],
),
모양만 가지는 빈 박스영역 만들고 싶를 때 자주 사용
SizedBox(height: 10, width: 10, ),
그림자, 둥근 모서리 등의 속성을 사용할 수 있는 위젯
shape: RoundedRectangleBorder( //모서리를 둥글게 하기 위해 사용
borderRadius: BorderRadius.circular(16.0),
),
elevation: 4.0, //그림자의 깊이를 설정
리스트를 표시하는 위젯
ListView + ListTile
body: ListView(
children: <Widget>[
ListTile(
title: Text('1. 화면 배치를 위한 위젯'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MultiMenu()),
);
},
),
ListTile(
title: Text('2. 위치, 정렬, 크기를 위한 위젯'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LayoutMenu()),
);
},
),
ListBody
final items = List.generate(100, (i) => i).toList();
body: SingleChildScrollView(
child: ListBody(
children: items.map((i) => Text('$i')).toList(),
),
),
열수를 지정하여 grid 형태로 표현
GridView.count()를 통해 그리드 생성
crossAxisCount 프로퍼티로 열 수 지정
여러 페이지를 좌우로 슬라이드 하여 넘길 수 있도록 기능 제공
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text('Tab'),
bottom: TabBar(
tabs: <Widget>[
Tab(icon: Icon(Icons.tag_faces)),
Tab(text: '공지사항'),
Tab(icon: Icon(Icons.wifi_rounded), text: '네트워크'),
],
),
),
body: TabBarView(
children: <Widget>[
Container(color: Colors.yellow,),
Container(color: Colors.orange,),
Container(color: Colors.black26,),
]),
),
);
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex, //현재 선택된 Index
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '홈',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: '개인정보',
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications),
label: '알림',
),
]),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
);
}
}
List _widgetOptions = [
Text(
'홈',
style: TextStyle(fontSize: 30),
),
Text(
'개인정보',
style: TextStyle(fontSize: 30),
),
// FirstPage(),
Text(
'알림',
style: TextStyle(fontSize: 30),
),
];
가장 일반적인 버튼
텍스트가 입력된 평평한 형태의 버튼
아이콘 표시용 버튼
FAB 버튼
이미지 표시 위젯
# To add assets to your application, add an assets section, like this:
assets:
- assets/
- assets/sample.jpg
- assets/flutter.jpg
- assets/flutter.png
assets/ 까지만 써도 하위 파일들이 모두 된다.
CircularProgressIndicator(
backgroundColor: Colors.orange,
),
SizedBox(
height: 20,
),
LinearProgressIndicator(
color: Colors.purpleAccent,
),
CircleAvatar(
radius: 100,
child: Icon(Icons.person, size: 200,),
),
materialDesign 에서 볼 수 있는 형태로 edittext를 꾸며줄 수 있다.
TextField(),
SizedBox(
height: 40,
),
TextField(
decoration: InputDecoration(
labelText: '여기에 입력하세요', // 힌트
),
),
SizedBox(
height: 40,
),
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(), // 외각선
labelText: '여기에 입력하세요',
),
textField에 쓰인 값 저장을 위해 Controller를 사용한다.
TextEditingController value1 = TextEditingController();
TextField(
controller: value1,
decoration: InputDecoration(
labelText: '여기에 입력하세요', // 힌트
),
),
TextFormField는 form내부를 구성하는 textfield 위젯이다. Validator를 만들어 원하는 형태로 검증을 할 수 있다.
TextFormField(
controller: _email,
validator: (value) =>
(value!.isEmpty) ? "이메일을 입력해 주세요" : null,
style: style,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email),
labelText: "Email",
border: OutlineInputBorder()),
),
Radio는 RadioListTile 혹은 ListTile에 Radio를 넣어서 구성할 수 있다.
ListTile은 라디오 버튼 영역만, RadioListTile은 해당 라디오가 차지한 전체 영역을 클릭했을 때 토글할 수 있다.
ListTile(
title: Text('남자'),
leading: Radio(
value: Gender.MAN,
groupValue: _gender,
onChanged: (value) {
setState(() {
_gender = Gender.MAN ;
});
},
),
),
이를 감싸는 form에서는 formKey를 속성으로 가져 validation이 모두 통과일 경우 currentState를 validate로 해줄 수 있다.
class _LoginPageState extends State<LoginPage> {
...
final _formKey = GlobalKey<FormState();
...
버튼 클릭이 되면 validation을 검증하도록 하였다
child: MaterialButton(
onPressed: () {
if (_formKey.currentState!.validate()) {}
},
AlertDialog, DatePicker, TimePicker 등등 사용 가능
Future<void> _neverSatisfied() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('제목'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Alert Dialog 입니다'),
Text('OK를 눌러 닫습니다'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('OK'),
onPressed: () {
// Todo
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
builder를 통해 alertdialog을 만들어주는 _neverStatisfied 함수를 만들어주었다. 시간이 걸리는 작업이므로 async로 처리한다.
위젯들 중에서는 onTap, onClcked 속성이 따로 달려있지 않아 제스처를 받는 위젯으로 감싸주어야 한다.
GestureDetector(
onTap: () {
print('GestureDetector 클릭!!');
},
child: Text('클릭 Me!!'),
),
SizedBox(
height: 40,
),
InkWell(
onTap: () {
print('InkWell 클릭!!');
},
child: Text('클릭 Me!!'),
),
Inkwell은 클릭 영역이 보이고, 제스쳐 detector는 영역이 보이지 않지만 둘다 감싸는 위젯을 클릭할 수 있게 해준다.
onTapDown, onTapUp, onTap, onTapCancel 등의 속성 있음