Flutter/2.0

Flutter 2.0에서 ScaffoldMessenger와 SnackBar 변화 정리

lgvv 2021. 8. 12. 02:56

Flutter 2.0에서 ScaffoldMessenger와 SnackBar 변화 정리

 

플러터 2.0에서는 해당 사용 부분이 많이 달라짐.

 

 

기존 Scaffold.of(context)의 한계

먼저 기존의 Scaffold.of(context)를 이해

이 메서드는 현재 위젯 트리에서 가장 가까운 Scaffold를 찾아 반환하라는 의미

 

하지만 여기엔 한 가지 큰 문제가 있음.

현재의 context Scaffold를 참조할 수 없는 위치에 있으면 Scaffold.of(context) 는 에러를 던짐

 

 

이 문제를 해결하기 위해, 우리는 보통 Builder 위젯을 사용.

이렇게 하면 Builder가 새로운 context를 제공하므로 Scaffold를 정상적으로 찾을 수 있었다.

Builder(
  builder: (context) {
    Scaffold.of(context).showSnackBar(SnackBar(
      content: Text('Hello!'),
    ));
  },
);

 

기존 SnackBar의 구조적 문제


그렇다면 Flutter 1점대에서는 SnackBar는 현재 context를 기반으로 표시되기 때문에

화면이 전환되면 새로운 context가 생기며 SnackBar가 사라지지 않는 문제가 발생

 

따라서 새로운 페이지로 이동해도 기존 SnackBar가 여전히 남아있는 UX적으로 불완전한 동작 

 

 

Flutter 2.0의 해결책 — ScaffoldMessenger

 

Flutter 2.0에서는 이 문제를 해결하기 위해 ScaffoldMessenger라는 새로운 클래스가 등장.

ScaffoldMessenger는 앱의 최상단에 위치하며, 모든 하위 Scaffold에 대한 SnackBar 관리 책임을 가짐.

 

즉, SnackBar가 context에 직접 종속되지 않기 때문에, 화면 전환 시 자동으로 SnackBar를 정리할 수 있게 되었음.

 

 

 

 

화면 이동 시 SnackBar 동작

  • 기존 방식 (Scaffold.of(context))
    • Scaffold 기반
    • SnackBar 남아있음 ❌
  • 변경 방식 (ScaffoldMessenger)
    • ScaffoldMessenger 기반
    • SnackBar 즉시 사라짐 ✅

 

 

코드 샘플

 

여기서 주목할 점은 Builder다.

Builder를 사용해 새로운 context를 생성하고, context를 통해 ScaffoldMessenger에 접근.

  • ScaffoldMessenger.of(context)
    • 가장 가까운 ScaffoldMessenger를 찾아 SnackBar를 표시
  • SnackBarAction
    • SnackBar 오른쪽의 액션 버튼을 정의

 

import 'package:flutter/material.dart'; // 데스크탑, 앱 등에 고루 UI를 적용할 수 있게 해주는 구글이 제공해주는 패키

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      // 실질적으로 모든 앱을 감싸고 있다.
      title: 'Appbar', // 앱을 총칭하는 이름 -> 스마트 폰 앱에서 최근 사용한 앱 보여줄 때의 이름
      theme: ThemeData(primarySwatch: Colors.red // 특정색의 응용들을 기본 색상으로 지정해서 사용하겠
          ),
      home: MyPage(), // home은 앱이 실행될 때 가장먼저 보여주는 페이
    );
  }
}

class MyPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Snack Bar'),
      ),
      body: HomeBody(), // 바디 부분에 홈 바디를 둔다.
      floatingActionButton: FloatingActionButton( // 플로팅 버튼도 배치
        child: Icon(Icons.thumb_up),
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar( // 스캐폴드메신저 만들기
            // 가장 가까운 스캐폴드 찾아서 반환해
            SnackBar( // 스낵바
              content: Text('Like a new Snack Bar!'), // 내
              duration: Duration(seconds: 5),
              action: SnackBarAction( // 스낵바 액션 - 우측에 달리는 아이템 버튼!
                label: 'Undo',
                onPressed: () {
                  Navigator.push(context, // 네비게이션 형식으로 push형식으로 전환
                      MaterialPageRoute(builder: (context) => ThirdPage())); // 화면전환 코드
                },
              ),
            ),
          );
        },
      ),
    );
  }
}

class HomeBody extends StatelessWidget {
  const HomeBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center( // 센터임!! 주
      child: ElevatedButton( // 엘레베이티드 버튼
        child: Text('Go to the second page'),
        onPressed: () {
          Navigator.push(
              context, MaterialPageRoute(builder: (context) => SecondPage())); // 두번쨰 페이지로 전환
        },
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Page'),
      ),
      body: Center(
        child: Text(
          '"좋아요가 추가되었습니다."',
          style: TextStyle(fontSize: 20.0, color: Colors.redAccent),
        ),
      ),
    );
  }
}

class ThirdPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScaffoldMessenger(
      child: Scaffold(
        appBar: AppBar(
          title: Text('Third Page'),
        ),
        body: Builder( // 빌더를 통해서 context를 받아야 해.
          builder: (context){
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  '"좋아요를 취소하시겠습니까?"',
                  style: TextStyle(fontSize: 20.0, color: Colors.redAccent),
                ),
                ElevatedButton(
                  onPressed: () {
                    ScaffoldMessenger.of(context).showSnackBar(SnackBar( // 스캐폴드 메신저가 스낵바 들고 있음!!
                      // 이렇게 하면 화면을 빠져나가서 context가 사라지면 스낵바가 사라져서 UI개선
                      content: Text("좋아요가 취소되었습니다"),
                      duration: Duration(seconds: 3),
                    ));
                  },
                  child: Text('취소하기'),
                ),
              ],
            ),
          );}

        ),
      ),
    );
  }
}

 

 

스크린샷

 

 

정리