클릭을 통해서 추가,삭제하는 앱 만들어보기
main
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(),
),
);
}
home_screen(부모) 전체코드
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 'package:flutter_statement_v01/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('홈스크린 메서드 호출됨');
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('텐코의 서재'),
backgroundColor: Theme.of(context).colorScheme.tertiaryContainer,
),
body: IndexedStack(
// 반드시 추가해야 되는 속성
// 페이지라는 인덱스로 로컬상태를 사용하고 있음
index: pageIndex,
children: [
BookListPage(
//_toggleSaveStates, 와 _toggleSaveStates()의 차이점은?
// 1은 메서드 자체를 전달
// 2는 return 값을 전달하는 것
onToggleSaved: _toggleSaveStates,
selectedBook: mySelectedBook,
),
BookCartPage(
selectedBook: 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',
),
],
),
),
);
}
}
// 로컬 상태 : 하단에 활동화 된 탭 인덱스 번호
int pageIndex = 0;
// 상품 --> 책(String 타입으로 관리 예정 -> 이후 클래스로 묶기)
// 공유 상태 카트에 담긴 책 정보(책 리스트 화면, 장바구니 화면에서 공유하는 데이터)
// 배열로 변수 선언 + 초기화
List<String> mySelectedBook = [];
// 상태를 변경할 수 있는 메서드 만들기 (토글)
void _toggleSaveStates(String book) {
// 다시 화면을 그려라 요청 함수
setState(() {
if (mySelectedBook.contains(book)) {
mySelectedBook.remove(book);
} else {
mySelectedBook.add(book);
}
});
}
1. 인덱스를 나타낼 수 있는 변수를 하나 선언
2. List<String> mySelectedBook = []; 이라는 빈 배열을 하나 선언 및 초기화
--> 자식에서 토글을 상태를 변경했을 때 해당 배열에 값이 담길 수 있도록 하기 위함
3. _toggleSaveStates(String book) 이라는 메서드를 만들어서
자식에서 book을 매개변수로 전달했을 때 해당 메서드를 사용하여 book이 포함되어있으면 삭제하고 book이 포함되지 않았다면 추가하는 메서드
children: [
BookListPage(
//_toggleSaveStates, 와 _toggleSaveStates()의 차이점은?
// 1은 메서드 자체를 전달
// 2는 return 값을 전달하는 것
onToggleSaved: _toggleSaveStates,
selectedBook: mySelectedBook,
),
BookCartPage(
selectedBook: mySelectedBook,
),
],
children 안에 page 자식들을 넣음
book_list_page 전체코드
import 'package:flutter/material.dart';
import 'package:flutter_statement_v01/common.utils/logger.dart';
class BookListPage extends StatelessWidget {
//객체가 태어날때 부모와 연결 (String을 매개변수로)
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(),
);
}
}
class BookListPage extends StatelessWidget {
//객체가 태어날때 부모와 연결 (String을 매개변수로)
final Function(String) onToggleSaved;
// 부모 위젯으로부터 넘겨 받은 장바구니 데이터(cart 목록)
final List<String> selectedBook;
BookListPage(
{required this.onToggleSaved, required this.selectedBook, super.key});
// 임시 데이터 - 교보문고의 볼 수 있는 책 목록
final List<String> books = [
'호모사피엔스',
'다트입문',
'홍길동전',
'코드리펙토링',
'전치사도감',
];
1. 부모에서 정의했던 _toggleSaveStates를 사용하기 위해서 final Function(String) onToggleSaved;라는 익명함수를 선언
2. 부모에서 정의했던 List<String> mySelectedBook = [];을 사용하기 위한 변수 선언
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(),
);
1. 부모에서 index가 변경될 때 다시 build가 실행이 됨 -> return ListView가 보여짐
2. final isSelectedBook = selectedBook.contains(book);을 통해서 부모의 selectedBook의 배열에 book이 있는지 없는지 확인하는 bool 값 선언
3. Iconbutton에서 onPressed의 익명함수를 사용, 부모와 연결되어있는 onToggleSaved(book)을 사용하여 클릭했을 때 book이 있으면 remove, 없으면 add
4. isSelectedBook ? Icons.remove_circle : Icons.add_circle 의 삼항연산자를 사용하여 결과값을 나타낸다.
book_cart_page
import 'package:flutter/material.dart';
class BookCartPage extends StatelessWidget {
// 사용자가 카드에 저장한 데이터만 화면에 뿌려주기
final List<String> selectedBook;
const BookCartPage({required this.selectedBook, super.key});
@override
Widget build(BuildContext context) {
return ListView(
children:
selectedBook.map((book) => ListTile(title: Text(book))).toList());
}
}
'플러터 > 화면구현' 카테고리의 다른 글
[플러터] RiverPod 상태 관리를 활용한 To-Do List 앱 만들기 (0) | 2025.01.23 |
---|---|
[플러터] MVVM 패턴으로 TODOList 만들기 (0) | 2025.01.22 |
[플러터] 텍스트필드와 스크롤 사용 (0) | 2025.01.20 |
[플러터] 화면구현 용어 기초 - 1 (0) | 2025.01.08 |
[플러터] Recipe App (2) | 2025.01.08 |