Flutter - How to only update/rebuild the child widget and not parent? - flutter

I have the following code where 'Snag' is the parent widget and 'Category Display' is the child widget. When I press 'CATEGORY' button the display updates but the entire 'SNAG' is also rebuild as a result I get a flicker on the screen.
Please see attached:
What is it that I am doing wrong here please?
My code is as follows:
class Snags extends StatefulWidget {
#override
_SnagsState createState() => _SnagsState();
}
class _SnagsState extends State<Snags> {
final _formKey = GlobalKey<FormState>();
TextEditingController _locationController = TextEditingController();
TextEditingController _conditionController = TextEditingController();
TextEditingController _recommendationController = TextEditingController();
TextEditingController _assetController = TextEditingController();
bool _newSnag = false;
bool _busy = false;
bool loaded = false;
late Snag? _snag;
late InfoHelper _info;
late StatusCodes _statusCodes;
late String _imagePath;
late String _status;
late String _siteID;
double maxImageSize = 700;
_getSnagObject() async {
if (loaded) return;
_info = ModalRoute
.of(context)!
.settings
.arguments as InfoHelper;
_siteID = _info.siteID; // Never NULL
_newSnag = _info.problemUID == null ? true : false;
if (!_newSnag) {
_snag = await MyDatabase.db.getSingleSnag(_info.problemUID!);
}
_statusCodes = await Provider.of<CP>(context,listen: false).getStatusCodes();
_imagePath = _newSnag ? '' : _snag!.imagePath;
_status = _newSnag ? '1' : _snag!.Status;
_locationController.text = _newSnag ? '' : _snag!.location;
_conditionController.text = _newSnag ? '' : _snag!.condition;
_recommendationController.text = _newSnag ? '' : _snag!.recommendation;
_assetController.text = _newSnag ? '' : _snag!.asset;
loaded = true;
}
deleteImage() async {
if (await File(_imagePath).exists()) await File(_imagePath).delete();
setState(() => _imagePath = '');
Navigator.pop(context);
}
annotatePicture() {
Navigator.pushNamed(context, '/imageMarkup', arguments: _imagePath).then((value) {
if (value != null)
setState(() {
_imagePath = value as String;
});
Navigator.pop(context);
});
}
void _openGallery() async {
XFile? picture = await ImagePicker().pickImage(
source: ImageSource.gallery,
maxHeight: maxImageSize,
maxWidth: maxImageSize,
);
await savePicture(picture);
}
void _openCamera() async {
final _picker = ImagePicker();
XFile? picture = await _picker.pickImage(
source: ImageSource.camera,
maxHeight: maxImageSize,
maxWidth: maxImageSize,
);
await savePicture(picture);
}
Future optionsDialogBoxWithDELorEDIT(BuildContext context1,
VoidCallback callBackDelete, VoidCallback? callBackEdit) {
return showDialog(
context: context1,
builder: (BuildContext context) {
return AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
GestureDetector(
child: new Text('Delete picture'),
onTap: callBackDelete,
),
SizedBox(height: 26.0,),
GestureDetector(
child: new Text('Annotate picture'),
onTap: callBackEdit,
),
],
),
),
);
});
}
Future<void> _optionsDialogBox() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
GestureDetector(
child: new Text('Take a picture'),
onTap: () => _openCamera(),
),
Padding(
padding: EdgeInsets.all(8.0),
),
GestureDetector(
child: new Text('Select from gallery'),
onTap: () => _openGallery(),
),
],
),
),
);
});
}
savePicture(XFile? picture) async {
if (picture != null) {
try {
final String path = (await getApplicationDocumentsDirectory()).path;
final String completePath = p.join(path, '${DateTime.now().toIso8601String() + ".png"}');
await picture.saveTo(completePath);
setState(() => _imagePath = completePath);
if (kDebugMode) print('IMAGE SAVED TO: $_imagePath');
} on MissingPlatformDirectoryException catch (e) {
if (kDebugMode) print(e);
MyFirebaseServices.recordNonFatalError(message: 'Could not save image. Error = $e');
}
// TRY SAVE IT IN EXTERNAL STORAGE AS WELL
try {
if ((await Provider.of<CP>(context, listen: false).getProfile())!.saveExternally) {
final String path = (await getExternalStorageDirectory())!.path;
final String completePath = p.join(path, '${DateTime.now().toIso8601String() + ".png"}');
await picture.saveTo(completePath);
}
} on MissingPlatformDirectoryException catch (e) {
if (kDebugMode) print(e);
MyFirebaseServices.recordNonFatalError(message: 'Could not save image. Error = $e');
}
}
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _getSnagObject(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done){
if (snapshot.hasError){
if (kDebugMode) {
print (snapshot.stackTrace);
} else {
MyFirebaseServices.logMessage(message: 'Snag - snapshot error ${snapshot.error}');
}
return ErrorStatus(message: 'Error: Please go back and try again!');
}
return Scaffold(
appBar: AppBar(
flexibleSpace: TitleBarBackground(),
title: Text('SNAG', style: TextStyle(color: Theme.of(context).colorScheme.onPrimary)),
backgroundColor: Theme.of(context).colorScheme.primary,
elevation: 1.0,
leading: IconButton(
padding: EdgeInsets.only(left: 8.0),
icon: Icon(
Icons.arrow_back,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () => Navigator.pop(context),
), ),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
GestureDetector(
onTap: () async =>
_imagePath.isEmpty
? await _optionsDialogBox()
: await optionsDialogBoxWithDELorEDIT(context, deleteImage, annotatePicture),
child: MainImage(
imagePath: _imagePath,
),
),
SafeArea(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
controller: _locationController,
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[a-zA-Z0-9-,. ]'))],
maxLines: 1,
maxLength: 30,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
labelText: 'Location',
prefixIcon: Icon(
Icons.add_location,
size: 35.0,
color: Theme
.of(context)
.colorScheme
.primary,
)),
validator: (value) =>
value == null || value.isEmpty
? '*REQUIRED*'
: null,
),
TextFormField(
controller: _assetController,
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[a-zA-Z0-9-,. ]'))],
maxLines: 1,
maxLength: 40,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
labelText: 'Asset',
prefixIcon: Icon(
Icons.web_asset,
size: 35.0,
color: Theme
.of(context)
.colorScheme
.primary,
)),
validator: (value) =>
value == null || value.isEmpty
? '*REQUIRED*'
: null,
),
TextFormField(
controller: _conditionController,
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[a-zA-Z0-9-,. ]'))],
maxLines: 1,
maxLength: 50,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
labelText: 'Condition',
prefixIcon: Icon(
Icons.star,
size: 35.0,
color: Theme
.of(context)
.colorScheme
.primary,
)),
validator: (value) =>
value == null || value.isEmpty
? '*REQUIRED*'
: null,
),
TextFormField(
controller: _recommendationController,
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[a-zA-Z0-9-,. ]'))],
maxLines: 4,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
labelText: 'Description',
prefixIcon: Icon(
Icons.description,
size: 35.0,
color: Theme
.of(context)
.colorScheme
.primary,
)),
validator: (value) =>
value == null || value.isEmpty
? '*REQUIRED*'
: null,
),
],
),
),
SizedBox(
height: 10.0,
),
CategoryDisplay(currentSelection:currentSelection(_status), statusCodes: _statusCodes, callBackFunction: changeSelection,),
SizedBox(
height: 20.0,
),
SaveButton(
label: 'SAVE',
busy: _busy,
callBackfunction: () async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
if (!_busy) {
setState(() => _busy = true);
if (_newSnag) {
var uid = Uuid();
Snag snag = Snag(
id: uid.v4(),
siteID: _siteID,
imagePath: _imagePath,
Status: _status,
recommendation: _recommendationController.text,
condition: _conditionController.text,
asset: _assetController.text,
location: _locationController.text,
);
await MyDatabase.db.insertSnag(snag);
await Future.delayed(Duration(milliseconds: 250));
Navigator.pop(context, snag);
} else {
Snag snag = Snag(
id: _snag!.id,
siteID: _snag!.siteID,
imagePath: _imagePath,
Status: _status,
recommendation: _recommendationController.text,
condition: _conditionController.text,
asset: _assetController.text,
location: _locationController.text,
);
await MyDatabase.db.updateSnag(snag);
await Future.delayed(Duration(milliseconds: 250));
Navigator.pop(context, snag);
}
setState(() => _busy = false);
}
}
},
)
],
),
),
),
],
),
),
);
}
return BusyStatus(message: 'Loading...');
},
);
}
changeSelection(String selection){
setState(() {
_status = selection;
});
}
currentSelection(String status){
//If it is String then send INT
int? s = int.tryParse(status);
if (s!=null) return s;
// If it is old String text then send correct INT conversion
if (status == 'OK') status = "1";
if (status == 'OBS') status = "2";
if (status == 'CAT3') status = "3";
if (status == 'CAT2') status = "4";
if (status == 'CAT1') status = "5";
print (status);
return int.parse(status);
}
}
class CategoryDisplay extends StatelessWidget {
const CategoryDisplay({required this.callBackFunction, required this.currentSelection, required this.statusCodes, Key? key}) : super(key: key);
final Function callBackFunction;
final int currentSelection;
final StatusCodes statusCodes;
String _getDescription(int status) {
if (status == 1) return statusCodes.aDes;
if (status == 2) return statusCodes.bDes;
if (status == 3) return statusCodes.cDes;
if (status == 4) return statusCodes.dDes;
if (status == 5) return statusCodes.eDes;
return ('SELECT STATUS');
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Text('CATEGORY', style: Theme.of(context).textTheme.subtitle1,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () => callBackFunction("1"),
child: Container(
padding: EdgeInsets.all(8.0),
width: MediaQuery
.of(context)
.size
.width / 6,
decoration: BoxDecoration(
color: currentSelection == 1
? Theme
.of(context)
.colorScheme
.primary
: Colors.transparent,
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(16.0)),
border: Border.all(
color: Theme
.of(context)
.colorScheme
.primary,
style: BorderStyle.solid,
width: 2.0,
),
),
child: Center(
child: Text(
statusCodes.a,
style: TextStyle(
color: currentSelection == 1
? Colors.white
: Theme
.of(context)
.colorScheme
.primary),
),
),
),
),
GestureDetector(
onTap: () => callBackFunction("2"),
child: Container(
padding: EdgeInsets.all(8.0),
width: MediaQuery
.of(context)
.size
.width / 6,
decoration: BoxDecoration(
color: currentSelection == 2
? Theme
.of(context)
.colorScheme
.primary
: Colors.transparent,
//borderRadius: BorderRadius.only(topRight: Radius.circular(16.0)),
border: Border.all(
color: Theme
.of(context)
.colorScheme
.primary,
style: BorderStyle.solid,
width: 2.0,
),
),
child: Center(
child: Text(
statusCodes.b,
style: TextStyle(
color: currentSelection == 2
? Colors.white
: Theme
.of(context)
.colorScheme
.primary),
),
),
),
),
GestureDetector(
onTap: () => callBackFunction("3"),
child: Container(
padding: EdgeInsets.all(8.0),
width: MediaQuery
.of(context)
.size
.width / 6,
decoration: BoxDecoration(
color: currentSelection == 3
? Theme
.of(context)
.colorScheme
.primary
: Colors.transparent,
//borderRadius: BorderRadius.only(bottomLeft: Radius.circular(16.0)),
border: Border.all(
color: Theme
.of(context)
.colorScheme
.primary,
style: BorderStyle.solid,
width: 2.0,
),
),
child: Center(
child: Text(
statusCodes.c,
style: TextStyle(
color: currentSelection == 3
? Colors.white
: Theme
.of(context)
.colorScheme
.primary),
),
),
),
),
GestureDetector(
onTap: () => callBackFunction("4"),
child: Container(
padding: EdgeInsets.all(8.0),
width: MediaQuery
.of(context)
.size
.width / 6,
decoration: BoxDecoration(
color: currentSelection == 4
? Theme
.of(context)
.colorScheme
.primary
: Colors.transparent,
//borderRadius: BorderRadius.only(bottomLeft: Radius.circular(16.0)),
border: Border.all(
color: Theme
.of(context)
.colorScheme
.primary,
style: BorderStyle.solid,
width: 2.0,
),
),
child: Center(
child: Text(
statusCodes.d,
style: TextStyle(
color: currentSelection == 4
? Colors.white
: Theme
.of(context)
.colorScheme
.primary),
),
),
),
),
GestureDetector(
onTap: () => callBackFunction("5"),
child: Container(
padding: EdgeInsets.all(8.0),
width: MediaQuery
.of(context)
.size
.width / 6,
decoration: BoxDecoration(
color: currentSelection == 5
? Theme
.of(context)
.colorScheme
.primary
: Colors.transparent,
borderRadius: BorderRadius.only(topRight: Radius.circular(16.0)),
border: Border.all(
color: Theme
.of(context)
.colorScheme
.primary,
style: BorderStyle.solid,
width: 2.0,
),
),
child: Center(
child: Text(
statusCodes.e,
style: TextStyle(
color: currentSelection == 5
? Colors.white
: Theme
.of(context)
.colorScheme
.primary),
),
),
),
),
],
),
SizedBox(height: 4,),
Center(
child: Text(
_getDescription(currentSelection),
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
)),
],
);
}
}

You are updating your parent, to avoid that change changeSelection to this:
changeSelection(String selection){
_status = selection;
}
then change your CategoryDisplay to StatefulWidget and define new _status inside CategoryDisplay and after that change onTap to this:
GestureDetector(
onTap: () {
setState(() {
_status = 1;
});
callBackFunction("1"),
}
repeat this for all your onTap.

Related

Why does my code give an error of a Null Check Operator used on a null value

Here is the code
I wanted to register a user and I used a FormKey and I got the error Null
check operator use don a null value
here is what the run terminal shows
E/flutter (30851): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Null check operator used on a null value
E/flutter (30851): #0 _RegisterScreenState.signUp (package:em_home/screens/signing/register_screen.dart:58:29)
class RegisterScreen extends StatefulWidget {
const RegisterScreen({Key? key}) : super(key: key);
#override
_RegisterScreenState createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
showSnackBar(String content, BuildContext context) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(content)));
}
bool _isLoading = false;
final formKey = GlobalKey<FormState>();
String email = "";
String password = "";
String fullName = "";
String dob = "";
String gender = "";
AuthMethods authMethods = AuthMethods();
Uint8List? image;
pickImage(ImageSource source) async {
final ImagePicker imagePicker = ImagePicker();
XFile? _file = await imagePicker.pickImage(source: source);
if (_file != null) {
return await _file.readAsBytes();
}
print("No Image selected");
}
void selectImage() async {
Uint8List im = await pickImage(ImageSource.gallery);
setState(() {
image = im;
});
}
signUp() async {
if (formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});
await authMethods.registerUser(email: email, password: password, name: fullName, gender: gender, dateOfBirth: dob,)
.then((value) async {
if (value == true) {
await HelperFunctions.saveUserLoggedInStatus(true);
await HelperFunctions.saveUserEmailSF(email);
await HelperFunctions.saveUserNameSF(fullName);
Navigator.pushReplacement(context, SlideLeftRoute(widget: const LoginScreen()));
} else {
showSnackBar(value, context);
setState(() {
_isLoading = false;
});
}
});
}
return null;
}
void navigatetoSignIn() {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => const LoginScreen()));
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor,
body: SingleChildScrollView(
child: Container(
color: backgroundColor,
padding: const EdgeInsets.symmetric(horizontal: 15),
width: double.infinity,
child: Column(
children: [
const SizedBox(
height: 30,
),
Container(
alignment: Alignment.topLeft,
child: IconButton(
onPressed: () {
Navigator.pushReplacement(
context, SlideLeftRoute(widget: const LoginScreen()));
},
icon: const Icon(
Icons.arrow_back,
size: 40,
),
),
),
const SizedBox(
height: 20,
),
Image.asset(
'assets/logos.png',
),
const Text(
"Create your account",
style: TextStyle(color: textColor, fontWeight: FontWeight.bold),
),
const SizedBox(
height: 30,
),
Stack(
children: [
// to check if Image is not equal to null
image != null
? CircleAvatar(
radius: 64,
backgroundImage: MemoryImage(image!),
)
: const CircleAvatar(
radius: 64,
backgroundImage: AssetImage(
"assets/default_profile.jpg",
),
),
Positioned(
child: IconButton(
onPressed: selectImage,
icon: const Icon(Icons.add_a_photo),
),
left: 80,
bottom: -10,
)
],
),
const SizedBox(
height: 30,
),
TextFormField(
decoration: textInputDecoration.copyWith(
labelText: "Email",
prefixIcon: Icon(
Icons.email,
)),
onChanged: (val) {
setState(() {
email = val;
});
},
// check tha validation
validator: (val) {
return RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(val!)
? null
: "Please enter a valid email";
},
),
const SizedBox(
height: 20,
),
TextFormField(
obscureText: true,
decoration: textInputDecoration.copyWith(
labelText: "Password",
prefixIcon: Icon(
Icons.lock,
color: backgroundColor,
)),
validator: (val) {
if (val!.length < 6) {
return "Password must be at least 6 characters";
} else {
return null;
}
},
onChanged: (val) {
setState(() {
password = val;
});
},
),
const SizedBox(
height: 20,
),
TextFormField(
decoration: textInputDecoration.copyWith(
labelText: "Full Name",
prefixIcon: Icon(
Icons.person,
)),
onChanged: (val) {
setState(() {
fullName = val;
});
},
validator: (val) {
if (val!.isNotEmpty) {
return null;
} else {
return "Name cannot be empty";
}
},
),
const SizedBox(
height: 20,
),
TextFormField(
decoration: textInputDecoration.copyWith(
labelText: "Date Of Birth DD/MM/YYYY",
prefixIcon: Icon(
Icons.lock,
)),
validator: (val) {},
onChanged: (val) {
setState(() {
dob = val;
});
},
),
const SizedBox(
height: 20,
),
TextFormField(
decoration: textInputDecoration.copyWith(
labelText: "Gender",
prefixIcon: Icon(
Icons.lock,
)),
validator: (val) {},
onChanged: (val) {
setState(() {
gender = val;
});
},
),
const SizedBox(
height: 20,
),
InkWell(
onTap: signUp,
child: Container(
width: double.infinity,
alignment: AlignmentDirectional.center,
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: const ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
),
color: buttonColor,
),
child: _isLoading
? const Center(
child: CircularProgressIndicator(
color: iconButtonColor,
),
)
: const Text(
"Sign up",
style: TextStyle(
color: buttonTextColor,
fontWeight: FontWeight.bold),
),
),
),
const SizedBox(
height: 50,
),
Container(
color: backgroundColor,
child: const Text("- or sign in using -"),
),
const SizedBox(
height: 25,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
GestureDetector(
onTap: () {
Navigator.push(context, CustomRoute(widget: const Scaffold(body: Center(child: Text("Coming Soon"),))));
},
child: Container(
width: 50,
height: 45,
color: Colors.white,
child: Image.asset('assets/google.png'),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
Navigator.push(context, CustomRoute(widget: const Scaffold(body: Center(child: Text("Coming Soon"),))));
},
child: Container(
width: 50,
height: 45,
color: Colors.white,
child: Image.asset('assets/facebook.png'),
),
),
],
),
],
),
),
),
);
}
}
please what do I do here is the snippet to the code
Wrap your UI with the FormFields inside a Form widget and pass the GlobalKey to it.
Quick guide from Flutter docs: https://docs.flutter.dev/cookbook/forms/validation

Flutter LinearProgressIndicator doesn't work

I've been learning flutter for a while and got stuck with a not working ProgressIndicator. The problem is that it doesn't detect any error, and progressbar doesn't update anyway when you enter a password into TextFormField. And if you have any comments about the code itself, I'd love to read it and learn something new
import 'RegisterThree.dart';
import 'package:flutter/material.dart';
class RegisterSec extends StatefulWidget {
const RegisterSec({Key? key}) : super(key: key);
#override
_RegisterPage2 createState() => _RegisterPage2();
}
class _RegisterPage2 extends State<RegisterSec> {
final _formKey = GlobalKey<FormState>();
// regular expression to check if string
RegExp pass_valid = RegExp(r"(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W)");
double password_strength = 0;
bool validatePassword(String pass) {
String _password = pass.trim();
if (_password.isEmpty) {
setState(() {
password_strength = 0;
});
} else if (_password.length < 6) {
setState(() {
password_strength = 1 / 4;
});
} else if (_password.length < 8) {
setState(() {
password_strength = 2 / 4;
});
} else {
if (pass_valid.hasMatch(_password)) {
setState(() {
password_strength = 4 / 4;
});
return true;
} else {
setState(() {
password_strength = 3 / 4;
});
return false;
}
}
return false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
width: double.infinity,
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 75),
SizedBox(
child: Text(
'Create a new password',
style: TextStyle(fontSize: 40),
textAlign: TextAlign.center,
)),
Padding(
padding: const EdgeInsets.all(20.0),
child: TextFormField(
onChanged: (value) {
_formKey.currentState!.validate();
},
validator: (value) {
if (value!.isEmpty) {
return "Password";
} else {
//call function to check password
bool result = validatePassword(value);
if (result) {
// create account event
return null;
} else {
return "Password should contain Capital, small letter & Number & Special";
}
}
},
decoration: InputDecoration(
border: UnderlineInputBorder(), hintText: "Password"),
obscureText: true,
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: TextFormField(
obscureText: true,
decoration: const InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Confirm password',
),
),
),
Padding(
padding: const EdgeInsets.all(20),
child: LinearProgressIndicator(
value: password_strength,
backgroundColor: Colors.grey[300],
minHeight: 5,
color: password_strength <= 1 / 4
? Colors.red
: password_strength == 2 / 4
? Colors.yellow
: password_strength == 3 / 4
? Colors.blue
: Colors.green,
),
),
SizedBox(
child: Text(
'Password should contain Capital, small letter & Number & Special',
style: TextStyle(
color: Color.fromARGB(255, 131, 131, 131),
fontSize: 13,
),
textAlign: TextAlign.left,
)),
SizedBox(
height: 30,
),
SizedBox(
child: ElevatedButton(
onPressed: password_strength != 1
? null
: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RegisterThree()));
},
child: Icon(Icons.navigate_next, color: Colors.white),
style: ElevatedButton.styleFrom(
shape: CircleBorder(),
padding: EdgeInsets.all(20),
primary: Colors.blue, // <-- Button color
onPrimary: Colors.white, // <-- Splash color
),
)),
SizedBox(
height: 280,
),
TextButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => HomePage()));
},
child: Text(
'Go back',
style: TextStyle(color: Colors.blue, fontSize: 15),
),
),
],
),
),
);
}
}
The reason why it does not work is that currentState is null. It is null because you have to wrap your TextFormField in a form. And to this form you have to pass the formKey, not the Container
Form(
key: _formKey,
child: Container(
width: double.infinity,
child: ...

type '(dynamic) => Null' is not a subtype of 'TextEditingController?'

I've been trying to accept user input in an extracted TextFormField method(Name, contact and email) and then save them to my firestore database using TextEditingController.
Here lies the case after accepting the input, the name and email controller always displays the same input. After saving, even for test purposes, the database always shows that only null values have been returned from the controllers...
Please help me solve this problem...
Snippet of the code:
class Profile extends StatefulWidget {
final String email;
Profile({Key key, this.email}) : super(key: key);
get user => AuthService();
#override
_ProfileState createState() => _ProfileState(email);
}
class _ProfileState extends State<Profile> {
String email;
String contain;
_ProfileState(this.email);
bool showPhone = false;
//Text editing controller
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
// initState
#override
void initState() {
super.initState();
_emailController.addListener(() {
final String text = _emailController.text;
_emailController.value = _emailController.value.copyWith(
text: text,
selection:
TextSelection(baseOffset: text.length, extentOffset: text.length),
composing: TextRange.empty,
);
});
_phoneController.addListener(() {
final String contact = _phoneController.text;
_phoneController.value = _phoneController.value.copyWith(
text: contact,
selection: TextSelection(
baseOffset: contact.length,
extentOffset: contact.length,
),
composing: TextRange.empty,
);
});
_nameController.addListener(() {
final String contact = _nameController.text;
_nameController.value = _nameController.value.copyWith(
text: contact,
selection: TextSelection(
baseOffset: contact.length,
extentOffset: contact.length,
),
composing: TextRange.empty,
);
});
}
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
_emailController.dispose();
_phoneController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
final _formKey = GlobalKey<FormState>();
final doc = Database();
final ThemeData themeData = Theme.of(context);
String name, email, contact;
return loading
? Loading()
: SafeArea(
child: StreamBuilder<UserData>(
stream: Database(uid: doc.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Scaffold(
appBar: AppBar(
backgroundColor: Aqua,
elevation: 1,
title: Text(
'Edit Profile Info',
style: themeData.textTheme.headline1.copyWith(
color: White,
fontSize: 22,
letterSpacing: 0.75,
),
),
),
body: new Container(
height: window.physicalSize.height * 0.85,
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 25,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Center(
child: new Stack(
children: [
addVertical(20),
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
boxShadow: [
BoxShadow(
blurRadius: 15,
spreadRadius: 5,
color: Cyan.withOpacity(0.5),
offset: Offset(0, 15),
),
],
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
"assets/images/IMG2.jpg"),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
color: Cyan,
),
child: IconButton(
icon: Icon(
Icons.edit,
color: Dark_Blue,
),
onPressed:
() {}, // change profile picture
),
),
),
],
),
),
addVertical(30),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 25, left: 35, right: 35, bottom: 10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildTextFormField(
"Full Name",
userData.fname ??
"Enter Full Name here...",
false,
false ?? null,
),
addVertical(15),
buildTextFormField(
"Contact",
userData.contact ?? "020 *** ****",
true,
false ?? null,
),
addVertical(15),
buildTextFormField(
"E-mail",
userData.email ?? "emaple123#gmail.com",
false,
true ?? null,
),
],
),
),
),
),
addVertical(35),
Padding(
padding: const EdgeInsets.only(
left: 35, right: 35, top: 25),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
primary: White,
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"CANCEL",
style: themeData.textTheme.bodyText1
.copyWith(
letterSpacing: 1.25,
fontWeight: FontWeight.bold,
fontSize: 18,
color: Mid_Blue,
),
),
),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.currentUser;
// User person = result.user;
if (result != null) {
await Database(uid: doc.uid)
.updateData(
"$name",
"$email",
"$contact",
);
Toast.show(
"Your details have been successfully updated!",
context,
duration: Toast.LENGTH_LONG,
gravity: Toast.BOTTOM,
);
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
} else {
Toast.show(
"Please try again later",
context,
gravity: Toast.BOTTOM,
duration: Toast.LENGTH_LONG,
);
}
}
},
style: ElevatedButton.styleFrom(
elevation: 2,
primary: Colors.green[300],
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
), //!elevatedButton background
),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 18,
letterSpacing: 2.2,
color: White,
),
),
),
],
),
),
],
//),
),
),
),
);
} else {
return Loading();
}
},
),
);
}
Widget buildTextFormField(
String labelText,
String placeholder,
bool isPhone,
bool isEmail,
) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
child: TextField(
onSubmitted: isPhone || isEmail
? (value) {
value = _phoneController.toString() != null
? _emailController.toString() != null
: _nameController;
}
: null,
controller: isEmail || isPhone
? (value) {
value = _emailController.toString() != null
? _nameController.toString() != null
: _phoneController;
}
: _emailController,
obscureText: isPhone ? showPhone : false,
decoration: InputDecoration(
suffixIcon: isPhone
? IconButton(
onPressed: () => setState(() => showPhone = !showPhone),
icon: Icon(
Icons.remove_red_eye,
color: Red,
),
)
: null, //!This makes the icon appear only for the password field
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Aqua,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
hintText: placeholder,
hintStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Cyan,
),
),
keyboardType:
isPhone ? TextInputType.phone : TextInputType.emailAddress,
),
);
}
}
This is the function that builds name textField
buildTextFormField(
"Full Name",
userData.fname ?? "Enter Full Name here...",
false, // isPhone
false ?? null, // isEmail
),
From here we can see that isPhone = false; isEmail = false;
This is the function for the and controller in TextFormField:
controller: isEmail || isPhone
? (value) {
value = _emailController.toString() != null
? _nameController.toString() != null
: _phoneController;
}
: _emailController,
Since isEmail and isPhone are both false, it returns _emailController for name Textfield. So name is being updated on email textfield.
The function for onSubmitted() returns null if isEmail & isPhone are both false. Therefore, for name field, it will return null as the value (probably why you are getting null in the database).
I have tried to better organise the code below:
class MyHomePage extends StatefulWidget {
final String email;
MyHomePage({Key key, this.email}) : super(key: key);
get user => AuthService();
#override
_MyHomePageState createState() => _MyHomePageState(email);
}
class _MyHomePageState extends State<MyHomePage> {
String email;
// not used in this class
String contain;
_MyHomePageState(this.email);
bool showPhone = false;
//Text editing controller
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
final TextEditingController _nameController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
_emailController.dispose();
_phoneController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
final _formKey = GlobalKey<FormState>();
final doc = Database();
final ThemeData themeData = Theme.of(context);
return loading
? Loading()
: SafeArea(
child: StreamBuilder<UserData>(
stream: Database(uid: doc.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
_emailController.text = userData.email;
_phoneController.text = userData.phone;
_nameController.text = userData.name;
return Scaffold(
appBar: AppBar(
backgroundColor: Aqua,
elevation: 1,
title: Text(
'Edit Profile Info',
style: themeData.textTheme.headline1.copyWith(
color: White,
fontSize: 22,
letterSpacing: 0.75,
),
),
),
body: new Container(
height: window.physicalSize.height * 0.85,
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 25,
),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
new Center(
child: new Stack(
children: [
addVertical(20),
Container(
width: 190,
height: 190,
decoration: BoxDecoration(
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
boxShadow: [
BoxShadow(
blurRadius: 15,
spreadRadius: 5,
color: Cyan.withOpacity(0.5),
offset: Offset(0, 15),
),
],
shape: BoxShape.circle,
image: DecorationImage(
image: AssetImage(
"assets/images/IMG2.jpg"),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: 0,
right: 0,
child: Container(
height: 55,
width: 55,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 5,
color: Theme.of(context)
.scaffoldBackgroundColor,
),
color: Cyan,
),
child: IconButton(
icon: Icon(
Icons.edit,
color: Dark_Blue,
),
onPressed:
() {}, // change profile picture
),
),
),
],
),
),
addVertical(30),
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 25, left: 35, right: 35, bottom: 10),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
buildTextFormField(
"Full Name",
"Enter Full Name here...",
false,
_nameController,
),
addVertical(15),
buildTextFormField(
"Contact",
"020 *** ****",
true,
_phoneController,
),
addVertical(15),
buildTextFormField(
"E-mail",
"example123#gmail.com",
false,
_nameController,
),
],
),
),
),
),
addVertical(35),
Padding(
padding: const EdgeInsets.only(
left: 35, right: 35, top: 25),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
primary: White,
),
onPressed: () {
Navigator.pop(context);
},
child: Text(
"CANCEL",
style: themeData.textTheme.bodyText1
.copyWith(
letterSpacing: 1.25,
fontWeight: FontWeight.bold,
fontSize: 18,
color: Mid_Blue,
),
),
),
ElevatedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result =
await _auth.currentUser;
// User person = result.user;
if (result != null) {
await Database(uid: doc.uid)
.updateData(
_nameController.text,
_emailController.text,
_phoneController.text,
);
Toast.show(
"Your details have been successfully updated!",
context,
duration: Toast.LENGTH_LONG,
gravity: Toast.BOTTOM,
);
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Homepage()));
} else {
Toast.show(
"Please try again later",
context,
gravity: Toast.BOTTOM,
duration: Toast.LENGTH_LONG,
);
}
}
},
style: ElevatedButton.styleFrom(
elevation: 2,
primary: Colors.green[300],
shape: new RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
), //!elevatedButton background
),
child: Text(
"SAVE",
style: TextStyle(
fontSize: 18,
letterSpacing: 2.2,
color: White,
),
),
),
],
),
),
],
//),
),
),
),
);
} else {
return Loading();
}
},
),
);
}
Widget buildTextFormField(
String labelText,
String placeholder,
bool isPhone,
TextEditingController controller,
) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 10),
child: TextField(
// onSubmitted: isPhone || isEmail
// ? (value) {
// value = _phoneController.toString() != null
// ? _emailController.toString() != null
// : _nameController;
// }
// : null,
controller: controller,
obscureText: isPhone ? showPhone : false,
decoration: InputDecoration(
suffixIcon: isPhone
? IconButton(
onPressed: () => setState(() => showPhone = !showPhone),
icon: Icon(
Icons.remove_red_eye,
color: Red,
),
)
: null, //!This makes the icon appear only for the password field
contentPadding: EdgeInsets.only(bottom: 3),
labelText: labelText,
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Aqua,
),
floatingLabelBehavior: FloatingLabelBehavior.always,
hintText: placeholder,
hintStyle: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Cyan,
),
),
keyboardType:
isPhone ? TextInputType.phone : TextInputType.emailAddress,
),
);
}
}

Can't save the right image url on Firestore

Actually I am getting data by user, saving it in a map and adding this item to a list. When user has added all items in the list, it press the floating action button to add this list to an existing list on the firestore.
All data is being saved successfully except the imageUrl. First item in the list has null imageUrl at firestore however the second item is assigned the imageUrl of first Item. And the sequence goes on... I don't know what am I missing! I have subtracted most of code to be specific. I think the problem is being caused by the variable itemImageUrl that is being overridden. Help!
This is the code:
class RetrieveShop extends StatefulWidget {
String nameShop;String docId;
RetrieveShop(this.nameShop,this.docId);
#override
_RetrieveShopState createState() => _RetrieveShopState();
}
class _RetrieveShopState extends State<RetrieveShop> {
var result;
bool isLoading = false;
bool isLoadingNow = false;
var _price = TextEditingController();
var _itemName = TextEditingController();
/* var _id = TextEditingController();
var _category = TextEditingController();*/
var _desc = TextEditingController();
File _image;
File _image2;
String itemImageUrl;
bool _showDg = false;
bool condition = true;
bool isPopular = false;
bool savingAllDataToFirestore = false;
List itemo=[];
Future getImageFromGallery() async {
var image = await ImagePicker()
.getImage(source: ImageSource.gallery, imageQuality: 80);
setState(() {
_image = File(image.path);
print('Image Path $_image');
});
}
Future getImageFromCamera() async {
var image = await ImagePicker().getImage(source: ImageSource.camera);
setState(() {
_image = File(image.path);
print('Image Path $_image');
});
}
Future uploadItemOfShop(BuildContext context) async {
Reference ref = storage.ref().child(
"${this.widget.nameShop}'s ${_itemName.text} Price ${_price.text}" +
DateTime.now().toString());
if (_image.toString() == '') {
Flushbar(
title: "Menu Item Image is empty",
message: "Please Add some Image first",
backgroundColor: Colors.red,
boxShadows: [
BoxShadow(
color: Colors.red[800],
offset: Offset(0.0, 2.0),
blurRadius: 3.0,
)
],
duration: Duration(seconds: 3),
)
..show(context);
} else {
setState(() {
isLoadingNow=true;
});
debugPrint('wah');
UploadTask uploadTask = ref.putFile(_image);
uploadTask.then((res) async {
itemImageUrl = await res.ref.getDownloadURL();
}).then((value){
setState(() {
isLoadingNow=false;
});
debugPrint("Nullifing the Image object");
_image=_image2; //Trying to null the file object after it is used so thinking that might
//the problem is being caused here
});
}
}
Widget listTile(BuildContext context,String doc) {
return !isLoadingNow?SingleChildScrollView(
child: ListTile(
title: Wrap(
// mainAxisAlignment: MainAxisAlignment.start,
direction: Axis.horizontal,
children: [
Text(
"Enter details of Item",
style: TextStyle(fontSize: 22, color: Colors.black87),
),
Stack(
children: [
SizedBox(
width: MediaQuery
.of(context)
.size
.width,
height: MediaQuery
.of(context)
.size
.height / 2.5,
child: (_image != null)
? Image.file(
_image,
fit: BoxFit.cover,
)
: Image.network(
"https://images.unsplash.com/photo-1502164980785-f8aa41d53611?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60",
fit: BoxFit.fill,
),
),
Container(
alignment: Alignment.topLeft,
color: Colors.white38,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(
Icons.add_a_photo,
size: 30.0,
color: Colors.black,
),
onPressed: () {
getImageFromCamera();
},
),
SizedBox(
width: 10,
),
IconButton(
icon: Icon(
Icons.create_new_folder_rounded,
size: 30.0,
color: Colors.black,
),
onPressed: () {
getImageFromGallery();
},
),
],
),
)
],
),
],
),
subtitle: Column(
children: [
TextField(
controller: _itemName,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'Enter Item name',
icon: Icon(Icons.fastfood),
alignLabelWithHint: true,
hintText: "Zinger Burger etc"),
autofocus: true,
),
TextField(
controller: _price,
autofocus: false,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'Enter Price',
icon: Icon(Icons.attach_money),
alignLabelWithHint: true,
hintText: "70 etc"),
),
SwitchListTile(
title: condition ? Text("Fresh") : Text("Used"),
value: condition,
onChanged: _onConditionChanged,
),
SwitchListTile(
title: isPopular ? Text("Popular") : Text("Not Popular"),
value: isPopular,
onChanged: _onPopularityChanged,
),
TextField(
autofocus: false,
maxLength: 150,
controller: _desc,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: InputDecoration(
labelText: 'Enter Description',
icon: Icon(Icons.description),
alignLabelWithHint: true,
hintText:
"This item contains cheez and paneer with delicious mayonees etc.."),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
child: Text(
"Save",
style: TextStyle(color: Colors.white, fontSize: 16),
),
color: Colors.red,
onPressed: () {
if(_image !=null){
int price=int.parse(_price.text);
String itemName=_itemName.text;
String itemDesc=_desc.text;
String categoryO=this.categoryToSave;
String imageUrl=this.itemImageUrl;
uploadItemOfShop(context).then((value){
String idO=getRandomString(3);
var item = {
'itemName': itemName,
'itemPrice': price,
'itemDesc': itemDesc,
'category': categoryO,
'condition': condition,
'imageUrl': imageUrl,
'isPopular': this.isPopular,
'id': idO,
};
setState(() {
itemo.add(item);
});
});
setState(() {
_showDg = false;
});
_price.clear();
_desc.clear();
_itemName.clear();
/* imageUrl='';
itemImageUrl='';*/
}else{
Fluttertoast.showToast(msg: 'Please select some image first');
}
}
),
],
),
],
),
selectedTileColor: Colors.red.shade300,
),
):Padding(
padding: const EdgeInsets.all(40.0),
child: Center(
child:CircularProgressIndicator()
),
);
}
Widget myList(String nameOfButton2, {BuildContext buildContext}) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
StreamBuilder(
stream: FirebaseFirestore.instance.collection('shops').where(
'name', isEqualTo: this.widget.nameShop).snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if(snapshot.hasData){
DocumentSnapshot list=snapshot.data.docs.single;
return isLoadingNow
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: list.data()['menu'].length,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context,int index){
return Card(
shadowColor: Colors.red,
//color: Colors.black,
elevation: 8.0,
//borderOnForeground: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
margin: EdgeInsets.only(
bottom: 10, right: 10),
child: ListTile(
leading: CachedNetworkImage(
fit: BoxFit.cover,
//height: 100,
placeholderFadeInDuration:
Duration(seconds: 2),
fadeOutDuration: Duration(seconds: 2),
imageUrl: list
.data()['menu'][index]['imageUrl'],
progressIndicatorBuilder: (context, url,
downloadProgress) =>
Center(
child:
CircularProgressIndicator(
value: downloadProgress.progress,
color: kPrimaryColor,
)),
errorWidget: (context, url, error) =>
Icon(Icons.error),
),
title:Text('Name: ${list.data()['menu'][index]['itemName']}'),
subtitle: Column(
crossAxisAlignment:CrossAxisAlignment.start,
children:[
Text(
"Price: ${list.data()['menu'][index]['itemPrice']} Rs",
style: TextStyle(
color: Colors.black54,
fontSize: 18),
),
Text(
"Description: ${list.data()['menu'][index]['itemDesc']}",
style: TextStyle(
color: Colors.black87, fontSize: 20),
),
list.data()['menu'][index]['condition']
? Text("Condition: Fresh")
: Text("Condition: Used"),
]
),
),
);
},
);
}
if(snapshot.hasError){
return Text('Please try again');
}
return Center(
child: CircularProgressIndicator(),
);
},
),
]
,
);
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("${this.widget.nameShop}"),
centerTitle: false,
actions: [
IconButton(
icon: Icon(Icons.add_comment),
onPressed: () {
setState(() {
_showDg = !_showDg;
});
})
],
),
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
_showDg ? listTile(context,this.widget.docId) : Text(""),
myList(this.widget.nameShop),
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Text("Items ${itemo.length.toString()}"),
onPressed: (){
if(itemo.length==0){
Fluttertoast.showToast(msg: 'Please add some items first');
}else{
FirebaseFirestore.instance.collection('shops').doc(this.widget.docId).update({
"menu": FieldValue.arrayUnion(itemo),
});
setState(() {
itemo=[];
});
}
},
),
);
}
}
Was just missing await before the object of uploadtask.

When routing to another page I get "There are multiple heroes that share the same tag within a subtree"

I am trying to navigate from one screen to another with route. When I hit the button for the page to move to the route provided I get the error:
I/flutter ( 8790): Another exception was thrown: There are multiple heroes that share the same tag within a subtree.
Sharing with you the source code:
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_counter/flutter_counter.dart';
import 'package:gender_selection/gender_selection.dart';
import 'package:group_chats/addContactUI.dart';
import 'package:group_chats/addPhone.dart';
import 'package:group_chats/letGoUI.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'Animation/FadeAnimation.dart';
import 'Model/insertUserModel.dart';
class AddProfileUI extends StatefulWidget {
final List<String> listFriends;
final String phone;
AddProfileUI(this.listFriends, this.phone);
#override
_AddProfileUIState createState() => _AddProfileUIState();
}
class _AddProfileUIState extends State<AddProfileUI> {
File _image;
final picker = ImagePicker();
final _formKey = GlobalKey<FormState>();
String name = "";
String city = "";
TextEditingController dobController = TextEditingController();
bool single = false;
Gender gender;
final _scaffoldKey = GlobalKey<ScaffoldState>();
int defaultValue = 18;
List<String> selectedContact;
SharedPreferences prefs;
bool _loading = true;
Future initPrefs() async {
prefs = await SharedPreferences.getInstance();
}
uploadImage(String userId) async {
try {
FormData formData = new FormData.fromMap({
"file":
await MultipartFile.fromFile(_image.path, filename: "$userId.jpg"),
});
var response = await Dio().post(
"https://us-central1-app-backend-fc090.cloudfunctions.net/webApi/api/v1/upload_profile/$userId",
data: formData);
if (response.statusCode == 200)
return response.data;
else
return null;
} catch (e) {
print(e.toString());
return null;
}
}
Future<InsertUserModel> insertData() async {
try {
for (int i = 0; i < selectedContact.length; i++) {
if (selectedContact[i][0] == "0") {
selectedContact[i] = selectedContact[i].replaceRange(0, 1, "+972");
}
}
var body = jsonEncode({
"gender": gender.index == 0 ? "male" : "female",
"city": city,
"last_login": {},
"friends": selectedContact ?? [],
"single": single,
"name": name,
"phone_number": widget.phone,
"age": defaultValue
});
final String apiUrl =
"https://us-central1-app-backend-fc090.cloudfunctions.net/webApi/api/v1/users/${AddPhoneUI.uid}";
final response = await http.post(apiUrl,
headers: {"content-type": "application/json"}, body: body);
if (response.statusCode == 200) {
final responseString = response.body;
return insertUserModelFromJson(responseString);
}
} catch (e) {
setState(() {
_loading = false;
});
}
}
getUserData() async {
try {
final String apiUrl =
"https://us-central1-app-backend-fc090.cloudfunctions.net/webApi/api/v1/users/";
final response = await http.get(apiUrl);
if (response.statusCode == 200) {
final responseString = response.body;
return insertUserModelFromJson(responseString);
}
} catch (e) {
setState(() {
_loading = false;
});
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.red,
content: Text('Error: ${e.toString()}'),
duration: Duration(milliseconds: 2500),
));
}
}
Future<void> _selectStartingDate(BuildContext context) async {
DateTime selectedDate = DateTime.now();
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1950),
lastDate: DateTime(
DateTime.now().year, DateTime.now().month, DateTime.now().day),
);
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
dobController.text = selectedDate.year.toString() +
"-" +
selectedDate.month.toString() +
"-" +
selectedDate.day.toString();
});
}
Future getImage() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
_image = File(pickedFile.path);
});
}
showPopUp(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
shape:
OutlineInputBorder(borderRadius: BorderRadius.circular(14.0)),
content: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 20.0,
),
Text(
"My Profile",
style: TextStyle(
fontSize: 27.0, fontWeight: FontWeight.w600),
),
SizedBox(
height: 30.0,
),
RichText(
text: TextSpan(
text: "Your friends will be able to\nsee ",
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
children: <TextSpan>[
TextSpan(
text: 'only',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w500)),
TextSpan(text: 'your picture and\name.'),
],
)),
SizedBox(
height: 10.0,
),
RichText(
text: TextSpan(
text: "Other fields are used for\n",
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
children: <TextSpan>[
TextSpan(
text: 'research issues',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.w500)),
TextSpan(text: "only."),
],
)),
SizedBox(
height: 10.0,
),
RichText(
text: TextSpan(
text: "Please fill all the fields.",
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
)),
SizedBox(
height: 80.0,
),
MaterialButton(
shape: OutlineInputBorder(
borderSide: BorderSide(width: 1.0)),
color: Colors.white38,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
"Ok, got it",
style: TextStyle(fontSize: 18.0),
),
),
onPressed: () {
Navigator.pop(context);
},
),
],
),
),
));
}
#override
void initState() {
super.initState();
selectedContact = widget.listFriends;
initPrefs();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: SafeArea(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {},
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddContactUI(),
)).then((value) {
setState(() {
selectedContact = value;
});
});
},
child: Column(
children: [
Icon(
Icons.supervisor_account,
size: 40.0,
),
Text("Add Friends"),
],
),
)
],
),
),
Container(
child: GestureDetector(
onTap: () async {
await getImage();
},
child: CircleAvatar(
radius: 60.0,
backgroundImage: _image != null
? Image.file(_image).image
: Image.asset("assets/empty.png").image,
backgroundColor: Colors.transparent,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: FadeAnimation(
1.7,
Form(
key: _formKey,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Color(0xff5E17EB).withOpacity(0.3),
blurRadius: 20,
offset: Offset(0, 10),
)
]),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey[200]))),
child: TextFormField(
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) =>
FocusScope.of(context).nextFocus(),
onChanged: (val) {
setState(() {
name = val;
});
},
validator: (val) =>
val.isEmpty ? "Enter Name" : null,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Name",
hintStyle: TextStyle(color: Colors.grey)),
),
),
Container(
decoration: BoxDecoration(
border: Border(
bottom:
BorderSide(color: Colors.grey[200]))),
padding: EdgeInsets.all(10),
child: TextFormField(
textInputAction: TextInputAction.next,
keyboardType: TextInputType.text,
onChanged: (val) {
setState(() {
city = val;
});
},
validator: (val) {
if (val.isEmpty) return "Enter City";
// if (val.length < 6)
// return "Password should be at least 6 characters";
return null;
},
decoration: InputDecoration(
border: InputBorder.none,
hintText: "City",
hintStyle: TextStyle(color: Colors.grey)),
),
),
Container(
height: 140.0,
padding: EdgeInsets.all(10),
child: GenderSelection(
selectedGender: gender,
maleText: "", //default Male
femaleText: "", //default Female
linearGradient: LinearGradient(colors: [
Colors.indigo,
Colors.black
]), //List: [Colors.indigo, Colors.black]
selectedGenderIconBackgroundColor:
Colors.indigo, // default red
checkIconAlignment:
Alignment.centerRight, // default bottomRight
selectedGenderCheckIcon:
null, // default Icons.check
onChanged: (Gender gender) {
this.gender = gender;
print(this.gender);
},
equallyAligned: true,
animationDuration: Duration(milliseconds: 400),
isCircular: true, // default : true,
isSelectedGenderIconCircular: true,
opacityOfGradient: 0.6,
padding: const EdgeInsets.all(3),
size: 120, //default : 120
),
),
SizedBox(
height: 10.0,
),
Row(
children: [
Checkbox(
value: single,
onChanged: (value) {
setState(() {
single = !single;
});
},
),
Text(
"Are you Single?",
style: TextStyle(fontSize: 16.0),
)
],
),
Padding(
padding: const EdgeInsets.all(14.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
"Age:",
style: TextStyle(fontSize: 18.0),
),
Counter(
initialValue: defaultValue,
buttonSize: 35.0,
textStyle: TextStyle(fontSize: 25.0),
minValue: 0,
color: Colors.black,
maxValue: 80,
step: 1,
decimalPlaces: 0,
onChanged: (value) {
// get the latest value from here
setState(() {
defaultValue = value;
});
},
),
],
),
),
],
),
),
),
),
),
SizedBox(
height: 20.0,
),
FadeAnimation(
1.9,
MaterialButton(
shape: OutlineInputBorder(borderSide: BorderSide(width: 1.0)),
color: Colors.black,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 20.0),
child: Text(
"Next",
style: TextStyle(
fontSize: 18.0,
color: Colors.white,
),
),
),
onPressed: () async {
if (_formKey.currentState.validate()) {
InsertUserModel result = await insertData();
// var imageResult = await uploadImage(result.id);
print(result.data[0].id);
if (_image != null) {
await uploadImage(result.data[0].id);
}
setState(() {
_loading = false;
});
if (result != null) {
await prefs.setString("userID", result.data[0].id);
final snackBar = SnackBar(
content: Text(
result.message,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 2000),
);
_scaffoldKey.currentState.showSnackBar(snackBar);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LetGoUI(result.data[0].id),
));
} else {
final snackBar = SnackBar(
content: Text(
"Here is some error, please try again later!",
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.red,
duration: Duration(milliseconds: 2000),
);
_scaffoldKey.currentState.showSnackBar(snackBar);
}
}
},
),
)
],
),
),
),
);
}
}
I really don't understand what is the problem because I'm not using any Heros and not have double FloatingActionButton.
How do I solve this?
Thanks
There might be problem with showing snackbar while navigating. you should remove that problem by calling following method before navigating.
void removeSnackBarCallsBeforeNavigation() {
ScaffoldMessenger.of(context).removeCurrentSnackBar();
}