Flutter Project : สร้างแอปเครื่องคิดเลขง่ายๆด้วย Flutter
สวัสดีครับ บล็อกนี้เป็นบล็อกที่ทำค้างไว้นาน เป็นเรื่องเกี่ยกวับการทำแอปเครื่องคิดเลขแบบง่ายๆ ผมเขียนไว้ตอนหัดเขียน Flutter ใหม่ๆ แต่ไหนๆก็เขียนแล้วก็เลยมาต่อให้จบ ซึ่งแอปเครื่องคิดเลขมักจะเป็นโจทย์ classic สำหรับตอนเราหัดเขียนโปรแรกม จะว่าไปแล้วผมเองก็เขียนแอปเครื่องคิดเลขนี้มาหลายครั้งอยู่เหมือนกัน เขียนใน Windows Form , Windows 10 App , Android แล้วก็มา Flutter
เริ่มต้น
ประเด็นหลักคือ เรียนรู้เกี่ยวกับการทำ layout ใน Flutter เลยจะไม่เน้นเรื่องของฟังชันก์นะ
โดยจะลอกหน้าตา แอปเครื่องคิดเลขใน Windows 10 แบบนี้
New Flutter Project กันเลย
import 'package:flutter/material.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: 'Calculator', theme: ThemeData( primarySwatch: Colors.blueGrey, ), home: MyCalculatorPage(title: 'Calculator'), ); } } class MyCalculatorPage extends StatefulWidget { MyCalculatorPage({Key key, this.title}) : super(key: key); final String title; @override _MyCalculatorPageState createState() => _MyCalculatorPageState(); } class _MyCalculatorPageState extends State<MyCalculatorPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'Calculator',style: TextStyle(fontSize: 28), ), Text( 'Hello',style: TextStyle(fontSize: 18), ), ], ), ) ); } }
ทำส่วนแสดงคำตอบ เป็นช่องว่างด้านบน
class _MyCalculatorPageState extends State<MyCalculatorPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( children: <Widget>[ buildAnswerWidget(), ], ), ); } Widget buildAnswerWidget() { return Container( padding: EdgeInsets.all(16), constraints: BoxConstraints.expand(height: 180), color: Color(0xffecf0f1), child: Align( alignment: Alignment.bottomRight, child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[ Text("12 +", style: TextStyle(fontSize: 18)), Text("8", style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold)) ]))); } }
ทำส่วนปุ่มตัวเลข ลองทำแค่แถวเดียวก่อน
Widget buildNumPadWidget() { return Container( color: Color(0xffecf0f1), child: Column( children: <Widget>[ Row(children: <Widget>[ Expanded( child: Container( color: Colors.white, height: 100, child: Center( child: Text("1", style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold))))), Expanded( child: Container( color: Colors.white, height: 100, child: Center( child: Text("2", style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold))))), Expanded( child: Container( color: Colors.white, height: 100, child: Center( child: Text("3", style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold))))), ]) ], )); }
เพิ่ม NumPad ต่อจากช่องคำตอบ
@override Widget build(BuildContext context) { ... body: Column( children: <Widget>[ buildAnswerWidget(), buildNumPadWidget() ], ), ); }
ทำแถวของปุ่มตัวเลขเพิ่ม
Widget buildNumPadWidget() { return Container( color: Color(0xffecf0f1), child: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Row(children: <Widget>[ buildNumberButton("7"), buildNumberButton("8"), buildNumberButton("9"), ]), Row(children: <Widget>[ buildNumberButton("4"), buildNumberButton("5"), buildNumberButton("6"), ]), Row(children: <Widget>[ buildNumberButton("1"), buildNumberButton("2"), buildNumberButton("3"), ]), ], )); } Expanded buildNumberButton(String str) { return Expanded( child: Container( color: Colors.white, height: 100, child: Center( child: Text(str, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold))))); }
เพิ่มปุ่ม บวกลบคูณหารและปุ่มสัญลักษณ์พิเศษต่างๆ
Widget buildNumPadWidget() { return Container( color: Color(0xffecf0f1), child: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Row(children: <Widget>[ buildNumberButton("CE"), buildNumberButton("C"), buildNumberButton("⌫"), buildNumberButton("÷"), ]), Row(children: <Widget>[ buildNumberButton("7"), buildNumberButton("8"), buildNumberButton("9"), buildNumberButton("×"), ]), Row(children: <Widget>[ buildNumberButton("4"), buildNumberButton("5"), buildNumberButton("6"), buildNumberButton("-"), ]), Row(children: <Widget>[ buildNumberButton("1"), buildNumberButton("2"), buildNumberButton("3"), buildNumberButton("+"), ]), Row(children: <Widget>[ buildNumberButton("±"), buildNumberButton("0"), buildNumberButton("."), buildNumberButton("="), ]), ], )); } Expanded buildNumberButton(String str) { return Expanded( child: Container( margin: EdgeInsets.all(2), color: Colors.white, height: 70, child: Center( child: Text(str, style: TextStyle( fontSize: 32, fontWeight: FontWeight.bold))))); }
ใส่สีของปุ่มให้แตกต่างกัน ถ้าเป้นปุ่มตัวเลขจะพืนหลังต่างจากปุ่มอื่นๆ
Expanded buildNumberButton(String str, {bool numberButton = true}) { if (numberButton) { return Expanded( child: Container( margin: EdgeInsets.all(1), color: Colors.white, height: 70, child: Center( child: Text(str, style: TextStyle( fontSize: 32, fontWeight: FontWeight.bold))))); } else { return Expanded( child: Container( margin: EdgeInsets.all(1), color: Color(0xffecf0f1), height: 70, child: Center( child: Text(str, style: TextStyle( fontSize: 28))))); } }
ต่อมาทำให้ปุ่มสามารถกดตัวเลขแล้วไปแสดงที่หน้าจอคำตอบได้
ก็ประกาศตัวแปรใน state
String answer; @override void initState() { answer = "0"; super.initState(); }
เอาค่าไปใส่ที่ Text
Widget buildAnswerWidget() { ... Text(answer, style: TextStyle(fontSize: 48, fontWeight: FontWeight.bold)) ])))); }
ที่ปุ่มตัวเลข เพิ่ม parameter เพื่อรับ function ว่ากดแล้วให้ทำอะไร
Widget buildNumberButton(String str, { @required Function()onTap, bool numberButton = true}) { Widget widget; if (numberButton) { widget = GestureDetector(onTap: onTap, ... ); } else { widget = GestureDetector(onTap: onTap, ... ); } return Expanded(child: widget); }
เพิ่ม onTap ให้ปุ่ม ปุ่มกดแล้วให้เพิ่มเลขที่ไปช่องคำตอบ
Widget buildNumPadWidget() { ... Row(children: <Widget>[ buildNumberButton("1", onTap: () { addNumberToAnswer(1);}), buildNumberButton("2", onTap: () { addNumberToAnswer(2);}), buildNumberButton("3", onTap: () { addNumberToAnswer(3);}), buildNumberButton("+", numberButton: false, onTap: () {}), ]), ... }
ที่ method สำหรับกดตัวเลขก็เช็คเงื่อนไขเล็กน้อย เช่น ถ้ามีค่าเป็น 0 จะ 0 ซ้อนกันไม่ได้
void addNumberToAnswer(int number) { setState(() { if(number == 0 && answer == "0"){ // Not do anything. } else if(number != 0 && answer == "0"){ answer = number.toString(); } else{ answer += number.toString(); } }); }
จากนั้นก็เพิ่ม method อื่นๆ ให้ครบ ซึ่งไม่น่าจะแยกแล้ว เช่น ปุ่ม backspace ลบตัวเลขตัวสุดท้ายออก
void removeAnswerLast() { if (answer == "0") { // Not do anything. } else { setState(() { if(answer.length > 1) { answer = answer.substring(0, answer.length - 1); }else{ answer = "0"; } }); } }
buildNumberButton("⌫", numberButton: false, onTap: () { removeAnswerLast(); }),
ปรับแต่งให้สวยงาม
โค้ดแบบเต็มๆ ดูกันที่ Github เลย
https://github.com/benznest/calculator-app-flutter