import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';
import 'package:tiktok_clone/features/authentication/email_screen.dart';
import 'package:tiktok_clone/features/authentication/widgets/form_button.dart';
class UsernameScreen extends StatefulWidget {
const UsernameScreen({super.key});
State<UsernameScreen> createState() => _UsernameScreenState();
}
class _UsernameScreenState extends State<UsernameScreen> {
final TextEditingController _usernameController = TextEditingController();
String _username = "";
void initState() {
super.initState();
_usernameController.addListener(() {
setState(() {
_username = _usernameController.text;
});
});
}
void dispose() {
_usernameController.dispose();
super.dispose();
}
void _onNextTap() {
if (_username.isEmpty) return;
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const EmailScreen(),
),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Sign up",
),
),
body: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Sizes.size36,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Gaps.v40,
const Text(
"Create username",
style: TextStyle(
fontSize: Sizes.size24,
fontWeight: FontWeight.w700,
),
),
Gaps.v8,
const Text(
"You can always change this later.",
style: TextStyle(
fontSize: Sizes.size16,
color: Colors.black54,
),
),
Gaps.v16,
TextField(
controller: _usernameController,
decoration: InputDecoration(
hintText: "Username",
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
),
),
),
cursorColor: Theme.of(context).primaryColor,
),
Gaps.v28,
GestureDetector(
onTap: _onNextTap,
child: FormButton(disabled: _username.isEmpty),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import '../../../constants/sizes.dart';
class FormButton extends StatelessWidget {
const FormButton({
super.key,
required this.disabled,
});
final bool disabled;
Widget build(BuildContext context) {
return FractionallySizedBox(
widthFactor: 1,
child: AnimatedContainer(
padding: const EdgeInsets.symmetric(
vertical: Sizes.size16,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Sizes.size5),
color:
disabled ? Colors.grey.shade300 : Theme.of(context).primaryColor,
),
duration: const Duration(milliseconds: 500),
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 500),
style: TextStyle(
color: disabled ? Colors.grey.shade400 : Colors.white,
fontWeight: FontWeight.w600,
),
child: const Text(
'next',
textAlign: TextAlign.center,
),
),
),
);
}
}
FormButton
이 StatelessWidget
으로 정의된 것은 Flutter의 위젯 트리와 상태 관리 방식에 기반합니다. 여기서 중요한 점은 상태의 변경이 어디서 발생하고, 어떻게 처리되는지를 이해하는 것입니다.
StatelessWidget
은 생성 시 전달받은 매개변수를 기반으로 UI를 렌더링하고, 이 매개변수들이 변경되지 않는 한 다시 렌더링되지 않습니다.FormButton
은 비활성화 여부 (disabled
)를 부모 위젯으로부터 매개변수로 전달받습니다. 이 매개변수는 _username.isEmpty
에 의해 결정되며, _username
은 UsernameScreen
의 상태입니다.UsernameScreen
에서 TextEditingController
(_usernameController
)의 변화에 따라 _username
이 업데이트되고, 이로 인해 UsernameScreen
이 리렌더링됩니다.FormButton
에 새로운 disabled
값이 전달되며, 이는 FormButton
의 외관을 결정합니다.FormButton
자체는 내부 상태를 가지지 않습니다. 그것의 모양과 동작은 전적으로 부모 위젯 (UsernameScreen
)으로부터 전달받은 disabled
매개변수에 의존합니다.UsernameScreen
에서 관리되고, FormButton
은 단순히 그 상태에 따라 다르게 보여질 뿐입니다. 따라서 FormButton
내부에 별도의 상태 관리 로직이 필요하지 않으며, StatelessWidget
으로 충분합니다.결론적으로, FormButton
은 StatelessWidget
으로 적절히 구현된 것입니다. 상태 변경에 따른 UI 업데이트는 상태를 소유한 UsernameScreen
이 담당하며, FormButton
은 단순히 전달받은 매개변수에 따라 렌더링됩니다. 이는 Flutter의 효율적인 상태 관리 및 위젯 트리 업데이트 메커니즘을 잘 활용하는 사례입니다.