Skip to content

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框架構建的簡單應用程序,用於記錄支出和收入。讓我們來簡要解釋一下每個部分:

  1. 導入包和依賴項:代碼一開始導入了一些Flutter和其他必要的包和依賴項,例如flutter/material.dartflutter_bloc/flutter_bloc.dart。這些包提供了構建Flutter應用所需的基本工具和功能。

  2. 自定義AppBarMyCustomAppBar類創建了一個自定義的AppBar小部件。它設置了標題、樣式和背景顏色。

  3. HomePageHomePage類定義了應用程序的主頁。它包含一個自定義的AppBar、一個顯示交易列表的主體部分和兩個浮動操作按鈕。主體部分使用BlocBuilder來構建交易列表,根據TransactionBloc的狀態來呈現不同的UI。

  4. 交易列表_buildTransactionList函數構建了一個交易列表,使用了ListView.builder來動態構建列表項。每個列表項都是一個Card小部件,顯示交易的標題、金額和一個刪除按鈕。當用戶點擊刪除按鈕時,會觸發刪除該交易的操作。

  5. 浮動操作按鈕_buildFloatingActionButtons函數構建了兩個浮動操作按鈕。一個按鈕用於清除所有交易記錄,另一個按鈕用於顯示當前所有交易的總金額。

  6. 添加交易對話框_showAddTransactionDialog函數用於顯示一個對話框,允許用戶輸入新的交易信息,包括交易的名稱、金額和類型(收入或支出)。用戶輸入完成後,可以點擊“確定”按鈕來添加交易記錄。

  7. 添加交易_addTransaction函數負責將用戶輸入的新交易信息添加到交易列表中,並使用BlocProvider來向TransactionBloc發送添加交易的事件。

這個應用程序使用了Flutter框架的一些核心概念,如小部件、狀態管理(使用Bloc)和對話框。通過這些功能,用戶可以方便地記錄和管理他們的收支情況。


分段解釋

_buildBody

這段代碼定義了一個名為 _buildBody 的函數,其作用是根據 TransactionBloc 的狀態構建對應的 UI。讓我們逐步解釋:

  1. BlocBuilder<TransactionBloc, TransactionState>:這是 Flutter Bloc 庫提供的一個小部件,用於構建基於 BLoC(Business Logic Component)模式的 UI。它會根據給定的 TransactionBlocTransactionState 動態地構建 UI。

  2. builder 方法:這是 BlocBuilder 的一個參數,是一個回調函數。它接收兩個參數:contextstatecontext 表示當前的 BuildContext,而 state 則表示由 TransactionBloc 發布的最新狀態。

  3. if (state is TransactionsLoaded):這是一個條件語句,用於檢查當前的狀態是否為 TransactionsLoaded 類型。如果是,表示已經成功加載了交易數據,那麽就調用 _buildTransactionList(state) 函數來構建交易列表的 UI。

  4. return const Center(child: CircularProgressIndicator()):如果當前狀態不是 TransactionsLoaded,則返回一個圓形進度指示器,以提示用戶正在加載數據。

這段代碼的作用是根據 TransactionBloc 的狀態動態構建 UI,當交易數據加載完成時,顯示交易列表,否則顯示加載指示器。


_buildTransactionList

這段代碼定義了一個名為 _buildTransactionList 的小部件函數,它的作用是根據傳入的 TransactionsLoaded 狀態構建交易列表的 UI。讓我們來逐步解釋:

  1. ListView.builder:這是一個 Flutter 提供的小部件,用於構建動態列表。它會根據 itemCount 參數確定列表的長度,並根據 itemBuilder 參數動態構建列表項。

  2. itemCount: state.transactions.length:這里使用了傳入的 TransactionsLoaded 狀態中的 transactions 屬性的長度作為列表項的數量。

  3. itemBuilder: (context, index):這是一個回調函數,用於構建每個列表項的 UI。它接收兩個參數:context 表示當前的 BuildContext,index 表示當前要構建的列表項的索引。

  4. final trans = state.transactions[index]:這行代碼從 state.transactions 中獲取了特定索引處的交易數據。

  5. CardListTile:這兩個小部件分別用於顯示列表項的卡片樣式和內容。ListTile 可以包含標題、副標題和尾部圖標。

  6. titlesubtitle:分別用於顯示交易的標題和金額。交易標題的樣式設置了字體大小、顏色和粗細,而金額的樣式設置了字體大小和顏色。

  7. trailing:這是列表項的尾部圖標,是一個刪除按鈕,用戶可以點擊它來刪除對應的交易。

這段代碼的作用是根據傳入的 TransactionsLoaded 狀態構建一個交易列表,每個列表項都顯示交易的標題、金額和一個刪除按鈕。


_buildFloatingActionButtons

這段代碼定義了一個名為 _buildFloatingActionButtons 的小部件函數,其作用是構建浮動操作按鈕的 UI。讓我們逐步解釋:

  1. Row 小部件:這是一個水平排列子部件的容器,其中的子部件將會水平排列顯示。

  2. mainAxisAlignment: MainAxisAlignment.spaceEvenly:這是設置子部件在水平方向上的對齊方式,此處設置為均勻分布,使得子部件在水平方向上均勻分布,間隔相等。

  3. children:這是一個子部件的列表,包含了兩個浮動操作按鈕。

  4. BlocBuilder<TransactionBloc, TransactionState>:這是一個用於根據 BLoC 的狀態構建 UI 的小部件。在這里,它用於構建一個刪除所有交易記錄的浮動操作按鈕。根據 TransactionBloc 的狀態,它會決定按鈕是否可見。

  5. IconButton:這是一個包含圖標的按鈕,點擊後會執行相應的操作。這里的按鈕用於清除所有交易記錄。當點擊按鈕時,會向 TransactionBloc 發送一個 ClearTransactions 事件,以清除所有交易記錄。

  6. 第二個 IconButton:這是另一個浮動操作按鈕,用於顯示當前所有交易的總金額。點擊按鈕後,會顯示一個對話框,其中包含了所有交易的總金額信息。

  7. showDialog:這是一個用於顯示對話框的函數,它接收一個 builder 參數,用於構建對話框的 UI。在這里,它顯示了一個標題為 “Total Amount” 的對話框,內容為當前所有交易的總金額信息。

這段代碼的作用是構建兩個浮動操作按鈕,一個用於清除所有交易記錄,另一個用於顯示當前所有交易的總金額。


_showAddTransactionDialog

這段代碼定義了一個名為 _showAddTransactionDialog 的函數,其作用是顯示一個對話框,允許用戶輸入新的交易信息。讓我們來逐步解釋:

  1. showDialog:這是一個 Flutter 提供的函數,用於顯示對話框。它接收一個 builder 參數,用於構建對話框的 UI。

  2. builder 參數:這是一個回調函數,用於構建對話框的內容。它接收一個 context 參數,表示當前的上下文環境。

  3. AlertDialog:這是一個 Material 風格的對話框,顯示在屏幕中央,包含了標題、內容和操作按鈕。

  4. Text:這是用於顯示文本的小部件,可以設置文本的樣式、大小和顏色。

  5. TextField:這是一個文本輸入框,用於接收用戶的輸入。在這里,分別用於輸入交易的名稱和金額。

  6. StatefulBuilder:這是一個狀態管理的小部件,用於管理交易類型的切換。它可以根據用戶的操作來更新 UI。

  7. SwitchListTile:這是一個帶有開關的列表瓦片,用於切換不同的選項。在這里,用於切換交易類型(收入或支出)。

  8. TextButton:這是一個文本按鈕,用於執行相應的操作。在這里,分別用於取消輸入和確認輸入。

  9. _addTransaction 函數:當用戶點擊確認按鈕後,會調用 _addTransaction 函數來添加新的交易記錄。

這段代碼的作用是顯示一個對話框,允許用戶輸入新的交易信息,包括名稱、金額和類型(收入或支出)。


_addTransaction

這段代碼是一個名為 _addTransaction 的函數,它的作用是將用戶輸入的交易信息添加到交易列表中。讓我們逐步解釋:

  1. Hive.box<Transaction>('transactions'):這是一個使用 Hive 數據庫的操作,用於獲取名為 ‘transactions’ 的數據存儲盒子。Hive 是一個輕量級的本地數據庫,用於在 Flutter 應用程序中存儲數據。

  2. final title = titleController.textfinal amount = int.parse(amountController.text) * (isIncome ? 1 : -1):這兩行代碼分別獲取了用戶輸入的交易名稱和金額。交易的金額根據用戶選擇的收入或支出類型進行了處理,如果是收入則為正數,支出則為負數。

  3. final key = transactions.values.length:這行代碼獲取了當前交易列表中的交易數量,並將其作為新交易的鍵值。

  4. final newTransaction = Transaction(title: title, amount: amount.toDouble(), key: key):這行代碼創建了一個新的交易對象,其中包含了用戶輸入的交易名稱、金額和鍵值。

  5. BlocProvider.of<TransactionBloc>(context).add(AddTransaction(newTransaction)):這行代碼通過 BlocProviderTransactionBloc 發送了一個添加交易的事件,將新交易添加到交易列表中。

  6. Navigator.of(context).pop():這行代碼用於關閉當前的對話框,返回到上一個界面。

這段代碼的作用是將用戶輸入的交易信息添加到交易列表中,並通過 TransactionBloc 來管理交易數據的狀態。


Last update : 13 novembre 2024
Created : 13 novembre 2024

Comments

Comments