I'm learning flutter and have a floating action button which opens a bottom sheet with some field inputs and date input to create an expense item.
I want to add the inputs to a dummy list of expenses I've already hardcoded. I believe I do this with a function and call setState((){variable.add(argument);});
The thing I really struggle with in Flutter is passing data between widgets, using arguments and constructors etc. Sorry for the dump of code - unsure how to share.
main.dart
import 'package:flutter/material.dart';
import 'screens/home.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
debugShowCheckedModeBanner: false,
home: const HomePage(),
);
}
}
home.dart
import 'package:expenses_v2/widgets/expenses_summary.dart';
import 'package:flutter/material.dart';
import '../widgets/floating_action_button.dart';
import '../widgets/transactions_list.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 27, 27, 38),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: const [
ExpensesSummary(),
SizedBox(
height: 10,
),
TransactionList(),
],
),
),
),
floatingActionButton: const CustomFloatingActionButton(),
);
}
}
transactions_list.dart
import 'package:expenses_v2/k_constants.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/transactions.dart';
final List<Transaction> transactions = [
Transaction(
id: '1',
title: 'New shoes',
amount: 69.99,
date: DateTime.now(),
),
Transaction(
id: '2',
title: 'Weekly groceries',
amount: 42.99,
date: DateTime.now(),
),
];
class TransactionList extends StatelessWidget {
const TransactionList({
Key? key,
}) : super(key: key);
void addTransaction(String item, double cost) {}
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemCount: transactions.length,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
tileColor: kprimaryColour,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(transactions[index].title),
Column(
children: [
Text('£${transactions[index].amount}'),
Text(
DateFormat('d-mm-yy')
.format(transactions[index].date),
style: TextStyle(
color: ksecondaryColour,
),
),
],
),
],
),
),
const SizedBox(height: 2)
],
);
}),
);
}
}
floatingactionbutton.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:expenses_v2/k_constants.dart';
class CustomFloatingActionButton extends StatefulWidget {
const CustomFloatingActionButton({
Key? key,
}) : super(key: key);
@override
State<CustomFloatingActionButton> createState() =>
_CustomFloatingActionButtonState();
}
class _CustomFloatingActionButtonState
extends State<CustomFloatingActionButton> {
//
final itemInput = TextEditingController();
final costInput = TextEditingController();
DateTime? dateInput;
@override
Widget build(BuildContext context) {
return FloatingActionButton(
backgroundColor: kaccentColour,
child: const Icon(Icons.add),
onPressed: () {
showModalBottomSheet(
constraints: const BoxConstraints(maxHeight: 600),
isScrollControlled: true,
backgroundColor: ksecondaryColour,
context: context,
builder: (context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Text('Add an expense item'),
const SizedBox(
height: 10,
),
TextField(
controller: itemInput,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Item',
),
),
const SizedBox(
height: 10,
),
TextField(
controller: costInput,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Cost',
),
),
const SizedBox(
height: 10,
),
// TextField(
// onChanged: (value) => dateInput = value,
// keyboardType: TextInputType.datetime,
// decoration: const InputDecoration(
// border: OutlineInputBorder(),
// hintText: 'Date',
// ),
// ),
const SizedBox(
height: 10,
),
SizedBox(
height: 200,
// Quick means to get a date picker in
child: CupertinoDatePicker(
onDateTimeChanged: (newDate) =>
dateInput = newDate),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
debugPrint(itemInput.text);
debugPrint(costInput.text);
debugPrint(dateInput.toString());
//TODO: Not sure if I need to clear the variables after submiting - unsure of best practice
// itemInput.text = '';
// costInput.text = '';
// dateInput = '';
},
child: const Text('Submit'))
],
),
),
);
});
});
}
}
transactions.dart
class Transaction {
Transaction({
required this.id,
required this.title,
required this.amount,
required this.date,
});
final String id;
final String title;
final double amount;
final DateTime date;
}
k_constants.dart
import 'package:flutter/material.dart';
Color kprimaryColour = const Color.fromARGB(255, 23, 37, 54);
Color ksecondaryColour = const Color.fromARGB(255, 73, 99, 130);
Color kaccentColour = const Color.fromARGB(255, 29, 209, 209);
CodePudding user response:
Use a callback method to return data from CustomFloatingActionButton
widget. and pass transactions
to the TransactionList
instead of creating global variable. Also the HomePage
needed to be statefulWiget to update the UI.
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Transaction> transactions = [
Transaction(
id: '1',
title: 'New shoes',
amount: 69.99,
date: DateTime.now(),
),
Transaction(
id: '2',
title: 'Weekly groceries',
amount: 42.99,
date: DateTime.now(),
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(255, 27, 27, 38),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
// ExpensesSummary(),
SizedBox(
height: 10,
),
TransactionList(transactions: transactions),
],
),
),
),
floatingActionButton: CustomFloatingActionButton(
callback: (transaction) {
if (transaction != null) {
transactions.add(transaction);
print(transaction);
setState(() {});
}
},
),
);
}
}
class TransactionList extends StatelessWidget {
final transactions;
const TransactionList({
Key? key,
required this.transactions,
}) : super(key: key);
void addTransaction(String item, double cost) {}
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemCount: transactions.length,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
tileColor: kprimaryColour,
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(transactions[index].title),
Column(
children: [
Text('£${transactions[index].amount}'),
Text(
DateFormat('d-mm-yy')
.format(transactions[index].date),
style: TextStyle(
color: ksecondaryColour,
),
),
],
),
],
),
),
const SizedBox(height: 2)
],
);
}),
);
}
}
class CustomFloatingActionButton extends StatefulWidget {
final Function(Transaction? transaction) callback;
const CustomFloatingActionButton({
Key? key,
required this.callback,
}) : super(key: key);
@override
State<CustomFloatingActionButton> createState() =>
_CustomFloatingActionButtonState();
}
class _CustomFloatingActionButtonState
extends State<CustomFloatingActionButton> {
//
final itemInput = TextEditingController();
final costInput = TextEditingController();
DateTime? dateInput;
@override
Widget build(BuildContext context) {
return FloatingActionButton(
backgroundColor: kaccentColour,
child: const Icon(Icons.add),
onPressed: () async {
showModalBottomSheet(
constraints: const BoxConstraints(maxHeight: 600),
isScrollControlled: true,
backgroundColor: ksecondaryColour,
context: context,
builder: (context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const Text('Add an expense item'),
const SizedBox(
height: 10,
),
TextField(
controller: itemInput,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Item',
),
),
const SizedBox(
height: 10,
),
TextField(
controller: costInput,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Cost',
),
),
const SizedBox(
height: 10,
),
// TextField(
// onChanged: (value) => dateInput = value,
// keyboardType: TextInputType.datetime,
// decoration: const InputDecoration(
// border: OutlineInputBorder(),
// hintText: 'Date',
// ),
// ),
const SizedBox(
height: 10,
),
SizedBox(
height: 200,
// Quick means to get a date picker in
child: CupertinoDatePicker(
onDateTimeChanged: (newDate) =>
dateInput = newDate),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
debugPrint(itemInput.text);
debugPrint(costInput.text);
debugPrint(dateInput.toString());
final result = Transaction(
amount: double.tryParse(costInput.text) ?? 0,
date: dateInput ?? DateTime.now(),
id: "your Id",
title: itemInput.text);
widget.callback(result);
},
child: const Text('Submit'))
],
),
),
);
});
});
}
}