Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- keychain error
- swift concurrency
- AnimationController
- widget
- flutter statefulwidget
- flutter ios 폴더
- docker overview
- lol api dart
- valorant api dart
- com.google.GIDSignIn
- dart
- flutter bloc
- flutter android 폴더
- 발로란트 api dart
- dart new 키워드
- dart new
- dart.dev
- generate parentheses dart
- Architectural overview
- 파이썬 부동소수점
- leetcode dart
- 롤 api dart
- tft api dart
- 파이썬
- flutter
- swift 동시성
- flutter widget
- riot api dart
- 롤토체스 api dart
- PlatformException(sign_in_failed
Archives
- Today
- Total
aspe
Flutter 프로젝트 '남음' - Bottom sheet 구현하기 본문
현재 내 주변에 어떤 가게들이 있는지 확인하기 위해 Google map을 사용합니다.
Google map위에 있는 Marker를 클릭하거나, sheet을 드래그하면 화면의 하단에서 sheet이 올라오게 구현 할 예정입니다.
(Marker를 클릭하는 것은 간단하니 우선 드래그를 구현해봅니다)

우선 Google map이 있는 tab을 Stack으로 감싸줍니다. 그래야 Positioned를 사용해 bottom에 sheet를 위치 시킬 수 있습니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Widget _iOSBuilder(BuildContext context) { | |
return SafeArea( | |
child: Stack( | |
children: [ | |
Column( | |
mainAxisSize: MainAxisSize.max, | |
children: [ | |
Expanded( | |
child: userLocation == null | |
? const Center( | |
child: Text("Loading..."), | |
) | |
: GoogleMap( | |
markers: Set.from(_markers), | |
mapType: MapType.normal, | |
initialCameraPosition: userLocation!, | |
myLocationEnabled: true, | |
onMapCreated: (GoogleMapController controller) { | |
_controller.complete(controller); | |
}, | |
)), | |
], | |
), | |
const MapBottomSheet() | |
], | |
), | |
); | |
} |
24 line이 우리가 구현 할 위젯입니다.
MapBottomSheet은 StatefulWidget으로 설정합니다. BottomSheet을 AnimatedContainer로 구현 할 것이기 때문에 state가 필요합니다.
기본 동작은 다음과 같습니다. Bottom sheet을 드래그 하여 높이가 100 ~ 500 사이이면 높이를 600으로 설정합니다. 그리고 다시 유저의 드래그로 500 ~ 550 사이로 들어오면 높이를 100으로 설정합니다.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
class MapBottomSheet extends StatefulWidget { | |
const MapBottomSheet({super.key}); | |
@override | |
State<MapBottomSheet> createState() => _MapBottomSheetState(); | |
} | |
class _MapBottomSheetState extends State<MapBottomSheet> { | |
late double _height; | |
final double _lowLimit = 50; | |
final double _highLimit = 600; | |
final double _upThresh = 100; | |
final double _boundary = 500; | |
final double _downThresh = 550; | |
/// 100 -> 600, 550 -> 100 으로 애니메이션이 진행 될 때, | |
/// 드래그로 인한 _height의 변화 방지 | |
bool _longAnimation = false; | |
@override | |
void initState() { | |
super.initState(); | |
_height = _lowLimit; | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Positioned( | |
bottom: 0.0, | |
child: GestureDetector( | |
onVerticalDragUpdate: ((details) { | |
// delta: y축의 변화량, 우리가 보기에 위로 움직이면 양의 값, 아래로 움직이면 음의 값 | |
double? delta = details.primaryDelta; | |
if (delta != null) { | |
/// Long Animation이 진행 되고 있을 때는 드래그로 높이 변화 방지, | |
/// 그리고 low limit 보다 작을 때 delta가 양수, | |
/// High limit 보다 크거나 같을 때 delta가 음수이면 드래그로 높이 변화 방지 | |
if (_longAnimation || | |
(_height <= _lowLimit && delta > 0) || | |
(_height >= _highLimit && delta < 0)) return; | |
setState(() { | |
/// 600으로 높이 설정 | |
if (_upThresh <= _height && _height <= _boundary) { | |
_height = _highLimit; | |
_longAnimation = true; | |
} | |
/// 100으로 높이 설정 | |
else if (_boundary <= _height && _height <= _downThresh) { | |
_height = _lowLimit; | |
_longAnimation = true; | |
} | |
/// 기본 작동 | |
else { | |
_height -= delta; | |
} | |
}); | |
} | |
}), | |
child: AnimatedContainer( | |
curve: Curves.bounceOut, | |
onEnd: () { | |
if (_longAnimation) { | |
setState(() { | |
_longAnimation = false; | |
}); | |
} | |
}, | |
duration: const Duration(milliseconds: 400), | |
decoration: const BoxDecoration( | |
boxShadow: [BoxShadow(blurRadius: 6, spreadRadius: 0.7)], | |
color: Colors.white, | |
borderRadius: | |
BorderRadius.vertical(top: Radius.circular(20))), | |
width: MediaQuery.of(context).size.width, | |
height: _height, | |
child: Column( | |
children: [ | |
const SizedBox( | |
height: 20, | |
), | |
Container( | |
width: 70, | |
height: 4.5, | |
decoration: const BoxDecoration( | |
color: Colors.grey, | |
borderRadius: BorderRadius.all(Radius.circular(10))), | |
), | |
], | |
), | |
))); | |
} | |
} |
AnimatedContainer의 onEnd에서 _longAnimation에 대한 처리를 꼭 해줘야 정상적으로 작동합니다.
가게 정보들이 해당 AnimatedContainer에 들어오는 건 곧 포스팅 합니다.
'Flutter > 프로젝트' 카테고리의 다른 글
Flutter - 남음 (0) | 2023.05.25 |
---|---|
Flutter 프로젝트 - Nameum (0) | 2023.02.17 |
Flutter - Nameum - 추천 검색바 만들기 (0) | 2023.02.17 |
Comments