Flutter

[Flutter] BottomSheet 사용법

참깨빵위에참깨빵_ 2024. 9. 21. 16:05
728x90
반응형

플러터에서 바텀 시트를 표시하는 함수는 2가지 있다.

 

  • showBottomSheet
  • showModalBottomSheet

 

2가지를 모두 확인해 본다. 아래는 showBottomSheet()의 공식문서다.

 

https://api.flutter.dev/flutter/material/ScaffoldState/showBottomSheet.html

 

showBottomSheet method - ScaffoldState class - material library - Dart API

PersistentBottomSheetController showBottomSheet( WidgetBuilder builder, { Color? backgroundColor, double? elevation, ShapeBorder? shape, Clip? clipBehavior, BoxConstraints? constraints, bool? enableDrag, bool? showDragHandle, AnimationController? transitio

api.flutter.dev

가장 가까운 Scaffold에 머티리얼 디자인 바텀 시트를 표시한다. 영구적인 바텀 시트를 표시하려면 Scaffold.bottomSheet를 쓴다. 바텀 시트를 닫거나 조작할 때 사용할 수 있는 컨트롤러를 리턴한다. Stateful에서 바텀 시트를 재작성하려면 이 메서드가 리턴한 컨트롤러에서 PersistentBottomSheetController.setState를 호출한다
새 바텀 시트는 둘러싸는 모달 루트에 대한 로컬 히스토리 엔트리가 되고 바텀 시트를 닫은 뒤로 버튼이 Scaffold의 앱 바에 추가된다. 전환 애니메이션 컨트롤러는 바텀 시트의 시작, 종료 애니메이션을 제어한다. 컨트롤러가 더 이상 필요하지 않을 때 애니메이션 컨트롤러를 호출하는 건 컨트롤러 소유자에 달려 있다. LocalHistoryEntry가 아니며 둘러싸는 Scaffold의 앱 바에 뒤로가기 버튼을 추가하지 않는 영구 바텀시트를 만들려면 Scaffold.bottomSheet 생성자 매개변수를 사용한다. 영구 바텀시트는 앱의 기본 컨텐츠를 보완하는 정보를 표시한다. 영구 바텀시트는 유저가 앱의 다른 부분과 상호작용할 때도 계속 표시된다...(중략)...모달 바텀시트는 showModalBottomSheet()를 써서 만들고 표시할 수 있다

 

아래는 공식문서의 코드 스니펫이다.

 

import 'package:flutter/material.dart';

/// Flutter code sample for [ScaffoldState.showBottomSheet].

void main() => runApp(const ShowBottomSheetExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ScaffoldState Sample')),
        body: const ShowBottomSheetExample(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: const Text('showBottomSheet'),
        onPressed: () {
          Scaffold.of(context).showBottomSheet(
            (BuildContext context) {
              return Container(
                height: 200,
                color: Colors.amber,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      const Text('BottomSheet'),
                      ElevatedButton(
                        child: const Text('Close BottomSheet'),
                        onPressed: () {
                          Navigator.pop(context);
                        },
                      ),
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

 

실행하면 아래 화면이 표시된다.

 

 

실행 후 화면 중앙의 버튼을 누르면 밑에서 노란 바텀시트가 애니메이션과 같이 올라온다.

앱 바를 보면 공식문서의 설명대로 왼쪽에 뒤로가기 버튼이 표시된다. 이걸 누르거나 Close BottomSheet 버튼을 누르면 바텀시트가 닫힌다.

 

 

아래는 다른 예시다.

 

import 'package:flutter/material.dart';

/// Flutter code sample for [ScaffoldState.showBottomSheet].

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ScaffoldState BottomSheet Sample')),
        body: const ShowBottomSheetExample(),
      ),
    );
  }
}

enum AnimationStyles { defaultStyle, custom, none }

const List<(AnimationStyles, String)> animationStyleSegments =
    <(AnimationStyles, String)>[
  (AnimationStyles.defaultStyle, 'Default'),
  (AnimationStyles.custom, 'Custom'),
  (AnimationStyles.none, 'None'),
];

class ShowBottomSheetExample extends StatefulWidget {
  const ShowBottomSheetExample({super.key});

  @override
  State<ShowBottomSheetExample> createState() => _ShowBottomSheetExampleState();
}

class _ShowBottomSheetExampleState extends State<ShowBottomSheetExample> {
  Set<AnimationStyles> _animationStyleSelection = <AnimationStyles>{
    AnimationStyles.defaultStyle
  };
  AnimationStyle? _animationStyle;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          SegmentedButton<AnimationStyles>(
            selected: _animationStyleSelection,
            onSelectionChanged: (Set<AnimationStyles> styles) {
              setState(() {
                _animationStyle = switch (styles.first) {
                  AnimationStyles.defaultStyle => null,
                  AnimationStyles.custom => AnimationStyle(
                      duration: const Duration(seconds: 3),
                      reverseDuration: const Duration(seconds: 1),
                    ),
                  AnimationStyles.none => AnimationStyle.noAnimation,
                };
                _animationStyleSelection = styles;
              });
            },
            segments: animationStyleSegments
                .map<ButtonSegment<AnimationStyles>>(
                    ((AnimationStyles, String) shirt) {
              return ButtonSegment<AnimationStyles>(
                  value: shirt.$1, label: Text(shirt.$2));
            }).toList(),
          ),
          const SizedBox(height: 10),
          ElevatedButton(
            child: const Text('showBottomSheet'),
            onPressed: () {
              Scaffold.of(context).showBottomSheet(
                sheetAnimationStyle: _animationStyle,
                (BuildContext context) {
                  return SizedBox(
                    height: 200,
                    child: Center(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        mainAxisSize: MainAxisSize.min,
                        children: <Widget>[
                          const Text('BottomSheet'),
                          ElevatedButton(
                            child: const Text('Close'),
                            onPressed: () {
                              Navigator.pop(context);
                            },
                          ),
                        ],
                      ),
                    ),
                  );
                },
              );
            },
          ),
        ],
      ),
    );
  }
}

 

메인 함수에서 위 파일을 실행시키면 중앙에 3개 중 하나를 선택할 수 있는 SegmentedButton과 그 밑에 버튼이 표시된다.

 

 

각 SegmentedButton은 바텀 시트의 애니메이션 스타일을 설정한다. Custom을 누르면 애니메이션이 좀 더 천천히 실행되며 바텀 시트가 나타나고 사라진다.

다음은 showModalBottomSheet 사용 예시다.

 

import 'package:flutter/material.dart';

/// Flutter code sample for [showModalBottomSheet].

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Bottom Sheet Sample')),
        body: const ModalBottomSheetExample(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: const Text('showModalBottomSheet'),
        onPressed: () {
          showModalBottomSheet<void>(
            context: context,
            builder: (BuildContext context) {
              return Container(
                height: 200,
                color: Colors.amber,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      const Text('Modal BottomSheet'),
                      ElevatedButton(
                        child: const Text('Close BottomSheet'),
                        onPressed: () => Navigator.pop(context),
                      ),
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

 

 

배경에 dim처리가 되어 있어서 바텀 시트 외의 부분은 조금 검게 보인다.

아래는 바텀시트의 왼쪽, 오른쪽 위를 조금 둥글게 바꾸는 예시다.

 

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorSchemeSeed: const Color(0xff6750a4),
        useMaterial3: true,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text('Bottom Sheet Sample')),
        body: const RoundedModalBottomSheetExample(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        child: const Text('showModalBottomSheet'),
        onPressed: () {
          showModalBottomSheet<void>(
            context: context,
            builder: (BuildContext context) {
              return SizedBox(
                height: 200,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      const Text('Modal BottomSheet'),
                      ElevatedButton(
                        child: const Text('Close BottomSheet'),
                        onPressed: () => Navigator.pop(context),
                      ),
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

 

 

반응형