Flutter

[Flutter] Form 위젯 연습 (피드백 폼 앱)

미로910 2024. 11. 11. 14:14
사용자가 이름, 이메일, 코멘트를 입력하고, 앱을 평가하고 고객 서비스 만족도를 선택한 뒤, 뉴스레터 구독 여부를 설정하여 폼을 제출할 수 있습니다. 앱은 사용자 피드백을 쉽게 받을 수 있도록 설계.

import 'package:flutter/material.dart';

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

// 한 파일안에 여러개의 클래스를 만들 수 있다.

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false, // 디너그 띠 X
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.redAccent),
      ),
      home: FeedbackForm(), // 앱 시작 시 피드백 폼 표시
    );
  }
}

// 상태 변경 가능한 UI 선언
class FeedbackForm extends StatefulWidget {
  const FeedbackForm({super.key});

  @override
  State<FeedbackForm> createState() => _FeedbackFormState();
}

class _FeedbackFormState extends State<FeedbackForm> {
  final _formKey = GlobalKey<FormState>(); // 폼 상태를 추적하기 위한 글로벌 키
  int _rating = 0;
  bool _subscribe = false;
  String _name = '';
  String _email = '';
  String _comments = '';
  String _successMessage = '';
  double _satisfaction = 0.0;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text('FeedbackForm'),
          backgroundColor: Theme.of(context).colorScheme.primaryContainer,
        ),
        body: SingleChildScrollView(
          // formField 터치시 소프트 키보드가 올라 옴. 여백 공간 적절히 줘야 함 (스크롤)
          padding: EdgeInsets.all(16.0),
          child: Form(
            key: _formKey,
            child: Column(
              children: [
                TextFormField(
                  decoration: InputDecoration(
                      labelText: '이름',
                      hintText: '홍길동',
                      border: OutlineInputBorder()),
                  validator: (value) {
                    // 입력 값 검증
                    if (value == null || value.isEmpty) {
                      return '이름을 입력해주세요.';
                    } else {
                      // 정상적으로 입력 했다면
                      return null;
                    }
                  }, // end of validator
                  onSaved: (value) {
                    _name = value!;
                  },
                ),
                const SizedBox(height: 16.0),
                TextFormField(
                  decoration: InputDecoration(
                      labelText: '이메일',
                      hintText: 'abc@naver.ocm',
                      border: OutlineInputBorder()),
                  validator: (value) {
                    // 입력 값 검증
                    if (value == null || value.isEmpty) {
                      return '이메일을 입력해주세요.';
                    } else {
                      // 정상적으로 입력 했다면
                      return null;
                    }
                  }, // end of validator
                  onSaved: (value) {
                    _email = value!;
                  },
                ),
                const SizedBox(height: 16.0),
                TextFormField(
                  maxLines: 4, // 여러줄 입력 가능
                  decoration: InputDecoration(
                      labelText: '코멘트',
                      hintText: '경혐을 공유해주세요',
                      border: OutlineInputBorder()),
                  validator: (value) {
                    // 입력 값 검증
                    if (value == null || value.isEmpty) {
                      return '코멘트를 입력해주세요.';
                    } else {
                      // 정상적으로 입력 했다면
                      return null;
                    }
                  }, // end of validator
                  onSaved: (value) {
                    _comments = value!;
                  },
                ),
                const SizedBox(height: 24),
                Text(
                  '우리 앱을 어떻게 평가하시겠습니까?',
                  style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      color: Theme.of(context).colorScheme.primary),
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: List.generate(5, (index) {
                    return IconButton(
                      onPressed: () {
                        setState(() {
                          // 버그는 추후 수정
                          _rating = index + 1; // 선택한 점수로 설정
                        });
                      },
                      color: Colors.deepPurple,
                      icon: Icon(
                          _rating > index ? Icons.star : Icons.star_border),
                    );
                  }),
                ),
                const SizedBox(height: 24),
                Text(
                  '고객 서비스 만족도는 어느 정도입니까?',
                  style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                      color: Theme.of(context).colorScheme.primary),
                ),
                // 슬라이더
                Slider(
                  value: _satisfaction,
                  // 추후 수정
                  min: 0,
                  max: 10,
                  onChanged: (value) {
                    setState(() {
                      print('value  $value');
                      _satisfaction = value!;
                    });
                  },
                  divisions: 10,
                  // 슬라이더 구간
                  label: _satisfaction.toString(),
                ),
                const SizedBox(height: 16),
                SwitchListTile(
                  title: const Text('뉴스레터를 구독하시겠습니까?'),
                  value: _subscribe,
                  onChanged: (value) {
                    setState(() {
                      _subscribe = value;
                    });
                  },
                ),
                const SizedBox(height: 16),
                ElevatedButton(
                  onPressed: () {
                    if (_formKey.currentState!.validate()) {
                      // 통과
                      _formKey.currentState!.save(); // 실행
                      // 변수에 값 할당
                      setState(() {
                        _successMessage = '제출이 완료 되었습니다';
                      });
                    } else {
                      // 실패
                      setState(() {
                        _successMessage = '';
                      });
                    }
                  },
                  child: const Text('제출'),
                ),
                // 다트 문법 활용
                if (_successMessage.isNotEmpty)
                  Padding(
                    padding: EdgeInsets.symmetric(vertical: 16),
                    child: Text(
                      _successMessage,
                      style: TextStyle(
                          color: Theme.of(context).colorScheme.primary,
                          fontSize: 16
                      ),
                    ),
                  )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

 

! (널 무효화 연산자)
: 변수의 값이 널이 아님을 확신할 때 사용하는 연산자입니다
Material 3(또는 Material You)는 Google이 제안한 디자인 시스템의 세 번째 버전으로, 이전의 Material Design을 더욱 확장하여 사용자 맞춤형 경험을 강조하는 디자인 철학입니다.
- 앱의 색상 테마
- 앱바, 버튼, 슬라이더 등 기본 위젯의 스타일 변화
- 동적 색상 적용 및 팔레트 확장
- 애니메이션 및 모션 개선
- 위젯의 모서리 스타일과 접근성 

 

실행 화면______