Pada modul ini kita akan membuat custom widget untuk menampilkan bar chart. Untuk itu kita buat file widget baru yaitu:
- chart.dart untuk menampilkan chart.
- chart_bar.dart untuk perhitungan dan styling bar dari chart.

//chart.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import './chart_bar.dart';
import '../models/trans.dart';
class Chart extends StatelessWidget {
final List<Trans> rcntTrx;
Chart(this.rcntTrx);
List<Map<String, Object>> get weekTrx {
return List.generate(7, (idx) {
final wDay = DateTime.now().subtract(Duration(days: idx));
double ttlSum = 0.0;
for (var i = 0; i < rcntTrx.length; i++) {
if (rcntTrx[i].txdate.day == wDay.day &&
rcntTrx[i].txdate.month == wDay.month &&
rcntTrx[i].txdate.year == wDay.year) {
ttlSum += rcntTrx[i].amount;
}
}
return {'d': DateFormat.E().format(wDay), 'a': ttlSum};
});
}
double get totalSpend {
return weekTrx.fold(0.0, (sum, element) {
return sum + element['a'];
});
}
double persentase(amount) {
if (totalSpend > 0) {
return amount / totalSpend;
} else {
return 0;
}
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
margin: EdgeInsets.all(8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: weekTrx.map((data) {
return ChartBar(data['d'], data['a'], persentase(data['a']));
}).toList(),
),
);
}
}
// chart_bar.dart
import 'package:flutter/material.dart';
class ChartBar extends StatelessWidget {
final String lbl;
final double amount;
final double persentase;
ChartBar(this.lbl, this.amount, this.persentase);
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text(
(amount / 1000).toStringAsFixed(0),
style: TextStyle(
fontSize: 10,
),
),
Container(
height: 60,
width: 10,
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1),
color: Color.fromRGBO(200, 200, 200, 1),
borderRadius: BorderRadius.circular(10),
),
),
FractionallySizedBox(
heightFactor: persentase,
child: Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
),
)
],
)),
SizedBox(height: 4),
Text(
lbl,
style: TextStyle(fontSize: 10),
)
],
);
}
}
Pada main.dart, kita panggil widget untuk menampilkan chart.
import 'package:flutter/material.dart';
import './models/trans.dart';
import './widgets/expense_list.dart';
import './widgets/form_expense.dart';
import './widgets/chart.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Expense Tracker',
theme: ThemeData(
primaryColor: Colors.red,
accentColor: Colors.red[200],
textTheme: TextTheme(bodyText2: TextStyle(color: Colors.purple)),
),
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()),
];
List<Trans> get _rcntTrx {
return _trans.where((element) {
return element.txdate.isAfter(DateTime.now().subtract(Duration(days: 7)));
}).toList();
}
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>[
Chart(_rcntTrx),
ExpenseList(_trans),
]),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _showFormExpense(context),
),
);
}
}
Penjelasan Code
coming soon…
Pembahasan project Aplikasi Expense Tracker berakhir di modul ini. Namun aplikasi ini akan digunakan untuk beberapa modul berikutnya untuk membahas widget datepicker dan theme.
Untuk file project final, dapat download disini.