Make the pie chart widget reusable
This commit is contained in:
@@ -9,6 +9,8 @@ import 'package:okane/ui/pages/account/total_balance_card.dart';
|
||||
import 'package:okane/ui/pages/account/upcoming_transactions_card.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
import 'package:okane/ui/widgets/piechart.dart';
|
||||
import 'package:okane/ui/widgets/piechart_card.dart';
|
||||
|
||||
class AccountListPage extends StatefulWidget {
|
||||
final bool isPage;
|
||||
@@ -102,18 +104,25 @@ class AccountListPageState extends State<AccountListPage> {
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: TotalBalanceCard(),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
child: UpcomingTransactionsCard(),
|
||||
),
|
||||
|
||||
Row(
|
||||
Wrap(
|
||||
children: [
|
||||
Padding(padding: EdgeInsets.all(16), child: BreakdownCard()),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
//child: BreakdownCard(),
|
||||
child: PieChartCard(
|
||||
titleText: "Spending Breakdown",
|
||||
fallbackText: "No spending available",
|
||||
items: [],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:okane/database/collections/transaction.dart';
|
||||
import 'package:okane/database/database.dart';
|
||||
import 'package:okane/ui/state/core.dart';
|
||||
import 'package:okane/ui/utils.dart';
|
||||
import 'package:okane/ui/widgets/piechart.dart';
|
||||
import 'package:okane/ui/widgets/piechart_card.dart';
|
||||
|
||||
const CATEGORY_INCOME = "Income";
|
||||
const CATEGORY_OTHER = "Other";
|
||||
@@ -71,101 +73,126 @@ class BreakdownCard extends StatelessWidget {
|
||||
return (expenses: expenses, colors: colors, usable: usableMoney);
|
||||
}
|
||||
|
||||
Widget _buildCard(Widget child, String? subtitle) {
|
||||
return ResponsiveCard(
|
||||
titleText: "Expense Breakdown",
|
||||
subtitleText: subtitle,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCenterText(String text) {
|
||||
return _buildCard(Center(child: Text(text)), null);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bloc = GetIt.I.get<CoreCubit>();
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: BlocBuilder<CoreCubit, CoreState>(
|
||||
builder: (context, state) {
|
||||
if (bloc.activeAccount == null) {
|
||||
return Text("No active account");
|
||||
return BlocBuilder<CoreCubit, CoreState>(
|
||||
builder: (context, state) {
|
||||
if (bloc.activeAccount == null) {
|
||||
return _buildCenterText("No account active");
|
||||
}
|
||||
|
||||
return FutureBuilder(
|
||||
future: getLastTransactions(bloc.activeAccount!, DateTime.now(), 30),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return _buildCard(
|
||||
Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: SizedBox(
|
||||
width: 150 - 16 * 2,
|
||||
height: 150 - 16 * 2,
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
return FutureBuilder(
|
||||
future: getLastTransactions(
|
||||
bloc.activeAccount!,
|
||||
DateTime.now(),
|
||||
30,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
final title = Padding(
|
||||
padding: EdgeInsets.only(bottom: 16),
|
||||
child: Text("Expense Breakdown"),
|
||||
);
|
||||
if (!snapshot.hasData) {
|
||||
return Column(children: [title, CircularProgressIndicator()]);
|
||||
}
|
||||
|
||||
if (snapshot.data!.isEmpty) {
|
||||
return Column(children: [title, Text("No transactions")]);
|
||||
}
|
||||
|
||||
final data = _computeSections(snapshot.data!);
|
||||
final sectionData =
|
||||
data.expenses.entries
|
||||
.map(
|
||||
(entry) => PieChartSectionData(
|
||||
value: entry.value,
|
||||
title: formatCurrency(entry.value, precise: false),
|
||||
titleStyle: TextStyle(fontWeight: FontWeight.bold),
|
||||
radius: 40,
|
||||
color: data.colors[entry.key]!,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return Column(
|
||||
children: [
|
||||
title,
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 150,
|
||||
height: 150,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: PieChart(
|
||||
PieChartData(
|
||||
borderData: FlBorderData(show: false),
|
||||
sectionsSpace: 0,
|
||||
centerSpaceRadius: 35,
|
||||
sections: sectionData,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
data.expenses.keys
|
||||
.map(
|
||||
(key) => LegendItem(
|
||||
text: key,
|
||||
color: data.colors[key]!,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: Text(
|
||||
"Available money: ${formatCurrency(data.usable)}",
|
||||
final data = _computeSections(snapshot.data!);
|
||||
final sectionData =
|
||||
data.expenses.entries
|
||||
.map(
|
||||
(entry) => PieChartSectionData(
|
||||
value: entry.value,
|
||||
title: formatCurrency(entry.value, precise: false),
|
||||
titleStyle: TextStyle(fontWeight: FontWeight.bold),
|
||||
radius: 40,
|
||||
color: data.colors[entry.key]!,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
.toList();
|
||||
if (sectionData.isEmpty) {
|
||||
return _buildCenterText("No expenses available");
|
||||
}
|
||||
return OkanePieChart(
|
||||
items:
|
||||
data.expenses.entries
|
||||
.map(
|
||||
(e) => (
|
||||
title: e.key,
|
||||
value: e.value,
|
||||
color: colorHash(e.key),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
return ResponsiveCard(
|
||||
titleText: "Expense Breakdown",
|
||||
child: BlocBuilder<CoreCubit, CoreState>(
|
||||
builder: (context, state) {
|
||||
if (bloc.activeAccount == null) {
|
||||
return Text("No active account");
|
||||
}
|
||||
|
||||
return FutureBuilder(
|
||||
future: getLastTransactions(
|
||||
bloc.activeAccount!,
|
||||
DateTime.now(),
|
||||
30,
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
|
||||
final data = _computeSections(snapshot.data!);
|
||||
final sectionData =
|
||||
data.expenses.entries
|
||||
.map(
|
||||
(entry) => PieChartSectionData(
|
||||
value: entry.value,
|
||||
title: formatCurrency(entry.value, precise: false),
|
||||
titleStyle: TextStyle(fontWeight: FontWeight.bold),
|
||||
radius: 40,
|
||||
color: data.colors[entry.key]!,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
if (sectionData.isEmpty) {
|
||||
return Center(child: Text("No expenses"));
|
||||
}
|
||||
return OkanePieChart(
|
||||
items:
|
||||
data.expenses.entries
|
||||
.map(
|
||||
(e) => (
|
||||
title: e.key,
|
||||
value: e.value,
|
||||
color: colorHash(e.key),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class TotalBalanceCard extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Total balance",
|
||||
"Total balance",
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
FutureBuilder(
|
||||
|
||||
Reference in New Issue
Block a user