Project Aplikasi Expense Tracker – Part 2

Pada modul ini kita akan pisahkan widget pada aplikasi menjadi beberapa widget. Selain itu perlu juga melakukan organizing file. Organizing file untuk proyek besar akan sangat membantu kita.

File Organizing

Disarankan untuk memisahkan file custom widget yang kita buat kedalam folder terpisah. Dan untuk file class non widget kedalam folder yang terpisah juga

Pada proyek file trans.dart, yaitu file class untuk model data transaksi expense dimasukan kedalam folder models.

Sementara untuk custom widget (widget untuk form input, expense list), dimasukan kedalam folder widgets.

Note: Untuk penamaan folder bebas, tidak ada aturan khusus.

Code Aplikasi

//file main.dart

import 'package:flutter/material.dart';

import './models/trans.dart';
import './widgets/expense_list.dart';
import './widgets/form_expense.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final List<Trans> _trans = [
    Trans(
        id: 'a001',
        title: 'Notebook Aspire A5',
        amount: 8200000,
        txdate: DateTime.now()),
    Trans(
        id: 'a002',
        title: 'Samsung Galaxy A5',
        amount: 3200000,
        txdate: DateTime.now()),
  ];

  void _addNewExpense(String newTitle, double newAmount) {
    final newExpense = Trans(
        id: DateTime.now().toString(),
        title: newTitle,
        amount: newAmount,
        txdate: DateTime.now());

    setState(() {
      _trans.add(newExpense);
    });
  }

  void _showFormExpense(BuildContext ctx) {
    showModalBottomSheet(
      context: ctx,
      builder: (_) {
        return FormExpense(_addNewExpense);
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Expense Tracker'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => _showFormExpense(context),
          )
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Card(
                color: Colors.blue,
                child: Text('Chart'),
              ),
              ExpenseList(_trans),
            ]),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () => _showFormExpense(context),
      ),
    );
  }
}

File form_expense.dart akan berisi widget yang menampilkan form untuk melakukan user input.

// file form_expense.dart

import 'package:flutter/material.dart';

class FormExpense extends StatefulWidget {
  final Function addExpenseH;

  FormExpense(this.addExpenseH);

  @override
  _FormExpenseState createState() => _FormExpenseState();
}

class _FormExpenseState extends State<FormExpense> {
  final titleCtrl = TextEditingController();

  final amountCtrl = TextEditingController();

  void submitForm() {
    final inpTitle = titleCtrl.text;
    final inpAmount = double.parse(amountCtrl.text);

    if (inpAmount <= 0 || inpTitle.isEmpty) {
      return;
    }

    widget.addExpenseH(titleCtrl.text, double.parse(amountCtrl.text));
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Container(
        padding: EdgeInsets.all(7),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            TextField(
              decoration: InputDecoration(labelText: 'Title'),
              controller: titleCtrl,
            ),
            TextField(
              decoration: InputDecoration(labelText: 'Amount'),
              controller: amountCtrl,
              keyboardType: TextInputType.number,
            ),
            FlatButton(
              onPressed: submitForm,
              child: Text('Add Expense'),
              textColor: Colors.red,
            ),
          ],
        ),
      ),
    );
  }
}

File expense_list.dart berisi widget yang menampilkan expense list.

// file expense_list.dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import '../models/trans.dart';

class ExpenseList extends StatelessWidget {
  final List<Trans> _trans;

  ExpenseList(this._trans);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      child: ListView.builder(
        itemCount: _trans.length,
        itemBuilder: (ctx, idx) {
          return Card(
            child: Row(
              children: <Widget>[
                Container(
                  padding: EdgeInsets.all(6),
                  margin: EdgeInsets.symmetric(
                    vertical: 10,
                    horizontal: 15,
                  ),
                  decoration: BoxDecoration(
                      border: Border.all(color: Colors.red, width: 2)),
                  child: Text(
                    // 'Rp. ${trx.amount}',
                    NumberFormat.currency(symbol: 'Rp. ', decimalDigits: 0)
                        .format(_trans[idx].amount),
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 14,
                        color: Colors.red),
                  ),
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Text(
                      _trans[idx].title,
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 12,
                      ),
                    ),
                    Text(
                      DateFormat.yMMMd().format(_trans[idx].txdate),
                      style: TextStyle(fontSize: 10, color: Colors.grey),
                    ),
                  ],
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

Pembahasan Code

Pembahasan code akan kita lakukan pada modul berikutnya.

Sharing is caring:

Leave a Comment