์ง๋ ์ฃผ์ ์ด์ด Udemy ๊ฐ์์์ ๋ง๋ค์ด๋ณธ ๊ฐ์ธ ๊ฐ๊ณ๋ถ ์ดํ์ ๋ฆฌ๋ทฐํด๋ณด๋ ค๊ณ ํ๋ค. ์ง๋ ์ฃผ์๋ ๋ฏธ์์ฑ์ด์๋๋ฐ ๊ฐ์๋ฅผ ๋ ํํ๋ฉด์ ๋ค ์์ฑํ๋ค. ๋ญ๊ฐ ์ด์ง ์ด์คํ์ง๋ง ์ด๋์ ๋ ํผ์์ ๊ตฌํํ ์ ์์ ๊ฒ ๊ฐ๋ค. ์ฃผ๋ง์ ๋จ์ 50%์ ๊ฐ์๋ฅผ ๋ค ๋ฃ๊ณ ํผ์ ๋ง๋ค์ด๋ณด๋ฉด์ ๋ณต์ตํด๋ณด๋ ค๊ณ ํ๋ค. ์๋ฌดํผ 3์ฃผ์ฐจ ๋ชจ๊ฐ์ฝ ์คํํธ!
์ฐ์ ์ ์ฒด์ ์ธ UI๋ ์๋์ ๊ฐ๋ค.
์๋น ํญ๋ชฉ์ด ์๋ ๊ฒฝ์ฐ zZ์ ์ด๋ฏธ์ง๋ฅผ ์ถ๋ ฅํ๋ฉฐ ์๋จ์๋ Chart๋ฅผ ํ์ธํ ์ ์๋ Switch์ ์๋ก์ด ์๋น ํญ๋ชฉ์ ๋ฑ๋กํ ์ ์๋ Button์ด AppBar์ ์ฐ์ธก๊ณผ Home์ ํ๋จ์ ์กด์ฌํ๋ค. Switch๋ฅผ Onํ๋ฉด ์ค๋์ ๊ธฐ์ค์ผ๋ก ์ง๋ 1์ฃผ์ผ ๊ฐ์ ์๋น๊ธ์ก์ด ์ถ๋ ฅ๋๋ค. ๊ธ์ ์์ฑํ๊ณ ์๋ ์ค๋์ ๋ชฉ์์ผ์ด๋ฏ๋ก Thursday.
์ฝ๋๋ฅผ ์ดํด๋ณด์. ๋ค ์ง์ฐ๊ณ main.dart์์ Switch์ FloatingButton๊ณผ ๊ด๋ จ๋ Widget๋ค๋ง ๋จ๊ฒจ๋จ๋ค.
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
final isLandscape = MediaQuery.of(context).orientation == Orientation.landscape;
final appBar = AppBar(
title: Text('๊ฐ๊ณ๋ถ'),
actions: <Widget>[
Builder(
builder: (context) => IconButton(
icon: Icon(Icons.add),
onPressed: () => _startAddNewTransaction(context),
),
),
],
);
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.stretch,
// Column์ด๋ฏ๋ก main์ y์ถ (์์์ ์๋)
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Show Chart'),
Switch(
value: _showChart,
onChanged: (val) {
setState(() {
_showChart = val;
});
}),
],
),
_showChart
? Container(
height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height -
MediaQuery.of(context).padding.top) *
0.3,
child: Chart(_recentTransactions),
)
: Container(
height: (MediaQuery.of(context).size.height -
appBar.preferredSize.height -
MediaQuery.of(context).padding.top) *
0.7,
child:
TransactionList(_userTransactions, _deleteTransaction)),
],
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _startAddNewTransaction(context),
),
),
);
}
}
Switch๊ฐ ์คํ๋๋ฉด(onChanged) setState๋ฅผ ์ด์ฉํ์ฌ _showChart์ ๊ฐ์ ๋ณ๊ฒฝํ๋ค. ํ๋จ์ ์ผํญ์ฐ์ฐ์ ์ฝ๋๊ฐ ์๋๋ฐ ์ด ๋ถ๋ถ์ ๋ณด์. _showChart๊ฐ true์ธ ๊ฒฝ์ฐ Chart๋ฅผ ํธ์ถํ๋ฉฐ Chart๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ TransactionLIst๋ฅผ ํธ์ถํ๋ฉฐ ์๋น ๊ธฐ๋ก๋ค์ ๋ณด์ฌ์ค๋ค. ๊ทธ๋ ๋ค๋ฉด Chart๋ฅผ ๊ตฌ์ฑํ๋ ChartBar๋ ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์์๊น?
import 'package:flutter/material.dart';
class ChartBar extends StatelessWidget {
final String label;
final double spendingAmount;
final double spendingPctOfTotal;
ChartBar(this.label, this.spendingAmount, this.spendingPctOfTotal);
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (ctx, constraints) {
return Column(
children: <Widget>[
Container(
height: constraints.maxHeight * 0.15,
child: FittedBox(
child: Text('${spendingAmount.toStringAsFixed(0)}์'),
),
),
SizedBox(
height: constraints.maxHeight * 0.05,
),
Container(
height: constraints.maxHeight * 0.6,
width: 10,
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1.0),
color: Color.fromRGBO(220, 220, 220, 1),
borderRadius: BorderRadius.circular(10),
),
),
FractionallySizedBox(
heightFactor: spendingPctOfTotal,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(10),
),
),
),
],
),
),
SizedBox(
height: constraints.maxHeight * 0.05,
),
Container(
height: constraints.maxHeight * 0.15,
child: FittedBox(
child: Text(label),
),
),
],
);
});
}
}
ChartBar์ ๊ฒฝ์ฐ LayoutBuilder๋ก ๊ตฌํํ๋ค. LayoutBuilder๋ ๋ฐ์ํ ๋ ์ด์์์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๋ถ๋ชจ์ ๋ ์ด์์ ํฌ๊ธฐ์ ๋ฐ๋ผ ์์๋ค์ ๋ ์ด์์์ ์ ํ๋ค. builer์ ์ธ์๋ก ctx, constraints๊ฐ ๋์ด๊ฐ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ constraints๊ฐ ๋ถ๋ชจ์ ํฌ๊ธฐ๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค. ์ดํ Column๋ค์ ์ดํด๋ณด๋ฉด constriants.maxHeight๊ณผ ๊ฐ์ด ๋ถ๋ชจ์ ํฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋ฐ์์ผ๋ก ๊ตฌํํ๋ฉด ๋ค์ํ ์ฌ์ด์ฆ์ ํธ๋ํฐ์์ ๊ฐ์ ๋ชจ์ต์ UI๋ฅผ ๊ตฌํํ ์ ์๊ฒ ๋๋ค. ์ข ๋ ์์ธํ ์ค๋ช ์ ์ํด ๊ณต์ ๋ฌธ์๋ฅผ ๋ค๊ณ ์๋ดค๋ค.
https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
์ค๊ฐ์ Stack์ด ์ฒ์ ๋ฑ์ฅํ๋๋ฐ Widget๋ค์ ๊ฒน์น๊ฒ ๋ง๋ค ์ ์๋ค. ์ด๋ฅผ ์ด๋์ ์ฌ์ฉํ๋๋ฉด ์๋น ํญ๋ชฉ์ด ์ถ๊ฐ๋ ๋ ๊ฐ๊ฐ์ ChartBar์ ์ง์ ํ ์์ผ๋ก ChartBar๊ฐ ์ถ๊ฐ๋๋๋ฐ ์ด ๋ถ๋ถ์ด ๋ ๊ฐ์ Widget์ด ๊ฒน์ณ์ง ๋ถ๋ถ์ด๋ค. ๋ง๋ก ์ค๋ช ํ์๋ฉด ์ด๋ ค์ฐ๋ ์ฌ์ง์ ๋ณด์.
์ด๋ฐ ์์ผ๋ก Grey์ ChartBar์ Blue์ ChartBar๊ฐ ๊ฒน์น๊ฒ ๋๋๋ฐ ์ด๋ฅผ ์ํด Stack๋ฅผ ์ฌ์ฉํ๋ค.
๋ค์์ new_transaction.dart. ํ์ผ๋ช ๊ทธ๋๋ก ์๋ก์ด ํธ๋์ญ์ ์ด ์ถ๊ฐ๋ ๋์ Widget์ด๋ค. ์ง๋ ์ฃผ์ ๊ฑฐ์ ์ ์ฌํ์ง๋ง ์ถ๊ฐ๋ ๊ฒ์ด ์๋ค๋ฉด ๋ ์ง ์ ํ๊ณผ ์ ํํ ๋ ์ง๋ฅผ ๋ณด์ฌ์ค๋ค๋ ์ ์ด๋ค. ์ด ๋ถ๋ถ์ DatePicket๋ฅผ ์ฌ์ฉํ๋ค.
์ฝ๋๊ฐ ์ข ๊ธธ๊ธดํ๋ฐ ์ฒ์ฒํ ์ดํด๋ณด์.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class NewTransaction extends StatefulWidget {
final Function addTx;
NewTransaction(this.addTx);
@override
State<NewTransaction> createState() => _NewTransactionState();
}
class _NewTransactionState extends State<NewTransaction> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime _selectedDate = DateTime.now();
void _submitData() {
final enteredTitle = _titleController.text;
final enteredAmount = double.parse(_amountController.text);
if (enteredTitle.isEmpty || enteredAmount <= 0) {
return;
}
widget.addTx(
enteredTitle,
enteredAmount,
_selectedDate,
);
Navigator.of(context).pop();
}
void _presentDatePicket() {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime.now(),
).then(
(pickedDate) {
if (pickedDate == null) {
return;
}
setState(
() {
_selectedDate = pickedDate;
},
);
},
);
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Card(
elevation: 3,
child: Container(
padding: EdgeInsets.only(top : 10, left : 10, right : 10, bottom : MediaQuery.of(context).viewInsets.bottom + 10),
child: Container(
padding: EdgeInsets.only(
top: 10,
left: 10,
right: 10,
bottom: MediaQuery.of(context).viewInsets.bottom + 10,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'ํ๋ชฉ๋ช
'),
// 1๋ฒ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ์๋ 1์ค์ฒ๋ผ ์ฝ๋๋ฅผ ์์ฑ
// onChanged: (val) => titleInput = val,
controller: _titleController,
onSubmitted: (_) => _submitData,
),
TextField(
decoration: InputDecoration(labelText: '๊ฐ๊ฒฉ'),
// 1๋ฒ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ์๋ 1์ค์ฒ๋ผ ์ฝ๋๋ฅผ ์์ฑ
// onChanged: (val) => amountInput = val,
controller: _amountController,
onSubmitted: (_) => _submitData,
),
Row(
children: <Widget>[
Expanded(
child: Text(
_selectedDate == null
? "Picked Date"
: '์ ํ๋ ๋ ์ง : ${DateFormat.yMd().format(_selectedDate)}',
),
),
FlatButton(
textColor: Theme.of(context).primaryColor,
child: Text("๋ ์ง ์ ํ"),
onPressed: _presentDatePicket,
),
],
),
RaisedButton(
child: Text("ํญ๋ชฉ ์ถ๊ฐ"),
color: Colors.blueAccent,
textColor: Colors.white,
onPressed: _submitData,
),
],
),
),
),
),
);
}
}
_presentDataPicket์ด ๋ ์ง ์ ํ ๋ฒํผ์ ๋๋ ์ ๋ ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ์ด๋ค. ์ฐ์ ํด๋น ๋ฒํผ์ ํด๋ฆญํ์ ๋ DatePicket๊ฐ ์คํ๋๋ฉด์ ๋ฌ๋ ฅ์ด ๋์ด์ง๋๋ฐ ์ด ๋ ์ด๊ธฐ ๋ ์ง๋ฅผ ๊ณ ๋ฅผ ์ ์๋ค. ์ด ๋ถ๋ถ์ด ๋ฐ๋ก initialDate๊ฐ ๋๋ค. FristDate๋ ์ ํ ๊ฐ๋ฅํ ์ต์(?) ๋ ์ง, lastDate๋ ์ ํ ๊ฐ๋ฅํ ์ต๋(?) ๋ ์ง์ด๋ค. .then์ ์ด์ฉํ์ฌ ์ ํ๋ ๋ ์ง๊ฐ ์๋ ๊ฒฝ์ฐ ์ค๋ ๋ ์ง๋ฅผ ๊ทธ๋๋ก returnํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ ํด๋น ๋ ์ง๋ฅผ returnํ๋ค. ์ด๋ ๊ฒ ๋ ์ง๋ฅผ ์ ํํ๊ณ ํ๋จ์ ํญ๋ชฉ ์ถ๊ฐ ๋ฒํผ์ ํด๋ฆญํ๋ฉด _submitData๊ฐ ์คํ๋๋ฉด์ transactionList์ ์๋ก์ด ์๋น ํญ๋ชฉ์ด ์ถ๊ฐ๋๋ค.
๋ญ๊ฐ ๊ธํ๊ฒ ๋ง๋ฌด๋ฆฌํ ๋๋์ด ์์ง๋ง ์ค์ ๋ก ๋ ํผ์์ ๋ค์ ํ ๋ฒ ์ง๋ณด๋ฉด์ ๋ค์ ํ๋ฒ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค. ํ์คํ Flutter๋ฅผ ์ฒ์ ์ ํ์ ๋๋ณด๋ค๋ ๋ง์ด ๋์ ๊ฒ ๊ฐ์ง๋ง ์์ง ๊ฐ ๊ธธ์ด ๋จผ ๊ฒ ๊ฐ๋ค.
'โน๏ธ ๋ผ์ดํ > 2021 ์ฌ๋ฆ๋ฐฉํ ๋ชจ๊ฐ์ฝ(๊ฐ์ธ)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์ฝ๋ ํ๊ตฌ๋ง 2] 6์ฃผ์ฐจ - Flutter : Shop App 3 (0) | 2021.08.10 |
---|---|
[์ฝ๋ ํ๊ตฌ๋ง 2] 5์ฃผ์ฐจ - Flutter : Shop App 2 (0) | 2021.08.10 |
[์ฝ๋ ํ๊ตฌ๋ง 2] 4์ฃผ์ฐจ - Flutter : Shop App 1 (0) | 2021.07.28 |
[์ฝ๋ ํ๊ตฌ๋ง 2] 2์ฃผ์ฐจ - Flutter : Personal Expense App (0) | 2021.07.15 |
[์ฝ๋ ํ๊ตฌ๋ง 2] 1์ฃผ์ฐจ - Flutter : Quiz App (0) | 2021.07.07 |