5.設定home page
設定lib\view\home_page.dart¶
Code¶
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive/hive.dart';
import '../models/expense.dart';
import '../bloc/transaction_bloc.dart';
bool isIncome = false;
class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget {
@override
Size get preferredSize => Size.fromHeight(150.0); // Set AppBar height here
@override
Widget build(BuildContext context) {
return AppBar(
toolbarHeight: 150,
title: Text('Expense',style: TextStyle(color: Colors.white,fontSize:40,letterSpacing: 0.5),textAlign: TextAlign.center,),
centerTitle: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.vertical(bottom: Radius.circular(10))),
backgroundColor: Color.fromARGB(255, 130, 104, 183),
);
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:MyCustomAppBar(),
body: _buildBody(context),
floatingActionButton: IconButton(
hoverColor: Colors.deepPurple[50],
icon: Icon(Icons.add, size: 80, color: Colors.deepPurple[200]),
onPressed: () => _showAddTransactionDialog(context),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: BottomAppBar(
color: Colors.white70,
shape: CircularNotchedRectangle(),
notchMargin: 10,
child: _buildFloatingActionButtons(context),
),
);
}
Widget _buildBody(BuildContext context) {
return BlocBuilder<TransactionBloc, TransactionState>(
builder: (context, state) {
if (state is TransactionsLoaded) {
return _buildTransactionList(state);
}
return const Center(child: CircularProgressIndicator());
},
);
}
Widget _buildTransactionList(TransactionsLoaded state) {
return ListView.builder(
itemCount: state.transactions.length,
itemBuilder: (context, index) {
final trans = state.transactions[index];
return Card(
color: Color.fromARGB(179, 232, 216, 251),
child: ListTile(
title: Text(trans.title, style: TextStyle(fontSize: 30, color: Colors.deepPurple[300], fontWeight: FontWeight.bold),textAlign: TextAlign.left,),
subtitle: Text("NT:${trans.amount.toInt().toString()}元",style: TextStyle(fontSize: 20, color: Colors.black87,letterSpacing: 2 )),
trailing: IconButton(
icon: const Icon(Icons.delete,size: 30,),
onPressed: () {
BlocProvider.of<TransactionBloc>(context).add(
DeleteTransaction(trans),
);
},
),
),
);
},
);
}
Widget _buildFloatingActionButtons(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
BlocBuilder<TransactionBloc, TransactionState>(
builder: (context, state) => IconButton(
onPressed: () {
BlocProvider.of<TransactionBloc>(context).add(
ClearTransactions(),
);
},
icon: Icon(Icons.delete, size: 30),
),
),
IconButton(
onPressed: () {
final transactionBox = Hive.box<Transaction>('transactions');
final totalAmount = (transactionBox.values.isEmpty ||
transactionBox.values.map((t) => t.amount).reduce((a, b) => a + b) == 0.0)
? "沒錢啦QAQ"
: transactionBox.values.map((t) => t.amount).reduce((a, b) => a + b).toString();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Total Amount',
style: TextStyle(fontSize: 30, color: Colors.black54, fontWeight: FontWeight.bold)),
content: Text('餘額剩下: $totalAmount',
style: TextStyle(color: Colors.deepPurple[300], fontSize: 15.0)),
actions: [
ElevatedButton(
child: Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: Icon(Icons.face, size: 30),
)
],
);
}
void _showAddTransactionDialog(BuildContext context) {
final titleController = TextEditingController();
final amountController = TextEditingController();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('新增收支紀錄',
style: TextStyle(fontSize: 30, color: Colors.black54, fontWeight: FontWeight.bold)),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: titleController,
decoration: InputDecoration(labelText: '名稱'),
),
TextField(
controller: amountController,
decoration: InputDecoration(labelText: '金額'),
keyboardType: TextInputType.number,
),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return SwitchListTile(
title: Text(isIncome ? '收入' : '支出'),
value: isIncome,
onChanged: (bool value) {
setState(() {
isIncome = value;
});
},
);
},
),
],
),
actions: [
TextButton(
child: Text('取消'),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child: Text('確定'),
onPressed: () => int.tryParse(amountController.text) != null ? _addTransaction(context, titleController, amountController) : null,
),
],
);
},
);
}
void _addTransaction(BuildContext context, TextEditingController titleController, TextEditingController amountController) {
final transactions = Hive.box<Transaction>('transactions');
final title = titleController.text;
final amount = int.parse(amountController.text) * (isIncome ? 1 : -1);
final key = transactions.values.length;
final newTransaction = Transaction(title: title, amount: amount.toDouble(), key: key);
BlocProvider.of<TransactionBloc>(context).add(
AddTransaction(newTransaction),
);
Navigator.of(context).pop();
}
}
解釋¶
這段代碼是一個Flutter應用的首頁界面,其中包括自定義的AppBar和主要內容部分。讓我們簡要解釋一下其中的關鍵部分:
自定義AppBar¶
這段代碼是一個使用Flutter框架構建的簡單應用程序,用於記錄支出和收入。讓我們來簡要解釋一下每個部分:
-
導入包和依賴項:代碼一開始導入了一些Flutter和其他必要的包和依賴項,例如
flutter/material.dart和flutter_bloc/flutter_bloc.dart。這些包提供了構建Flutter應用所需的基本工具和功能。 -
自定義AppBar:
MyCustomAppBar類創建了一個自定義的AppBar小部件。它設置了標題、樣式和背景顏色。 -
HomePage:
HomePage類定義了應用程序的主頁。它包含一個自定義的AppBar、一個顯示交易列表的主體部分和兩個浮動操作按鈕。主體部分使用BlocBuilder來構建交易列表,根據TransactionBloc的狀態來呈現不同的UI。 -
交易列表:
_buildTransactionList函數構建了一個交易列表,使用了ListView.builder來動態構建列表項。每個列表項都是一個Card小部件,顯示交易的標題、金額和一個刪除按鈕。當用戶點擊刪除按鈕時,會觸發刪除該交易的操作。 -
浮動操作按鈕:
_buildFloatingActionButtons函數構建了兩個浮動操作按鈕。一個按鈕用於清除所有交易記錄,另一個按鈕用於顯示當前所有交易的總金額。 -
添加交易對話框:
_showAddTransactionDialog函數用於顯示一個對話框,允許用戶輸入新的交易信息,包括交易的名稱、金額和類型(收入或支出)。用戶輸入完成後,可以點擊“確定”按鈕來添加交易記錄。 -
添加交易:
_addTransaction函數負責將用戶輸入的新交易信息添加到交易列表中,並使用BlocProvider來向TransactionBloc發送添加交易的事件。
這個應用程序使用了Flutter框架的一些核心概念,如小部件、狀態管理(使用Bloc)和對話框。通過這些功能,用戶可以方便地記錄和管理他們的收支情況。
分段解釋¶
_buildBody¶
這段代碼定義了一個名為 _buildBody 的函數,其作用是根據 TransactionBloc 的狀態構建對應的 UI。讓我們逐步解釋:
-
BlocBuilder<TransactionBloc, TransactionState>:這是 Flutter Bloc 庫提供的一個小部件,用於構建基於 BLoC(Business Logic Component)模式的 UI。它會根據給定的TransactionBloc和TransactionState動態地構建 UI。 -
builder方法:這是BlocBuilder的一個參數,是一個回調函數。它接收兩個參數:context和state。context表示當前的 BuildContext,而state則表示由TransactionBloc發布的最新狀態。 -
if (state is TransactionsLoaded):這是一個條件語句,用於檢查當前的狀態是否為TransactionsLoaded類型。如果是,表示已經成功加載了交易數據,那麽就調用_buildTransactionList(state)函數來構建交易列表的 UI。 -
return const Center(child: CircularProgressIndicator()):如果當前狀態不是TransactionsLoaded,則返回一個圓形進度指示器,以提示用戶正在加載數據。
這段代碼的作用是根據 TransactionBloc 的狀態動態構建 UI,當交易數據加載完成時,顯示交易列表,否則顯示加載指示器。
_buildTransactionList¶
這段代碼定義了一個名為 _buildTransactionList 的小部件函數,它的作用是根據傳入的 TransactionsLoaded 狀態構建交易列表的 UI。讓我們來逐步解釋:
-
ListView.builder:這是一個 Flutter 提供的小部件,用於構建動態列表。它會根據itemCount參數確定列表的長度,並根據itemBuilder參數動態構建列表項。 -
itemCount: state.transactions.length:這里使用了傳入的TransactionsLoaded狀態中的transactions屬性的長度作為列表項的數量。 -
itemBuilder: (context, index):這是一個回調函數,用於構建每個列表項的 UI。它接收兩個參數:context表示當前的 BuildContext,index表示當前要構建的列表項的索引。 -
final trans = state.transactions[index]:這行代碼從state.transactions中獲取了特定索引處的交易數據。 -
Card和ListTile:這兩個小部件分別用於顯示列表項的卡片樣式和內容。ListTile可以包含標題、副標題和尾部圖標。 -
title和subtitle:分別用於顯示交易的標題和金額。交易標題的樣式設置了字體大小、顏色和粗細,而金額的樣式設置了字體大小和顏色。 -
trailing:這是列表項的尾部圖標,是一個刪除按鈕,用戶可以點擊它來刪除對應的交易。
這段代碼的作用是根據傳入的 TransactionsLoaded 狀態構建一個交易列表,每個列表項都顯示交易的標題、金額和一個刪除按鈕。
_buildFloatingActionButtons¶
這段代碼定義了一個名為 _buildFloatingActionButtons 的小部件函數,其作用是構建浮動操作按鈕的 UI。讓我們逐步解釋:
-
Row小部件:這是一個水平排列子部件的容器,其中的子部件將會水平排列顯示。 -
mainAxisAlignment: MainAxisAlignment.spaceEvenly:這是設置子部件在水平方向上的對齊方式,此處設置為均勻分布,使得子部件在水平方向上均勻分布,間隔相等。 -
children:這是一個子部件的列表,包含了兩個浮動操作按鈕。 -
BlocBuilder<TransactionBloc, TransactionState>:這是一個用於根據 BLoC 的狀態構建 UI 的小部件。在這里,它用於構建一個刪除所有交易記錄的浮動操作按鈕。根據TransactionBloc的狀態,它會決定按鈕是否可見。 -
IconButton:這是一個包含圖標的按鈕,點擊後會執行相應的操作。這里的按鈕用於清除所有交易記錄。當點擊按鈕時,會向TransactionBloc發送一個ClearTransactions事件,以清除所有交易記錄。 -
第二個
IconButton:這是另一個浮動操作按鈕,用於顯示當前所有交易的總金額。點擊按鈕後,會顯示一個對話框,其中包含了所有交易的總金額信息。 -
showDialog:這是一個用於顯示對話框的函數,它接收一個builder參數,用於構建對話框的 UI。在這里,它顯示了一個標題為 “Total Amount” 的對話框,內容為當前所有交易的總金額信息。
這段代碼的作用是構建兩個浮動操作按鈕,一個用於清除所有交易記錄,另一個用於顯示當前所有交易的總金額。
_showAddTransactionDialog¶
這段代碼定義了一個名為 _showAddTransactionDialog 的函數,其作用是顯示一個對話框,允許用戶輸入新的交易信息。讓我們來逐步解釋:
-
showDialog:這是一個 Flutter 提供的函數,用於顯示對話框。它接收一個builder參數,用於構建對話框的 UI。 -
builder參數:這是一個回調函數,用於構建對話框的內容。它接收一個context參數,表示當前的上下文環境。 -
AlertDialog:這是一個 Material 風格的對話框,顯示在屏幕中央,包含了標題、內容和操作按鈕。 -
Text:這是用於顯示文本的小部件,可以設置文本的樣式、大小和顏色。 -
TextField:這是一個文本輸入框,用於接收用戶的輸入。在這里,分別用於輸入交易的名稱和金額。 -
StatefulBuilder:這是一個狀態管理的小部件,用於管理交易類型的切換。它可以根據用戶的操作來更新 UI。 -
SwitchListTile:這是一個帶有開關的列表瓦片,用於切換不同的選項。在這里,用於切換交易類型(收入或支出)。 -
TextButton:這是一個文本按鈕,用於執行相應的操作。在這里,分別用於取消輸入和確認輸入。 -
_addTransaction函數:當用戶點擊確認按鈕後,會調用_addTransaction函數來添加新的交易記錄。
這段代碼的作用是顯示一個對話框,允許用戶輸入新的交易信息,包括名稱、金額和類型(收入或支出)。
_addTransaction¶
這段代碼是一個名為 _addTransaction 的函數,它的作用是將用戶輸入的交易信息添加到交易列表中。讓我們逐步解釋:
-
Hive.box<Transaction>('transactions'):這是一個使用 Hive 數據庫的操作,用於獲取名為 ‘transactions’ 的數據存儲盒子。Hive 是一個輕量級的本地數據庫,用於在 Flutter 應用程序中存儲數據。 -
final title = titleController.text和final amount = int.parse(amountController.text) * (isIncome ? 1 : -1):這兩行代碼分別獲取了用戶輸入的交易名稱和金額。交易的金額根據用戶選擇的收入或支出類型進行了處理,如果是收入則為正數,支出則為負數。 -
final key = transactions.values.length:這行代碼獲取了當前交易列表中的交易數量,並將其作為新交易的鍵值。 -
final newTransaction = Transaction(title: title, amount: amount.toDouble(), key: key):這行代碼創建了一個新的交易對象,其中包含了用戶輸入的交易名稱、金額和鍵值。 -
BlocProvider.of<TransactionBloc>(context).add(AddTransaction(newTransaction)):這行代碼通過BlocProvider向TransactionBloc發送了一個添加交易的事件,將新交易添加到交易列表中。 -
Navigator.of(context).pop():這行代碼用於關閉當前的對話框,返回到上一個界面。
這段代碼的作用是將用戶輸入的交易信息添加到交易列表中,並通過 TransactionBloc 來管理交易數據的狀態。
Created : 13 novembre 2024