Related
I'm creating a Flutter Widget and when I try to remove an item from the list I'm using, it always removes the last one, I was thinking it could be a Key problem, but nothing suits it, do anyone know how I could solve this?
The code
create_game.dart
import 'package:flutter/material.dart';
import 'package:pontinho/components/custom_input.dart';
class CreateGame extends StatefulWidget {
const CreateGame({super.key});
#override
State<CreateGame> createState() => _CreateGameState();
}
class _CreateGameState extends State<CreateGame> {
List<String> names = [''];
void changeName(int nameIndex, String change) {
setState(() {
names[nameIndex] = change;
});
}
void removeName(int nameIndex) {
print(names);
print(nameIndex);
setState(() {
names.removeAt(nameIndex);
});
}
ListView createNamesInput() {
return ListView.builder(
itemCount: names.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
key: ObjectKey(index),
title: CustomInput(
key: ObjectKey(index),
labelText: "Nome",
onChanged: (String changed) => changeName(index, changed),
text: names[index],
onRemoved: () => removeName(index),
),
);
},
);
// return names
// .asMap()
// .entries
// .map((el) => CustomInput(
// key: ObjectKey('${el.key}'),
// labelText: "Nome",
// onChanged: changeName,
// index: el.key,
// text: names[el.key],
// onRemoved: removeName,
// ))
// .toList();
}
void addName() {
setState(() {
names.add('');
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: GestureDetector(
onTap: (() => Navigator.pop(context)),
child: const Icon(
Icons.arrow_back,
color: Colors.black,
size: 40,
),
),
backgroundColor: Colors.white,
titleTextStyle: const TextStyle(
color: Colors.black,
fontSize: 20,
),
title: const Text("CRIE SEU JOGO"),
),
body: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
// child: createNamesInput(),
child: Column(
children: [
createNamesInput(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: addName,
child: Row(
children: const [
Icon(Icons.add),
Text('Adicionar Jogador'),
],
),
),
],
),
),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: () => print('Iniciar!'),
child: const Text('Iniciar!'),
),
)
],
),
),
);
}
}
custom_input.dart
import 'package:flutter/material.dart';
typedef OneArgumentCallback = void Function(String changed);
class CustomInput extends StatefulWidget {
final OneArgumentCallback onChanged;
final VoidCallback onRemoved;
final String labelText;
final String text;
const CustomInput({
super.key,
required this.onChanged,
required this.labelText,
required this.text,
required this.onRemoved,
});
#override
State<CustomInput> createState() => _CustomInputState();
}
class _CustomInputState extends State<CustomInput> {
late final TextEditingController inputController;
#override
void initState() {
super.initState();
inputController = TextEditingController(text: widget.text);
}
void changeContent(String value) {
widget.onChanged(
value,
);
}
#override
Widget build(BuildContext context) {
return TextFormField(
key: widget.key,
controller: inputController,
textDirection: TextDirection.ltr,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: widget.labelText,
suffixIcon: IconButton(
onPressed: () => widget.onRemoved(),
icon: const Icon(
Icons.close,
color: Colors.red,
),
),
),
autocorrect: false,
onChanged: (value) => changeContent(value),
);
}
}
Indeed it is a key issue, you have to create a combined key that must be unique for each item, I merged the index with names[index],
CustomInput(
key: ObjectKey('$index:${names[index]}'),
labelText: "Nome",
onChanged: (String changed) => changeName(index, changed),
text: names[index],
onRemoved: () => removeName(index),
),
note that if you try this code alone the textfield will lose focus because the key has changed, this will be solved by removing the setState inside the onChange
void changeName(int nameIndex, String change) {
names[nameIndex] = change;
}
here you don't need setState because the UI will be updated by default when you are typing in the textfield
I hope I made it clear
I was thinking it could be a Key problem
That's correct; You need to use names[index] as the value for your Key:
ListTile(
key: ObjectKey(names[index]),
title: CustomInput(
I am trying to send a data from ontap listview screen to form screen like image below. I have searched many references on google but I can't find any references that can help me, if you can provide solutions or references, I will greatly appreciate it.
enter image description here
This is my sample code (ListPage Screen) :
const ListPage({Key? key}) : super(key: key);
#override
State<ListPage> createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
TextEditingController textFieldController = TextEditingController();
var _controller = TextEditingController();
late bool searching, error;
var data;
late String query;
String dataurl = "https://www.something.co.id/mobile/search_data.php";
#override
void initState() {
searching = true;
error = false;
query = "";
super.initState();
}
void getSuggestion() async {
//get suggestion function
var res = await http
.post((Uri.parse(dataurl + "?query=" + Uri.encodeComponent(query))));
//in query there might be unwant character so, we encode the query to url
if (res.statusCode == 200) {
setState(() {
data = json.decode(res.body);
//update data value and UI
});
} else {
//there is error
setState(() {
error = true;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: AppLayout.getHeight(100),
automaticallyImplyLeading: false,
title: searchField(),
backgroundColor: Styles.background,
elevation: 0.0,
),
body: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
child: data == null
? Container(
padding: EdgeInsets.all(20),
child: searching
? Text("Please wait")
: Text("Search any location")
//if is searching then show "Please wait"
//else show search peopels text
)
: Container(
child: searching
? showSearchSuggestions()
: Text("Find any location"),
)
// if data is null or not retrived then
// show message, else show suggestion
),
),
);
}
Widget showSearchSuggestions() {
List suggestionlist = List.from(data["data"].map((i) {
return SearchSuggestion.fromJSON(i);
}));
//serilizing json data inside model list.
return Column(
children: suggestionlist.map((suggestion) {
return InkResponse(
// onTap: () {
// //when tapped on suggestion
// print(suggestion.id); //pint student id
// },
child: GestureDetector(
onTap: () {
_sendDataBack(context);
},
child: SizedBox(
width: double.infinity, //make 100% width
child: Card(
child: Container(
decoration: BoxDecoration(color: Styles.background),
padding: EdgeInsets.all(15),
child: Text(
suggestion.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
);
}).toList(),
);
}
// get the text in the TextField and send it back to the FirstScreen
void _sendDataBack(BuildContext context) {
String textToSendBack = textFieldController.text;
Navigator.pop(context, textToSendBack);
}
Widget searchField() {
//search input field
return Container(
height: 50,
child: TextField(
controller: _controller,
autofocus: true,
style: Styles.textStyle,
decoration: InputDecoration(
hintStyle: TextStyle(color: Styles.colorDeepGrey),
hintText: "Search Location...",
prefixIcon: Icon(Icons.search),
suffixIcon: _controller.text.length > 0
? IconButton(
onPressed: () {
_controller.clear();
setState(() {});
},
icon: Icon(Icons.cancel, color: Colors.grey))
: null,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Styles.colorLightBlack.withOpacity(0.20),
width: 2,
),
borderRadius: BorderRadius.circular(4),
), //under line border, set OutlineInputBorder() for all side border
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Styles.primaryColor,
width: 1,
),
borderRadius: BorderRadius.circular(4),
), // focused border color
), //decoration for search input field
onChanged: (value) {
query = value; //update the value of query
getSuggestion(); //start to get suggestion
},
),
);
}
}
//serarch suggestion data model to serialize JSON data
class SearchSuggestion {
String id, name;
SearchSuggestion({required this.id, required this.name});
factory SearchSuggestion.fromJSON(Map<String, dynamic> json) {
return SearchSuggestion(
id: json["id"],
name: json["name"],
);
}
}
Sample Code NextPage Screen :
class NextPage extends StatefulWidget {
#override
_NextPageState createState() => _NextPageState();
}
class _NextPageState extends State<NextPage> {
int _currentStep = 0;
StepperType stepperType = StepperType.vertical;
String text = 'Text';
var _controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text('Flutter Stepper Demo'),
centerTitle: true,
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Stepper(
type: stepperType,
physics: ScrollPhysics(),
currentStep: _currentStep,
onStepTapped: (step) => tapped(step),
onStepContinue: continued,
onStepCancel: cancel,
steps: <Step>[
//Form Pengirim
Step(
title: new Text('Location'),
content: Column(
children: <Widget>[
SizedBox(
height: 50,
child: TextField(
onTap: () {
_awaitReturnValueFromSecondScreen(context);
},
controller: _controller,
autofocus: true,
onChanged: (text) {
setState(() {});
},
style: Styles.textStyle,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
hintText: 'Location',
contentPadding:
EdgeInsets.only(left: 15, right: 15),
hintStyle: TextStyle(color: Styles.colorDeepGrey),
suffixIcon: _controller.text.length > 0
? IconButton(
onPressed: () {
_controller.clear();
setState(() {});
},
icon: Icon(Icons.cancel,
color: Colors.grey))
: null,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
Styles.colorLightBlack.withOpacity(0.20),
width: 2,
),
borderRadius: BorderRadius.circular(4),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Styles.primaryColor,
width: 1,
),
borderRadius: BorderRadius.circular(4),
),
),
),
),
],
),
isActive: _currentStep >= 0,
state: _currentStep >= 0
? StepState.complete
: StepState.disabled,
),
],
),
),
],
),
),
);
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
// start the SecondScreen and wait for it to finish with a result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DataAlamat(),
));
// after the SecondScreen result comes back update the Text widget with it
setState(() {
text = result;
});
}
tapped(int step) {
setState(() => _currentStep = step);
}
continued() {
_currentStep < 2 ? setState(() => _currentStep += 1) : null;
}
cancel() {
_currentStep > 0 ? setState(() => _currentStep -= 1) : null;
}
}
Pass the tapped item value to the next page via named parameter of other page class.
class ListPage extends StatelessWidget {
const ListPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return ListTile(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return NextPage(value: index);
},
));
},
title: Text(index.toString()),
);
},
),
);
}
}
class NextPage extends StatelessWidget {
final int value;
const NextPage({Key? key, required this.value}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(value.toString()),
),
);
}
}
Example in ListView screen, you have a variable called List<String> listLocations. Your ListView widget be like:
ListView.builder(
itemCount: listLocations.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return SecondScreen(listLocations[index]);
},
)),
child: ...
);
}
}
And your SecondScreen is a StatefulWidget (well it is a Form screen, so it would be Stateful, not Stateless, use TextEditingController in Form widget):
import 'package:flutter/material.dart';
class SecondScreen extends StatefulWidget {
final String location;
SecondScreen(this.location, {Key? key}) : super(key: key);
#override
State<SecondScreen> createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
var _textEditingController = TextEditingController();
#override
void initState() {
_textEditingController.text = widget.location;
super.initState();
}
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
You need to pass the location value in init state, and don't forget to dispose it.
So i made an Create methode for my Sqlapi. I made a test project where the create method worked. So now i try to implement it in to the real application but now i get some strange error (Shown in the image). Any help would be amazing.
Error:
All the containers are the same.
My flutter code:
import 'package:flutter/material.dart';
import 'package:schepp/main.dart';
import '../Data_provider/api_service.dart';
import '../Model/KlantModel.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const appTitle = 'Inloggen';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: appTitle,
home: Registerenpage(
title: appTitle
),
);
}
}
class Registerenpage extends StatefulWidget {
const Registerenpage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_RegisterenpageState createState() => _RegisterenpageState();
}
class _RegisterenpageState extends State<Registerenpage> {
_RegisterenpageState();
final ApiService api = ApiService();
final _addFormKey = GlobalKey<FormState>();
final _mailaddres = TextEditingController();
final _wachtwoord = TextEditingController();
final _klantvoornaam = TextEditingController();
final _tussenvoegsel = TextEditingController();
final _klantachternaam = TextEditingController();
final _bedrijfsnaam = TextEditingController();
final _telefoonnummer = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: SingleChildScrollView(
child: Column(
key: _addFormKey,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children:<Widget>[
Container(
margin: const EdgeInsets.only(left: 10.0, right: 10.0, top:80.0),
child: const Text("Registeren",style: TextStyle(color:Colors.white,fontSize:20),),
),
Container(),
Container(),
Container(),
Container(),
Container(
margin: const EdgeInsets.only(left: 50.0, right: 50.0),
child: Column(
children: <Widget>[
const Text('Mailaddres'),
TextFormField(
controller: _mailaddres,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
labelText: 'Mailaddress *', labelStyle: TextStyle(color:Colors.white),
),
style: const TextStyle(color: Colors.white, fontSize: 16.0,),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter mailaddres';
}
return null;
},
onChanged: (value) {},
),
],
),
),
Container(),
Container(),
Container(
padding: const EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
ElevatedButton(
child: const Text('Registeren', style: TextStyle(color:Colors.white,fontSize:16)),
onPressed: () {
if (_addFormKey.currentState!.validate()) {
_addFormKey.currentState!.save();
api.createCase(Cases(mailaddres: _mailaddres.text, wachtwoord: _wachtwoord.text,
klantvoornaam: _klantvoornaam.text, tussenvoegsel: _tussenvoegsel.text,
klantachternaam: _klantachternaam.text, bedrijfsnaam: _bedrijfsnaam.text,
telefoonnummer: _telefoonnummer.text));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Loginpagina(title: widget.title),
),
);
}
},
style: ElevatedButton.styleFrom(
primary: Colors.deepOrange,
padding: const EdgeInsets.all(20),
),
)
],
),
),
]
),
),
);
}
}
This is my KlantModel:
import 'dart:convert';
List<Cases> welcomeFromJson(String str) => List<Cases>.from(json.decode(str).map((x) => Cases.fromJson(x)));
String welcomeToJson(List<Cases> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Cases {
Cases({
this.klantId,
this.mailaddres,
this.wachtwoord,
this.klantvoornaam,
this.tussenvoegsel,
this.klantachternaam,
this.bedrijfsnaam,
this.telefoonnummer,
});
int? klantId;
String? mailaddres;
String? wachtwoord;
String? klantvoornaam;
String? tussenvoegsel;
String? klantachternaam;
String? bedrijfsnaam;
String? telefoonnummer;
factory Cases.fromJson(Map<String, dynamic> json) => Cases(
klantId: json["KlantId"],
mailaddres: json["Mailaddres"],
wachtwoord: json["Wachtwoord"],
klantvoornaam: json["Klantvoornaam"],
tussenvoegsel: json["Tussenvoegsel"],
klantachternaam: json["Klantachternaam"],
bedrijfsnaam: json["Bedrijfsnaam"],
telefoonnummer: json["Telefoonnummer"],
);
Map<String, dynamic> toJson() => {
"KlantId": klantId,
"Mailaddres": mailaddres,
"Wachtwoord": wachtwoord,
"Klantvoornaam": klantvoornaam,
"Tussenvoegsel": tussenvoegsel,
"Klantachternaam": klantachternaam,
"Bedrijfsnaam": bedrijfsnaam,
"Telefoonnummer": telefoonnummer,
};
}
--------! UPDATE --------
I get this error when i press on the ElevatedButton in the last container. i'am trying to sent the information to another dart file which updates it to a rest api. if i'm correct it gets stuck at the if (_addFormKey.currentState!.validate())
You are not using Form widget. Wrap your Column with Form and use the _addFormKey on Form widget instead of Column.
child: Form(
key: _addFormKey,
child: Column(
And validator
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter mailaddres';
}
return null;
},
And
onPressed: () {
final isValidate = _addFormKey.currentState?.validate();
if (isValidate == null) {
print("got Null isValidate");
return;
}
if (isValidate) {
_addFormKey.currentState!.save();
More about Form
I just started to learn Flutter/Dart as my lecturer ask me to do some simple mobile app of e-ticket where the user need to choose from the radio button the movie that have value price for each radio button . Then the user need to input quantity value then click the submit button where the result of calculation need to be shown in another result page. Below is the picture of the assignment and some code that i have done. But i got stuck in the calculation part where i did not find any solution to this. hope anyone can help me with this.
main dart
main dart
import 'package:flutter/material.dart';
import 'result.dart';
import 'customer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
enum SingingCharacter { avengers, batman, kimetsu }
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final txtName= TextEditingController();
final txtEmail = TextEditingController();
final txtQuantity = TextEditingController();
SingingCharacter? _character = SingingCharacter.avengers;
void _gotoResultScreen(){
Customer c= Customer(txtName.text, txtEmail.text, int.parse(txtQuantity.text));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultPage(
title:"Result Screen",
content:"Congratulation! You've reached this page",
customer: c,)
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Name'),
TextField(
controller: txtName,
),
Text('Email'),
TextField(
controller: txtEmail,
),
Text('Choose a movie:'),
ListTile(
title: const Text('AVENGERS(RM20)'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.avengers,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
),
ListTile(
title: const Text('BATMAN(RM10)'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.batman,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
}
),
),
ListTile(
title: const Text('KIMETSU NO YAIBA(RM12)'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.kimetsu,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
}
),
),
Text('quantity'),
TextField(
controller: txtQuantity,
),
RaisedButton(
onPressed:_gotoResultScreen,
//do something
child: new Text('Calculate'),
),
],
),
),
);
}
}
result.dart
import 'package:flutter/material.dart';
import 'customer.dart';
class ResultPage extends StatefulWidget {
const ResultPage({Key? key, required this.title, required this.content, required this.customer}) : super(key: key);
final String title;
final String content;
final Customer customer;
#override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(widget.content,),
Text("Name:" + widget.customer.name),
Text("Email: " + widget.customer.email),
Text("Quantity:" + widget.customer.quantity.toString()),
],
),
),
);
}
}
customer.dart
class Customer {
final String name;
final String email;
final int quantity;
const Customer(this.name, this.email, this.quantity);
}
You can use Map<String, int> to store the movie name and its price. Then create the List<Map<String, int>> and use the .map() function on this list to covert the list into listTile.
You should pass necessary arguments to result page. You passed Customer, that is ok. But movie info is missing. For example:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultPage(
title:"Result Screen",
content:"Congratulation! You've reached this page",
customer: c,
movieName: ...,
price: ...,
)
),
);
And of course you should edit Result page accordingly.
After go to result page. Use price data and multiply quantity. This is gonna be final price.
However, this is not a good approach, Instead of using this kind of solutions, I recommend create a Movie class like Customer. Also quantity info should not be a field of Customer, it should be field of Movie class.
You can calculate the price like this.
Let's say your _character value is SingingCharacter.avengers
int price = getPriceFromCharacter(_character) * int.parse(txtQuantity.text);
getPriceFromCharacter(SingingCharacter? character) {
switch(character) {
case SingingCharacter.avengers: {
return 20;
}
case SingingCharacter.batman: {
return 10;
}
case SingingCharacter.kimetsu: {
return 12;
}
}
}
void _gotoResultScreen(){
int price = getPriceFromCharacter(_character) * int.parse(txtQuantity.text);
Customer c= Customer(txtName.text, txtEmail.text, int.parse(txtQuantity.text));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultPage(
title:"Result Screen",
content:"Congratulation! You've reached this page",
customer: c,)
),
);
}
This is the answer to the solution above.
main.dart
import 'package:flutter/material.dart';
import 'calculate.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'eTicket'),
);
}
}
enum ticketName {Avengers,Batman,Kimetsu}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _name = new TextEditingController();
final TextEditingController _email = new TextEditingController();
final TextEditingController _quantity = new TextEditingController();
final formKey = GlobalKey<FormState>();
String name = "";
String email = "";
String quantity = "";
String movieName = "";
String ticketPrice = "";
ticketName? _movieName = ticketName.Avengers;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric (
horizontal: 20,),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Name',textAlign: TextAlign.left),
const Padding (
padding: EdgeInsets.symmetric(vertical: 5.0),
),
TextFormField(
validator: (value) {
if(value!.isEmpty ){
return 'Please Enter Your Name';
}
},
onSaved: (value){
name = value!;
},
controller: _name,
decoration: InputDecoration(
hintText: '',
filled: true,
fillColor: Colors.black.withOpacity (0.2),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular (10))),
),
const SizedBox(
height: 5,
),
const Padding (
padding: EdgeInsets.symmetric(vertical: 5.0),
),
const Text('Email',textAlign: TextAlign.left),
const Padding (
padding: EdgeInsets.symmetric(vertical: 5.0),
),
TextFormField(
validator: (value) {
if(value!.isEmpty || !value.contains('#') || !value.contains('.com'))
{
return 'Email is Invalid';
}
},
onSaved: (value){
email = value!;
},
controller: _email,
decoration: InputDecoration(
hintText: '',
filled: true,
fillColor: Colors.black.withOpacity (0.2),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular (10))),
),
const SizedBox(
height: 5,
),
const Padding (
padding: EdgeInsets.symmetric(vertical: 10.0),
),
//add radio button here
const Text("Choose a Movie: "),
const Padding (
padding: EdgeInsets.symmetric(vertical: 10.0),
),
Row(children: [
Radio(
value: ticketName.Avengers,
groupValue: _movieName,
onChanged: (ticketName?value){
setState(() {
_movieName = value;
movieName = 'Avengers (RM 20.00)';
ticketPrice = '20';
});
},
),
const SizedBox (width: 5.0,),
const Text("Avengers (RM 20.00)"),
],),
Row(children: [
//add radio button here
Radio(
value: ticketName.Batman,
groupValue: _movieName,
onChanged: (ticketName? value){
setState(() {
_movieName = value;
movieName = 'Batman (RM 10.00) ';
ticketPrice = '10';
});
},
),
const SizedBox (width: 5.0,),
const Text("Batman (RM 10.00)"),
],),
Row(children: [
//add radio button here
Radio(
value: ticketName.Kimetsu,
groupValue: _movieName,
onChanged: (ticketName? value){
setState(() {
_movieName = value;
movieName = 'Kimetsu No Yaiba (RM 12.00) ';
ticketPrice = '12';
});
},
),
const SizedBox (width: 5.0,),
const Text("Kimetsu No Yaiba (RM 12.00)"),
],),
const Padding (
padding: EdgeInsets.symmetric(vertical: 10.0),
),
const Text('Quantity',textAlign: TextAlign.left),
const Padding (
padding: EdgeInsets.symmetric(vertical: 5.0),
),
TextFormField(
validator: (value) {
if(value!.isEmpty || value.length < 0){
return 'Please Enter the Correct Quantity';
}
},
onSaved: (value){
var quantity = int.parse(value!);
},
controller: _quantity,
decoration: InputDecoration(
hintText: 'Quantity',
filled: true,
fillColor: Colors.black.withOpacity (0.2),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular (10))),
),
const SizedBox(
height: 5,
),
const Padding (
padding: EdgeInsets.symmetric(vertical: 10.0),
),
ElevatedButton(
onPressed: (){
final isValid = formKey.currentState?.validate();
if(isValid!=null) {
formKey.currentState!.save();
Navigator.push(
context, MaterialPageRoute(builder: (context) => calculatePrice(name: _name.text, movieName: movieName, ticketPrice: ticketPrice, email: _email.text, quantity: _quantity.text)));
}
},
child: const Text('Calculate'),
),
],
),
),
),
),
);
}
}
calculate.dart
import 'package:flutter/material.dart';
import 'main.dart';
class calculatePrice extends StatelessWidget {
late String name, email, movieName, ticketPrice, quantity;
calculatePrice({required this.name, required this.email, required this.quantity, required this.movieName, required this.ticketPrice});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("eTicket"),
),
body: Center(
child: Padding(
padding: const EdgeInsets.symmetric (
horizontal: 20,),
child: Column(
mainAxisAlignment: MainAxisAlignment.center ,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Name : $name'),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text('Email $email'),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text('Movie : $movieName'),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text('Quantity $quantity'),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text('Total Price : ${int.parse(quantity) * int.parse(ticketPrice) }'),
const Padding ( padding: EdgeInsets.symmetric(vertical: 10.0),),
ElevatedButton(
onPressed: (){
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MyApp()));
},
child: const Text('Back'),
),
],
),
)
),
);
}
}
This is the answer based on the above code. Thank you for the suggestion and helps from the others.
main.dart
import 'package:flutter/material.dart';
import 'result.dart';
import 'customer.dart';
import 'movie.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'e-Ticket',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: const MyHomePage(title: 'Movie e-Ticket'),
);
}
}
enum SingingCharacter { avengers, batman, kimetsu }
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final txtName = TextEditingController();
final txtEmail = TextEditingController();
final txtQuantity = TextEditingController();
SingingCharacter? _character = SingingCharacter.avengers;
getPriceFromCharacter(SingingCharacter? character, int quantity) {
switch (character) {
case SingingCharacter.avengers:
{
return 20 * quantity;
}
case SingingCharacter.batman:
{
return 10 * quantity;
}
case SingingCharacter.kimetsu:
{
return 12 * quantity;
}
}
}
getMovieName(SingingCharacter? character) {
switch (character) {
case SingingCharacter.avengers:
{
return "Avengers";
}
case SingingCharacter.batman:
{
return "Batman";
}
case SingingCharacter.kimetsu:
{
return "Kimetsu";
}
}
}
// this to go to result screen and show the result
void _gotoResultScreen() {
int price = getPriceFromCharacter(_character, int.parse(txtQuantity.text));
String movieName = getMovieName(_character);
Customer c = Customer(txtName.text, txtEmail.text);
Movie m = Movie(int.parse(txtQuantity.text), price, movieName);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultPage(
title: "Total Price",
content: "Here is your details",
customer: c,
movie: m,
)),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
const Text('Name', textAlign: TextAlign.left),
TextField(
controller: txtName,
decoration: InputDecoration(
hintText: '',
filled: true,
fillColor: Colors.green.withOpacity(0.2),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none
)
),
),
const Text('Email'),
TextField(
controller: txtEmail,
decoration: InputDecoration(
hintText: '',
filled: true,
fillColor: Colors.green.withOpacity(0.2),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide.none
),)
),
// This is for radio button in list tile
const Text('Choose a movie:'),
ListTile(
title: const Text('AVENGERS(RM20)'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.avengers,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
},
),
),
ListTile(
title: const Text('BATMAN(RM10)'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.batman,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
}),
),
ListTile(
title: const Text('KIMETSU NO YAIBA(RM12)'),
leading: Radio<SingingCharacter>(
value: SingingCharacter.kimetsu,
groupValue: _character,
onChanged: (SingingCharacter? value) {
setState(() {
_character = value;
});
}),
),
// Input Quantity of the movie
const Text('quantity'),
TextField(
controller: txtQuantity,
decoration: InputDecoration(
hintText: '',
filled: true,
fillColor: Colors.green.withOpacity(0.2),
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(10)
)
),
),
// When user click calculate Button
ElevatedButton(
onPressed: _gotoResultScreen,
child: const Text('Calculate'),
),
],
),
),
);
}
}
movie.dart
class Movie {
final int quantity;
final int totalPrice;
final String movieName;
const Movie( this.quantity,this.totalPrice,this.movieName);
}
result.dart
import 'package:e_tickets/main.dart';
import 'package:flutter/material.dart';
import 'customer.dart';
import 'movie.dart';
class ResultPage extends StatefulWidget {
const ResultPage({Key? key, required this.title, required this.content, required this.customer, required this.movie}) : super(key: key);
final String title;
final String content;
final Customer customer;
final Movie movie;
#override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(widget.content,),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text("Name: " + widget.customer.name),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text("Email: " + widget.customer.email),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text("Quantity: " + widget.movie.quantity.toString()),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text("Movie Name: " + widget.movie.movieName),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
Text("Total Price: RM" + widget.movie.totalPrice.toString()),
const Padding ( padding: EdgeInsets.symmetric(vertical: 5.0),),
ElevatedButton(
onPressed: (){
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MyApp()));
},
child: const Text('Back'),
),
],
),
),
);
}
}
customer.dart
class Customer {
final String name;
final String email;
const Customer(this.name, this.email);
}
I want to display data from firestore using provider in flutter. i got stuck please help. below are my codes
//product display page
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/pages/product_details.dart';
import 'package:shopping/provider/app_provider.dart';
class Product extends StatefulWidget {
#override
_ProductState createState() => _ProductState();
}
class _ProductState extends State<Product> {
#override
Widget build(BuildContext context) {
final product = Provider.of<AppProvider>(context);
return GridView.builder(
itemCount: productList.length,
gridDelegate:new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, int index){
return Padding(
padding:const EdgeInsets.all(4.0),
child:SingleProd(
//where i want to get the product details
prodName: product.featuredProducts[index].name.toString(),
),
);
}
);
}
}
class SingleProd extends StatelessWidget {
final prodName;
final prodPicture;
final prodOldPrice;
final prodPrice;
SingleProd({this.prodName, this.prodPicture,this.prodOldPrice,this.prodPrice});
#override
Widget build(BuildContext context) {
return Card(
child: Hero(tag: new Text("hero 1"),
child:
Material( child: InkWell(
onTap: ()=>Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>ProductDetails(
//here we are passing the value of the products to Product detail page
productDetailName:prodName,
)
)
),
child:GridTile(
footer: Container(
color: Colors.white,
child: new Row(
children: <Widget>[
new Expanded(
child: new Text(prodName, style: TextStyle(fontWeight: FontWeight.bold, fontSize:16.0),),
),
new Text(
"\$$prodPrice", style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),)
],
)
),
child: Image.asset(prodPicture,
fit: BoxFit.cover,),
),
),
),
),
);
}
}
//product class
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
class Product{
static const ID = "id";
static const CATEGORY = "category";
static const NAME = "name";
static const PRICE = "price";
static const BRAND = "brand";
static const COLORS = "colors";
static const QUANTITY = "quantity";
static const SIZES = "sizes";
static const SALE = "sale";
static const FEATURED = "featured";
static const PICTURE = "picture";
String _id;
String _name;
String _brand;
String _category;
String _picture;
double _price;
int _quantity;
List _colors;
List _sizes;
bool _onSale;
bool _featured;
// getters
String get name => _name;
String get id => _id;
String get category => _category;
String get brand => _brand;
String get picture => _picture;
double get price => _price;
int get quantity => _quantity;
List get colors => _colors;
List get sizes => _sizes;
bool get onSale => _onSale;
bool get featured => _featured;
// named constructure
Product.fromSnapshot(DocumentSnapshot snapshot){
Map data = snapshot.data;
_name = data[NAME];
_id = data[ID];
_category = data[CATEGORY];
_brand = data[BRAND];
_price = data[PRICE];
_quantity = data[QUANTITY];
_colors = data[COLORS];
_onSale = data[SALE];
_featured = data[FEATURED];
_picture = data[PICTURE];
}
}
//provider page for the product
import 'package:flutter/material.dart';
import 'package:shopping/db/product.dart';
import 'package:shopping/models/product.dart';
class AppProvider with ChangeNotifier {
List<Product>_fearturedProducts=[];
//method
void _getFeaturedProducts()async{
_fearturedProducts=await _productService.getFeaturedProducts();
notifyListeners();
}
}
//connection to Firestore to collect data
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shopping/models/product.dart';
class ProductService{
Firestore _firestore=Firestore.instance;
String collection="Products";
Future<List<Product>>getFeaturedProducts(){
_firestore.collection(collection).where('featured', isEqualTo:true).getDocuments()
.then((snap){
List<Product>featuredProducts=[];
snap.documents.map((snapshot)=> featuredProducts.add(Product.fromSnapshot(snapshot)));
return featuredProducts;
});
}
}
guys, I have managed to solve the question. The answer is as follow //product page
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/pages/product_details.dart';
import 'package:shopping/provider/app_provider.dart';
import 'package:shopping/models/product.dart';
class Products extends StatefulWidget {
#override
ProductsState createState() => ProductsState();
}
class ProductsState extends State<Products> {
List<Product> products;
#override
Widget build(BuildContext context) {
final productProvider = Provider.of<CRUDModel>(context);
return StreamBuilder<QuerySnapshot>(
stream: productProvider.fetchProductsAsStream(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
products = snapshot.data.documents
.map((doc) => Product.fromMap(doc.data, doc.documentID))
.toList();
return GridView.builder(
itemCount: products.length,
gridDelegate:new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, index){
return Padding(
padding:const EdgeInsets.all(4.0),
child:SingleProd(
product:products[index]
// prodPicture: productList[index]['picture'],
//prodOldPrice: productList[index]['oldPrice'],
//prodPrice: productList[index]['price'],
),
);
}
);
}
else {
return Text('fetching');
}
}
);
}
}
class SingleProd extends StatelessWidget {
//final prodName;
//final prodPicture;
//final prodOldPrice;
//final prodPrice;
final Product product;
SingleProd({ #required this.product});
//SingleProd({product.picture});
#override
Widget build(BuildContext context) {
return Card(
child: Hero(tag: product.id,
child:
Material( child: InkWell(
onTap: ()=>Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>ProductDetails(
//here we are passing the value of the products to Product detail page
productDetailName:product.name,
productDetailNewPrice:product.price,
productDetailPicture:product.picture,
//productDetailOldPrice:prodOldPrice,
//productDetailNewPrice:prodPrice,
//productDetailPicture: prodPicture,
)
)
),
child:GridTile(
footer: Container(
color: Colors.white,
child: new Row(
children: <Widget>[
new Expanded(
child: new Text(product.name, style: TextStyle(fontWeight: FontWeight.bold, fontSize:16.0),),
),
new Text(
'${product.price} \$', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),)
],
)
),
child: Image.asset('assets/${product.picture}.jpg',
fit: BoxFit.cover,),
),
),
),
),
);
}
}
//product class
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:ui';
class Product {
String id;
String name;
String brand;
String category;
String picture;
double price;
int quantity;
List colors;
List sizes;
bool sale;
bool featured;
Product(
{this.id, this.name, this.brand,
this.category, this.picture,this.price,
this.quantity,this.colors,this.sizes,this.sale,this.featured}
);
Product.fromMap(Map snapshot,String id) :
id = id ?? '',
name= snapshot['name'] ?? '',
brand = snapshot['brand'] ?? '',
category = snapshot['category'] ?? '',
picture= snapshot['picture'] ?? '',
price= snapshot['price'] ?? '',
quantity= snapshot['quantity'] ?? '',
colors= snapshot['colors'] ?? '',
sizes= snapshot['sizes'] ?? '',
sale= snapshot['sale'] ?? '',
featured= snapshot['featured'] ?? '';
toJson() {
return {
"name": name,
"brand": brand,
"category": category,
"picture": picture,
"price": price,
"quantity": quantity,
"colors": colors,
"sizes": sizes,
"sale": sale,
"featured": brand,
};
}
}
//provider class for the product
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shopping/db/Api.dart';
import 'package:shopping/models/product.dart';
class CRUDModel extends ChangeNotifier {
//Api _api = locator<Api>();
String path="Products";
Api _api= Api();
List<Product> products;
Future<List<Product>> fetchProducts() async {
var result = await _api.getDataCollection();
products = result.documents
.map((doc) => Product.fromMap(doc.data, doc.documentID))
.toList();
notifyListeners();
return products;
}
Stream<QuerySnapshot> fetchProductsAsStream() {
notifyListeners();
return _api.streamDataCollection();
}
Future<Product> getProductById(String id) async {
var doc = await _api.getDocumentById(id);
notifyListeners();
return Product.fromMap(doc.data, doc.documentID) ;
}
}
//connection to firestore
import 'package:cloud_firestore/cloud_firestore.dart';
class Api{
final Firestore _db = Firestore.instance;
String ref="Products";
//CollectionReference ref;
/*Api({this.path } ) {
ref = _db.collection(path);
}*/
Future<QuerySnapshot> getDataCollection() {
//return ref.getDocuments() ;
return _db.collection(ref).where('featured', isEqualTo:true).getDocuments();
}
Stream<QuerySnapshot> streamDataCollection() {
// return ref.snapshots() ;
//return _db.snapshots(ref).getDocuments();
return _db.collection(ref).snapshots();
}
Future<DocumentSnapshot> getDocumentById(String id) {
// return ref.document(id).get();
return _db.document(id).get();
}
Future<void> removeDocument(String id){
//return ref.document(id).delete();
return _db.document(id).delete();
}
Future<DocumentReference> addDocument(Map data) {
// return ref.add(data);
return _db.collection(ref).add(data);
}
Future<void> updateDocument(Map data , String id) {
//return ref.document(id).updateData(data) ;
return _db.document(ref).updateData(data);
}
}
//homepage where i displayed the products
import 'package:flutter/material.dart';
import 'package:carousel_pro/carousel_pro.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shopping/commons/common.dart';
import 'package:provider/provider.dart';
import 'package:shopping/provider/app_provider.dart';
import '../provider/user_provider.dart';
//My packages imports
import 'package:shopping/componets/horizontal_listview.dart';
import 'package:shopping/componets/product.dart';
import 'package:shopping/pages/cart.dart';
import 'package:shopping/pages/login.dart';
class HomePage extends StatefulWidget {
// List<Product> products;
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController _searchController = new TextEditingController();
//final FirebaseAuth _firebaseAuth=FirebaseAuth.instance;
#override
Widget build(BuildContext context) {
final user = Provider.of<UserProvider>(context);
final productProvider=Provider.of<CRUDModel>(context);
Widget image_carousel = new Container(
height: 200.0,
child: new Carousel(
boxFit: BoxFit.cover,
images: [
AssetImage('images/c1.jpg'),
AssetImage('images/m1.jpeg'),
AssetImage('images/m2.jpg'),
AssetImage('images/w1.jpeg'),
AssetImage('images/w3.jpeg'),
AssetImage('images/w4.jpeg'),
],
autoplay:true,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds:1000 ),
dotSize: 4.0,
indicatorBgPadding: 8.0,
dotBgColor: Colors.transparent,
),
);
return Scaffold(
appBar: new AppBar(
iconTheme: IconThemeData(color: blue),
elevation: 0.1,
backgroundColor: white,
title: Material(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[50],
elevation: 0.0,
child: TextFormField(
controller: _searchController,
decoration: InputDecoration(
hintText: "Search",
border: InputBorder.none,
),
validator: (value) {
if (value.isEmpty) {
return "The Search field cannot be empty";
}
return null;
}),
),
actions: <Widget>[
new IconButton(
icon: Icon(
Icons.search,
color: blue,
),
onPressed: () {},
),
new IconButton(
icon: Icon(
Icons.shopping_cart,
color: blue,
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => new Cart()));
}),
],
),
drawer: new Drawer(
child: new ListView(
children: <Widget>[
//drawer header
new UserAccountsDrawerHeader(
accountName: Text("Afolabi"),
accountEmail: Text("mtreal62#gmail.com"),
currentAccountPicture: GestureDetector(
child: new CircleAvatar(
backgroundColor: Colors.grey,
child: Icon(
Icons.person,
color: Colors.white,
),
),
),
decoration: BoxDecoration(
color: blue,
),
),
//body
InkWell(
onTap: () {},
child: ListTile(
title: Text("Home Page"),
leading: Icon(
Icons.home,
color: blue,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("My Account"),
leading: Icon(
Icons.person,
color: blue,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("My Orders"),
leading: Icon(
Icons.shopping_basket,
color: blue,
),
),
),
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => new Cart()));
},
child: ListTile(
title: Text("Shopping Cart"),
leading: Icon(
Icons.shopping_cart,
color: blue,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("Favourites"),
leading: Icon(
Icons.favorite,
color: blue,
),
),
),
Divider(),
InkWell(
onTap: () {},
child: ListTile(
title: Text("Settings"),
leading: Icon(
Icons.settings,
),
),
),
InkWell(
onTap: () {},
child: ListTile(
title: Text("About"),
leading: Icon(
Icons.help,
),
),
),
InkWell(
onTap: () {
user.signOut();
// changeScreenReplacement(context, Login());
},
child: ListTile(
title: Text("Log Out"),
leading: Icon(
Icons.transit_enterexit,
),
),
),
],
),
),
body: new Column(
children: <Widget>[
//Image Carousel for the home Page Banner
image_carousel,
//padding widget after carousel
new Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
alignment: Alignment.centerLeft,
child: new Text("Categories"),
),
),
//Horizontal layout start from here
HorizontalList(),
//End of the horizontal layout
//padding widget for Recent products categories
new Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
alignment: Alignment.centerLeft,
child: new Text("Recent Products"),
),
),
// Text(appProvider.featuredProducts.length.toString(),
//style: TextStyle(color: Colors.black),),
Flexible(
child: Products(),
),
//Horizontal layout start from here
],
),
);
}
}
Future _signOut() async {
try {
await FirebaseAuth.instance.signOut();
} catch (e) {
print(e); // TODO: show dialog with error
}
}
You are never calling _getFeaturedProducts() from your AppProvider class. So _fearturedProducts will always be null
In class AppProvider you are calling method on undefined name:
_productService.getFeaturedProducts()
Every IDE should show you this error ex. In my Android Studio it looks like this: