I am working on an app which has the below dart files.
- Holderscreen.dart
- dashboard.dart
- Kitchen.dart
I am passing the information from the holder page to the other pages like below
On the initialization of the app and calling the getUserDate returns userRes which is a type of future<map<string,dynamic> but after doing an update in Kitchen screen I recall the method in holder.dart file which refreshes/gets the userRes again but this time it is a type of _map<string,dynamic> because of which the kitchen screen shows the exception
Any idea what is going on. Based on suggestions I will share my code as asked, dont want to add a lot of code and cause confusion for the community
HolderClass
class HolderScreen extends StatefulWidget {
@override
_MyHolderPageState createState() => _MyHolderPageState();
}
class _MyHolderPageState extends State<HolderScreen> {
PageController pageController = PageController();
SideMenuController sideMenu = SideMenuController();
Future<Map<String, dynamic>> tempUserRes = {} as Future<Map<String, dynamic>>;
@override
void initState() {
sideMenu.addListener((index) {
pageController.jumpToPage(index);
});
super.initState();
tempUserRes = getUserData();
print(tempUserRes.runtimeType);
}
Future<void> getUserDataForeignFunc() async {
Future<Map<String, dynamic>> userRes;
userRes = await getUserProfileData(widget.accessToken, widget.ivString, widget.profileType);
print(tempUserRes.runtimeType);
print(userRes.runtimeType);
setState(() {
tempUserRes = userRes;
});
print(tempUserRes.runtimeType);
print(userRes.runtimeType);
}
Future<Map<String, dynamic>> getUserData() async {
Future<Map<String, dynamic>> userRes;
userRes = await getUserProfileData(widget.accessToken, widget.ivString, widget.profileType);
print(tempUserRes.runtimeType);
print(userRes.runtimeType);
return userRes;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
centerTitle: true,
),
body: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SideMenu(
controller: sideMenu,
style: SideMenuStyle(
// showTooltip: false,
displayMode: SideMenuDisplayMode.auto,
showHamburger: true,
hoverColor: Colors.blue[100],
selectedHoverColor: Colors.blue[100],
selectedColor: Colors.lightBlue,
selectedTitleTextStyle: const TextStyle(color: Colors.white),
selectedIconColor: Colors.white,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(10)),
// ),
// backgroundColor: Colors.grey[200]
),
items: [
SideMenuItem(
title: 'Dashboard',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.home),
badgeContent: const Text(
'3',
style: TextStyle(color: Colors.white),
),
tooltipContent: "This is a tooltip for Dashboard item",
),
SideMenuItem(
title: 'Kitchen',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.supervisor_account),
),
SideMenuExpansionItem(
title: "Expansion Item",
icon: const Icon(Icons.kitchen),
children: [
SideMenuItem(
title: 'Expansion Item 1',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.home),
badgeContent: const Text(
'3',
style: TextStyle(color: Colors.white),
),
tooltipContent: "Expansion Item 1",
),
SideMenuItem(
title: 'Expansion Item 2',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.supervisor_account),
)
],
),
SideMenuItem(
title: 'Files',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.file_copy_rounded),
trailing: Container(
decoration: const BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6.0, vertical: 3),
child: Text(
'New',
style: TextStyle(fontSize: 11, color: Colors.grey[800]),
),
)),
),
SideMenuItem(
title: 'Download',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.download),
),
SideMenuItem(
builder: (context, displayMode) {
return const Divider(
endIndent: 8,
indent: 8,
);
},
),
SideMenuItem(
title: 'Settings',
onTap: (index, _) {
sideMenu.changePage(index);
},
icon: const Icon(Icons.settings),
),
// SideMenuItem(
// onTap:(index, _){
// sideMenu.changePage(index);
// },
// icon: const Icon(Icons.image_rounded),
// ),
// SideMenuItem(
// title: 'Only Title',
// onTap:(index, _){
// sideMenu.changePage(index);
// },
// ),
const SideMenuItem(
title: 'Exit',
icon: Icon(Icons.exit_to_app),
),
],
),
const VerticalDivider(width: 0,),
Expanded(
child: PageView(
controller: pageController,
children: [
//DashboardScreen(userRes: tempUserRes),
KitchenScreen(accessToken:widget.accessToken, userRes: tempUserRes, profileType: widget.profileType, ivString: widget.ivString, notifyParent: getUserDataForeignFunc),
Container(
color: Colors.white,
child: const Center(
child: Text(
'Expansion Item 1',
style: TextStyle(fontSize: 35),
),
),
),
Container(
color: Colors.white,
child: const Center(
child: Text(
'Expansion Item 2',
style: TextStyle(fontSize: 35),
),
),
),
Container(
color: Colors.white,
child: const Center(
child: Text(
'Files',
style: TextStyle(fontSize: 35),
),
),
),
Container(
color: Colors.white,
child: const Center(
child: Text(
'Download',
style: TextStyle(fontSize: 35),
),
),
),
// this is for SideMenuItem with builder (divider)
const SizedBox.shrink(),
Container(
color: Colors.white,
child: const Center(
child: Text(
'Settings',
style: TextStyle(fontSize: 35),
),
),
),
],
),
),
],
),
);
}
}
KitchenClass
class KitchenScreen extends StatefulWidget {
final Future<Map<String, dynamic>> userRes;
final String accessToken;
final String profileType;
final String ivString;
final Function() notifyParent;
const KitchenScreen({Key? key, required this.accessToken, required this.userRes, required this.profileType, required this.ivString, required this.notifyParent}) : super(key: key);
@override
State<KitchenScreen> createState() => _KitchenScreenState();
}
class _KitchenScreenState extends State<KitchenScreen> {
bool showAddKitchenForm = false;
@override
void initState() {
super.initState();
showAddKitchenForm = false;
}
bool _showPassword = false;
Future<void> showAddKitchenFormFunc() async {
print("sadf");
setState(() {
showAddKitchenForm = true;
});
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
body: Center(
child: FutureBuilder<Map<String, dynamic>>(
future: widget.userRes,
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container(
height: size.height,
width: size.width,
color: Colors.blueGrey,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
}
String firstName = snapshot.data != null ? snapshot.data!['firstName']: '';
String lastName = snapshot.data != null ? snapshot.data!['lastName']: '';
String email = snapshot.data != null ? snapshot.data!['email']: '';
var userHasKitchen = false;
if (snapshot.data != null && snapshot.data!['kitchen'] == null) {
userHasKitchen = false;
} else if (snapshot.data != null && snapshot.data!['kitchen'] != null &&
snapshot.data!['kitchen'].length == 0) {
userHasKitchen = false;
} else if (snapshot.data != null && snapshot.data!['kitchen'] != null &&
snapshot.data!['kitchen'].length > 0) {
userHasKitchen = true;
}
if (!userHasKitchen && !showAddKitchenForm) {
return GFButton(
onPressed: () {
showAddKitchenFormFunc();
},
text: "Add Kitchen",
size: GFSize.LARGE,
fullWidthButton: true,
icon: Icon(Icons.kitchen_outlined),
);
} else if (userHasKitchen && !showAddKitchenForm) {
return Container(
width: size.width,
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
decoration: BoxDecoration(
color: const Color(0xFF48484A),
borderRadius: BorderRadius.circular(5)),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Last Name has kitchen:',
style: TextStyle(fontSize: 16, color: Colors.white38)),
const SizedBox(height: 7),
Text(lastName,
style:
const TextStyle(fontSize: 19, color: Colors.white)),
],
),
);
} else if (showAddKitchenForm) {
return KitchenFormPage(accessToken: widget.accessToken, profileType: widget.profileType, ivString: widget.ivString, notifyParent: widget.notifyParent);
}
return Container();
}),
));
}
}
KitchenForm.dart
class KitchenFormPage extends StatefulWidget {
final String accessToken;
final String profileType;
final String ivString;
final Function() notifyParent;
const KitchenFormPage({Key? key, required this.accessToken, required this.profileType, required this.ivString, required this.notifyParent}) : super(key: key);
@override
_KitchenFormPageState createState() => _KitchenFormPageState();
}
class _KitchenFormPageState extends State<KitchenFormPage> {
int currentStep = 0;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
TextEditingController kitchenNameController = TextEditingController();
TextEditingController kitchenDescriptionController = TextEditingController();
TextEditingController kitchenAddress1Controller = TextEditingController();
TextEditingController kitchenAddress2Controller = TextEditingController();
TextEditingController kitchenCityController = TextEditingController();
TextEditingController kitchenStateController = TextEditingController();
TextEditingController kitchenZipCodeController = TextEditingController();
Future<void> saveOrUpdateKitchenFunc() async {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('Processing Data'),
backgroundColor: Colors.green.shade300,
));
Map<String, dynamic> kitchenData = {
'kitchenName': kitchenNameController.text,
'kitchenDescription': kitchenDescriptionController.text,
'kitchenAddress1': kitchenAddress1Controller.text,
'kitchenAddress2': kitchenAddress2Controller.text,
'kitchenCity': kitchenCityController.text,
'kitchenState': kitchenStateController.text,
'kitchenZipCode': kitchenZipCodeController.text,
'action':'Save'
};
dynamic res = await saveOrUpdateKitchen(widget.accessToken, kitchenData, widget.profileType, widget.ivString);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
if (res['ErrorCode'] == null) {
widget.notifyParent();
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Error: ${res['Message']}'),
backgroundColor: Colors.red.shade300,
));
}
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text(
"Adding a Kitchen",
),
centerTitle: true,
),
body: Form(
key: _formKey,
child: Container(
padding: const EdgeInsets.all(20),
child: Stepper(
controlsBuilder: (BuildContext ctx, ControlsDetails dtl) {
return Row(
children: <Widget>[
GFButton(
onPressed: currentStep == 1? saveOrUpdateKitchenFunc : dtl.onStepContinue,
shape: GFButtonShape.square,
child: Text(currentStep != 1 ? 'Continue' : 'Save'),
),
Spacer(),
Visibility(
child: GFButton(
onPressed: dtl.onStepCancel,
shape: GFButtonShape.square,
child: Text(currentStep != 1 ? '' : 'Cancel'),
),
visible: currentStep != 1 ? false : true),
],
);
},
type: StepperType.vertical,
currentStep: currentStep,
onStepCancel: () => currentStep == 0
? null
: setState(() {
currentStep -= 1;
}),
onStepContinue: () {
if (kitchenNameController.text.isEmpty &&
currentStep == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text('Error: Kitchen Name is a required field.'),
backgroundColor: Colors.red.shade300,
));
} else if (kitchenDescriptionController.text.isEmpty &&
currentStep == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Error: Kitchen Description is a required field.'),
backgroundColor: Colors.red.shade300,
));
} else if (kitchenAddress1Controller.text.isEmpty &&
currentStep == 1) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Error: Kitchen Address 1 is a required field.'),
backgroundColor: Colors.red.shade300,
));
} else if (kitchenCityController.text.isEmpty &&
currentStep == 1) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text('Error: Kitchen City is a required field.'),
backgroundColor: Colors.red.shade300,
));
} else if (kitchenStateController.text.isEmpty &&
currentStep == 1) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content:
Text('Error: Kitchen State is a required field.'),
backgroundColor: Colors.red.shade300,
));
} else if (kitchenZipCodeController.text.isEmpty &&
currentStep == 1) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Error: Kitchen Zip Code is a required field.'),
backgroundColor: Colors.red.shade300,
));
} else {
bool isLastStep = (currentStep == getSteps().length - 1);
if (isLastStep) {
} else {
setState(() {
currentStep += 1;
});
}
}
},
onStepTapped: (step) => setState(() {
currentStep = step;
}),
steps: getSteps(),
))),
),
);
}
List<Step> getSteps() {
return <Step>[
Step(
state: currentStep > 0 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 0,
title: const Text("Kitchen Info"),
content: Column(
children: [
CustomInput(
hint: "Kitchen Name",
inputBorder: OutlineInputBorder(),
controller: kitchenNameController,
validatorValue: kitchenNameController.text,
validationType: 'text',
),
CustomInput(
hint: "Kitchen Description",
inputBorder: OutlineInputBorder(),
controller: kitchenDescriptionController,
validatorValue: kitchenDescriptionController.text,
validationType: 'text',
),
],
),
),
Step(
state: currentStep > 1 ? StepState.complete : StepState.indexed,
isActive: currentStep >= 1,
title: const Text("Kitchen Address"),
content: Column(
children: [
CustomInput(
hint: "Address 1",
controller: kitchenAddress1Controller,
inputBorder: OutlineInputBorder(),
validatorValue: kitchenAddress1Controller.text,
validationType: 'text',
),
CustomInput(
hint: "Address 2",
controller: kitchenAddress2Controller,
inputBorder: OutlineInputBorder(),
validatorValue: kitchenAddress2Controller.text,
validationType: 'text',
),
CustomInput(
hint: "City",
controller: kitchenCityController,
inputBorder: OutlineInputBorder(),
validatorValue: kitchenCityController.text,
validationType: 'text',
),
CustomInput(
hint: "State",
controller: kitchenStateController,
inputBorder: OutlineInputBorder(),
validatorValue: kitchenStateController.text,
validationType: 'text',
),
CustomInput(
hint: "Postal Code",
controller: kitchenZipCodeController,
inputBorder: OutlineInputBorder(),
validatorValue: kitchenZipCodeController.text,
validationType: 'number',
),
],
),
),
];
}
}
3
Answers
I was able to resolve it via the following changes
You can clearly see that you are await a future result here:
then you are getting the result as dynamic userRes, it’s no longer a future but you return it as one.
The one error I can spot is in the function
getUserDataForeignFunc()
.Since you await
getUserProfileData()
the type will beMap<String, dynamic>
, the type of the completed future.