video_player의 경우 버전이 낮으면 init 시에 에러발생함
GridView를 쓸 때는 children으로 하는 게 아니라 ListView처럼 builder로 하는 게 낫다.
gridDelegate는 컨트롤러라기보다는 GridView를 구성하는 데에 도움을 주는 헬퍼 정도..
FadeInImage는
넘치는 텍스트를 다 보이게 하고싶으면, expanded 위젯을 활용
키보드가 실행된다거나 하는 이유로 화면이 resize되는 이슈를 줄이기 위한 방법
-> Scaffold 속성 중 resizeToAvoidBottomInset을 False로 세팅
여러개의 리스트를 보여주기 위한 여러가지 방법
GridView, ListTile!
Material Design의 세팅값들이 자동으로 적용되는 게 싫은 경우, main.dart에서 ThemeData에서 적용하기 싫은 값들에 Colors.transparent를 설정해주면 됨!
여러개의 텍스트를 하나로 묶어서 사용하고 싶을 땐 RichText + TextSpan 위젯을 사용!
Dismissible 위젯 : 슬라이드해서 보여지는 걸 다르게 할 수 있음! 마치 틱톡처럼!
(얘는 사이드 프로젝트에 꼭 사용해야겠다!)
Dismissible 위젯은 꼭 슬라이드했을 때 onDismissed 콜백 함수가 작동해야 오류가 안 뜸
Animation Builder 없이도 Animation 만들기!
RotationTransition 위젯의 turns 속성에 Animation<double>을 정의하면 애니메이션을 작동하게 할 수 있음.
late final Animation<double> _animation =
Tween(begin: 0.0, end: 1.0).animate(_animationController);
RotationTransition(
turns: _animation,)
Tween은 begin과 end 값을 더블로 받아서 이 과정에 사용할 수 있음. 소스는 아래와 같음.
한번 이렇게 컨트롤러를 정의해두면 속성의 설정에 맞춰 다른 생성을 진행해 다방면의 위젯에 활용할 수 있음
AnimatedModalBarrier 위젯을 활용하면 활성화된 슬라이드 외의 다른 부분을 어둡게 오버레이+비활성화 시킬 수 있다
Scroll 기능을 커스터마이징해서 사용할 수 있게끔 나온 위젯 = CustomScrollView 위젯.
slivers 속성에서 SliverAppBar를 사용!
VerticalDivider 사용하면 말 그대로 수직선을 세팅 가능! (다만 높이가 있는 위젯에 대해서만 작동하므로, 높이가 없는 경우 SizedBox 위젯으로 감싸주고 height를 쎄팅한다.
FractionallySizedBox는 부모 위젯에 비례한 사이즈 조절할 때 사용
CloseButton 위젯 : 닫기 기능을 하는 X 버튼 위젯
ListWheelScrolView : 리스트를 수레바퀴처럼 돌아가는 방식으로 스크롤할 수 있음!
body: ListWheelScrollView(
useMagnifier: true,
magnification: 1.5,
itemExtent: 200,
children: [
for (var x in [1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1])
FractionallySizedBox(
widthFactor: 1,
child: Container(
color: Colors.teal,
alignment: Alignment.center,
child: const Text(
'Pick me',
style: TextStyle(
color: Colors.white,
fontSize: 39,
),
),
),
),
],
),
CupertinoActivityIndicator() : 아이폰용 로딩 인디케이터
CircularProgressIndicator() : 안드로이드용 로딩 인디케이터
CircularProgressIndicator.adaptive() : 스마트폰 기종에 따라 다른 로딩 인디케이터를 표시함
어플 버전정보를 보여주고 싶을 때는 만들어져 있는 기능이나 위젯을 이용하면 된다!
1. ListView > ListTile > onTap -> showAboutDialog 메소드 사용
2. 그냥 ListView 아래에 AboutListTile을 사용해도 됨! 커스터마이징하기에는 1번이 편함
(아래 코드 참고)
ListTile
body: ListView(
children: [
ListTile(
onTap: () => showAboutDialog(
context: context,
applicationVersion: "1.0",
applicationLegalese:
"All rights reserved. Please don't copy me."),
title: const Text(
"About",
style: TextStyle(
fontWeight: FontWeight.w600,
),
),
subtitle: const Text("About this app......"),
),
const AboutListTile()
],
),
Date, Time, Period를 설정할 때 꿀팁 함수들
= showDatePicker, showTimePicker, showDateRangePicker
(아래 소스 참고)
body: ListView(
children: [
ListTile(
onTap: () async {
final date = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1992),
lastDate: DateTime(2024),
);
print(date);
final time = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
print(time);
final booking = await showDateRangePicker(
context: context,
firstDate: DateTime(1992),
lastDate: DateTime(2024),
builder: (context, child) {
return Theme(
data: ThemeData(
appBarTheme: const AppBarTheme(
foregroundColor: Colors.white,
backgroundColor: Colors.deepPurple,
),
),
child: child!,
);
},
);
print(booking);
},
title: const Text("What is your birthday?"),
),
],
),
스위치와 체크박스를 사용하는 방법
body: ListView(
children: [
CupertinoSwitch(
value: _notifications,
onChanged: _onNotificationsChanged,
),
Switch(
value: _notifications,
onChanged: _onNotificationsChanged,
),
Switch.adaptive(
value: _notifications,
onChanged: _onNotificationsChanged,
),
SwitchListTile(
value: _notifications,
onChanged: _onNotificationsChanged,
title: const Text("Enable Notification"),
subtitle: const Text("detailed information"),
),
Checkbox(
value: _notifications,
onChanged: _onNotificationsChanged,
),
CheckboxListTile(
activeColor: Colors.black,
checkColor: Colors.white,
value: _notifications,
onChanged: _onNotificationsChanged,
title: const Text("Enable notifications"),
),
로그아웃 기능 구현 시 참고
ListTile(
title: const Text("Log out (iOS)"),
textColor: Colors.red,
onTap: () {
showCupertinoDialog(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text("Are you sure?"),
content: const Text("Please don't go"),
actions: [
CupertinoDialogAction(
onPressed: () => Navigator.of(context).pop(),
child: const Text("No"),
),
CupertinoDialogAction(
onPressed: () => Navigator.of(context).pop(),
isDestructiveAction: true,
child: const Text("Yes"),
),
],
),
);
},
),
ListTile(
title: const Text("Log out (Android)"),
textColor: Colors.red,
onTap: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Are you sure?"),
content: const Text("Please don't go"),
actions: [
IconButton(
onPressed: () => Navigator.of(context).pop(),
icon: const FaIcon(FontAwesomeIcons.car),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Yes"),
),
],
),
);
},
),
ListTile(
title: const Text("Log out (iOS / Bottom)"),
textColor: Colors.red,
onTap: () {
showCupertinoModalPopup(
context: context,
builder: (context) => CupertinoActionSheet(
title: const Text("Are you sure?"),
actions: [
CupertinoActionSheetAction(
isDefaultAction: true,
onPressed: () => Navigator.of(context).pop(),
child: const Text("Not log out"),
),
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () => Navigator.of(context).pop(),
child: const Text("Yes Please"),
),
],
),
);
},
),
자동회전 시 바뀌는 화면 비율을 조정하기 위한 방법
-> Scaffold를 OrientationBuilder로 감싸고 세로화면일 때와 가로화면일 때의 코드를 다르게 세팅한다.
orientation 값이 portrait일 때가 세로, landscape일 때가 가로
@override
Widget build(BuildContext context) {
return OrientationBuilder(
builder: (context, orientation) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Sizes.size40,
),
child: Column(
children: [
Gaps.v80,
const Text(
"Sign up for TikTok",
style: TextStyle(
fontSize: Sizes.size24,
fontWeight: FontWeight.w700,
),
),
Gaps.v20,
const Text(
"Create a profile, follow other accounts, make your own videos, and more.",
style: TextStyle(
fontSize: Sizes.size16,
color: Colors.black45,
),
textAlign: TextAlign.center,
),
Gaps.v40,
if (orientation == Orientation.portrait) ...[
GestureDetector(
onTap: () => _onEmailTap(context),
child: const AuthButton(
icon: FaIcon(FontAwesomeIcons.user),
text: "Use email or password"),
),
Gaps.v16,
const AuthButton(
icon: FaIcon(FontAwesomeIcons.apple),
text: "Continue with Apple"),
],
if (orientation == Orientation.landscape) ...[
Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => _onEmailTap(context),
child: const AuthButton(
icon: FaIcon(FontAwesomeIcons.user),
text: "Use email or password"),
),
),
Gaps.h16,
const Expanded(
child: AuthButton(
icon: FaIcon(FontAwesomeIcons.apple),
text: "Continue with Apple"),
),
],
),
],
],
),
),
),
SystemChrome을 활용한 자동회전 방지 방법
: main.dart에서 다음과 같이 세팅
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations(
[
DeviceOrientation.portraitUp,
],
);
음성이 자동으로 재생되는 영상은 많은 브라우저에서 권한을 막고 있음. 해결법은 간단. 웹일 때만 비디오가 초기화될 때 음소거 처리하면 됨.
** flutter에는 미리 정의된 constant variable들이 있고, k로 시작함
void _initVideoPlayer() async {
await _videoPlayerController.initialize();
await _videoPlayerController.setLooping(true);
if (kIsWeb) {
await _videoPlayerController.setVolume(0);
}
_videoPlayerController.play();
setState(() {});
_videoPlayerController.addListener(_onVideoChange);
}
음소거 버튼 만들기!
1. 버튼 추가
: 뮤트 플래그가 참이면 음소거버튼이, 거짓이면 볼륨하이버튼이 나오게끔 세팅. 탭할 시 _onMuteTap 함수가 작동하도록 세팅.
GestureDetector(
onTap: _onMuteTap,
child: _isMuted
? const FaIcon(
FontAwesomeIcons.volumeXmark,
color: Colors.black54,
)
: const FaIcon(
FontAwesomeIcons.volumeHigh,
color: Colors.black54,
),
),
2. _isMuted 플래그는 웹일 경우에는 볼륨 제로로 시작하기 때문에 kIsWeb일 경우에는 isMuted=true로, 아닐 경우에는 false로 세팅되도록 작성
bool _isMuted = kIsWeb ? true : false;
3. _onMuteTap 함수는 음소거가 아닌 상태에서 누르면, 음소거가 되게끔, 음소거인 상태에서 누르면 시스템 볼륨값을 가져오도록 세팅(setVideoPlayerVolume 메소드가 해당 부분)
void _onMuteTap() {
setState(() {
if (!_isMuted) {
_videoPlayerController.setVolume(0);
} else {
_setVideoPlayerVolume();
}
_isMuted = !_isMuted;
});
}
4. setVideoPlayerVolume 메소드의 경우 volume_controller 패키지를 임포트해서 시스템 볼륨을 가져온 뒤, Video Player의 볼륨값을 시스템 볼륨값으로 세팅하는 메소드
void _setVideoPlayerVolume() async {
_systemVolume = await VolumeController().getVolume();
_videoPlayerController.setVolume(_systemVolume);
}
특정 위젯의 높이나 너비값을 유동적으로 변하게 하고 싶을 때?
LayoutBuilder를 사용하면 됨.
LayoutBuilder 위젯은 어느 위치에 두느냐에 따라 최대, 최소값이 달라질 수 있음! 가령 높이와 너비가 세팅된 SizedBox 내부에 들어가면 제대로 반영하지 못할 수 있음!
(코드 작성은 아래 코드 참고)
class LayoutBuilderCodeLab extends StatelessWidget {
const LayoutBuilderCodeLab({super.key});
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => Container(
width: constraints.maxWidth,
height: constraints.maxHeight,
color: Colors.teal,
child: Center(
child: Text(
"${size.width} / ${constraints.maxWidth}",
style: const TextStyle(
color: Colors.white,
fontSize: 98,
),
),
),
),
),
);
}
}
Container 위젯 내부에 constraints 속성이 자체적으로 있기 때문에, 여기서 높이나 너비의 최대, 최소값을 세팅할 수 있음!
출처
노마드 코더 유료 강의
(광고 아니고 ㄹㅇ 강추!)
'Flutter & Dart' 카테고리의 다른 글
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (7) (0) | 2023.12.26 |
---|---|
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (5) - 동영상 플레이어 만들기 (1) | 2023.12.05 |
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (4) (2) | 2023.11.30 |
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (3) (1) | 2023.11.24 |
공부하다 배운 Flutter 꿀팁 무작정 적어두기 (2) (2) | 2023.11.23 |