[FLUTTER] 상태관리 응용 앱 제작 1 - StatefulWidget

2025. 1. 20. 11:49·Flutter

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단계 작업

  1. 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
'Flutter' 카테고리의 다른 글
  • [FLUTTER] 상태관리 응용 앱 제작 2 - InheritedWidget
  • [FLUTTER] 콜백 함수(Callback Function)
  • [FLUTTER] Stack 위젯
  • [FLUTTER] 기초 화면 구성
noily4748
noily4748
백엔드 개발을 공부하고 있는 개발자 입니다!
  • noily4748
    noily4748 님의 블로그
    noily4748
  • 전체
    오늘
    어제
    • 분류 전체보기 (37)
      • 웹 (2)
      • Flutter (11)
      • Dart (5)
      • 디자인 패턴 (4)
      • 디스코드 (2)
      • [Flutter] 눈길 팀 프로젝트 (10)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
noily4748
[FLUTTER] 상태관리 응용 앱 제작 1 - StatefulWidget
상단으로

티스토리툴바