Today we are going to add new expense screen for adding expenses
- Add screen navigation from expense list screen.
- Add a model class for expense
- Expense screen design and passing expense data back to parent screen.
Add Expense Page
Let us create a dummy expense page with title and button to save the details.
import 'package:flutter/cupertino.dart'; class ExpensePage extends StatefulWidget { final String userName; ExpensePage(this.userName); @override _MyListState createState() => _MyListState(); } class _MyListState extends State<ExpensePage> { @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('Add Expense'), trailing: CupertinoButton( padding: EdgeInsets.zero, child: Icon( CupertinoIcons.floppy_disk, size: 35, ), onPressed: () {}, ), ), child: Text('Expenses'), ); } }
Now we need to add the code for calling Expense page on tap of the + button in ExpenseList page.
Navigator.push( context, MaterialPageRoute( builder: (context) => ExpensePage(userName), )); },
We are passing username as an argument in the constructor, this is to link user with the expense. Later this will be changed to user object instead of user name.
Expense model class
Here is code snippet of Expense model class which will act as the placeholder for Expense.
class Expense { String name; DateTime dateTime; double amount; String details; }
Expense Screen design
Let us modify the expense page and add name, date, amount and detail field. I will be using a static ListView to arrange all these fields. For date field will be using CupertinoDateField. Here is the expensePage.dart page.

import 'package:easysplit/model/expense.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; class ExpensePage extends StatefulWidget { final String userName; ExpensePage(this.userName); @override _MyListState createState() => _MyListState(); } class _MyListState extends State<ExpensePage> { TextEditingController nameController; DateTime _dateTime; TextEditingController dateController; TextEditingController amountController; TextEditingController detailController; Expense expense = Expense(); @override void initState() { super.initState(); nameController = TextEditingController(text: ''); dateController = TextEditingController(text: formatDate(DateTime.now())); amountController = TextEditingController(text: ''); detailController = TextEditingController(text: ''); } @override Widget build(BuildContext context) { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('Add Expense'), trailing: CupertinoButton( padding: EdgeInsets.zero, child: Icon( CupertinoIcons.floppy_disk, size: 35, ), onPressed: () { expense.name = nameController.text; expense.dateTime = _dateTime; if (amountController.text != '') { expense.amount = double.parse(amountController.text); } expense.details = detailController.text; Navigator.pop(context, expense); }, ), ), child: _myListView(context)); } Widget _myListView(BuildContext context) { return Scaffold( body: ListView( children: <Widget>[ ListTile( title: Text('Name:'), ), ListTile( title: CupertinoTextField( controller: nameController, ), ), ListTile( title: Text('Date:'), ), ListTile( title: CupertinoTextField( controller: dateController, readOnly: true, onTap: () => _showDatePicker(context), )), ListTile( title: Text('Amount:'), ), ListTile( title: CupertinoTextField( controller: amountController, keyboardType: TextInputType.number, inputFormatters: <TextInputFormatter>[ FilteringTextInputFormatter.digitsOnly ], // Only numbers can be entered ), ), ListTile( title: Text('Detail:'), ), ListTile( title: CupertinoTextField( controller: detailController, maxLines: 3, ), ), ], ), ); } void _showDatePicker(ctx) { // showCupertinoModalPopup is a built-in function of the cupertino library showCupertinoModalPopup( context: ctx, builder: (_) => Container( height: 500, color: Color.fromARGB(255, 255, 255, 255), child: Column( children: [ Container( height: 400, child: CupertinoDatePicker( initialDateTime: DateTime.now(), onDateTimeChanged: (val) { setState(() { _dateTime = val; if (_dateTime != null) { dateController.text = formatDate(_dateTime); } }); }), ), // Close the modal CupertinoButton( child: Text('OK'), onPressed: () => Navigator.of(ctx).pop(), ) ], ), )); } String formatDate(DateTime dateTime) { DateFormat dateFormat = DateFormat('dd MMM hh:mm'); return dateFormat.format(dateTime); } }
Pass custom object back to parent screen
In order to pass the expense object back to expense list page, pass the expense object as part of the Navigator pop code.
Navigator.pop(context, expense);
In the parent page (expense list), add a new function which does the navigation to expense page. This function will be called in onTap of the + button. This function will be added with async attribute for retrieving the expense object when returned from expense page.
void moveToExpensePage(String item) async { final Expense expense = await Navigator.push( context, MaterialPageRoute( builder: (context) => ExpensePage(userName), )); print(expense.amount); }
References