เปลี่ยนชนิดคีย์บอร์ด เป็นตัวเลข เมื่อต้องรับข้อมูลที่เป็นตัวเลขเท่านั้น
ใน new_trans.dart ตรงที่รับค่าจำนวนเงิน amount เพิ่มพารามิเตอร์ของ TextField(keyboardType: TextInputType.numberWithOptions(decimal: true),)
ให้เรียกฟังก์ชันเพิ่มรายการใหม่ เมื่อกดคีย์ Done หรือ Enter บนคีย์บอร์ด
ใน new_trans.dart เพิ่มพารามิเตอร์ของ TextField(onSubmitted: (_)=>addNewTrans(),)
สร้างฟังก์ชัน addNewTrans()
เนื่องจากมีหลายที่ ต้องเรียกใช้ฟังก์ชันในการเพิ่มรายการ เช่นเมื่อกดปุ่ม Done หรือ Enter จาก TextField() หรือเมื่อกดปุ่ม Add Item ถ้าสร้างเป็นฟังก์ชัน จะได้ไม่ต้องเขียนโค้ดซ้ำและสะดวกในการแก้ไขมากกว่าการใช้ฟังก์ชันไม่มีชื่อ
ใน new_trans.dart สร้างฟังก์ชัน void addNewTrans(){}
ถ้าตรวจสอบพบว่ามีข้อมูลอินพุทที่ถูกต้อง ก็ให้สร้างรายการใหม่ ถ้าไม่ถูกต้องเช่นตัวเลขเป็นค่าลบ หรือไม่มีชื่อรายการ ก็ไม่ต้องเพิ่ม
สังเกตว่าฟังก์ชันพอยเตอร์ของ onSubmitted: (){} ต้องมีพารามิเตอร 1 ตัว แต่เราไม่ได้ใช้งาน ก็ให้ใส่พารามิเตอร์ไม่มีชื่อแทน คือ (_){} และเนื่องจากเราเรียกใช้ฟังก์ชัน addNewTrans() เพียงคำสั่งเดียวใน {} ก็เขียนฟังก์ชันแบบสั้นแทนได้ คือ (_) => addNewTrans(),
ส่วนฟังก์ชันพอยเตอร์ของ onPressed: (){} ไม่มีพารามิเตอร์ ก็ใช้อันเดิม (){} และเรียกใช้ addNewTrans() เช่นเดียวกัน
import 'package:flutter/material.dart';
class NewTransaction extends StatelessWidget {
final Function addTrans;
final title = TextEditingController();
final amount = TextEditingController();
NewTransaction(this.addTrans);
void addNewTrans() {
if (title.text.isEmpty || double.parse(amount.text) <= 0) {
print('not added');
return;
}
addTrans(title.text, double.parse(amount.text));
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 5,
margin: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: title,
onSubmitted: (_) => addNewTrans(),
),
TextField(
decoration: InputDecoration(labelText: 'Amount'),
controller: amount,
keyboardType: TextInputType.numberWithOptions(decimal: true),
onSubmitted: (_) => addNewTrans(),
),
ElevatedButton(
onPressed: () {
print(title.text);
print(amount.text);
//addTrans(title.text, double.parse(amount.text));
addNewTrans();
},
child: Text('Add Item'),
),
],
),
);
}
}
ปรับจุดทศนิยม ให้เป็น 2 ตำแหน่ง
ใน list.dart ปรับพารามิเตอร์ของการแสดงผลตัวเลขเป็นแบบกำหนดจุดทศนิยม 2 ตำแหน่งด้วย amount.toStringAsFixed(2)
...
child: Text(
'${transactions[index].amount.toStringAsFixed(2)} บาท',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.purple),
),
...


ไปที่ main.dart เพิ่ม actions พารามิเตอร์ที่เป็น IconButton widget ให้กับ appBar
main.dart
...
appBar: AppBar(
title: Text('Expense App'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {},
),
],
),
...

class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {},
),
appBar: AppBar(
title: Text('Expense App'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {},
),
],
),
...

สร้าง Action Sheet NewTransaction()
เมื่อกดปุ่ม + หรือ FloatingActionButton ให้แสดงวิดเจ็ต NewTransaction()
ใน main.dart สร้างฟังก์ชัน startNewTrans() ให้แสดงวิดเจ็ต NewTransaction() มื่อกดปุ่ม + หรือ FloatingActionButton
ในฟังก์ชัน startNewTrans เรียกใช้ showModalBottomSheet() เพื่อสร้าง Action Sheet NewTransaction()
class MyHomePage extends StatelessWidget {
void startNewTrans(BuildContext ct) {
showModalBottomSheet(
context: ct,
builder: (_) {
return NewTransaction();
});
}
...
เรียกใช้ฟังก์ชัน startNewTrans() จาก onPressed: ของ IconButton + และ FloatingActionButton
main.dart
...
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: ()=> startNewTrans(context),
),
appBar: AppBar(
title: Text('Expense App'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: ()=> startNewTrans(context),
),
],
),
...
คราวนี้จะมีปัญหาตรงที่ เราไม่มีฟังก์ชันพอยเตอร์ ที่จะส่งไปยังวิดเจ็ต NewTransaction() เพราะอยู่คนละคลาสกัน ตอนนี้ยังหาวิธีทำไม่ได้ ต้องย้ายคลาส UserTransaction() ที่เป็น StatefulWidget ซึ่งสร้าง NewTransaction() และแสดงรายการ TransactionList() มาไว้ใน main.dart และทำให้ main.dart เป็น StatefulWidget แทน
ถ้าเรานำ UserTransaction() มาจัดการใน main.dart ที่เป็น StatefulWidget แล้ว เราก็ไม่ต้องการ UserTransaction() ที่เป็น StatefulWidget อีก
ใน main.dart กดขวาที่ StatelessWidget เลือก Refactor... เลือก Convert to StatefulWidget
...
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
void startNewTrans(BuildContext ct) {
showModalBottomSheet(
context: ct,
builder: (_) {
return NewTransaction();
});
}
...
ปรับแก้ main.dart ดังนี้
import 'package:expense/widgets/list.dart';
import 'package:expense/widgets/new_trans.dart';
import 'package:flutter/material.dart';
import 'models/trans.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Expense App',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
final List _userTrans = [
Transaction(
id: 't1',
title: 'bag',
amount: 99.5,
dt: DateTime.now(),
),
Transaction(
id: 't2',
title: 'paper',
amount: 59.5,
dt: DateTime.now(),
),
];
void _addNewTrans(String txTitle, double txAmount) {
final newTrans = Transaction(
id: DateTime.now().toString(),
title: txTitle,
amount: txAmount,
dt: DateTime.now(),
);
setState(() {
_userTrans.add(newTrans);
});
}
void startNewTrans(BuildContext ct) {
showModalBottomSheet(
context: ct,
builder: (_) {
return NewTransaction(_addNewTrans);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => startNewTrans(context),
),
appBar: AppBar(
title: Text('Expense App'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () => startNewTrans(context),
),
],
),
body: SingleChildScrollView(
child: Column(
//mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: double.infinity,
child: Card(
color: Colors.blue,
child: Text('Chart'),
elevation: 5,
),
),
//UserTransaction()
TransactionList(_userTrans),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
run app
กดปุ่ม + หรือ FloatingActionButton เพื่อแสดง Action Sheet NewTransaction
ใส่ข้อมูล กดปุ่ม Add Item
กดในที่ว่างๆภายนอก Action Sheet NewTransaction เพื่อปิด Action Sheet


ถึงแม้ว่าเราจะแสดง Action Sheet New Transaction ด้วยปุ่ม + หรือ FloatingActionButton แต่เมื่อดปุ่ม Add Item ก็ยังเพิ่มรายการไม่ได้ เพราะดูเหมือนว่าข้อมูลที่ใส่ผ่าน Action Sheet NewTransaction จะถูกรีเซตเมื่อกด TextField() อันใดอันหนึ่ง ทำให้ไม่ผ่านเงื่อนไขการเพิ่มรายการ ซึ่งต้องมีข้อมูลจากทั้งสอง TextField()
สาเหตุมาจาก NewTransaction เป็น StatelessWidget ทำให้เก็บค่าไม่ได้ ดังนั้นเราต้องทำให้ NewTransaction เป็น StatefulWidget เพื่อให้เก็บค่าได้
เปิดไฟล์ new_trans.dart กดขวาที่ StatelessWidget เลือก Refactor... เลือก Convert to StatefulWidget
สังเกตว่า ฟลัตเตอร์จะสร้าง widget ที่เชื่อมโยงระหว่างคลาส Widget กับคลาส State ทำให้เราเรียกใช้ตัวแปรจาก Widget ภายใน State ได้ แม้ว่าจะอยู่คนละคลาสกัน
...
class NewTransaction extends StatefulWidget {
final Function addTrans;
NewTransaction(this.addTrans);
@override
_NewTransactionState createState() => _NewTransactionState();
}
class _NewTransactionState extends State {
final title = TextEditingController();
final amount = TextEditingController();
void addNewTrans() {
if (title.text.isEmpty || double.parse(amount.text) <= 0) {
print('not added');
return;
}
widget.addTrans(title.text, double.parse(amount.text));
}
...
เมื่อกดปุ่ม Add Item และเพิ่มรายการได้เรียบร้อย ถ้าต้องการให้ปิด Action Sheet NewTransaction ไปด้วย ให้เพิ่มโค้ด Navigator.of(context).pop(); ต่อจาก widget.addTrans(title.text, double.parse(amount.text)); ดังนี้
new_trans.dart
...
widget.addTrans(title.text, double.parse(amount.text));
Navigator.of(context).pop();
...
run app
กดปุ่ม + หรือ FloatingActionButton เพื่อแสดง ActionSheet NewTransaction
ใส่ข้อมูล กดปุ่ม Add Item หรือกดปุ่ม Done หรือ Enter บนคีย์บอร์ด


2020. mnet50.com