frindlymap/lib/modules/ui/src/screens/onboarding_screen.dart

113 lines
4.3 KiB
Dart

import 'package:flutter/material.dart';
import '../theme.dart';
/// 처음 시작: 이름과 번호만 받는다. 키는 이 기기 안에서 조용히 만들고, 복구 코드는 없다.
///
/// [onStart] 가 문제를 돌려주면(번호·이름이 잘못됨) 아래에 보여 준다.
class OnboardingScreen extends StatefulWidget {
const OnboardingScreen({super.key, required this.onStart});
final Future<String?> Function(String phone, String name) onStart;
@override
State<OnboardingScreen> createState() => _OnboardingScreenState();
}
class _OnboardingScreenState extends State<OnboardingScreen> {
final _phone = TextEditingController();
final _name = TextEditingController();
String? _error;
bool _busy = false;
@override
void dispose() {
_phone.dispose();
_name.dispose();
super.dispose();
}
bool get _ready => _phone.text.trim().length >= 9 && _name.text.trim().isNotEmpty;
Future<void> _start() async {
FocusScope.of(context).unfocus();
setState(() {
_busy = true;
_error = null;
});
final err = await widget.onStart(_phone.text, _name.text);
if (mounted && err != null) {
setState(() {
_busy = false;
_error = err;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: LayoutBuilder(
builder: (context, box) => SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: box.maxHeight - 24),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
const SizedBox(height: 72),
Align(
alignment: Alignment.centerLeft,
child: Container(
width: 64,
height: 64,
decoration: BoxDecoration(color: Pal.accent, borderRadius: BorderRadius.circular(18)),
child: const Icon(Icons.hub, color: Colors.black, size: 36),
),
),
const SizedBox(height: 24),
const Text('Friendly Map', style: TextStyle(fontSize: 28, fontWeight: FontWeight.w800)),
const SizedBox(height: 8),
const Text('대화하다 알게 된 관계를\n가족 · 학교 · 회사로 이어 두세요.', style: TextStyle(fontSize: 16, height: 1.5, color: Pal.muted)),
const SizedBox(height: 40),
TextField(
controller: _name,
textInputAction: TextInputAction.next,
onChanged: (_) => setState(() {}),
decoration: const InputDecoration(labelText: '이름'),
),
const SizedBox(height: 12),
TextField(
controller: _phone,
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.done,
onChanged: (_) => setState(() {}),
onSubmitted: (_) => _ready && !_busy ? _start() : null,
decoration: const InputDecoration(labelText: '내 휴대폰 번호', hintText: '010-1234-5678'),
),
if (_error != null)
Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(_error!, style: const TextStyle(color: Pal.danger, fontSize: 13)),
),
const SizedBox(height: 32),
const Text('모든 정보는 이 폰 안에 암호화되어 저장되고, 내가 보낸 것만 상대에게 가요.',
style: TextStyle(fontSize: 12, height: 1.5, color: Pal.muted)),
const SizedBox(height: 12),
SizedBox(
height: 52,
child: FilledButton(
onPressed: _ready && !_busy ? _start : null,
child: _busy
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
: const Text('시작하기', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700)),
),
),
]),
),
),
),
),
);
}
}