본문 바로가기
Flutter

[flutter] 상태관리가 되는 앱 만들어 보기 02 (inheritedWidget)

by JINJINC 2025. 1. 20.
728x90
반응형

3단계 작업 (InheritedWidget 사용 해보기)

앞의 단계를 보고싶다면 아래의 링크로

prop Drilling

props Drilling을 해결하기 위한 방안

Inherited_Parent

// InheritedWidget 내장 데이터 타입을 상속받아 구현한다.
import 'package:flutter/cupertino.dart';

import '../common.utils/logger.dart';

class InheritedParent extends InheritedWidget {
  // 공유 상태 데이터 관리에 목적
  // List<String> mySelectedBooks = [];
  List<String> state; // 카드에 담긴 책 목록(공유 상태)
  // 부모에게 콜백 메서드를 관리하는 목적
  void Function(String book) onPressed; // 이벤트 핸들러

  // HOW <-- 사용방법
  // WHY <---
  InheritedParent({
    required this.state,
    required this.onPressed,
    required super.child,
  });

  // 상태가 변경되었는지 여부를 판단하는 메서드
  // 주의점 ! InheritedWidget --> 재정의 클래스를 InheritedParent 넣어 주자.

  // 메서드안 InheritedParent 타입 확인
  @override
  bool updateShouldNotify(covariant InheritedParent oldWidget) {
    logger.d('InheritedParent - updateShouldNotify() 호출 확인 ');

    // 상태가 달라졌다면 어떻게 판별할까?
    if (state.length != oldWidget.state.length) {
      logger.d('상태 변경 됨 ');
      return true;
    }

    for (int i = 0; i < state.length; i++) {
      // state --> String --> '호모사피엔스'
      // state[i] --> String ---> 호모사피엔스
      if (state[i] != oldWidget.state[i]) {
        return true;
      }
    }

    // 상태 변경이 없으면 false 를 반환하여 자식 위젯을 다시 빌드 않되도록 한다.
    return false; // 상태 변경 없음
  }
}

home_screen

import 'package:flutter/material.dart';
import 'package:flutter_statement/_03/inherited_parent.dart';

import '../common.utils/logger.dart';
import 'book_cart_page.dart';
import 'book_list_page.dart';

class HomeScreen extends StatefulWidget {
  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
  void initState() {
    // TODO: implement initState
    super.initState();
    setState(() {
      pageIndex = 0;
    });
  }

  void _changePageIndex(int index) {
    setState(() {
      pageIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    logger.d('HomeScreen build 메서드 호출됨 ');
    return SafeArea(
        child: Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            _changePageIndex(0);
          },
          icon: Icon(
            Icons.arrow_back_ios,
            color: Colors.white,
          ),
        ),
        title: Text(
          '지니의 서재',
          style: TextStyle(color: Colors.white),
        ),
        actions: [
          IconButton(
            onPressed: () {
              _changePageIndex(1);
            },
            icon: Icon(Icons.shopping_cart),
            color: Colors.white,
          )
        ],
        backgroundColor: Theme.of(context).colorScheme.onSurfaceVariant,
      ),
      body: InheritedParent(
        state: mySelectedBook,
        onPressed: _toggleSaveStates,

        //super.child
        child: IndexedStack(
          index: pageIndex,
          children: [
            BookListPage(),
            BookCartPage(),
          ],
        ),
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: pageIndex,
        onTap: (index) {
          return _changePageIndex(index);
        },
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.list), label: 'book-list'),
          BottomNavigationBarItem(
              icon: Icon(Icons.shopping_cart), label: 'book-cart'),
        ],
      ),
    ));
  }
}

book_list_page

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import '../common.utils/logger.dart';
import 'inherited_parent.dart';

class BookListPage extends StatelessWidget {
  BookListPage({super.key});

  //임시데이터 - 교보문고에 볼 수 있는 책 목록 리스트
  final List<String> books = [
    '호모사피엔스',
    'dart입문',
    '홍길동전',
    'code Refactoring',
    '전치사도감',
  ];

  @override
  Widget build(BuildContext context) {
    //buildcontext를 사용하여 inheritedParentWidget에 접근할 수 있음
    InheritedParent? inheritedParent =
        context.dependOnInheritedWidgetOfExactType();
    List<String> mySelectedBooked = inheritedParent?.state ?? [];

    logger.d('데이터 확인 : ${inheritedParent?.state.toString()}');

    return inheritedParent == null
        ? const Center(
            child: Text('데이터가 없습니다.'),
          )
        : SafeArea(
            child: Scaffold(
              body: ListView(
                children: books.map(
                  (book) {
                    //함수의 body에는 식을 작성할 수 있다
                    final isSelecedBook = mySelectedBooked!.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: Theme.of(context).primaryColor),
                        ),
                      ),
                      title: Text(
                        book,
                        style: TextStyle(
                            fontSize: 12, fontWeight: FontWeight.bold),
                      ),
                      trailing: IconButton(
                        onPressed: () {
                          inheritedParent.onPressed(book);
                        },
                        icon: isSelecedBook
                            ? Icon(Icons.remove_circle)
                            : Icon(Icons.add_circle),
                        color: isSelecedBook ? Colors.red : Colors.green,
                      ),
                    );
                  },
                ).toList(),
              ),
            ),
          );
  }
}

book_cart_page

import 'package:flutter/material.dart';
import 'package:flutter_statement/_03/inherited_parent.dart';

class BookCartPage extends StatelessWidget {
  const BookCartPage({super.key});

  @override
  Widget build(BuildContext context) {
    InheritedParent inheritedParent = context
        .dependOnInheritedWidgetOfExactType<InheritedParent>()!; //null assert

    List<String> mySelectedBooked = inheritedParent.state;

    return ListView(
        children: mySelectedBooked.map((cart) {
      return ListTile(
        leading: Container(
          width: 35,
          height: 35,
          decoration: BoxDecoration(
            color: Theme.of(context).secondaryHeaderColor,
            borderRadius: BorderRadius.circular(8.0),
            border: Border.all(color: Theme.of(context).primaryColor),
          ),
        ),
        title: Text(
          cart,
          style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
        ),
        trailing: IconButton(
            onPressed: () {
              inheritedParent.onPressed(cart);
            },
            icon: Icon(
              Icons.remove_circle,
              color: Theme.of(context).primaryColor,
            )),
      );
    }).toList());
  }
}

inheritedWidget의 단점이자 사용하지 않는 이유
=> 상위에 위치해있는 경우 상태가 변할 때 하위의 모든 build가 rebuild되어진다.
그렇기 때문에 너무 큰 단점이기 때문에, 상태관리 provider, riverpod등의 상태관리 라이브러리를 사용하게 된다.

728x90
반응형

댓글