๐ฏ ํ์ต ๋ชฉํ
- ๋ค์ํ ํน์ ์์ ฏ(FutureBuilder, StreamBuilder, TabBar, PageView ๋ฑ)์ ๋์ ์๋ฆฌ ์ดํด
- ๋น๋๊ธฐ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ UI ๊ตฌ์ฑ ๋ฅ๋ ฅ ํฅ์
- ํญ ์ ํ ๋ฐ ์ฌ๋ผ์ด๋ UI ๊ตฌํ ๋ฅ๋ ฅ ์ต๋
- ๋ณต์กํ ์์ ฏ ์กฐํฉ์ ํตํ ์ค์ ์ฑ UI ์ค๊ณ ๊ฒฝํ
๐น FutureBuilder
โ ๊ฐ๋ ์ ๋ฆฌ
FutureBuilder๋ Future ๊ฐ์ฒด์ ์ํ ๋ณํ์ ๋ฐ๋ผ UI๋ฅผ ์๋์ผ๋ก ๊ฐฑ์ ํด์ฃผ๋ ์์ ฏ์
๋๋ค.
๋น๋๊ธฐ ์์
์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆด ๋ ๋ก๋ฉ ์คํผ๋, ์ฑ๊ณต/์คํจ ๋ฉ์์ง๋ฅผ ํ์ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
โ ๏ธ ์ฃผ์: future๋ build() ํจ์ ๋ด๋ถ๊ฐ ์๋, initState() ๋ฑ ์ธ๋ถ์์ ์ ์ธํด์ผ ๋ถํ์ํ ์ฌ๋น๋๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๐ง ์ฃผ์ ์์ฑ
์์ฑ | ์ค๋ช |
future | ๊ฐ์ํ Future ๊ฐ์ฒด |
builder | AsyncSnapshot์ ์ธ์๋ก ๋ฐ์ UI๋ฅผ ๋ฐํํ๋ ํจ์ |
snapshot.hasData | ์ ์์ ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ๋์ง ์ฌ๋ถ |
snapshot.hasError | ์ค๋ฅ ๋ฐ์ ์ฌ๋ถ |
snapshot.connectionState | none, waiting, active, done ์ํ ๋ฐํ |
๐งช ๊ธฐ๋ณธ ์์
class FutureDemo extends StatefulWidget {
@override
_FutureDemoState createState() => _FutureDemoState();
}
class _FutureDemoState extends State<FutureDemo> {
late Future<String> _calculation;
@override
void initState() {
super.initState();
_calculation = Future.delayed(Duration(seconds: 2), () => '๋ฐ์ดํฐ ๋ก๋ ์๋ฃ!');
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _calculation,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('์๋ฌ: ${snapshot.error}'));
} else {
return Center(child: Text(snapshot.data!));
}
},
);
}
}
๐งฉ ์ค์ต ๊ณผ์
- Future.delayed(Duration(seconds: 3))๋ก ๋ณ๊ฒฝํ๊ณ , ์๋ฃ ๋ฉ์์ง์ ํจ๊ป ์์ด์ฝ ํ์ํด๋ณด๊ธฐ
- Future.error('๋ฌธ์ ๋ฐ์')์ ์ด์ฉํด ์๋ฌ ๋ฐ์ ์ํฉ ํ ์คํธ
-
๋๋ณด๊ธฐ
์ฐธ๊ณ
- Future.error('๋ฌธ์ ๋ฐ์')๋ฅผ Future.delayed์ ์ฝ๋ฐฑ ์์์ ์ง์ ๋ฆฌํดํ๋ฉด snapshot.data๋ก ์ ๋ฌ๋๊ฑฐ๋ ๋ฌด์๋ ์ ์์ด.
- throw '๋ฌธ์ ๋ฐ์'์ฒ๋ผ ์ง์ ์์ธ๋ฅผ ๋์ ธ์ผ FutureBuilder๊ฐ hasError == true๋ก ์ธ์ํจ.
import 'package:flutter/material.dart'; void main() { runApp(DiaryApp()); } class DiaryApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(title: 'Future Error Demo', home: FutureErrorDemo()); } } class FutureErrorDemo extends StatefulWidget { @override _FutureErrorDemoState createState() => _FutureErrorDemoState(); } class _FutureErrorDemoState extends State<FutureErrorDemo> { late Future<String> _calculation; @override void initState() { super.initState(); // 2์ด ํ ์๋ฌ ๋์ง๊ธฐ (์ ํํ '์๋ฌ ๋ฐ์: ๋ฌธ์ ๋ฐ์' ์ถ๋ ฅ๋จ) _calculation = Future.delayed(Duration(seconds: 2), () => throw '๋ฌธ์ ๋ฐ์'); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('์๋ฌ ํ ์คํธ')), body: FutureBuilder<String>( future: _calculation, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center( child: Text( '์๋ฌ ๋ฐ์: ${snapshot.error}', // ← ์ฌ๊ธฐ์ ์ ํํ ์ถ๋ ฅ๋จ style: TextStyle(color: Colors.red, fontSize: 18), ), ); } else { return Center(child: Text(snapshot.data!)); } }, ), ); } }
๐น StreamBuilder
โ ๊ฐ๋ ์ ๋ฆฌ
StreamBuilder๋ ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๊ฐ์งํ์ฌ UI๋ฅผ ์๋์ผ๋ก ๊ฐฑ์ ํฉ๋๋ค.
์ผ์ ๋ฐ์ดํฐ, WebSocket ํต์ , ํ์ด๋จธ ๋ฑ์ ํ์ฉ๋ฉ๋๋ค.
๐ง ์ฃผ์ ์์ฑ
์์ฑ | ์ค๋ช |
stream | ๊ตฌ๋ ํ Stream ๊ฐ์ฒด |
builder | Stream์์ ๋ฐํ๋ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก UI๋ฅผ ์์ฑ |
snapshot.connectionState | waiting, active, done ๋ฑ ์ํ ํ์ธ |
snapshot.hasData / hasError | ๋ฐ์ดํฐ ๋์ฐฉ ์ฌ๋ถ ๋ฐ ์๋ฌ ์ฌ๋ถ |
๐งช ๊ธฐ๋ณธ ์์
https://youtube.com/shorts/e8BHDkDwoKY?feature=share
class StreamDemo extends StatefulWidget {
@override
_StreamDemoState createState() => _StreamDemoState();
}
class _StreamDemoState extends State<StreamDemo> {
late Stream<int> _counterStream;
@override
void initState() {
super.initState();
_counterStream = Stream.periodic(Duration(seconds: 1), (count) => count).take(10);
}
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: _counterStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Text('์คํธ๋ฆผ ๋๊ธฐ ์ค...'));
} else if (snapshot.hasError) {
return Center(child: Text('์๋ฌ: ${snapshot.error}'));
} else if (snapshot.connectionState == ConnectionState.active) {
return Center(child: Text('์นด์ดํธ: ${snapshot.data}'));
} else {
return Center(child: Text('์คํธ๋ฆผ ์ข
๋ฃ'));
}
},
);
}
}
๐งฉ ์ค์ต ๊ณผ์
- Stream.periodic(Duration(seconds: 5), (_) => Random().nextInt(100))๋ก ๋๋ค ์ซ์ ํ์ : ์นด์ดํธ ๊ฐ์ด ๋๋ค์ผ๋ก ์ถ๋ ฅ
- https://youtube.com/shorts/pC3edBiMIZ8
- 0~100์ฌ์ด์ ๋๋ค ๊ฐ ๊น์ง ์นด์ดํธ
class _StreamDemoState extends State<StreamDemo> { late Stream<int> _counterStream; var num = Random().nextInt(100); // 0 ์ด์ 100 ๋ฏธ๋ง์ ์ ์ @override void initState() { super.initState(); _counterStream = Stream.periodic( Duration(seconds: 1), (count) => count, ).take(num); }
if (i == 3) throw Exception('์๋ฌ ๋ฐ์') ํํ๋ก ์๋ฌ ํ ์คํธ
๐น TabBar & TabBarView
โ ๊ฐ๋ ์ ๋ฆฌ
DefaultTabController๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , TabBar์ TabBarView๋ฅผ ์ฐ๊ณํ์ฌ ํญ UI๋ฅผ ๊ตฌํํฉ๋๋ค.
๐ง ์ฃผ์ ๊ตฌ์ฑ ์์
๊ตฌ์ฑ์์ | ์ค๋ช |
TabBar | ํญ ๋ฒํผ UI |
TabBarView | ํญ์ ๋ฐ๋ฅธ ์ฝํ ์ธ UI |
length | ํญ ๊ฐ์ |
TabController | ์๋ ์ ์ด์ ์ฌ์ฉ ๊ฐ๋ฅ (์ ํ ์ด๋ฒคํธ ๊ฐ์ง ๋ฑ) |
๐งช ๊ธฐ๋ณธ ์์
class TabDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text('ํญ ์์ '),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.home), text: 'ํ'),
Tab(icon: Icon(Icons.person), text: 'ํ๋กํ'),
Tab(icon: Icon(Icons.settings), text: '์ค์ '),
],
),
),
body: TabBarView(
children: [
Center(child: Text('ํ ํ๋ฉด')),
Center(child: Text('ํ๋กํ ํ๋ฉด')),
Center(child: Text('์ค์ ํ๋ฉด')),
],
),
),
);
}
}
๐งฉ ์ค์ต ๊ณผ์
- ํญ์ ์์ด์ฝ๊ณผ ํ ์คํธ๋ฅผ ์กฐํฉํ์ฌ ์ปค์คํฐ๋ง์ด์ง
- TabController๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ณ , ํญ ๋ณ๊ฒฝ ์ index๋ฅผ ์ถ๋ ฅํด๋ณด๊ธฐ
๐น PageView
โ ๊ฐ๋ ์ ๋ฆฌ
PageView๋ ์ฌ๋ฌ ํ์ด์ง๋ฅผ ์ค์์ดํ๋ก ์ ํํ ์ ์๋ ์์ ฏ์
๋๋ค.
PageController๋ฅผ ์ฌ์ฉํ๋ฉด ํ๋ก๊ทธ๋จ์ ์ผ๋ก ํ์ด์ง ์ ํ๋ ๊ฐ๋ฅํ๋ฉฐ, ํ์ด์ง ๋ณ๊ฒฝ ์ด๋ฒคํธ๋ ๊ฐ์งํ ์ ์์ต๋๋ค.
๐ง ์ฃผ์ ์์ฑ
์์ฑ | ์ค๋ช |
children | ํ์ด์ง๋ก ํ์ํ ์์ ฏ ๋ฆฌ์คํธ |
controller | PageController๋ฅผ ํตํด ์ด๊ธฐ ํ์ด์ง, ์ด๋ ์ ์ด |
onPageChanged | ํ์ด์ง ์ ํ ์ ์ฝ๋ฐฑ ์คํ |
๐งช ๊ธฐ๋ณธ ์์
class PageDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PageView(
children: [
Container(color: Colors.red),
Container(color: Colors.green),
Container(color: Colors.blue),
],
);
}
}
๐งฉ ์ค์ต ๊ณผ์
- PageController(initialPage: 1) ์ค์
- ํ์ด์ง ์ด๋ ๋ฒํผ ๊ตฌํ (controller.jumpToPage, animateToPage)
- onPageChanged๋ฅผ ํตํด ํ์ฌ ํ์ด์ง ์ธ๋ฑ์ค๋ฅผ ์ถ๋ ฅ
๐น ๋ณตํฉ UI ์ค์ต ํ๋ก์ ํธ
โจ ์ค์ต: ์ผ๊ธฐ์ฅ ์ฑ ํ๋ฉด ์ค๊ณ
- ๋ฉ์ธ ํ๋ฉด
- PageView๋ก ์๋ณ ๋ฌ๋ ฅ ํ๋ฉด์ ์ข์ฐ ์ค์์ดํ
- GridView๋ก ๋ฌ๋ ฅ ๊ตฌ์ฑ
- ์ผ๊ธฐ ์์ฑ ํ๋ฉด
- FutureBuilder๋ก ์ด๋ฏธ์ง ์ฒจ๋ถ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๊ตฌํ
- Future.delayed๋ก 2์ด ํ ์ด๋ฏธ์ง URL ๋ก๋ฉ, Image.network๋ก ํ์
- ์ผ์ ์ถ๊ฐ/์์ ํ๋ฉด
- TabBar ๋๋ PageView๋ก ๋ฐ๋ณต ์ค์ ํ๋ฉด ๊ตฌ์ฑ
- ์: ํญ "๋งค์ผ / ๋งค์ฃผ / ๋งค์" ์ ํ ์ ๊ฐ๊ธฐ ๋ค๋ฅธ ์ต์ ํ์
'Flutter + Dart > Flutter + Dart ๊ณต๋ถ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ฆฌ์คํธ๋ทฐ ๋ฐ ๊ทธ๋ฆฌ๋๋ทฐ ํ์ต, ์ํ ๊ด๋ฆฌ ์๊ฐ (StatefulWidget) (0) | 2025.05.14 |
---|---|
ํ๋ฉด ์ ํ (Navigator) ๊ธฐ์ด ํ์ต (0) | 2025.05.14 |
๊ธฐ๋ณธ ์์ ฏ ํ์ต ๋ฐ ์ฌ์ฉ์ ์ ๋ ฅ ์ฒ๋ฆฌ ๊ธฐ์ด (0) | 2025.05.13 |
์์ ฏ๊ณผ ๊ธฐ๋ณธ์ ์ธ ๋ ์ด์์ (0) | 2025.05.12 |
Flutter์์ ๋ฒํผ์ผ๋ก ํ๋ฉด ์ ํํ๋ ๋ฒ(๋ค๋ฅธ ํ์ผ์ ์๋ ํ๋ฉด์ผ๋ก ์ด๋) (0) | 2025.05.12 |