Flutter Render Conditionally, Lift-State-up(props)

강정우·2023년 5월 14일
2

Flutter&Dart

목록 보기
16/87
post-thumbnail

조건부 렌더링(Render content Conditionally)

  • var 변수에 widget를 담을 수 있다.
var 변수 = customWidget();

다만, 위와같이 설정한다면 var의 변수에 들어갈 수 있는 타입은 customWidget에 한하기 때문에 의미가 없다.
그래서 우리가 의도한 대로 customWidget만들 담을 수 있는 변수를 만드려면 var => Widget 타입을 넣어주면 된다.

  • 따라서 아래와 같은 함수를 만들어 조건부 렌더링을 할 수 있다.
class _QuizState extends State<Quiz> {
  Widget activeScreen = const StartScreen(swichScreen);

  void swichScreen() {
    setState(
      () {
        activeScreen = const QuestionsScreen();
      },
    );
  }

  
  Widget build(context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              colors: [
                Color.fromARGB(255, 214, 91, 235),
                Color.fromARGB(255, 240, 113, 155),
              ],
              begin: Alignment.topLeft,
              end: Alignment.bottomRight,
            ),
          ),
          child: activeScreen,
        ),
      ),
    );
  }
}
  • 이때 setState를 통하여 return값 안에있는 ui들이 재평가된 후 ui에 반영할 수 있도록 작성하였고 child argument에 activescreen이라는 Widget만을 반환하는 함수를 넣어 조건부로 렌더링 하도록 작성한 코드이다.

  • 하지만 아직 해당 메서드가 button에 연결이 안 되어있다. 이제 lift state up을 통하여 prop을 옮겨보자.

Lifting state up

  • 우선 contionally하게 렌더링해줄 버튼은 바로 StartScreen인스턴스에게 있다. 그렇기 위해선 class에서 condition을 바꿔주는 메서드를 내려받아서 버튼에 해당 함수를 달아줘야한다.
class StartScreen extends StatelessWidget {
	const StartScreen(void Function() startQuiz, {super.key});
    
    
    ...
    
  • 이때 return type과 인수를 맞춰줘야한다.
    현재 상황에서는 return 값이 없으니 void라고 명시하고

  • 추가적으로 만약 swichScreen에 인수가 있다면 Function() 안에 추가해줘야한다.
    즉, 우리는 startQuiz라는 함수를 Function()으로 pointer를 얻은 것이다.
    props으로 내려받았다고 생각하면 편함

  • 또한 생성자함수와 메서드는 엄연히 다른 영역이기에 이를 연결해줄 속성값도 명시해줘야한다.

class StartScreen extends StatelessWidget {
	const StartScreen(this.startQuiz, {super.key});
    
    final void Function() startQuiz;
    
    
    ...
    
  • 이제 생성자 함수가 props에 명시되어있는 값을 this.로 받아서 생성한다.
    이제 비로소 버튼에 pointing이 가능하다.

  • 그런데도 오류가 나고있다. 이 오류는 무엇때문에 나는 것일까? =>
    activeScreen이라는 변수가 생성(initializing)될 때 StartScreen의 생성자 함수에서 switchScreen이 사용되기 때문이다.
    이때 문제가 뭐냐면 함수가 생성된 후 사용되어야하는데 이 시기기 너무 빨라 생성과 사용이 같은 틱이 이루어지기 때문에 올바르지 않은 코드라는 뜻이다.

즉, class가 인스턴스화되고 속성값(변수)들이 선언되고, 함수들이 선언되는 시간이 동일하기 때문에 pointing 하는 함수들을 참조하지 못한다는 것이다.

  • 그럼 어떻게 할까?

initState

  • 우선 initState 함수는 State 클래스가 제공하는 함수이다.
  • 즉, 해당함수는 State가 제공하는 initState함수를 @overide함으로써 State 클래스를 상속받는 시점에 선언한다는 것이다.

class _QuizState extends State<Quiz> {
  Widget? activeScreen;

  
  void initState() {
    activeScreen = StartScreen(swichScreen);
    super.initState();
  }

  void swichScreen() {
    setState(
      () {
        activeScreen = const QuestionsScreen();
      },
    );
  }
  • 이때 또 한가지 주의할 점은 initState는 QuizState함수로부터 호출될 때 실행되기 때문에 compile 단계에서는 null이기 때문에 Widget에 null일 수 있다는 optional keyword? 를 붙여줘야한다.

중요! Widget life-cycle

  • 플러터에 의해 실행되는 모든 함수는 모두 라이프사이클을 갖고있다. 이는 굉장히 중요한 부분이다.
  1. initState(): 플러터에 의해 실행되며 StatefulWidget의 2번째 클래스의 super classdls State 객체가 생성될 때 실행된다.

  2. build(): Widget이 처음 build함수가 실행될 때, setState에 의하여 실행될 때

  3. dispose(): Widget이 삭제되기 전에 실행. => contionally 렌더링 된다든지 할 때

?

  • 하지만 정말 또다른 솔루션이 존재한다. 물론 접근하는 개념은 완전히 다르다.
  • 3항 연산자를 이용하여 해당 변수의 상태를 확인해 조건부 렌더링을 진행하는 것이다.
class _QuizState extends State<Quiz> {
  var activeScreen = 'start-screen';

  void swichScreen() {
    setState(() {
      activeScreen = 'questions-screen';
    });
  }

  
  Widget build(context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
        ...
          ),
          child: activeScreen == 'start-screen'
              ? StartScreen(swichScreen)
              : const QuestionsScreen(),
        ),
      ),
    );
  }
}

if


Widget build(context) {
  Widget screenWidget = StartScreen(swichScreen);
  if( activeScreen == 'questions-screen' ){
  screenWidget = const QuestionsScreen();
  }

  return MaterialApp(
    home: Scaffold(
      body: Container(
        decoration: const BoxDecoration(
          gradient: LinearGradient(
            colors: [
              Color.fromARGB(255, 214, 91, 235),
              Color.fromARGB(255, 240, 113, 155),
            ],
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
          ),
        ),
        child: screenWidget,
      ),
    ),
  );
}
  • 물론 변수를 class단에 설정하는 것이 아닌 build 메서드 안에 선언하여 지역변수처럼 사용하고 또 그안에 함수로 로직을 작성할 수도 있다.

if+

  • 만약 if를 list와 함께 쓴다면 다 개쩌는 방식으로도 작성이 가능하다.
final myList = [
	1, 2, 
    if(condition)
    	3
    else 
    	4
];
  • 이때 condition은 당연 var혹은 어떠한 로직에 의하여 반환값이 true, false여야한다.

  • 또한 위와같이 condition으로 작성하게 될 경우 {} 사용하지 않는다.
    왜냐하면 statement body가 들어가게된다면 해당 if문은 오직 다음 1줄만 실행하기 때문이다.

final myList = [
	1, 2, 
    condition ? 3 : 4
];
  • 그래서 플루터의 Column(), Row() Widget과 함께 최고의 시너지를 자랑하는 if문이다.
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글