Flutter / Dart - I have a simple diabetic app but am new to programming, so struggling a lot, even after watching hours of Flutter youtube videos! My app currently has 4 input fields on one row and the result of a calculation of input1 - input2 below it. I would like to change this, so that the top row has Input / Input / Output (in similar boxes) and the second row has Input / Input / Output (in similar boxes). The code for my current app is shown below after some help on Stackoverflow. It has been suggested that I use the command "resultTextFieldController.text = resultOfCalculation;" to put the result in a text box, but my main issue is where to put that line as everywhere I have put it so far, the line gets underlined in red.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Help with a meal....';
return MaterialApp(
debugShowCheckedModeBanner: false,
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
backgroundColor: Colors.grey,
foregroundColor: Colors.black,
),
body: const AddTwoNumbers(),
),
);
}
}
class AddTwoNumbers extends StatefulWidget {
const AddTwoNumbers({super.key});
#override
// ignore: library_private_types_in_public_api
_AddTwoNumbersState createState() => _AddTwoNumbersState();
}
class _AddTwoNumbersState extends State<AddTwoNumbers> {
TextEditingController num1controller = TextEditingController();
TextEditingController num2controller = TextEditingController();
TextEditingController num3controller = TextEditingController();
TextEditingController num4controller = TextEditingController();
String result = "0";
_calculate() {
if (num1controller.text.isNotEmpty && num2controller.text.isNotEmpty) {
setState(() {
double sum = double.parse(num1controller.text) -
double.parse(num2controller.text);
result = sum.toStringAsFixed(1);
});
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: TextField(
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20.0),
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num1controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Level',
hintText: 'Enter First Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20.0),
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num2controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Level',
hintText: 'Enter Second Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num3controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs',
hintText: 'Enter Third Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num4controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs 2',
hintText: 'Enter Fourth Number',
),
),
),
const SizedBox(
width: 8,
),
],
),
Text(
'Difference between Target Level and Current Level: $result'),
],
),
),
),
);
}
}
Full Code
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Help with a meal....';
return MaterialApp(
debugShowCheckedModeBanner: false,
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
backgroundColor: Colors.grey,
foregroundColor: Colors.black,
),
body: const AddTwoNumbers(),
),
);
}
}
class AddTwoNumbers extends StatefulWidget {
const AddTwoNumbers({super.key});
#override
// ignore: library_private_types_in_public_api
_AddTwoNumbersState createState() => _AddTwoNumbersState();
}
class _AddTwoNumbersState extends State<AddTwoNumbers> {
TextEditingController numR1C1controller =
TextEditingController(); //Changed name of the texteditingcontroller as Row as R and Column as C
TextEditingController numR1C2controller = TextEditingController();
TextEditingController numR1C3controller = TextEditingController();
TextEditingController numR2C1controller = TextEditingController();
TextEditingController numR2C2controller = TextEditingController();
TextEditingController numR2C3controller = TextEditingController();
String result = "0";
String result2 = "0";
_calculateR1() {
if (numR1C1controller.text.isNotEmpty &&
numR1C2controller.text.isNotEmpty) {
setState(() {
double sum = double.parse(numR1C1controller.text) -
double.parse(numR1C2controller.text);
numR1C3controller.text = sum.toStringAsFixed(1);
result = sum.toStringAsFixed(1);
});
}
}
_calculateR2() {
if (numR2C1controller.text.isNotEmpty &&
numR2C2controller.text.isNotEmpty) {
setState(() {
double sum = double.parse(numR2C1controller.text) -
double.parse(numR2C2controller.text);
numR2C3controller.text = sum.toStringAsFixed(1);
result2 = sum.toStringAsFixed(1);
});
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: TextField(
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20.0),
onChanged: (value) => _calculateR1(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: numR1C1controller,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Level',
hintText: 'Enter First Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20.0),
onChanged: (value) => _calculateR1(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: numR1C2controller,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Level',
hintText: 'Enter Second Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20.0),
onChanged: (value) => _calculateR1(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: numR1C3controller,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Result',
hintText: '',
),
),
),
const SizedBox(
width: 8,
),
],
),
const SizedBox(
height: 8,
),
Row(
children: [
Expanded(
child: TextField(
onChanged: (value) => _calculateR2(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: numR2C1controller,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs',
hintText: 'Enter Third Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculateR2(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: numR2C2controller,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs 2',
hintText: 'Enter Fourth Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20.0),
onChanged: (value) => _calculateR2(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: numR2C3controller,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Result',
hintText: '',
),
),
),
const SizedBox(
width: 8,
),
],
),
Text(
'Difference between Target Level and Current Level: $result'),
],
),
),
),
);
}
}
Output
Add this line to the textfield widget to allow only numbers to be inputted in the text field.
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
Hope this helps. Happy Coding :)
Related
Flutter / Dart - I have a simple app but am new to programming, so struggling.
Currently, the setup is with all the TextInput boxes in a Column. I would like to change this to having them in a Row. I assumed this would be easy by simply replacing the Word Column with Row on line 44. But it doesn't work and when I try to run it, the "errors_patch.dart" page opens (which I have never seen before) with a highlighted error on line 51 "int assertionStart, int assertionEnd, Object? message);".
How can I simply change from Column to Row?
How can I have the result show in real time rather than needing to click on the "Subtraction" button to get it?
Many thanks in advance.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Help with a meal....';
return MaterialApp(
debugShowCheckedModeBanner: false,
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
backgroundColor: Colors.grey,
foregroundColor: Colors.black,
),
body: const AddTwoNumbers(),
),
);
}
}
class AddTwoNumbers extends StatefulWidget {
const AddTwoNumbers({super.key});
#override
// ignore: library_private_types_in_public_api
_AddTwoNumbersState createState() => _AddTwoNumbersState();
}
class _AddTwoNumbersState extends State<AddTwoNumbers> {
TextEditingController num1controller = TextEditingController();
TextEditingController num2controller = TextEditingController();
TextEditingController num3controller = TextEditingController();
TextEditingController num4controller = TextEditingController();
String result = "0";
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
TextField(
keyboardType: const TextInputType.numberWithOptions(decimal: true),
controller: num1controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Level',
hintText: 'Enter First Number',
),
),
const SizedBox(
height: 8,
),
TextField(
keyboardType: const TextInputType.numberWithOptions(decimal: true),
controller: num2controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Level',
hintText: 'Enter Second Number',
),
),
const SizedBox(
height: 8,
),
TextField(
keyboardType: const TextInputType.numberWithOptions(decimal: true),
controller: num3controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs',
hintText: 'Enter Third Number',
),
),
const SizedBox(
height: 8,
),
TextField(
keyboardType: const TextInputType.numberWithOptions(decimal: true),
controller: num4controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs 2',
hintText: 'Enter Fourth Number',
),
),
const SizedBox(
height: 8,
),
Wrap(children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: Colors.purple),
child: const Text("Subtraction"),
onPressed: () {
setState(() {
double sum = double.parse(num1controller.text) -
double.parse(num2controller.text);
result = sum.toStringAsFixed(1);
});
},
),
const SizedBox(
height: 20,
width: 20,
),
]),
Text('Difference between Target Level and Current Level: $result'),
],
),
);
}
}
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Help with a meal....';
return MaterialApp(
debugShowCheckedModeBanner: false,
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
backgroundColor: Colors.grey,
foregroundColor: Colors.black,
),
body: const AddTwoNumbers(),
),
);
}
}
class AddTwoNumbers extends StatefulWidget {
const AddTwoNumbers({super.key});
#override
// ignore: library_private_types_in_public_api
_AddTwoNumbersState createState() => _AddTwoNumbersState();
}
class _AddTwoNumbersState extends State<AddTwoNumbers> {
TextEditingController num1controller = TextEditingController();
TextEditingController num2controller = TextEditingController();
TextEditingController num3controller = TextEditingController();
TextEditingController num4controller = TextEditingController();
String result = "0";
_calculate() {
if (num1controller.text.isNotEmpty && num2controller.text.isNotEmpty) {
setState(() {
double sum = double.parse(num1controller.text) -
double.parse(num2controller.text);
result = sum.toStringAsFixed(1);
});
}
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num1controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Level',
hintText: 'Enter First Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num2controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Level',
hintText: 'Enter Second Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num3controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs',
hintText: 'Enter Third Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num4controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs 2',
hintText: 'Enter Fourth Number',
),
),
),
const SizedBox(
width: 8,
),
],
),
Text(
'Difference between Target Level and Current Level: $result'),
],
),
),
),
);
}
}
Since you are switching to Row it doesn't know the width of the TextFields, so you need to wrap them with Expanded or Flexible widgets to make them take the space they need (or you can give them some fixed width if you wish), something like in the solution below. And the answer to the second question is also in the solution, you just call the calculate method on every TextField change.
class AddTwoNumbers extends StatefulWidget {
const AddTwoNumbers({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_AddTwoNumbersState createState() => _AddTwoNumbersState();
}
class _AddTwoNumbersState extends State<AddTwoNumbers> {
TextEditingController num1controller = TextEditingController();
TextEditingController num2controller = TextEditingController();
TextEditingController num3controller = TextEditingController();
TextEditingController num4controller = TextEditingController();
String result = "0";
_calculate() {
setState(() {
double sum =
double.parse(num1controller.text) - double.parse(num2controller.text);
result = sum.toStringAsFixed(1);
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
children: <Widget>[
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num1controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Target Level',
hintText: 'Enter First Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num2controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Level',
hintText: 'Enter Second Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num3controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs',
hintText: 'Enter Third Number',
),
),
),
const SizedBox(
width: 8,
),
Expanded(
child: TextField(
onChanged: (value) => _calculate(),
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
controller: num4controller,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Current Meal carbs 2',
hintText: 'Enter Fourth Number',
),
),
),
const SizedBox(
width: 8,
),
],
),
Text(
'Difference between Target Level and Current Level: $result'),
],
),
),
),
);
}
}
There are many ways to do what you're trying to achieve. For changing Column to Rows, you can use a container with a column as a child and rows as children in it. You can see the documentation for further assistance.
For your second question, you can use onChanged() callback or you can use TextEditingController to see the realtime changes in your TextField.
Below is the AddNewDocument.dart file. Whenever I add any document it should show new push notification to the user and should navigate to document details page. Is there any method to implement this?
import 'package:AtDocHUB/Controller/DocumentController.dart';
import 'package:AtDocHUB/Model/Document.dart';
import 'package:AtDocHUB/View/Document/DocumentPage.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:form_field_validator/form_field_validator.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:jiffy/jiffy.dart';
DateTime now = DateTime.now();
String formattedDate = DateFormat("yyyy-MM-dd").format(now);
//import 'package:flutter_text_form_field/flutter_text_form_field.dart';
class AddNewDocument extends StatefulWidget {
#override
State createState() {
return _AddNewDocumentState();
}
}
class _AddNewDocumentState extends State<AddNewDocument> {
late final String date1;
late String datainput;
final List<String> items1 = ["Open", "Closed", "Cancel", "Submitted"];
String? selectedItem;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
TextEditingController docTitleController = TextEditingController();
TextEditingController tokenNoController = TextEditingController();
TextEditingController partyNameController = TextEditingController();
TextEditingController startDateController = TextEditingController();
TextEditingController endDateController = TextEditingController();
late String docType = docTypeController.text;
final List<String> items = [
"Residential",
"Commercial",
];
String? selectedValue;
List<DropdownMenuItem<String>> _addDividersAfterItems(List<String> items) {
List<DropdownMenuItem<String>> _menuItems = [];
for (var item in items) {
_menuItems.addAll(
[
DropdownMenuItem<String>(
value: item,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
),
),
//If it's last item, we will not add Divider after it.
if (item != items.last)
const DropdownMenuItem<String>(
enabled: false,
child: Divider(),
),
],
);
}
return _menuItems;
}
List<int> _getDividersIndexes() {
List<int> _dividersIndexes = [];
for (var i = 0; i < (items.length * 2) - 1; i++) {
//Dividers indexes will be the odd indexes
if (i.isOdd) {
_dividersIndexes.add(i);
}
}
return _dividersIndexes;
}
#override
void dispose() {
docStatusController.dispose();
docTypeController.dispose();
super.dispose();
}
#override
Future<Document>? _futureDocument;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(255, 3, 87, 156),
title: Text('Add New Document'),
leading: IconButton(
icon: BackButtonIcon(),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => DocumentPage()))),
),
body:
//Container(
SafeArea(
child: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: (_futureDocument == null)
? buildColumn()
: buildFutureBuilder(),
),
),
),
);
}
Form buildColumn() {
return Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 10,
),
TextFormField(
minLines: 1,
maxLines: 3,
textCapitalization: TextCapitalization.words,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp("[ ',-/ a-z A-Z á-ú Á-Ú 0-9]")),
],
controller: docTitleController,
style: TextStyle(fontSize: 12),
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Document Title',
hintText: 'Document title required'),
// validator: ,
validator:
MultiValidator([RequiredValidator(errorText: 'Required*')]),
),
SizedBox(
height: 20,
),
Wrap(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: TextFormField(
style: TextStyle(fontSize: 12),
minLines: 1,
maxLines: 2,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(14)
],
controller: tokenNoController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
counterText: "",
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Token No',
hintText: 'Token no required'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter token No';
} else if (value.length < 14) {
return 'Please enter 14 digits number';
}
return null;
},
),
),
SizedBox(
width: 15,
),
SizedBox(
height: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width,
child: TextFormField(
minLines: 1,
maxLines: 4,
inputFormatters: [
LengthLimitingTextInputFormatter(50),
FilteringTextInputFormatter.allow(RegExp("[a-z A-Z]")),
],
controller: partyNameController,
keyboardType: TextInputType.name,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Party Name',
hintText: 'Party name required'),
validator:
MultiValidator([RequiredValidator(errorText: 'Required*')]),
),
),
SizedBox(
height: 20,
),
SizedBox(
height: 20,
),
Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: TextFormField(
// autovalidateMode: AutovalidateMode.onUserInteraction,
//FilteringTextInputFormatter.allow(RegExp("[- 0-9]")),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[- 0-9]")),
LengthLimitingTextInputFormatter(10)
],
controller: startDateController,
keyboardType: TextInputType.datetime,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
prefixIcon: Icon(Icons.calendar_month),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Start Date',
hintText: 'yyyy-MM-dd',
),
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
1991), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101),
// onConfirm:widget.onChanged,
).then((pickedDate) {
if (pickedDate != null) {
// print(
// pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('yyyy-MM-dd').format(pickedDate);
print(formattedDate);
setState(() {
startDateController.text = formattedDate;
//set output date to TextField value.
});
print(startDateController.text);
} else {
print("Date is not selected");
}
});
final int dur = int.parse(durationController.text);
var stDate = DateTime.parse(startDateController.text);
var jiffy = Jiffy(stDate).add(
months: dur,
days: -1,
// days: 1095,
);
DateTime d = jiffy.dateTime;
String s = jiffy.format('yyyy-MM-dd');
setState(() {
endDateController.text = s.toString();
});
},
validator: MultiValidator(
[RequiredValidator(errorText: 'Required*')]),
),
),
SizedBox(
width: 13,
),
SizedBox(
// height: MediaQuery.of(context).size.height * 0.06,
width: MediaQuery.of(context).size.width * 0.5,
child: TextFormField(
// maxLength: 8,
// autovalidateMode: AutovalidateMode.onUserInteraction,
autofocus: false,
controller: endDateController,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[- 0-9]")),
LengthLimitingTextInputFormatter(10)
],
keyboardType: TextInputType.datetime,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
// prefixIcon: Icon(Icons.calendar_month),
counterText: "",
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'End Date',
hintText: ' yyyy-MM-dd'),
onTap: () async {},
validator: MultiValidator(
[RequiredValidator(errorText: 'Required*')]),
),
),
],
),
SizedBox(
height: 20,
),
SizedBox(
// height: MediaQuery.of(context).size.height * 0.06,
width: MediaQuery.of(context).size.width,
child: TextFormField(
minLines: 1,
maxLines: 4,
// autovalidateMode: AutovalidateMode.onUserInteraction,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[a-z A-Z 0-9]")),
// FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(50)
],
controller: rentDescController,
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Rent Description',
hintText: 'Rent Description required'),
// validator:
// MultiValidator([RequiredValidator(errorText: 'Required*')]),
),
),
SizedBox(
height: 20,
),
Container(
//alignment: Alignment.center,
height: 35,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Color.fromARGB(255, 3, 89, 168),
),
child: ElevatedButton(
onPressed: () {
formKey.currentState?.validate();
final isValidForm = formKey.currentState!.validate();
if (isValidForm) {
//formKey.currentState?.validate();
// final int docId = int.parse(docIdController.text).toInt();
final String docTitle = docTitleController.text;
final int tokenNo =
int.parse(tokenNoController.text).toInt();
//final String tokenNo = tokenNoController.text;
final String partyName = partyNameController.text;
final String startDate = startDateController.text;
final String endDate = endDateController.text;
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => DocumentPage()));
setState(() {
_futureDocument = createDocument(
// docId,
docTitle,
tokenNo,
startDate,
endDate,
// createdAt,
);
});
// }
}
},
child: const Text("Save"),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 3, 89, 168))),
))
],
),
);
//])
//);
}
FutureBuilder<Document> buildFutureBuilder() {
return FutureBuilder<Document>(
future: _futureDocument,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.docTitle);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
);
}
}
Below is the main.dart file where I have initialised App Id and other required things.Push Notification is coming in app properly and when I tap on the notification it opens the application. But i wants to show push notification when the new document is added to the app and when user tap on that particular notification it should navigate user to the document details page.I have also attached AddNewDocumentFile.dart.
import 'package:flutter/material.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'View/LoginPage.dart';
import 'View/homePageAdmin.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await SharedPrefService.init();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class SharedPrefService {
static late SharedPreferences pref;
static Future<void> init() async {
pref = await SharedPreferences.getInstance();
var usrEmail = pref.getString('email');
}
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
void initState() {
// TODO: implement initState
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
OneSignal.shared.setAppId('e89acaa4-5388-4e3a-bd69-44d197bdcbd7');
OneSignal.shared
.promptUserForPushNotificationPermission()
.then((accepted) {});
}
#override
Widget build(BuildContext context) {
var usrEmail = SharedPrefService.pref.getString('email');
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: usrEmail == null ? LoginPage() : homePageAdmin(),
);
}
}
You just need to call the OneSignal.shared.postNotification(notification); after your createDocument function completes.
Below is the main.dart file where I have initialised App Id and other required things.Push Notification is coming in app properly and when I tap on the notification it opens the application. But i wants to show push notification when the new document is added to the app and when user tap on that particular notification it should navigate user to the document details page.I have also attached AddNewDocumentFile.dart.
import 'package:flutter/material.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'View/LoginPage.dart';
import 'View/homePageAdmin.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await SharedPrefService.init();
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class SharedPrefService {
static late SharedPreferences pref;
static Future<void> init() async {
pref = await SharedPreferences.getInstance();
var usrEmail = pref.getString('email');
}
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
void initState() {
// TODO: implement initState
super.initState();
initPlatformState();
}
Future<void> initPlatformState() async {
OneSignal.shared.setAppId('e89acaa4-5388-4e3a-bd69-44d197bdcbd7');
OneSignal.shared
.promptUserForPushNotificationPermission()
.then((accepted) {});
}
#override
Widget build(BuildContext context) {
var usrEmail = SharedPrefService.pref.getString('email');
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: usrEmail == null ? LoginPage() : homePageAdmin(),
);
}
}
Below is the AddNewDocument.dart file.
Whenever I add any document it should show new push notification to the user and should navigate to document details page.
Is there any method to implement this?
import 'package:AtDocHUB/Controller/DocumentController.dart';
import 'package:AtDocHUB/Model/Document.dart';
import 'package:AtDocHUB/View/Document/DocumentPage.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:form_field_validator/form_field_validator.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:jiffy/jiffy.dart';
DateTime now = DateTime.now();
String formattedDate = DateFormat("yyyy-MM-dd").format(now);
//import 'package:flutter_text_form_field/flutter_text_form_field.dart';
class AddNewDocument extends StatefulWidget {
#override
State createState() {
return _AddNewDocumentState();
}
}
class _AddNewDocumentState extends State<AddNewDocument> {
late final String date1;
late String datainput;
final List<String> items1 = ["Open", "Closed", "Cancel", "Submitted"];
String? selectedItem;
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
TextEditingController docTitleController = TextEditingController();
TextEditingController tokenNoController = TextEditingController();
TextEditingController partyNameController = TextEditingController();
TextEditingController startDateController = TextEditingController();
TextEditingController endDateController = TextEditingController();
late String docType = docTypeController.text;
final List<String> items = [
"Residential",
"Commercial",
];
String? selectedValue;
List<DropdownMenuItem<String>> _addDividersAfterItems(List<String> items) {
List<DropdownMenuItem<String>> _menuItems = [];
for (var item in items) {
_menuItems.addAll(
[
DropdownMenuItem<String>(
value: item,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
),
),
//If it's last item, we will not add Divider after it.
if (item != items.last)
const DropdownMenuItem<String>(
enabled: false,
child: Divider(),
),
],
);
}
return _menuItems;
}
List<int> _getDividersIndexes() {
List<int> _dividersIndexes = [];
for (var i = 0; i < (items.length * 2) - 1; i++) {
//Dividers indexes will be the odd indexes
if (i.isOdd) {
_dividersIndexes.add(i);
}
}
return _dividersIndexes;
}
#override
void dispose() {
docStatusController.dispose();
docTypeController.dispose();
super.dispose();
}
#override
Future<Document>? _futureDocument;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color.fromARGB(255, 3, 87, 156),
title: Text('Add New Document'),
leading: IconButton(
icon: BackButtonIcon(),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => DocumentPage()))),
),
body:
//Container(
SafeArea(
child: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(10),
child: (_futureDocument == null)
? buildColumn()
: buildFutureBuilder(),
),
),
),
);
}
Form buildColumn() {
return Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 10,
),
TextFormField(
minLines: 1,
maxLines: 3,
textCapitalization: TextCapitalization.words,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp("[ ',-/ a-z A-Z á-ú Á-Ú 0-9]")),
],
controller: docTitleController,
style: TextStyle(fontSize: 12),
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Document Title',
hintText: 'Document title required'),
// validator: ,
validator:
MultiValidator([RequiredValidator(errorText: 'Required*')]),
),
SizedBox(
height: 20,
),
Wrap(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: TextFormField(
style: TextStyle(fontSize: 12),
minLines: 1,
maxLines: 2,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(14)
],
controller: tokenNoController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
counterText: "",
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Token No',
hintText: 'Token no required'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter token No';
} else if (value.length < 14) {
return 'Please enter 14 digits number';
}
return null;
},
),
),
SizedBox(
width: 15,
),
SizedBox(
height: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width,
child: TextFormField(
minLines: 1,
maxLines: 4,
inputFormatters: [
LengthLimitingTextInputFormatter(50),
FilteringTextInputFormatter.allow(RegExp("[a-z A-Z]")),
],
controller: partyNameController,
keyboardType: TextInputType.name,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Party Name',
hintText: 'Party name required'),
validator:
MultiValidator([RequiredValidator(errorText: 'Required*')]),
),
),
SizedBox(
height: 20,
),
SizedBox(
height: 20,
),
Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: TextFormField(
// autovalidateMode: AutovalidateMode.onUserInteraction,
//FilteringTextInputFormatter.allow(RegExp("[- 0-9]")),
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[- 0-9]")),
LengthLimitingTextInputFormatter(10)
],
controller: startDateController,
keyboardType: TextInputType.datetime,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
prefixIcon: Icon(Icons.calendar_month),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Start Date',
hintText: 'yyyy-MM-dd',
),
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
1991), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101),
// onConfirm:widget.onChanged,
).then((pickedDate) {
if (pickedDate != null) {
// print(
// pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('yyyy-MM-dd').format(pickedDate);
print(formattedDate);
setState(() {
startDateController.text = formattedDate;
//set output date to TextField value.
});
print(startDateController.text);
} else {
print("Date is not selected");
}
});
final int dur = int.parse(durationController.text);
var stDate = DateTime.parse(startDateController.text);
var jiffy = Jiffy(stDate).add(
months: dur,
days: -1,
// days: 1095,
);
DateTime d = jiffy.dateTime;
String s = jiffy.format('yyyy-MM-dd');
setState(() {
endDateController.text = s.toString();
});
},
validator: MultiValidator(
[RequiredValidator(errorText: 'Required*')]),
),
),
SizedBox(
width: 13,
),
SizedBox(
// height: MediaQuery.of(context).size.height * 0.06,
width: MediaQuery.of(context).size.width * 0.5,
child: TextFormField(
// maxLength: 8,
// autovalidateMode: AutovalidateMode.onUserInteraction,
autofocus: false,
controller: endDateController,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[- 0-9]")),
LengthLimitingTextInputFormatter(10)
],
keyboardType: TextInputType.datetime,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
// prefixIcon: Icon(Icons.calendar_month),
counterText: "",
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'End Date',
hintText: ' yyyy-MM-dd'),
onTap: () async {},
validator: MultiValidator(
[RequiredValidator(errorText: 'Required*')]),
),
),
],
),
SizedBox(
height: 20,
),
SizedBox(
// height: MediaQuery.of(context).size.height * 0.06,
width: MediaQuery.of(context).size.width,
child: TextFormField(
minLines: 1,
maxLines: 4,
// autovalidateMode: AutovalidateMode.onUserInteraction,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp("[a-z A-Z 0-9]")),
// FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(50)
],
controller: rentDescController,
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
errorStyle: const TextStyle(fontSize: 0.05),
border: OutlineInputBorder(),
hintStyle: TextStyle(fontSize: 12),
labelStyle: TextStyle(
fontSize: 12,
),
labelText: 'Rent Description',
hintText: 'Rent Description required'),
// validator:
// MultiValidator([RequiredValidator(errorText: 'Required*')]),
),
),
SizedBox(
height: 20,
),
Container(
//alignment: Alignment.center,
height: 35,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
color: Color.fromARGB(255, 3, 89, 168),
),
child: ElevatedButton(
onPressed: () {
formKey.currentState?.validate();
final isValidForm = formKey.currentState!.validate();
if (isValidForm) {
//formKey.currentState?.validate();
// final int docId = int.parse(docIdController.text).toInt();
final String docTitle = docTitleController.text;
final int tokenNo =
int.parse(tokenNoController.text).toInt();
//final String tokenNo = tokenNoController.text;
final String partyName = partyNameController.text;
final String startDate = startDateController.text;
final String endDate = endDateController.text;
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => DocumentPage()));
setState(() {
_futureDocument = createDocument(
// docId,
docTitle,
tokenNo,
startDate,
endDate,
// createdAt,
);
});
// }
}
},
child: const Text("Save"),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 3, 89, 168))),
))
],
),
);
//])
//);
}
FutureBuilder<Document> buildFutureBuilder() {
return FutureBuilder<Document>(
future: _futureDocument,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.docTitle);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
);
}
}
I'm new in flutter, Can someone help me how to access the ss variable or the setState protected method of a State class I created inside of a Widget outside that class. I'm trying to organize codes that's why I extract the Widget out of that State class.
main.dart
import 'package:flutter/material.dart';
import 'screens.dart';
void main() => runApp(const App());
enum ScreenState {
home,
login,
register,
}
class App extends StatefulWidget {
const App({super.key});
#override
State<StatefulWidget> createState() => _AppState();
}
class _AppState extends State<App> {
ScreenState ss = ScreenState.register;
#override
Widget build(BuildContext context) {
switch (ss) {
case ScreenState.register:
return registerPage();
default:
return registerPage();
}
}
}
screens.dart
import 'package:flutter/material.dart';
import 'components.dart';
Widget registerPage() {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.blueGrey,
),
home: Scaffold(
appBar: appHeader(),
body: registerForm(),
),
);
}
components.dart
import 'package:flutter/material.dart';
PreferredSizeWidget appHeader() {
return AppBar(
title: const Text('Register Form'),
actions: [
TextButton(
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Home',
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.underline,
),
),
),
onPressed: () {/* _AppState.ss = ScreenState.home; */},
),
TextButton(
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Text(
'Log In',
style: TextStyle(
color: Colors.white,
decoration: TextDecoration.underline,
),
),
),
onPressed: () {/* _AppState.ss = ScreenState.login; */},
),
],
);
}
Widget registerForm() {
return SingleChildScrollView(
child: Center(
child: Container(
constraints: const BoxConstraints(
minWidth: 300,
maxWidth: 500,
),
padding: const EdgeInsets.symmetric(
vertical: 40,
horizontal: 20,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text('Account Information'),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'Create username',
hintText: 'Enter username',
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
validator: (value) {
return value!.isEmpty ? 'Please create a username' : null;
},
),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.visiblePassword,
decoration: const InputDecoration(
labelText: 'Create password',
hintText: 'Enter password',
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
validator: (value) {
return value!.isEmpty ? 'Please create a password' : null;
},
),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.visiblePassword,
decoration: const InputDecoration(
labelText: 'Re-enter password',
hintText: 'Enter password',
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
validator: (value) {
return value!.isEmpty ? 'Please re-enter password' : null;
},
),
const SizedBox(
height: 30,
),
const Text('Personal information'),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'Last name',
hintText: 'Enter your last name',
prefixIcon: Icon(Icons.edit),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
validator: (value) {
return value!.isEmpty ? 'Please enter your last name' : null;
},
),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'First name',
hintText: 'Enter your first name',
prefixIcon: Icon(Icons.edit),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
validator: (value) {
return value!.isEmpty ? 'Please enter your first name' : null;
},
),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'Middle name (optional)',
hintText: 'Enter your middle name',
prefixIcon: Icon(Icons.edit),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
),
const SizedBox(
height: 10,
),
TextFormField(
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'Suffix (optional)',
hintText: 'Enter your suffix',
prefixIcon: Icon(Icons.edit),
border: OutlineInputBorder(),
),
onChanged: (String value) {},
),
],
),
),
),
);
}
I commented out the code that I expect to work but it turns out to be an error. Please teach me on how to fix the error. Thank you so much.
You can change your state to this:
class _AppState extends State<App> {
ScreenState ss = ScreenState.register;
#override
Widget build(BuildContext context) {
switch (ss) {
case ScreenState.register:
return registerPage(changeState);
default:
return registerPage(changeState);
}
}
void changeState(ScreenState s) {
setState(() {
ss = s;
});
}
}
Then registerPage like
Widget registerPage(Function(ScreenState) f) {
with
appBar: appHeader(f),
and then appHeader like
PreferredSizeWidget appHeader(Function(ScreenState) f) {
and then this for example:
onPressed: () {f(ScreenState.home);},
I am fairly new to flutter and dart as a whole and i have been creating an application that interacts with firebase, this is the code for my registration screen. When clicking on a text input field the keyboard ends up blocking the forms and you cannot see what you are entering... I have tried many things such as singlechildscroll view and resizeToAvoidBottomInset and nothing seems to be affecting it. Anyone have any idea?
Register page:
class RegistrationPage extends StatefulWidget{
const RegistrationPage({Key? key}) : super(key: key);
#override
_RegistrationPageState createState() => _RegistrationPageState();
}
class _RegistrationPageState extends State<RegistrationPage> {
final _auth = FirebaseAuth.instance;
//our form key
final _formKey = GlobalKey<FormState>();
//editing controllers
final firstNameEditingController = new TextEditingController();
final lastNameEditingController = new TextEditingController();
final emailEditingController = new TextEditingController();
final passwordEditingController = new TextEditingController();
final confirmPasswordEditingController = new TextEditingController();
final cityEditingController = new TextEditingController();
#override
Widget build(BuildContext context) {
//firstname Field
final firstNameField = TextFormField(
autofocus: false,
controller: firstNameEditingController,
keyboardType: TextInputType.name,
validator: (value)
{
RegExp regEx = new RegExp(r'^.{2,}$');
if(value!.isEmpty)
{
return("First Name cannot be empty");
}
if(!regEx.hasMatch(value))
{
return("Enter valid name(min 2 characters)");
}
return null;
},
onSaved: (value) {
firstNameEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.face),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "First name",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
));
//Last name Field
final lastNameField = TextFormField(
autofocus: false,
controller: lastNameEditingController,
keyboardType: TextInputType.name,
validator: (value)
{
RegExp regEx = new RegExp(r'^.{2,}$');
if(value!.isEmpty)
{
return("Last Name cannot be empty");
}
return null;
},
onSaved: (value) {
lastNameEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.face),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Last name",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
));
//email Field
final emailField = TextFormField(
autofocus: false,
controller: emailEditingController,
keyboardType: TextInputType.text,
validator: (value)
{
if(value!.isEmpty)
{
return("Please enter your email");
}
//email validation
if(!RegExp("^[a-zA-Z0-9+_.-]+#[a-zA-Z0-9.-]+.[a-z]").hasMatch(value))
{
return("Please enter a valid email");
}
},
onSaved: (value) {
emailEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.account_circle),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Email address",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
));
//City
final cityField = TextFormField(
autofocus: false,
controller: cityEditingController,
keyboardType: TextInputType.text,
validator: (value)
{
RegExp regEx = new RegExp(r'^.{2,}$');
if(value!.isEmpty)
{
return("City cannot be empty");
}
return null;
},
onSaved: (value) {
cityEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.location_on),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "City",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
));
//password Field
final passwordField = TextFormField(
autofocus: false,
controller: passwordEditingController,
obscureText: true,
validator: (value)
{
RegExp regEx = new RegExp(r'^.{6,}$');
if(value!.isEmpty)
{
return("Password is required!");
}
if(!regEx.hasMatch(value))
{
return("Enter valid password(Min. 6 characters)");
}
},
onSaved: (value) {
passwordEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
)
);
//Confirm password Field
final confirmPasswordField = TextFormField(
autofocus: false,
controller: confirmPasswordEditingController,
obscureText: true,
validator: (value)
{
if (confirmPasswordEditingController.text != passwordEditingController.text){
return "Passwords do not match";
}
return null;
},
onSaved: (value) {
confirmPasswordEditingController.text = value!;
},
textInputAction: TextInputAction.done,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Confirm Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
)
)
);
final registerButton = Material(
elevation: 5,
borderRadius: BorderRadius.circular(30),
color: Colors.pink,
child: MaterialButton(
padding: EdgeInsets.fromLTRB(20, 15, 20, 15),
minWidth: MediaQuery.of(context).size.width,
onPressed: () {
signUp(emailEditingController.text, passwordEditingController.text);
},
child: Text("Register", textAlign:TextAlign.center,
style: TextStyle(
fontSize: 20, color: Colors.white, fontWeight: FontWeight.bold),
),
)
);
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back, color: Colors.pink),
onPressed:(){
Navigator.of(context).pop();
},
)
),
body: Center(
child: new SingleChildScrollView(
reverse: true,
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(36.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 180,
child: Image.asset(
"assets/logo.png",
fit: BoxFit.contain,
)),
SizedBox(height: 30),
firstNameField,
SizedBox(height: 10),
lastNameField,
SizedBox(height: 10),
emailField,
SizedBox(height:10),
cityField,
SizedBox(height: 10),
passwordField,
SizedBox(height: 10),
confirmPasswordField,
SizedBox(height: 10),
registerButton,
],
),
),
)
)
)
)
);
Main.dart
Future<void> main() async { //asynchronous programming, waits for events to happen before producing a result
WidgetsFlutterBinding.ensureInitialized(); //in this case initializes the Firebase waits for a callback
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget { //Used to build application
const MyApp({Key? key}) : super(key: key); //Anything in here runs when the app starts
ThemeData _appTheme() { //The login screen is first to load but this is not in place of main as it is bad practise
final ThemeData base = ThemeData.light(); //Will be used for implementation of light and dark theme
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: travelBlue,
onPrimary: Colors.white,
secondary: travelBlue,
error: travelErrorRed,
),
);
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp( //Runs when main.dart is executed
title: 'Login',
theme: _appTheme(),
home: LoginPage(),
);
}
}
try wrapping your column with padding
Padding(
padding: MediaQuery.of(context).viewInsets,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// your entire form widgets here
]
)
)