25.12.08
회원 가입 페이지(Register Screen), 설정 페이지(Settings_Screen) 약관 로직 개선 및 위치기반 서비스 동의 추가
This commit is contained in:
@@ -1,6 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
const String _serviceTermContent = """
|
||||
제1조 (목적)
|
||||
본 약관은 메타큐랩 서비스 이용과 관련하여 회사와 회원의 권리, 의무 및 책임 사항, 기타 필요한 사항을 규정함을 목적으로 합니다.
|
||||
|
||||
제2조 (약관의 효력 및 변경)
|
||||
1. 본 약관은 서비스를 이용하고자 하는 모든 회원에 대하여 그 효력을 발생합니다.
|
||||
2. 회사는 관련 법령을 위배하지 않는 범위에서 본 약관을 개정할 수 있습니다.
|
||||
3. 개정된 약관은 적용일자 및 개정 사유를 명시하여 현행 약관과 함께 서비스 화면에 게시합니다.
|
||||
""";
|
||||
|
||||
const String _privacyTermContent = """
|
||||
1. 수집하는 개인정보의 항목
|
||||
회사는 회원가입, 상담, 서비스 신청 등을 위해 아래와 같은 개인정보를 수집하고 있습니다.
|
||||
- 필수 항목: 아이디, 비밀번호, 이름, 전화번호, 이메일 주소
|
||||
- 선택 항목: 닉네임, 마케팅 정보 수신 동의 여부
|
||||
|
||||
2. 개인정보의 수집 및 이용 목적
|
||||
회사는 다음의 목적을 위해 개인정보를 수집 및 이용합니다.
|
||||
- 서비스 제공에 관한 계약 이행 및 요금 정산
|
||||
- 회원 관리 및 본인 확인
|
||||
""";
|
||||
|
||||
const String _locationTermContent = """
|
||||
1. 위치정보의 수집 및 이용 목적
|
||||
회사는 이용자의 현재 위치를 확인하여 긴급 구조 요청, 주행 경로 기록, 주변 시설 검색 등 위치 기반 서비스를 제공하기 위해 위치정보를 수집 및 이용합니다.
|
||||
|
||||
2. 위치정보의 보유 및 이용 기간
|
||||
회사는 위치정보의 수집 및 이용 목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다. 단, 관련 법령의 규정에 의하여 보존할 필요가 있는 경우 법령에서 정한 기간 동안 보관합니다.
|
||||
""";
|
||||
|
||||
const String _marketingTermContent = """
|
||||
1. 수집 및 이용 목적
|
||||
이벤트 정보 및 참여 기회 제공, 광고성 정보 제공 등 마케팅 활동을 위해 사용됩니다.
|
||||
|
||||
2. 수신 동의 철회
|
||||
회원은 언제든지 이메일 또는 고객센터를 통해 마케팅 정보 수신 동의를 철회할 수 있습니다. 수신 동의를 철회하더라도 기본 서비스 이용에는 제한이 없습니다.
|
||||
""";
|
||||
|
||||
class RegisterScreen extends StatefulWidget {
|
||||
const RegisterScreen({super.key});
|
||||
|
||||
@@ -18,6 +56,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
|
||||
bool _isServiceAgreed = false;
|
||||
bool _isPrivacyAgreed = false;
|
||||
bool _isLocationAgreed = false;
|
||||
bool _isMarketingAgreed = false;
|
||||
|
||||
final Color mainBlueColor = const Color(0xFF0033CC);
|
||||
@@ -43,7 +82,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
if (_phoneController.text.isEmpty) return _showError('전화번호를 입력해주세요.');
|
||||
if (_phoneController.text.length < 12) return _showError('올바른 전화번호를 입력해주세요.');
|
||||
if (_nicknameController.text.isEmpty) return _showError('닉네임을 입력해주세요.');
|
||||
if (!_isServiceAgreed || !_isPrivacyAgreed) {
|
||||
|
||||
if (!_isServiceAgreed || !_isPrivacyAgreed || !_isLocationAgreed) {
|
||||
return _showError('(필수) 약관에 모두 동의해주세요.');
|
||||
}
|
||||
|
||||
@@ -72,15 +112,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
}
|
||||
|
||||
void _showTermDetails(String title, String content) {
|
||||
String fullContent = content.length < 50
|
||||
? '$content\n\n'
|
||||
'제1조 (목적)\n본 약관은 메타큐랩(이하 "회사")이 제공하는 모든 서비스의 이용조건 및 절차, 이용자와 회사의 권리, 의무, 책임사항과 기타 필요한 사항을 규정함을 목적으로 합니다.\n\n'
|
||||
'제2조 (용어의 정의)\n1. "서비스"라 함은 회원이 이용할 수 있는 관련 제반 서비스를 의미합니다.\n'
|
||||
'2. "회원"이라 함은 회사의 "서비스"에 접속하여 본 약관에 따라 회사와 이용계약을 체결하고 회사가 제공하는 "서비스"를 이용하는 고객을 말합니다.\n\n'
|
||||
'제3조 (약관의 게시와 개정)\n1. 회사는 이 약관의 내용을 회원이 쉽게 알 수 있도록 서비스 초기 화면에 게시합니다.\n'
|
||||
'2. 회사는 "약관의 규제에 관한 법률" 등 관련 법령을 위배하지 않는 범위에서 이 약관을 개정할 수 있습니다.\n\n'
|
||||
'(이하 생략)'
|
||||
: content;
|
||||
String fullContent = content;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -281,21 +313,28 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
title: '(필수) 서비스 이용약관 동의',
|
||||
value: _isServiceAgreed,
|
||||
onChanged: (v) => setState(() => _isServiceAgreed = v!),
|
||||
details: '서비스 이용약관 내용입니다...',
|
||||
details: _serviceTermContent,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildAgreementRow(
|
||||
title: '(필수) 개인정보 수집 및 이용 동의',
|
||||
value: _isPrivacyAgreed,
|
||||
onChanged: (v) => setState(() => _isPrivacyAgreed = v!),
|
||||
details: '개인정보 처리방침 내용입니다...',
|
||||
details: _privacyTermContent,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildAgreementRow(
|
||||
title: '(필수) 위치기반 서비스 이용약관 동의',
|
||||
value: _isLocationAgreed,
|
||||
onChanged: (v) => setState(() => _isLocationAgreed = v!),
|
||||
details: _locationTermContent,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildAgreementRow(
|
||||
title: '(선택) 이벤트 및 마케팅 수신 동의',
|
||||
value: _isMarketingAgreed,
|
||||
onChanged: (v) => setState(() => _isMarketingAgreed = v!),
|
||||
details: '마케팅 정보 수신 동의 내용입니다...',
|
||||
details: _marketingTermContent,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -15,6 +15,44 @@ const BoxShadow _cleanShadow = BoxShadow(
|
||||
spreadRadius: 0,
|
||||
);
|
||||
|
||||
const String _serviceTermContent = """
|
||||
제1조 (목적)
|
||||
본 약관은 메타큐랩 서비스 이용과 관련하여 회사와 회원의 권리, 의무 및 책임 사항, 기타 필요한 사항을 규정함을 목적으로 합니다.
|
||||
|
||||
제2조 (약관의 효력 및 변경)
|
||||
1. 본 약관은 서비스를 이용하고자 하는 모든 회원에 대하여 그 효력을 발생합니다.
|
||||
2. 회사는 관련 법령을 위배하지 않는 범위에서 본 약관을 개정할 수 있습니다.
|
||||
3. 개정된 약관은 적용일자 및 개정 사유를 명시하여 현행 약관과 함께 서비스 화면에 게시합니다.
|
||||
""";
|
||||
|
||||
const String _privacyTermContent = """
|
||||
1. 수집하는 개인정보의 항목
|
||||
회사는 회원가입, 상담, 서비스 신청 등을 위해 아래와 같은 개인정보를 수집하고 있습니다.
|
||||
- 필수 항목: 아이디, 비밀번호, 이름, 전화번호, 이메일 주소
|
||||
- 선택 항목: 닉네임, 마케팅 정보 수신 동의 여부
|
||||
|
||||
2. 개인정보의 수집 및 이용 목적
|
||||
회사는 다음의 목적을 위해 개인정보를 수집 및 이용합니다.
|
||||
- 서비스 제공에 관한 계약 이행 및 요금 정산
|
||||
- 회원 관리 및 본인 확인
|
||||
""";
|
||||
|
||||
const String _locationTermContent = """
|
||||
1. 위치정보의 수집 및 이용 목적
|
||||
회사는 이용자의 현재 위치를 확인하여 긴급 구조 요청, 주행 경로 기록, 주변 시설 검색 등 위치 기반 서비스를 제공하기 위해 위치정보를 수집 및 이용합니다.
|
||||
|
||||
2. 위치정보의 보유 및 이용 기간
|
||||
회사는 위치정보의 수집 및 이용 목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다. 단, 관련 법령의 규정에 의하여 보존할 필요가 있는 경우 법령에서 정한 기간 동안 보관합니다.
|
||||
""";
|
||||
|
||||
const String _marketingTermContent = """
|
||||
1. 수집 및 이용 목적
|
||||
이벤트 정보 및 참여 기회 제공, 광고성 정보 제공 등 마케팅 활동을 위해 사용됩니다.
|
||||
|
||||
2. 수신 동의 철회
|
||||
회원은 언제든지 설정 메뉴 또는 고객센터를 통해 마케팅 정보 수신 동의를 철회할 수 있습니다. 수신 동의를 철회하더라도 기본 서비스 이용에는 제한이 없습니다.
|
||||
""";
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@@ -27,6 +65,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
bool _isRentalAlertEnabled = true;
|
||||
bool _isStorageStatusAlert = true;
|
||||
bool _isEnvSensorAlert = true;
|
||||
bool _isMarketingAlertEnabled = true;
|
||||
|
||||
bool _isBiometricEnabled = false;
|
||||
bool _isAutoLogoutEnabled = true;
|
||||
@@ -331,15 +370,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showTermsSheet(BuildContext context) {
|
||||
void _showTermContentSheet(BuildContext context, String title, String content) {
|
||||
_showCommonModal(
|
||||
context,
|
||||
'이용 약관',
|
||||
title,
|
||||
SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Text(
|
||||
'제 1 조 (목적)\n이 약관은 스마트 헬멧 서비스(이하 "서비스")의 이용 조건 및 절차, 이용자와 회사의 권리, 의무, 책임 사항을 규정함을 목적으로 합니다.\n\n제 2 조 (용어의 정의)\n1. "이용자"란 앱에 접속하여 본 약관에 따라 서비스를 이용하는 회원을 말합니다.\n2. "헬멧"이란 회사가 대여하는 스마트 IoT 안전모를 말합니다.\n\n(이하 생략... 더미 데이터입니다.)\n\n제 3 조 (약관의 효력)\n본 약관은 서비스를 신청한 때부터 효력이 발생합니다.',
|
||||
style: TextStyle(color: _subTextColor, height: 1.6),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
content,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(color: _subTextColor, height: 1.6),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -723,6 +766,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
(val) => setState(() => _isEnvSensorAlert = val),
|
||||
showDivider: true,
|
||||
),
|
||||
_buildToggleItem(
|
||||
'이벤트 및 마케팅 알림',
|
||||
'혜택 및 소식 받기',
|
||||
_isMarketingAlertEnabled,
|
||||
(val) => setState(() => _isMarketingAlertEnabled = val),
|
||||
showDivider: true,
|
||||
onTapLabel: () => _showTermContentSheet(context, '마케팅 수신 동의', _marketingTermContent),
|
||||
),
|
||||
_buildDivider(16),
|
||||
_buildInfoLink('알림 방식', Icons.notifications_active_outlined, value: '배너 + 진동', onTap: () => _showNotificationStyleSheet(context)),
|
||||
],
|
||||
@@ -769,7 +820,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
children: [
|
||||
_buildInfoLink('버전 정보', null, value: _currentAppVersion, showArrow: false),
|
||||
_buildDivider(16),
|
||||
_buildInfoLink('이용 약관 및 개인정보 처리방침', Icons.article_outlined, onTap: () => _showTermsSheet(context)),
|
||||
_buildInfoLink('서비스 이용약관', Icons.description_outlined, onTap: () => _showTermContentSheet(context, '서비스 이용약관', _serviceTermContent)),
|
||||
_buildDivider(16),
|
||||
_buildInfoLink('개인정보 처리방침', Icons.privacy_tip_outlined, onTap: () => _showTermContentSheet(context, '개인정보 처리방침', _privacyTermContent)),
|
||||
_buildDivider(16),
|
||||
_buildInfoLink('위치기반 서비스 이용약관', Icons.location_on_outlined, onTap: () => _showTermContentSheet(context, '위치기반 서비스 이용약관', _locationTermContent)),
|
||||
_buildDivider(16),
|
||||
_buildInfoLink('오픈소스 라이선스', Icons.code, onTap: () => _showLicenseSheet(context)),
|
||||
_buildDivider(16),
|
||||
@@ -838,7 +893,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
|
||||
Widget _buildToggleItem(
|
||||
String title, String subtitle, bool value, ValueChanged<bool> onChanged,
|
||||
{required bool showDivider}) {
|
||||
{required bool showDivider, VoidCallback? onTapLabel}) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
@@ -847,13 +902,25 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: TextStyle(color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w500)),
|
||||
const SizedBox(height: 2),
|
||||
Text(subtitle, style: TextStyle(color: _subTextColor, fontSize: 11)),
|
||||
],
|
||||
child: GestureDetector(
|
||||
onTap: onTapLabel,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(title, style: TextStyle(color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.w500)),
|
||||
if (onTapLabel != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 4.0),
|
||||
child: Icon(Icons.info_outline, size: 14, color: _subTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(subtitle, style: TextStyle(color: _subTextColor, fontSize: 11)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Transform.scale(
|
||||
|
||||
Reference in New Issue
Block a user