
열거형, 정해진 수의 상수값을 갖는 일종의 클래스
enum Category { food, travel, leisure, work }
const categoryIcons = {
Category.food: Icons.lunch_dining,
Category.travel: Icons.flight,
Category.leisure: Icons.movie,
Category.work: Icons.work,
};
-> 여러가지 옵션을 미리 지정하여 단순 오타로 인한 코드 오류 방지가 가능
인스턴스 화 될때 마다 각각의 요소가 고유의 id를 얻고자 할때
Expense({
required this.title,
required this.amount,
required this.date,
}): id = uuid.v4();
개체를 생성하고 얻을 때마다 각 개체에 고유의 id가 생성됨
ListView.builder(
itemCount: expenses.length,
itemBuilder: (ctx, index) => Dismissible(
key: ValueKey(expenses[index]),
background: Container(
color: Theme.of(context).colorScheme.error.withOpacity(0.75),
margin: EdgeInsets.symmetric(
horizontal: Theme.of(context).cardTheme.margin!.horizontal,
),
),
onDismissed: (direction) {
onRemoveExpense(expenses[index]);
},
child: ExpenseItem(
expenses[index],
),
),
);
🚨 길이를 모르는 리스트를 Column 위젯에 넣으면 길이가 무한정 길어지는 문제가 발생
ListView 위젯을 통해 Scrollable 하게 만들 필요성이 있음
builder 속성을 사용하여 스크롤 가능한 리스트를 생성
보이기 직전에만 생성이 가능하고, 보이지 않을때는 생성 X -> 앱의 퍼포먼스 향상
어떤 요소를 포함할지, 언제 빌드할지를 알려주는 역할
서로다른 위젯이 어떻게 출현할 지도 결정이 가능
itemCount : 몇개의 아이템이 렌더링 될것인가
둘다 Scrollable 하게 만드는 목적은 동일
그러나 반복되는 위젯을 사용할 경우에는 캐싱문제로 ListView를 권장
(가계부앱같은 경우 Expense라는 위젯이 계속 반복되기에 ListView를 사용한듯)
왜 builder를 따로 제공할까?
바로 itemcount의 존재때문, 렌더링개수의 범위를 알려줌으로써 더 빠른 렌더링이 가능하다고 함.
출처: https://velog.io/@tygerhwang/Flutter-Scroll-View-%EB%A7%8C%EB%93%A4%EA%B8%B03-ListView
만약 이 리스트를 중첩해서 위젯에 할당할 경우
Colunm 안의 Colunm같은 경우에는 Expanded 를 통해 크기 관련 문제를 해결하는 것이 필수!
body: Column(
children: [
// Toolbar
Chart(expenses: _registeredExpenses),
Expanded(
child: mainContent,
),
],
),
-> 위 코드처럼 Colunm안에 mainContent라는 내용을 집어넣을 경우 Expanded를 통해서 크기 관련 문제를 해결해야함.
스타일링 목적의 위젯, 카드형 이미지

상단에 대부분 배터리 잔량, 시간등 장치를 위한 표기가 있음,
이를 고려하기 위한 위젯

overlay의 목적,
context와 builder를 전달해야함
모달창을 닫을때는 Naviagator 클래스를 사용
https://api.flutter.dev/flutter/material/showModalBottomSheet.html
사용자의 입력을 받기 위한 위젯
TextField(
controller: _amountController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
prefixText: '\$',
label: Text('Amount'),
),
),
Textfield위젯을 통해 사용자가 입력을 했으면, 그 입력값을 가져오는 것이 필요
이때 사용하는 것이 TextEditingController
다만, 위젯이 필요없다면(오버레이가 닫힐경우) 플러터에게 컨트롤러를 지우라고 명령하는 것이 필수 -> 계속 메모리를 소모하기 때문
dispose 메서드를 통해 플러터에게 위젯이나 State를 지우라고 명령하는 것이 필요함
void dispose() {
_titleController.dispose();
_amountController.dispose();
super.dispose();
}
like JS의 Promise
지금은 없지만 미래에 요청할 데이터가 담길 상자의 느낌
https://velog.io/@jintak0401/FlutterDart-%EC%97%90%EC%84%9C%EC%9D%98-Future-asyncawait
void main() {
// future 라는 변수에서 미래에(3초 후에) int가 나올 것입니다
Future<int> future = futureNumber();
future.then((val) {
// int가 나오면 해당 값을 출력
print('val: $val');
}).catchError((error) {
// error가 해당 에러를 출력
print('error: $error');
});
print('기다리는 중');
}
라는 코드가 존재할때, 동기적 실행이라면 val이 먼저 나오고 기다리는 중이라는 문자열이 나오겠지만, then 함수를 통해서 Future<int> 를 다루기 때문에 비동기적으로 실행되어
기다리는 중이 먼저 나오고 그다음 Future<int> 값을 얻을 수 있다.
이를 통해 Future<int> 의 값을 얻을때까지 기다리는 것이 아니라, 효율적으로 실행 할 수 있게 된다.
Future<ProcessedData> createData() {
return _loadFromDisk().then((id){
return _fetchNetworkData(id);
}).then((data){
return ProcessedData(data);
})
}
Future<ProcessedData> createDate() async {
final id = await _loadFromDisk();
final data = await _fetchNetworkData(id);
return ProcessedData(data);
}
두 코드를 비교했을때 어떤 값이 리턴이 되는지 async/await 을 통해 더 명확하게 파악이 가능하다.
팝업 메시지를 구현할때 필요한 두가지 위젯
if (_titleController.text.trim().isEmpty ||
amountIsInvalid ||
_selectedDate == null) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Invalid input'),
content: const Text(
'Please make sure a valid title, amount, date and category was entered :)'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(ctx);
},
child: const Text('Okay'),
)
],
),
);
return;
}
위 코드는 사용자의 입력이 빈값이 아닌지, 날짜를 선택했는지, 가격이 제대로 입력되었는지를 확인하는 기능의 코드이다.
만약 어느 하나라도 조건을 만족하지 않으면 팝업 메시지를 통해서 유저에게 잘못된 입력을 안내한다.
context를 입력으로 받아서 AlertDialog로 변환하는 것을 볼 수 있다.
위젯을 스와이핑을 통해 없앨 수 있도록 하는 기능을 담당
onDismissed : 화면상에서만 삭제될 경우를 대비하여 실제 데이터에서도 지우도록 하는 옵션화면 아래 간단한 메시지를 보여줄 때 사용하는 위젯
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 5),
content: const Text('Expense Deleted.'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
setState(() {
_registeredExpenses.insert(expenseIndex, expense);
});
},
),
),
);
ScaffoldMessenger를 통해 Snackbar 위젯 사용을 관리한다.
위 코드는 undo 버튼을 snackbar를 통해서 짧은시간 노출하고, 사용자가 실수로 지웠을경우나 되돌릴경우,
이 버튼을 눌러 지워졌던 Expense를 그 자리에 똑같이(-> insert O, add X) 다시 출력하는 기능을 담당했다.
색채 배합을 정해두고 이를 활용할 수 있도록 하는 기능
var kColorScheme = ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 123, 190, 248),
);
var kDarkColorScheme = ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 7, 13, 126),
);
darkTheme: ThemeData.dark().copyWith(
useMaterial3: true,
colorScheme: kDarkColorScheme,
cardTheme: const CardTheme().copyWith(
color: kDarkColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
),
이렇게 copyWith를 사용하여 색채배합에서 원하는 색을 사용하는 것도 가능하다.
다크모드 지원역시 이 ColorScheme을 이용하면 쉽게 구현이 가능하다.