📝정적페이지 생성 (StatelessWidget)
import 'package:flutter/material.dart';
// main method
void main() {
runApp(App());
}
// Root (whatever you want, you draw it here)
class App extends StatelessWidget {
// build method hepls your UI render
@override
Widget build(BuildContext context) {
return MaterialApp(
);
}
}
Flutter를 이용해 정적인 화면을 시뮬레이터 화면을 띄우려면 StatelessWidget을 상속받고 build라는 함수를 구현해야한다.
Flutter의 실행과정을 간단히 이야기하자면 main.dart 페이지에서 실행이 되며 runApp을 통해 앱이 실행된다.
📝 Custom Widget 생성
const CurrencyCard(
name: 'Euro',
code: 'EUR',
amount: '6 428',
icon: Icons.euro_rounded,
isInverted: false,
),
/** -------- Currecy Card --------- **/
class CurrencyCard extends StatelessWidget {
final String name, code, amount;
final IconData icon;
final bool isInverted;
final _blackColor = const Color(0xFF1F2123);
const CurrencyCard({
super.key,
required this.name,
required this.code,
required this.amount,
required this.icon,
required this.isInverted,
});
@override
Widget build(BuildContext context) {
return Container( ....);
}
}
생성자를 만들어서 나만의 위젯을 만들어 재활용 할 수 있다.
📝정적페이지 생성 (StatefulWidget)
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatefulWidget {
const App({super.key});
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
int counter = 0;
void onClicked() {
setState(() {
counter = counter + 1;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: const Color(0xFFF4EDDB),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Click Count',
style: TextStyle(fontSize: 30),
),
Text(
'$counter',
style: const TextStyle(fontSize: 30),
),
IconButton(
iconSize: 40,
onPressed: onClicked,
icon: const Icon(
Icons.add_box_rounded,
),
),
],
),
),
),
);
}
}
데이터가 동적으로 변해야 할 때 쓴다. 예를 들자면 "클릭시 카운트가 올라간다."가 이에 해당한다. 동적페이지를 만들기 위해서는 StatefulWidget을 상속받아 사용해야한다.
StatefulWidget을 사용할 때 주의점들은 아래와 같다.
- 전체가 리렌더링되면 부하가 크기 때문에 부분적으로 따로 위젯을 뽑아 사용한다. (Custom Widget으로 분리)
- 상태가 변한 걸로 화면이 바뀌었다는 걸 인식하기 때문에 같은 위젯들의 상태가 변화하는 경우 key를 할당해줘야한다. → Custom으로 여러개 만든 경우 그들끼리 구분하기 위해서 필요하다. React이서 Key를 정해주는 것과 같은 개념
- 데이터 변화가 있는 후 setState를 작동시 state의 변화를 감지해 statefulWidget을 다시 랜더링하게 된다.
동작 방식
- const App({super.key}); 에서 상위에 위젯 키를 넘긴다
- State<App> createState() => _AppState(); → _AppState에서 Build할 내용을 받아와서 상태를 만든다
📝Widget Lifecycle (위젯 라이프 사이클)
- initState()
- build를 하기 전에 항상 먼저 실행된다. (대표적으로 API를 불러올 때 사용된다)
- dispose()
- 화면에서 사라질 때 실행한다.
- build()
- 화면을 그려주는 역할을 한다. setState와 같이 리랜더링시 이 부분만 작동하게 된다.
📝Key란?
대부분의 경우는 key를 사용하지 않는다. 하지만 상태를 유지하고 있는 같은 종류의 위젯을 컬렉션에 더하거나, 제거하거나, 정렬할 때 key가 필요하다. 예를 들면 ToDoList를 보면 List안에 내용들은 똑같은 내용 즉, 똑같은 위젯을 재활용한 것이기 때문에 이것들을 관리하기 위해서는 key가 필요합니다. 이건 동적인 데이터이기 때문에 key가 쓰이는 경우는 StatefulWidget에 해당합니다.
자세한 내용은 아래 블로그를 참고하시길 바랍니다.
📝LocalKey vs GlobalKey
Key는 LocalKey와 GlobalKey 두개의 종류가 있습니다.
LocalKey
Widget 안에서 사용되는 유니크한 키입니다. 다양한 LocalKey 종류가 있는데 특징은 아래와 같습니다.
- ValueKey
- 하나의 정보에서 생성되는 키, 숫자, 문자열 등
- ObjectKey
- 객체에서 생성하는 키, 같은 타입이라도 객체의 내용이 다르면 다른 키가 된다.
- UniqueKey
- 특정 Widget 내에서 고유 한 키
- PageStorageKey
- 페이지 스크롤 위치가 있는 키
GlobalKey
전체 위젯 트리에서 고유한 전역적인 식별자로 어디에서나 전역적으로 해당 위젯을 식별하고 액세스할 수 있다.
StatefulWidget을 사용하는 위젯에서 상태를 관리하거나 위젯 트리 외부에서 상태에 액세스해야 할 때 사용됩니다.
GlobalKey 사용되는 곳
- 해당 위젯의 상태에 직접 접근가능 (위젯 크기, 값 등…)
final key = GlobalKey<MyWidgetState>();
MyWidget(key: key);
key.currentState.someMethod();
- Form 위젯에서 여러 FormField 관리할 때 수행 (해당 내용은 Docs에서 가이드한 내용이기 때문에 너무 깊게 생각할 필요는 없음)
final formKey = GlobalKey<FormState>();
if (formKey.currentState.validate()) {
formKey.currentState.save();
// ... 폼 저장 처리
}
📝Build Context
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatefulWidget {
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
textTheme: const TextTheme(
titleLarge: TextStyle(
color: Colors.red,
),
),
),
home: const Scaffold(
backgroundColor: Color(0xFFF4EDDB),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyLargeTitle(),
],
),
),
),
);
}
}
class MyLargeTitle extends StatelessWidget {
const MyLargeTitle({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(
'My Large Title',
style: TextStyle(
fontSize: 30,
color: Theme.of(context).textTheme.titleLarge?.color,
),
);
}
}
하위 위젯인 MyLargeTitle에서 상위에 있는 위젯을 사용하려면 상위 위젯에 대한 문맥(context)를 알아야하는데 BuildContext가 그러한 역할을 해준다. 그래서 Theme.of(context)를 이용해 context에 있는 Theme을 가져오고 그 안에 textTheme 그 안에 titleLarge를 가져오면 된다.