Related
I'm developing a job search app that scrapes data from Indeed using Python which is being sent back to my Flutter UI as JSON data. The JSON data is being received successfully, however, Im getting an error of Null check operator used on a null value. The error appears to be stemming from the _jobSearch widget.
The relevant error-causing widget was ListView lib/ui/home_page.dart:256
Exception caught by scheduler library
Null check operator used on a null value
Here is the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_job_portal/theme/colors.dart';
import 'package:flutter_job_portal/theme/images.dart';
import 'package:flutter_job_portal/ui/bottom_menu_bar.dart';
import 'package:flutter_job_portal/ui/job_detail_page.dart';
String job = ""; //user's response will be assigned to this variable
String final_response = "";
final _formkey = GlobalKey<FormState>(); //key created to interact with the form
//function to validate and save user form
Future<void> _savingData() async {
final validation = _formkey.currentState.validate();
if (!validation) {
return;
}
_formkey.currentState.save();
}
Future<List<Job>> _getJobs() async {
final url = 'http://127.0.0.1:5000/job';
final response1 = await http.post(Uri.parse(url), body: json.encode({'job': job}));
final response2 = await http.get(Uri.parse(url));
final decoded = json.decode(response2.body);
List<Job> jobs = [];
for (var i in decoded) {
Job job = Job(i['Title'], i['Company'], i['Location'], i['Salary']);
jobs.add(job);
}
return jobs;
}
class Job {
final String title;
final String company;
final String location;
final String salary;
Job(this.title, this.company, this.location, this.salary);
}
class HomePage extends StatelessWidget {
const HomePage({Key key}) : super(key: key);
Widget _appBar(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
children: [
CircleAvatar(
backgroundImage: AssetImage(Images.user1),
),
Spacer(),
IconButton(
icon: Icon(Icons.notifications_none_rounded),
onPressed: () {},
)
],
),
);
}
Widget _header(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 12),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Hello, Alex!",
style: TextStyle(
fontSize: 15,
color: KColors.subtitle,
fontWeight: FontWeight.w500,
)),
SizedBox(
height: 6,
),
Text("Swipe to find your future",
style: TextStyle(
fontSize: 20,
color: KColors.title,
fontWeight: FontWeight.bold)),
SizedBox(
height: 10,
),
Row(
children: [
Expanded(
child: Container(
height: 45,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: KColors.lightGrey,
borderRadius: BorderRadius.circular(10)),
child: Form(
key: _formkey,
child: TextFormField(
decoration: InputDecoration(
hintText: 'Search job title or keywords',
),
onSaved: (value) {
job =
value; //getting data from the user form and assigning it to job
},
),
),
),
),
SizedBox(
width: 16,
),
Container(
decoration: BoxDecoration(
color: KColors.primary,
borderRadius: BorderRadius.circular(10),
),
height: 40,
child: IconButton(
color: KColors.primary,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () async {
_savingData();
_getJobs();
},
),
)
],
)
],
),
);
}
Widget _recommendedSection(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
margin: EdgeInsets.symmetric(vertical: 12),
height: 200,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Recommended",
style: TextStyle(fontWeight: FontWeight.bold, color: KColors.title),
),
SizedBox(height: 10),
Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_recommendedJob(context,
company: "Google",
img: Images.google,
title: "UX Designer",
sub: "\$45,000 Remote",
isActive: true),
_recommendedJob(context,
company: "DropBox",
img: Images.dropbox,
title: "Research Assist",
sub: "\$45,000 Remote",
isActive: false)
],
),
),
],
),
);
}
Widget _recommendedJob(
BuildContext context, {
String img,
String company,
String title,
String sub,
bool isActive = false,
}) {
return Padding(
padding: const EdgeInsets.only(right: 10),
child: GestureDetector(
onTap: () {
Navigator.push(context, JobDetailPage.getJobDetail());
},
child: AspectRatio(
aspectRatio: 1.3,
child: Container(
decoration: BoxDecoration(
color: isActive ? KColors.primary : Colors.white,
borderRadius: BorderRadius.circular(7),
),
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 40,
width: 40,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: isActive ? Colors.white : KColors.lightGrey,
borderRadius: BorderRadius.circular(7),
),
child: Image.asset(img),
),
SizedBox(height: 16),
Text(
company,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
SizedBox(height: 6),
Text(
title,
style: TextStyle(
fontSize: 14,
color: isActive ? Colors.white : KColors.title,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 6),
Text(
sub,
style: TextStyle(
fontSize: 12,
color: isActive ? Colors.white38 : KColors.subtitle,
),
),
],
),
),
),
),
);
}
Widget _jobSearch(BuildContext context) {
return new Container(
child: FutureBuilder(
future: _getJobs(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].location),
);
},
);
}
},
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: KColors.background,
bottomNavigationBar: BottomMenuBar(),
body: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_appBar(context),
_header(context),
_recommendedSection(context),
_jobSearch(context)
],
),
),
),
),
);
}
}
I pass Data from parent to child, and one of these parameters is a Fn. What I need is to return data from child to parent again, but I get an error as below:
[ Closure call with mismatched arguments: function '_ReservationBranchesSlotsScreenState._changeData' Receiver: Closure:
({String areaId, int coastPerPerson, int selectedBranchChecked, String
formatted, bool showCreateReservationButton, bool isExpanded, int
expandedIndex}) => void from Function '_changeData#176179245':. Tried
calling: _ReservationBranchesSlotsScreenState._changeData(areaId:
"d98a4e0e-d408-40c8-b387-9a405683a389", coastPerPerson: 0,
expandedIndex: -1, formatted: null, isExpanded: false,
selectedBrnachChecked: 0, showCreateReservationButton: false) Found:
_ReservationBranchesSlotsScreenState._changeData({String areaId, int coastPerPerson, int selectedBranchChecked, String formatted, bool
showCreateReservationButton, bool isExpanded, int expandedIndex}) =>
void ]
I create a Function in the parent Widget that do some actions, and pass this Fn to the child widget as below.
This is the parent widget Fn:
void _changeData({
String areaId,
int coastPerPerson,
int selectedBranchChecked,
String formatted,
bool showCreateReservationButton,
bool isExpanded,
int expandedIndex
}){
setState(() {
_areaId = areaId;
_coastPerPerson = coastPerPerson;
_selectedBranchChecked = selectedBranchChecked;
_formatted = formatted;
_showCreateReservationButton = showCreateReservationButton;
_isExpanded = isExpanded;
_expandedIndex = expandedIndex;
});
}
also below when I pass this Fn to the child widget:
SelectBranchWidget(
branches: _branches,
coastPerPerson: _coastPerPerson,
areaId: _areaId,
selectedBranchChecked: _selectedBranchChecked,
formatted: _formatted,
isExpanded: _isExpanded,
showCreateReservationButton: _showCreateReservationButton,
expandedIndex: _expandedIndex,
***changeData: _changeData,***
),
and here is the child widget which I need to return a Data from it to the parent again:
class SelectBranchWidget extends StatefulWidget {
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
widget.coastPerPerson = widget.branches[i].branchAreas[branchAreaIndex].costPerSeat;
widget.areaId = widget.branches[i].branchAreas[branchAreaIndex].guid;
print('areaId IS ${widget.areaId}');
widget.selectedBranchChecked = i;
widget.formatted = null;
widget.isExpanded = false;
widget.showCreateReservationButton = false;
widget.expandedIndex = -1;
widget.changeData(
areaId: widget.areaId,
coastPerPerson: widget.coastPerPerson,
selectedBrnachChecked:widget.selectedBranchChecked,
formatted: widget.formatted,
showCreateReservationButton:widget.showCreateReservationButton,
isExpanded:widget.isExpanded,
expandedIndex: widget.expandedIndex
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
You can use two approaches to achieve this.
User first one if you want to return from a route (from next screen to previous one)
Use second Approach if you want to make changes to Parent Widget from its child widget on same screen.
1st Approach (Returning an object from route)
Create a Model of data you are passing.
class MyModel {
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
MyModel({
this.areaId,
this.coastPerPerson,
this.selectedBranchChecked,
this.formatted ,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
});
}
I'm assuming that You Parent Looks like this. And you're navigating from Parent Page to
SelectBranchWidget page
class YourParentWidget extends StatefulWidget {
YourParentWidget({Key key}) : super(key: key);
#override
_YourParentWidgetState createState() => _YourParentWidgetState();
}
class _YourParentWidgetState extends State<YourParentWidget> {
navigateAndChangeData(){
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return SelectBranchWidget();
})).then((value) {
///Use this to get value from next screen to PArent Screen
if(value != null) {
var model = value as MyModel;
//Now you have access to all returning values
//model.areaId
//model.coastPerPerson
//model.selectedBranchChecked
//...
///Make changes accordingly
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
///Your UI Conponents....
);
}
}
Now In SelectBranchWidget Widget, What you need to do in _changedValues function is to return model values. i.e.
class SelectBranchWidget extends StatefulWidget {
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
MyModel model = MyModel(
areaId: widget.branches[i].branchAreas[branchAreaIndex].guid,
coastPerPerson: widget.branches[i].branchAreas[branchAreaIndex].costPerSeat,
selectedBranchChecked: 1,
formatted: null,
isExpanded: false,
showCreateReservationButton: false,
expandedIndex: -1
);
Navigator.of(context).pop(model);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
2nd Approach (Making changes to Parent Widget while staying on child Widget)
Make your Parent Widget's State Class and changeData function public (i.e. remove underscore before the state class name)
i.e.
class YourParentWidget extends StatefulWidget {
YourParentWidget({Key key}) : super(key: key);
#override
YourParentWidgetState createState() => YourParentWidgetState();
}
class YourParentWidgetState extends State<YourParentWidget> {
String _areaId;
int _coastPerPerson;
int _selectedBranchChecked;
String _formatted;
bool _showCreateReservationButton;
bool _isExpanded;
int _expandedIndex;
void changeData({
String areaId,
int coastPerPerson,
int selectedBranchChecked,
String formatted,
bool showCreateReservationButton,
bool isExpanded,
int expandedIndex
}){
setState(() {
_areaId = areaId;
_coastPerPerson = coastPerPerson;
_selectedBranchChecked = selectedBranchChecked;
_formatted = formatted;
_showCreateReservationButton = showCreateReservationButton;
_isExpanded = isExpanded;
_expandedIndex = expandedIndex;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
///Your UI Conponents....
);
}
}
Then Pass widget.key fro YourParentWidget to yor SelectBranchWidget (If you'reusing it inside your UI of YourParentWidget) i.e.
return Scaffold(
body: Column(
children: [
///Your UI Conponents....
SelectBranchWidget(parentKey: widget.key,
///... Other PArameters as well
)
],
)
);
Now in Your SelectBranchWidget Widget, do the following
class SelectBranchWidget extends StatefulWidget {
GlobalKey<YourParentWidgetState> parentKey;
List<RestaurantBranch> branches;
int coastPerPerson;
String areaId;
int selectedBranchChecked;
String formatted;
bool isExpanded;
bool showCreateReservationButton;
int expandedIndex;
Function changeData;
SelectBranchWidget(
{
this.parentKey,
this.branches,
this.coastPerPerson,
this.areaId,
this.selectedBranchChecked,
this.formatted,
this.isExpanded,
this.showCreateReservationButton,
this.expandedIndex,
this.changeData,
});
#override
_SelectBranchWidgetState createState() => _SelectBranchWidgetState();
}
class _SelectBranchWidgetState extends State<SelectBranchWidget> {
void _changedValues(int i, int branchAreaIndex) {
widget.coastPerPerson = widget.branches[i].branchAreas[branchAreaIndex].costPerSeat;
widget.areaId = widget.branches[i].branchAreas[branchAreaIndex].guid;
print('areaId IS ${widget.areaId}');
widget.selectedBranchChecked = i;
widget.formatted = null;
widget.isExpanded = false;
widget.showCreateReservationButton = false;
widget.expandedIndex = -1;
///Then do this to make changes to your parent widget's state
widget.parentKey.currentState.changeData(
areaId: widget.areaId,
coastPerPerson: widget.coastPerPerson,
selectedBrnachChecked:widget.selectedBranchChecked,
formatted: widget.formatted,
showCreateReservationButton:widget.showCreateReservationButton,
isExpanded:widget.isExpanded,
expandedIndex: widget.expandedIndex
);
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(Radius.circular(10))),
padding: EdgeInsets.all(8),
margin: EdgeInsets.only(left: 16, right: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'Select Branch',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
SizedBox(
height: 8,
),
Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Number of branches = ${widget.branches.length}',
style: TextStyle(color: Colors.grey, fontSize: 10),
),
),
),
SizedBox(
height: 16,
),
ConstrainedBox(
constraints:
BoxConstraints(maxHeight: 230, maxWidth: double.infinity),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.branches.length,
itemBuilder: (ctx, i) {
return GestureDetector(
onTap: () {
if (widget.branches[i].branchAreas.length == 1) {
_changedValues(i, 0);
return;
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
title: Text(
'Select Area',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold),
),
content: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
// height: (_branches[i].branchAreas.length == 1)
// ?70
// : (_branches[i].branchAreas.length == 2)
// ? 100
// :150,
width: 100.0,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: 120),
child: ListView.builder(
shrinkWrap: true,
itemCount:
widget.branches[i].branchAreas.length,
itemBuilder:
(BuildContext context, int index) {
return GestureDetector(
onTap: () {
_changedValues(i, index);
Navigator.of(context).pop();
},
child: Padding(
padding: const EdgeInsets.only(
top: 16, left: 36.0, right: 36),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
widget.branches[i]
.branchAreas[index].name,
style: TextStyle(
color: Colors.white),
),
Divider(
color: Colors.grey,
thickness: 1,
),
],
),
),
);
},
),
),
),
);
});
},
child: Card(
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.branches[i].branchDistrict.name,
style: TextStyle(color: Colors.white, fontSize: 16),
),
(widget.selectedBranchChecked == i)
? Container(
height: 40,
width: 40,
child: Image.asset(
getAssetsName(AssetsImage.checkIcon),
fit: BoxFit.cover,
),
)
: Container(
padding: EdgeInsets.only(top: 5, bottom: 5),
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: SizedBox(),
),
],
),
),
),
);
},
),
),
],
),
);
}
}
Edit: You just need to call (wherever you're initiating it from) your YourParentWidget like YourParentWidget(key: GlobalKey<YourParentWidgetState>())
Pardon me if any typo occurs
I am a beginner in flutter.
I have created two ListView Builder where one ListView.builder is inside another builder as given below.
I have a list of categories which is inside one listview.builder and other are the list of products which also listview.builder Wrapped inside the above listview build>
So now i need to display the only items belonging to the 1st listview builder which means If I have a categories named soup (1st ListView.builder) Now i need to display only the products that belongs to soup categories from 2nd Listview.Builder but all the products are listed inside all the categories list. So, Please help me to find solution any help would be appriciated.Thank you in advance.
Below are the codes.
//lib/routes/menu_description.dart(File name)
import 'package:flutter/material.dart';
import '../api_service.dart';
import 'package:html/parser.dart';
import 'package:percent_indicator/percent_indicator.dart';
class MenuDescription extends StatefulWidget {
MenuDescription({Key key}) : super(key: key);
#override
_MenuDescriptionState createState() => _MenuDescriptionState();
}
int itemspressed;
class _MenuDescriptionState extends State<MenuDescription> {
#override
Widget build(BuildContext context) {
return Container(
// color: Colors.grey[200],
child: FutureBuilder(
future: fetchWpPosts(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.separated(
separatorBuilder: (context, index) => Divider(
height: 20.0,
color: Colors.black,
),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Map wpcategoriespost = snapshot.data[index];
return Container(
color: Colors.grey[200],
child: InkWell(
splashColor: Colors.grey[800],
onTap: () {
setState(() {
itemspressed = index;
});
},
child: Padding(
padding: const EdgeInsets.only(
left: 5, right: 5, bottom: 5, top: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Text(
"${wpcategoriespost['name']}",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.lightGreen[900],
),
),
),
Center(
child: Text(
parse(("${wpcategoriespost['description']}")
.toString())
.documentElement
.text,
style:
TextStyle(fontSize: 14, color: Colors.black),
),
),
Container(
padding: EdgeInsets.only(top: 20.0),
child: Categories(),)
],
),
),
),
);
},
);
}
return new CircularPercentIndicator(
radius: 120.0,
lineWidth: 13.0,
animation: true,
percent: 1.0,
progressColor: Colors.orange,
center: new Text(
"100.0%",
style:
new TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
));
},
),
);
}
}
class Categories extends StatefulWidget {
#override
_CategoriesState createState() => _CategoriesState();
}
class _CategoriesState extends State<Categories> {
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: fetchWpPosts(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Map wppost = snapshot.data[index];
return Card(
margin: const EdgeInsets.only(
left: 15,
right: 15,
bottom: 15,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
elevation: 3,
child: InkWell(
splashColor: Colors.grey[300],
onTap: () {
setState(() {
itemspressed = index;
});
},
child: Padding(
padding: const EdgeInsets.only(
left: 12, right: 5, bottom: 12, top: 5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${wppost['name']}",
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Colors.lightGreen[900]),
),
Container(
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: Border.all(color: Colors.grey[350]),
color: itemspressed == index
? Colors.grey[350]
: null,
),
child: IconButton(
iconSize: 22,
icon: Icon(
Icons.add,
color: Colors.blueAccent[400],
),
onPressed: () {
setState(() {});
print('Add to cart');
},
),
),
],
),
Text(
parse(("${wppost['description']}").toString())
.documentElement
.text,
style: TextStyle(fontSize: 14, color: Colors.black),
),
Text(
parse(("${wppost['price']} €").toString())
.documentElement
.text,
style: TextStyle(
fontSize: 15,
color: Colors.amber[700],
fontWeight: FontWeight.bold),
),
],
),
),
),
) ;
},
);
}
return new CircularPercentIndicator(
radius: 120.0,
lineWidth: 13.0,
animation: true,
percent: 1.0,
progressColor: Colors.orange,
center: new Text(
"100.0%",
style:
new TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
));
},
),
);
}
}
//lib/api_service.dart
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<List> fetchWpPosts() async{
final response = await http.get('https://word.tkco.in/wp-json/wc/v3/products?consumer_key=ck_94114a31e4576e61a9292f961489e7701029753e&consumer_secret=cs_dd4dc6e7945d8dcd14d888bc1d0ea0806b116dfb');
var convertDatatoJson = jsonDecode(response.body);
if (response.statusCode == 200) {
}
return convertDatatoJson;
}
I am working on a project in which i have a class which has a row that has two children. Child one contains a TabView with a child class TabViewChild in which i am generating a gridview. On the other hand child two contains a listView. So the problem is, when i click on the gridview item i am passing that item's value to a static list and passing that list to a listview of other class. But i have no idea how to change the state of that class on item clicked or how can i achieve this task in a better way as i am new to Flutter. I want that when a person click on any gridview's item that item appears in the listview simultaneously.
class ItemMenus {
int id;
String code;
String name;
String salePrice;
String photo;
String categoryName;
String percentage;
int quantity;
ItemMenus({this.id, this.code, this.name, this.salePrice, this.photo,
this.categoryName, this.percentage, this.quantity});
ItemMenus.fromJson(Map<String, dynamic> json)
:
id = int.parse(json['id']),
code = json['code'],
name = json['name'],
salePrice = json['sale_price'],
photo = json['photo'],
categoryName = json['category_name'],
percentage = json['percentage'];
#override
String toString() {
return 'ItemMenus{id: $id, code: $code, name: $name, salePrice: $salePrice, photo: $photo, categoryName: $categoryName, percentage: $percentage}';
}
}
import 'package:food_app/model/mdl_item_menus.dart';
class ItemMenuList{
List<ItemMenus> _itmMenuLst = [];
ItemMenuList._();
static final ItemMenuList instanceItmMenuLst = ItemMenuList._();
static ItemMenuList get instance => instanceItmMenuLst;
void addItem(ItemMenus im){
_itmMenuLst.add(im);
}
List<ItemMenus> get list => _itmMenuLst;
#override
String toString() {
return 'ItemMenuList{_itmMenuLst: $_itmMenuLst}';
}
}
import 'package:flutter/material.dart';
import 'package:food_app/database/tables/tbl_categories.dart';
import 'package:food_app/model/list/item_menu_list.dart';
import 'package:food_app/model/mdl_categories.dart';
import 'package:food_app/model/mdl_item_menus.dart';
import 'package:food_app/pos/tab_bar_view.dart';
class EPos extends StatefulWidget{
#override
_EPosState createState() => _EPosState();
}
class _EPosState extends State<EPos> {
final categoryDBHelper = TblCategories.categoriesInstance;
final ItemMenuList instance2 = ItemMenuList.instance;
String _orderType = 'Dine-In', _info = 'Table No. 1', _userName = 'ZiaUddin';
List<Categories> catLst = [];
List<ItemMenus> itmLst = [];
Future getCategories() async {
catLst.clear();
var categories = await categoryDBHelper.getCategories();
categories.forEach((element) {
catLst.add(Categories(
id: element['id'],
categoryName: element['category_name'],
description: element['description'],
userId: element['user_id'],
companyId: element['company_id'],
delStatus: element['del_status']));
});
catLst.forEach((element) {
print(element);
});
return catLst;
}
#override
void initState() {
// TODO: implement initState
super.initState();
// itmLst = instance2.list;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
//#region AppBar
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.15,
color: Colors.redAccent,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
margin: EdgeInsets.fromLTRB(8, 10, 0, 0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(20),
color: Colors.red,
),
child: Row(
children: [
Icon(
Icons.arrow_back,
color: Colors.white,
size: 25,
),
Padding(
padding: const EdgeInsets.fromLTRB(4, 8, 10, 8),
child: Text(
_orderType,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
fontFamily: 'Ubuntu',
letterSpacing: 2.0,
),
),
),
],
),
),
Container(
margin: EdgeInsets.fromLTRB(8, 10, 0, 0),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(10),
color: Colors.red,
),
child: Padding(
padding: const EdgeInsets.fromLTRB(20, 8, 20, 8),
child: Text(
_info,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.amberAccent,
fontFamily: 'Ubuntu',
letterSpacing: 2.0,
),
),
),
),
Container(
margin: EdgeInsets.only(top: 15, right: 5),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(30),
color: Colors.red,
),
child: Row(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 8, 8, 8),
child: Text(
_userName,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
color: Colors.white,
fontFamily: 'Ubuntu',
letterSpacing: 1.0,
),
),
),
CircleAvatar(
backgroundColor: Colors.red,
radius: MediaQuery.of(context).size.height * 0.041,
child: CircleAvatar(
radius: MediaQuery.of(context).size.height * 0.04,
backgroundImage: AssetImage('assets/user.png'),
),
),
],
),
),
],
),
),
//endregion
Expanded(
child: Row(
children: [
//# region Menu
Flexible(
flex: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.yellowAccent,
shape: BoxShape.rectangle,
),
child: Column(
children: [
Expanded(
child: Container(
color: Colors.white,
child: FutureBuilder(
future: getCategories(),
builder: (context, snapShot) {
if (snapShot.connectionState ==
ConnectionState.none &&
snapShot.hasData == null) {
return Center(
child: CircularProgressIndicator());
}
return MaterialApp(
debugShowCheckedModeBanner: false,
home: DefaultTabController(
length: catLst.length,
child: Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize:
Size.fromHeight(kToolbarHeight),
child: Container(
height: MediaQuery.of(context)
.size
.height *
0.1,
child: TabBar(
indicatorColor:
Colors.amberAccent,
isScrollable: true,
tabs: catLst
.map<Widget>((Categories c) {
return Tab(
icon: Icon(
Icons.style,
color: Colors.amberAccent,
size: 15,
),
child: Text(
c.categoryName
.toUpperCase(),
style: TextStyle(
color: Colors.black,
fontWeight:
FontWeight.w400,
),
),
);
}).toList(),
),
),
),
body: TabBarView(
children: catLst.map((Categories c) {
return TabBarViewChild(categoryName:c.categoryName,
callback: (){
setState(() {
instance2.addItem(ItemMenus(name: c.categoryName));
itmLst = instance2.list;
print('I am Callback');
});
},);
}).toList(),
),
),
),
);
}),
),
),
],
),
),
),
//endregion
//# region OrderList
Flexible(
flex: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
),
child: ListView.builder(
itemCount: itmLst.length,
itemBuilder: (context, index){
return ListTile(
title: Text(itmLst[index].name),
);
},
),
),
),
//endregion
],
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:food_app/database/tables/tbl_item_menus.dart';
import 'package:food_app/model/list/item_menu_list.dart';
import 'package:food_app/model/mdl_item_menus.dart';
import 'package:food_app/pos/new_sale.dart';
class TabBarViewChild extends StatefulWidget {
String categoryName;
VoidCallback callback;
TabBarViewChild({Key key,#required this.categoryName, this.callback}) : super(key:key);
#override
_TabBarViewChildState createState() => _TabBarViewChildState();
}
class _TabBarViewChildState extends State<TabBarViewChild> {
final ItemMenuList instance1 = ItemMenuList.instance;
List<ItemMenus> itmLst = [];
final itemMenus = TblItemMenus.itemMenusInstance;
Future getSpecificItemMenus() async{
var items = await itemMenus.getSpecificItemMenus(widget.categoryName);
itmLst.clear();
items.forEach((e) {
itmLst.add(ItemMenus(id: e['id'], categoryName: e['category_name'], code: e['code'],
name: e['name'], percentage: e['percentage'], photo: e['photo'], quantity: e['quantity'],
salePrice: e['sale_price']));
});
for (var value in itmLst) {
print(value);
}
return itmLst;
}
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: FutureBuilder(
future: getSpecificItemMenus(),
builder: (context, snapShot) {
if (snapShot.connectionState == ConnectionState.none
&& snapShot.hasData == null) {
return Center(child: CircularProgressIndicator());
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
scrollDirection: Axis.vertical,
itemCount: itmLst.length,
itemBuilder: (context, index){
return Padding(
padding: const EdgeInsets.all(5.0),
child: InkWell(
child: Card(
elevation: 4,
color: Colors.amberAccent,
child: Center(
child: Text(
itmLst[index].name.toUpperCase(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
fontSize: 13,
fontWeight: FontWeight.w700,
fontFamily: 'Ubuntu',
letterSpacing: 1.0,
),
),
),
),
onTap: (){
// Provider.of<ProItemMenus>(context, listen: false).addNewItemInList(ItemMenus(name: itmLst[index].name.toUpperCase()));
instance1.addItem(ItemMenus(categoryName: itmLst[index].name.toUpperCase()));
print(itmLst[index].name.toUpperCase());
},
),
);
}
);
}
),
);
}
}
So the problem now, there are two stateful widgets.
And as we are going to share the data or the state between them, basically we need to have more one stateful Widget which will the parent of them.
This term of is this problem known as State Management.
Currently there are many reknown State Management , such as Provider and Bloc. Reference.
But personally, I recommend to use Provider, which has simple Syntaxes and Concept.
I have tried many solutions from this platform and others too but my problem remains the same. I have done this before and successfully ripple the listview item in another custom listview. But this time i am not getting any clue where i am lacking. The previous solution make things more messy if try this time. Ripple is appearing under listview item what i want is to only listview item do ripples.
class Dashboard extends StatefulWidget {
#override
_DashboardState createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
// Map<String, dynamic> args;
List<DashboardItem> lst = [
DashboardItem(
img: 'assets/sales.png', name: 'Sales', subtitle: 'Your daily sales'),
DashboardItem(
img: 'assets/order.png', name: 'Orders', subtitle: 'Your new orders'),
DashboardItem(
img: 'assets/report.png',
name: 'Reports',
subtitle: 'Your daily reports'),
DashboardItem(
img: 'assets/setting.png',
name: 'Setting',
subtitle: 'Application setting'),
DashboardItem(
img: 'assets/register.png',
name: 'Register',
subtitle: 'Close your register'),
DashboardItem(
img: 'assets/logout.png', name: 'Logout', subtitle: 'You can rest')
];
#override
Widget build(BuildContext context) {
// args = ModalRoute.of(context).settings.arguments;
// print('Dashboard : ${args['regId']}');
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
elevation: 0.0,
title: Text('My Dashboard'),
centerTitle: true,
),
body: LayoutBuilder(
builder: (context, constraints){
if(constraints.maxWidth < 600){
return ListView.builder(
itemCount: lst.length,
shrinkWrap: true,
itemBuilder: (context, position){
return Container(
padding: position == lst.length ? const EdgeInsets.only(top:16.0) : const EdgeInsets.only(top:16.0, bottom: 16.0),
child: InkWell(
child: DashboardCard(lst[position]),
onTap: (){
Toast.show('In List', context);
},
),
);
},
);
}
else{
return GridView.builder(
itemCount: lst.length,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (context, position){
return InkWell(
child: DashboardCard(lst[position]),
onTap: (){
Toast.show('In Grid', context);
},
);
},
);
}
},
),
);
}
}
DashboardCard Class
class DashboardCard extends StatelessWidget {
final DashboardItem _dashboardItem;
const DashboardCard(this._dashboardItem);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
height: 230,
width: 280,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 10.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(
image: AssetImage(_dashboardItem.img),
fit: BoxFit.contain,
width: 80,
height: 80,
),
SizedBox(height: 20,),
Text(
_dashboardItem.name,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
fontFamily: 'Ubuntu',
letterSpacing: 2.0,
color: Colors.redAccent,
),
),
Padding(
padding: const EdgeInsets.all(3.0),
child: Text(
_dashboardItem.subtitle,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.normal,
letterSpacing: 2.0,
color: Colors.grey,
),
),
),
],
),
),
),
),
);
}
}
The issue is with the Center widget in your DashboardCard:
You can fix it wrapping your DashboardCard Container widget in an InkWell widget and pass a onTap parameter to the DashboardCard constructor:
class DashboardCard extends StatelessWidget {
final DashboardItem _dashboardItem;
final VoidCallback onTap;
const DashboardCard(
this._dashboardItem, {
this.onTap,
});
#override
Widget build(BuildContext context) {
return Center(
child: InkWell(
onTap: onTap,
child: Container(
height: 230,
width: 280,
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 10.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image(
image: AssetImage(_dashboardItem.img),
fit: BoxFit.contain,
width: 80,
height: 80,
),
SizedBox(
height: 20,
),
Text(
_dashboardItem.name,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
fontFamily: 'Ubuntu',
letterSpacing: 2.0,
color: Colors.redAccent,
),
),
Padding(
padding: const EdgeInsets.all(3.0),
child: Text(
_dashboardItem.subtitle,
maxLines: 2,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.normal,
letterSpacing: 2.0,
color: Colors.grey,
),
),
),
],
),
),
),
),
),
);
}
}
The use it in other places like:
DashboardCard(
lst[position],
onTap: () {
Toast.show('In List', context);
},
),