23 분 소요

1. 사용자 입력 처리 위젯

1) 텍스트 필드

TextField(
	style: TextStyle(fontSize: 15),
	textAlign: TextAlign.center
)

(1) 입력된 데이터 얻기 - TextEditingController

  • 텍스트 필드에 입력된 데이터 얻기
      class TextState extends State<TestScreen> {
          final controller = TextEditingController(); // 데이터 획득, 변경 이벤트 감지 클래스
          Widget build(BuildContext context){
              return Column(
              children:[
                  TextField(
                      style: TextStyle(fontSize: 15.0),
                      controller: controller,  // 컨트롤러 연결
                  ),
                  ElevatedButton(
                      child: Text('submit'),
                      onPressed: () {
                          print('submit : ' + controller.text);  // 획든한 데이터 출력
                })
          ]);
        }
      }
    
  • 텍스트 필드값 변경 감지 방법
      @override
      void initState() {
          super.initState();
          controller.addListener(_printValue); // 한 글자씩 입력될 때마다 (매개변수 함수) 처리
      }
    	
      @override
      void dispose() {
          super.dispose();
          controller.dispose();  // 텍스트 감지 필요 없을 시 호출
      }
    

(2) 꾸미기 - InputDecoration

  • InputDecoration 속성
    • labelText : 라벨 문자열
    • helperText : 아래쪽에 출력되는 설명 문자열
    • hintText : 입력 상자 안쪽에 출력되었다가 글 입력 시 사라지는 문자열
    • errorText : 아래쪽에 출력되는 오류 문자열
    • prefixIcon : 입력 앞 부분에 고정으로 출력되는 아이콘 이미지
    • counterText : 아래쪽에 출력되는 문자열
    • border : 테두리 지정(OutlineInputBoarder, UnderlineInputBoarder 중 하나 이용)
  • 코드 예시
      TextField(
        style : TextStyle(fontSize:15.0),
        controller : controller,
          decoration: InputDecoration(
            labelText: 'Name',
            prefixIcon: Icon(Icons.input),
            border: OutlineInputBorder(),
            hintText: "Hint Text",
            helperText: "이름을 입력하세요.",
            counterText: "$textCounter characters")) // errorText: "error text"
    

(3) 액션 버튼 - textInputAction

  • textInputAction 속성
    • TextInputAction.next : 다음 위젯으로 포커스 이동
    • TextInputAction.previous : 이전 위젯으로 포커스 이동
    • TextInputAction.search : 검색 버튼
    • TextInputAction.send : 전송 버튼
  • 코드 예시
      TextField(
          textInputAction: TextInputAction.search
      )
    

(4) 키보드 유형 - keyboardType

  • keyboardType 상수 설정
    • TextInputType.number : 숫자 입력
    • TextInputType.text : 문자 입력
    • TextInputType.phone : 전화번호 입력
    • TextInputType.emailAddress : 이메일 주소 입력
    • TextInputType.url : URL 입력
  • 코드 예시
      TextField(
          keyboardType: TextInputType.number
      )
    

(5) 텍스트 감추기 - obscureText

  • 예시 : ****
  • 코드 예시
      TextField(
          obscureText: true
      )
    

(6) 여러 줄 입력 - maxLines, minLines

  • 기본 : 한 줄 입력
  • 코드 예시
      TextField(
          minLines: 2,
          maxLines: 5
      )
    

✨코드 예시

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: TestScreen()));
  }
} 

class TestScreen extends StatefulWidget {
  @override
  TextState createState() => TextState();
} 

class TextState extends State<TestScreen> {
  final controller = TextEditingController();
  int textCounter = 0;
  
  _printValue() {
    print("_printValue(): ${controller.text}");
    setState(() {
      textCounter = controller.text.length;
    });
  }
  
  @override
  void initState() {
    super.initState();
    controller.addListener(_printValue);
  }
  
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    print("build....");
    return Column(
      children: [
        Text('TextField Test'),
        TextField(
          style: TextStyle(fontSize: 15.0),
          controller: controller,
          decoration: InputDecoration(
            labelText: 'Data',
            prefixIcon: Icon(Icons.input),
            border: OutlineInputBorder(),
            hintText: "Hint Text",
            helperText: "데이터를 입력하세요.",
            counterText: "$textCounter characters",
          ),
          textInputAction: TextInputAction.search,
          keyboardType: TextInputType.emailAddress,
          minLines: 5,
          maxLines: 5,
)],);}}

2) 체크박스, 라디오 버튼, 슬라이더, 스위치

(1) 체크박스 - Checkbox

  • 체크박스 출력하기
      Checkbox(
          value: isChecked,
          onChanged: (bool? value) {
              setState(() {
                  isChecked = value;
      });})
    

(2) 라디오 버튼 - Radio

  • groupValue : 그룹 간에 하나만 선택할 수 있음
      Row(
          children: [
              Radio(
                  value: "android",
                  groupValue: selectPlaform,  // 한 묶음
                  onChanged: (String? value) {
                      setState(() {
                          selectPlatform = value;
                      });
                  }
              ),
              Text('android')
      ]),
    	
      Row(
          children: [
              Radio(
                  value: "ios",
                  groupValue: selectPlaform,  // 한 묶음
                  onChanged: (String? value) {
                      setState(() {
                          selectPlatform = value;
                      });
                  }
              ),
              Text('ios')
      ]),
      Text('select platform is $selectPlatform')
    

(3) 슬라이더 - Slider

  • 슬라이더 출력하기(min, max 속성으로 값 설정)
      Slider(
          value: selectValue,
          min: 0,
          max: 10,
          onChanged: (double value) {
              setState(() {
                  selectValue = value;
      });})
    

(4) 스위치 - Switch

  • 스위치 출력하기
      Switch(
          value: selectValue,
          onChanged: (bool value) {
              setState(() {
                  selectValue = value;
      });})
    

✨코드 예시

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: TestScreen()));
  }
}
  
class TestScreen extends StatefulWidget {
  @override
  TextState createState() => TextState();
} 

class TextState extends State<TestScreen> {
  bool? isChecked = true;
  String? selectPlatform;
  double sliderValue = 5.0;
  bool switchValue = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Checkbox Test'),
        Row(
          children: [
            Checkbox(
                value: isChecked,
                onChanged: (bool? value) {
                  setState(() {
                    isChecked = value;
                  });
                }),
            Text("checkbox value is $isChecked")
          ],
        ),
        Text('Radio Test'),
        Row(
          children: [
            Radio(
                value: "android",
                groupValue: selectPlatform,
                onChanged: (String? value) {
                  setState(() {
                    selectPlatform = value;
                  });
                }),
            Text('android')
          ],
        ),
        Row(
          children: [
            Radio(
              value: "ios",
              groupValue: selectPlatform,
              onChanged: (String? value) {
                setState(() {
                  selectPlatform = value;
                });
              },
            ),
            Text('ios')
          ],
        ),
        Text('select plaform is $selectPlatform'),
        Text('Slider Test'),
        Slider(
            value: sliderValue,
            min: 0,
            max: 10,
            onChanged: (double value) {
              setState(() {
                sliderValue = value;
              });
            }),
        Text('slider value is $sliderValue'),
        Text('Switch Test'),
        Switch(
            value: switchValue,
            onChanged: (bool value) {
              setState(() {
                switchValue = value;
              });
            }),
        Text('select value is $switchValue')
],);}}

3) 폼 이용하기

  • 유효성 검증, 데이터 관리 등을 위해 이용 가능

(1) 폼에 키값 대입하기

  • TextFormField에는 validator와 onSaved 속성에 함수를 설정할 수 있음(TextEditingController를 사용하지 않고 사용자 입력 데이터 받을 수 있음)
      class MyFormState extends State {
        final _formKey = GlobalKey<FormState>();
    	
        @override
        Widget build(BuildContext context) {
          return Column(children: [Form(key: _formKey)]);
        }
      }
    

(2) 유효성 검증과 데이터 저장하기

  • FormState가 제공하는 validate(), save() 함수 실행 시 validator, onSaved 속성에 설정한 함수 호출
    Form(
    key: _formKey,
    child: Column(TextFormField(
      decoration: InputDecoration(labelText: 'FirstName'),
      validator: (value) {
        if (value?.isEmpty ?? flase) {
          return 'Please enter first name';
        }
        return null;
      },
      onSaved: (String? value) {
        firstName = value;
      },
    ))),
    ElevatedButton(
    onPressed: () {
      if (_formKey.currentState?.validate() ?? false) {
        _formKey.currentState?.save();
        print('firstName: $firstName, lastName : $lastName');
      }
    },
    child: Text('submit')
    )
    

✨코드 예시

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}
 
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: TestScreen()));
  }
}

class TestScreen extends StatefulWidget {
  @override
  MyFormState createState() => MyFormState();
}

class MyFormState extends State<TestScreen> {
  final _formKey = GlobalKey<FormState>();
  String? firstName;
  String? lastName;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Form Test'),
        Form(
            key: _formKey,
            child: Column(
              children: [
                TextFormField(
                  decoration: InputDecoration(labelText: 'FirstName'),
                  validator: (value) {
                    if (value?.isEmpty ?? false) {
                      return 'Please enter first name';
                    }
                    return null;
                  },
                  onSaved: (String? value) {
                    firstName = value;
                  },
                ),
                TextFormField(
                    decoration: InputDecoration(labelText: 'LastName'),
                    validator: (value) {
                      if (value?.isEmpty ?? false) {
                        return 'Please enter last name';
                      }
                      return null;
                    },
                    onSaved: (String? value) {
                      lastName = value;
                    }),
              ],
            )),
        ElevatedButton(
            onPressed: () {
              if (_formKey.currentState?.validate() ?? false) {
                _formKey.currentState?.save();
                print('firstName: $firstName, lastName : $lastName');
              }
            },
            child: Text('submit'))
],);}}

2. 목록 구성과 다이얼로그 위젯

1) 리스트 뷰

(1) 스크롤이 필요한 상황

  • 스크롤 지원 리스트 뷰
      ListView(
          scrollDirection: Axis.horizontal  // 위젯을 가로로 나열하기
          children: [
          Container(height: 300, color: Colors.red),
          Container(height: 300, color: Colors.green),
          Container(height: 300, color: Colors.blue)
      ])
    

(2) 목록 구성하기

  • 항목을 스크롤에 따라 불러오기
      List<String> citys = [
          '서울시', '인천시', '부산시', '대구시', '대전시', '광주시', '울산시', '세종시'
      ]
    	
      @override
      Widget build(BuildContext context) {
          return ListView.builder(
              itemCount: citys.length,
              itemBuilder: (context, index){
                  return Container(
                      padding: EdgeInsets.only(left: 10, top: 10),
                      height: 100,
                      child: Text(citys[index])
      );},);}
    
  • 항목 구분자 설정하기
      ListView.separated(
          itemCount: citys.length,
          itemBuilder: (context, index) {
              return Container(
                  padding: EdgeInsets.only(left: 10, top: 10),
                  height: 100,
                  child: Text(citys[index]),
              )
          }
          separatorBuilder: (context, index) {
              return Divider(height: 2, color: Colors.black);
          }
      )
    

(2) 항목 구성하기 - ListTile

  • 항목 구성하기
      ListView.separated(
          itemCount: users.lengtth,
          itemBuilder: (context, index) {
              return ListTile(
                  leading: CircleAvatar(
                      radius: 25,
                      backgroundImage: AssetImage('images/big.jpg'),
      ),
      title: Text(users[index].name),
      subtitle: Text(users[index].phone),
      trailing: Icon(Icons.move_vert),
      onTap: () {
          print(users[index].name);
      }
      )},
      separatorbuilder: (context, index) {
          return Divider(height: 2, color: Colors.black);
      },)
    

✨코드 예시

import 'package:flutter/material.dart';

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

class User {
  String name;
  String phone;
  String email;
  User(this.name, this.phone, this.email);
}
  
class MyApp extends StatelessWidget {
  List<User> users = [
    User('홍길동', '0100001', 'a@a.com'),
    User('김길동', '0100002', 'b@a.com'),
    User('이길동', '0100003', 'c@a.com'),
    User('박길동', '0100004', 'd@a.com'),
    User('홍길동', '0100001', 'a@a.com'),
    User('김길동', '0100002', 'b@a.com'),
    User('이길동', '0100003', 'c@a.com'),
    User('박길동', '0100004', 'd@a.com')
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: ListView.separated(
              itemCount: users.length,
              itembuilder: (context, index) {
                return ListTile(
                    leading: CircleAvatar(
                      radius: 25,
                      backgroundImage: AssetImage('images/big.jpg'),
                    ),
                    title: Text(users[index].name),
                    subtitle: Text(users[index].phone),
                    trailing: Icon(Icon.more_vert),
                    onTap: () {
                      print(users[index].name);
                    });
              },
              separatorBuilder: (context, index) {
                return Divider(height: 2, color: Colors.black);
              },
)));}}

2) 그리드 뷰

  • 항목을 세로로 배치하기
      GridView.builder(
          itemCount: citys.length,
          itemBuilder: (context, index) {
              return Card(
                  child: Text(citys[index]),
              );},
          gridDelegate: SliverGridDelegateWithfixedCrossaxisCount(crossAxisCount: 2),
      )
    
  • 항목을 가로로 배치하기
      GridView.builder(
          scrollDirection: Axis.horizontal,
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
      )
    

✨코드 예시

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  List<String> citys = ['서울시', '인천시', '부산시', '대구시', '대전시', '광주시', '울산시', '세종시'];
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: GridView.builder(
              itemCount: citys.length,
              itemBuilder: (context, index) {
                return Card(
                  child: Column(
                    children: [
                      Text(citys[index]),
                      Image.asset('images/big.jpg')
                    ],
                  ),
                );
              },
              scrollDirection: Axis.horizontal,
              gridDelegate:
                  SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
            )));
  }
}

3) 페이지 뷰

✨ 코드 예시

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  PageController controller =
      PageController(initialPage: 1, viewportFraction: 0.8);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: PageView(
              controller: controller,
              children: [
                Container(
                  margin: EdgeInsets.all(20),
                  color: Colors.red,
                  child: Center(
                    child: Text('OnePage',
                        style: TextStyle(color: Colors.white, fontSize: 30)),
                  ),
                ),
                Container(
                  margin: EdgeInsets.all(20),
                  color: Colors.green,
                  child: Center(
                    child: Text('TwoPage',
                        style: TextStyle(color: Colors.white, fontSize: 30)),
                  ),
                ),
                Container(
                  margin: EdgeInsets.all(20),
                  color: Colors.blue,
                  child: Center(
                    child: Text('ThreePage',
                        style: TextStyle(color: Colors.white, fontSize: 30)),
                  ),
                )
              ],
            )));
  }
}

4) 다이얼 로그 띄우기

(1) 알림 창 - AlertDialog

  • 기본 알림 창 띄우기
      showDialog(
          context: context,
          barrierDismissible: false,
          builder: (BuildContext context) {
              return AlertDialog(
                  title: Text("Dialog Title"),
                  content: Text("Dialog Message"),
                  action: [
                      TextButton(
                          onPressed: () {
                              Navigator.of(context).pop();
                          },
                          child: Text("OK")
                      )
                  ],
      );});
    
  • 사용자 입력을 받는 알림 띄우기
      AlertDialog(
          title: Text("Dialog Title"),
          content: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                  TextField(
                      decoration: InputDecoration(borer: OutlineInputBorder()),
                  ),
                  Row(
                      children: [
                          Checkbox(value: true, onChanged: (value) {}),
                          Text('수신동의')
                      ],
                  )
              ],
          ),
          actions: [
              TextButton(
                  onPressed: (){
                      Navigator.of(context).pop();
                  },
                  child: Text("OK")
              )
          ]
      )
    

(2) 보텀 시트 - BottomSheet

  • showBottomSheet() : 검은 화면 아님
  • showModalBottomSheet() : 검은 화면 됨(모달)
  • 보텀 시트 띄우기
      showModalBottomsheet(
          context: context,
          backgroundcolor: Color.yellow,
          builder: (context) {
              return SafeArea(
                  child: Column(
                      mainAxisSize: MainAxissize.min,
                      children: [
                          ListTile(
                              leading: Icon(Icons.add),
                              title: Text('ADD'),
                              onTap: () {
                                  Navigator.of(context).pop();
                              },
                          ),
                          ListTile(
                              leading: Icon(Icons.remove),
                              title: Text('REMOVE'),
                              onTap: () {
                                  Navigator.of(context).pop();
      },)]))})
    

(3) 날짜, 시간 선택 창 - DatePickerDialog, TimePickerDialog

  • 날짜 선택 창(DatePickerDialog)
      DateTime? picked = await showDatePicker(
          context: context,
          initialDate: new DateTime.now(),
          firstDate: new DateTime(2016),
          lastDate: new DateTime(2030); 
      )
    
  • 시간 선택 창
      TimeOfDay? selectedTime = awaitshowTimePicker(
          initialtime: TimeOfDay.now()
          context: context,
      )
    

✨ 코드 예시

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

void main() {
  runApp(MyApp());
}
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: Text('Test'),
            ),
            body: TestScreen()));
  }
}
  
class TestScreen extends StatefulWidget {
  @override
  TextState createState() => TextState();
}

class TextState extends State<TestScreen> {
  DateTime dateValue = DateTime.now();
  TimeOfDay timeValue = TimeOfDay.now();

  _dialog() {
    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text("Dialog Title"),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                decoration: InputDecoration(border: OutlineInputBorder()),
              ),
              Row(
                children: [
                  Checkbox(value: true, onChanged: (value) {}),
                  Text('수신동의')
                ],)
            ],
          ),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: Text("OK"))
          ],
        );
      });
  }

  _bottomSheet() {
    showBottomSheet(
      context: context,
      backgroundColor: Colors.yellow,
      builder: (context) {
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ListTile(
              leading: Icon(Icons.add),
              title: Text('ADD'),
              onTap: () {
                Navigator.of(context).pop();
              },
            ),
            ListTile(
              leading: Icon(Icons.remove),
              title: Text('REMOVE'),
              onTap: () {
                Navigator.of(context).pop();
              }
            )
          ],
        );
      });
  }

  _modalBottomSheet() {
    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.yellow,
      builder: (context) {
        return SafeArea(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ListTile(
                leading: Icon(Icons.add),
                title: Text('ADD'),
                onTap: () {
                  Navigator.of(context).pop();
                },
              ),
              ListTile(
                leading: Icon(Icons.remove),
                title: Text('REMOVE'),
                onTap: () {
                  Navigator.of(context).pop();
                },
              )
            ],
          )
        );
      });
  }

  Future datePicker() async {
    DateTime? picked = await showDatePicker(
      context: context,
      initialDate: new DateTime.now(),
      firstDate: new DateTime(2016),
      lastDate: new DateTime(2030));
    if (picked != null) setState(() => dateValue = picked);
  }

  Future timePicker() async {
    TimeOfDay? selectedTime = await showTimePicker(
      initialTime: TimeOfDay.now(),
      context: context,
    );
    if (selectedTime != null) setState(() => timeValue = selectedTime);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(onPressed: _dialog, child: Text('dialog')),
          ElevatedButton(onPressed: _bottomSheet, child: Text('bottomsheet')),
          ElevatedButton(
            onPressed: _modalBottomSheet, child: Text('modal bottomsheet')),
          ElevatedButton(onPressed: datePicker, child: Text('datepicker')),
          Text('date : ${DateFormat('yyyy-MM-dd').format(dateValue)}'),
          ElevatedButton(onPressed: timePicker, child: Text('timepicker')),
          Text('time : ${timeValue.hour}:${timeValue.minute}'),
        ],
      )
    );
  }
}

5) 탭바 뷰

  • 탭 화면을 구성하는 위젯
      controller = TabController(length: 3, vsync: this);
    	
      TabBar(
          controller: controller,
          tabs: <Widget>[
              Tab(text: 'One'),
              Tab(text: 'Two'),
              Tab(text: 'Three')
          ]
      )
    

✨ 코드 예시

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
} 

class _HomeScreenState extends State<MyApp>
    with SingleTickerProviderStateMixin {
  late TabController controller;
  
  @override
  void initState() {
    super.initState();
    controller = TabController(length: 3, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
                title: Text('Tab Test'),
                bottom: TabBar(controller: controller, tabs: <Widget>[
                  Tab(text: 'One'),
                  Tab(text: 'Two'),
                  Tab(text: 'Three'),
                ])),
            body: TabBarView(
              controller: controller,
              children: <Widget>[
                Center(
                  child: Text(
                    'One Screen',
                    style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                  ),
                ),
                Center(
                  child: Text(
                    'Two Screen',
                    style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                  ),
                ),
                Center(
                  child: Text(
                    'Three Screen',
                    style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                  ),
                )
              ],
            )));
  }
}

댓글남기기