1단계 작업 (stl, stf 기본 위젯 사용)
1단계 - main1. dart - Stateless
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: MySatelessWidget(),
),
);
}
// Stateless
// 상태의 변화 없이 항상 동일한 UI를 그립니다.
// 이런 위젯은 데이터나 변수의 값이 변하지 않을 때 사용합니다.
// 포인트 !
// 사실 상태는 변수에 할당된 값 입니다.
class MySatelessWidget extends StatelessWidget {
const MySatelessWidget({super.key});
final msg = '안녕하세요 저는 상태가 없는(고정된) 위젯입니다.';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(msg),
),
);
}
}
1단계 - main2. dart - Stateful
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: MyStatefulWidget(),
));
}
//////////////////////
// 포인트 !
// 사실 상태는 변수에 할달된 값이다. 상태가 있다라는 말은
// 값을이 변화할 수 있는 가능성을 이야기 한다.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
// 변수 ---고정 --> 상수
final msg = '저는 stateful 위젯입니다.';
// 변수에 할당된 값이 상수가 아니기 때문에 언제든지 변경이 가능하다.
// 즉, 여기를 상태변수라고 부른다.
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//child: Text('${widget.msg} onTap Count : ${widget.count}'),
child: InkWell(
onTap: () {
setState(() {
count += 1;
});
},
child: Text('${msg} onTap Count : ${count}'),
),
),
);
}
}
/////////////////////////////////
2단계 작업
- StatefulWidget
StatefulWidget은 상태를 변경할 수 있고, 상태가 변경된 경우 setState()를 호출하여 자식 위젯들을 갱신할 수 있습니다.
도서 관리 앱 - 위젯 트리 구조 (StatefulWidget을 이용한 상태 관리)
homePage (StatefulWidget)
| ├── 공유 상태: 대출 목록에 추가된 도서 목록 (공유 상태)
|
├── Library (Store 역할)
| ├── Book 1 (도서 목록)
| ├── Book 2
| ├── Book 3
| └── Book 4
|
└── BorrowList (Cart 역할)
├── Book 2 (대출 목록에 추가된 책)
└── Book 4

main2.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'home_screen.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
// 머터리얼 3 적용 방법
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
home: HomeScreen(),
),
);
}
HomeScreen
import 'package:flutter/material.dart';
import 'package:flutter_statement_v01/_02/book_cart_page.dart';
import 'package:flutter_statement_v01/_02/book_list_page.dart';
import '../common.utils/logger.dart';
// 상태가 있는 위젯
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// 로컬 상태 : 하단에 활동화 된 탭 인덱스 번호
int pageIndex = 0;
// 공유 상태 카드에 담긴 북 정보
// (책 리스트 화면, 장바구니 화면에서 공유하는 데이터)
// 상품 --> 책 (String) 데이터 타입으로 관리하자.
List<String> mySelectedBook = [];
// 상태를 변경하는 메서드 만들기
void _toggleSaveStates(String book) {
// 다시 화면을 그려라 요청 함수
setState(() {
if (mySelectedBook.contains(book)) {
mySelectedBook.remove(book);
} else {
mySelectedBook.add(book);
}
});
}
@override
Widget build(BuildContext context) {
logger.d('HomeScreen build 메서드 호출 됨');
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('텐코에 서재'),
backgroundColor: Theme.of(context).colorScheme.tertiaryContainer,
),
body: IndexedStack(
// 반드시 추가해야 되는 속성
index: pageIndex,
children: [
BookListPage(
// _toggleSaveStates, _toggleSaveStates() 차이점은?
onToggleSaved: _toggleSaveStates,
selectedBook: mySelectedBook,
),
BookCartPage(mySelectedBooks: mySelectedBook),
],
),
bottomNavigationBar: BottomNavigationBar(
// 필수 속성
currentIndex: pageIndex,
onTap: (index) {
// 행위 .. 생략..
setState(() {
pageIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.list),
label: 'book-list',
),
BottomNavigationBarItem(
icon: Icon(Icons.shopping_cart),
label: 'cart',
),
],
),
),
);
}
}
book_list_page.dart
import 'package:flutter/material.dart';
import '../common.utils/logger.dart';
class BookListPage extends StatelessWidget {
final Function(String) onToggleSaved;
// 부모 위젯으로 부터 넘겨 받은 장바구니 데이터(cart 목록)
final List<String> selectedBook;
BookListPage(
{required this.onToggleSaved, required this.selectedBook, super.key});
// 임시 데이터 - 교보문고에 볼 수 책 목록 리스트
final List<String> books = [
'호모사피엔스',
'다트입문',
'홍길동전',
'코드리팩토링',
'전치사도감',
];
@override
Widget build(BuildContext context) {
logger.d('데이터 확인 : ${selectedBook.toString()}');
return ListView(
children: books.map(
// book <-- books 에 0인덱스는 '호모사피엔스';
(book) {
// 함수에 바디에는 식을 작성할 수 있다.
// 장바구에 유무을 어떻게 확인하지???
// 부모가 관리하는 장바구니 데이터에 book 이란 데이터가
// 있는가 없는가 체크 하고자 한다면?
final isSelectedBook = selectedBook.contains(book);
return ListTile(
leading: Container(
width: 35,
height: 35,
decoration: BoxDecoration(
color: Theme.of(context).secondaryHeaderColor,
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: Colors.black),
),
),
title: Text(
book,
style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
),
trailing: IconButton(
onPressed: () {
// 부모에게 콜백을 호출하는데 데이터도 함께 전달 시킨다.
onToggleSaved(book);
},
icon: Icon(
// isSelectedBook --> map 안에 지역 변수
isSelectedBook ? Icons.remove_circle : Icons.add_circle,
color: isSelectedBook ? Colors.red : Colors.green,
),
),
);
},
).toList(),
);
}
}
book_cart_page.dart
import 'package:flutter/material.dart';
class BookCartPage extends StatelessWidget {
// 사용자가 카드에 저장한 데이터만 화면에 뿌려 주자.
final List<String> mySelectedBooks;
const BookCartPage({required this.mySelectedBooks, super.key});
@override
Widget build(BuildContext context) {
return ListView(
children: mySelectedBooks
.map((book) => ListTile(title: Text(book)))
.toList());
}
}
결과


'Flutter' 카테고리의 다른 글
| [FLUTTER] 상태관리 응용 앱 제작 2 - InheritedWidget (1) | 2025.01.21 |
|---|---|
| [FLUTTER] 콜백 함수(Callback Function) (0) | 2025.01.20 |
| [FLUTTER] Stack 위젯 (0) | 2025.01.20 |
| [FLUTTER] 기초 화면 구성 (0) | 2025.01.20 |
| [FLUTTER] 위젯(Widget) (0) | 2025.01.20 |