Flutter DropdownButton doesn't update in StatefulBuilder - flutter

I am using List items for DropdownButton widget in StatefulBuilder. When I select any item on DropdownButton its not updating at this stage. But when I close the showModalBottomSheet widget and open again its updating. How can I fix this? Thanks for help.
This is my menu code for items
List<DropdownMenuItem<String>> tur = [];
void kategoritur(){
tur = [];
tur.add(new DropdownMenuItem(
child: Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/g.png'),
fit: BoxFit.fitWidth,
),
),
height: 50, width: 50,
),
SizedBox(width: 30,),
new Text("Şırınga"),
],
),
value: "Siringa",)
);
tur.add(new DropdownMenuItem(
child: Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/t.png'),
fit: BoxFit.fitWidth,
),
),
height: 50, width: 50,
),
SizedBox(width: 30,),
new Text("Hap"),
],
),
value: "Hap",)
);
tur.add(new DropdownMenuItem(
child: Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/f.png'),
fit: BoxFit.fitWidth,
),
),
height: 50, width: 50,
),
SizedBox(width: 30,),
new Text("Şişe"),
],
),
value: "Sise",)
);
tur.add(new DropdownMenuItem(
child: Row(
children: <Widget>[
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/e.png'),
fit: BoxFit.fitWidth,
),
),
height: 50, width: 50,
),
SizedBox(width: 30,),
new Text("Damla"),
],
),
value: "Damla",)
);
}
This is my main code
showModalBottomSheet(
useRootNavigator: true,
context: context,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(24),
),
),
builder: (context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: StatefulBuilder(
builder: (context, setModalState) {
return Container(
padding: const EdgeInsets.all(32),
child: Column(
children: [
FlatButton(
onPressed: () async {
var selectedTime =
await showTimePicker(
builder: (BuildContext context, Widget child) {
return MediaQuery(
data: MediaQuery.of(context)
.copyWith(alwaysUse24HourFormat: false),
child: Theme(
data: ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
primary: Color(0xFF52b788),
onPrimary: Color(0xFF52b788),
surface: Colors.white,
onSurface: Color(0xFF52b788),
),
dialogBackgroundColor:
Color(0xFF52b788),
),
child: child),
);
},
context: context,
initialTime:
TimeOfDay.now(),
);
if (selectedTime != null) {
final now = DateTime.now();
var selectedDateTime =
DateTime(
now.year,
now.month,
now.day,
selectedTime.hour,
selectedTime
.minute);
_alarmTime =
selectedDateTime;
setModalState(() {
_alarmTimeString =
DateFormat('HH:mm')
.format(
selectedDateTime);
});
}
},
child: Text(
_alarmTimeString,
style:
TextStyle(fontSize: 32),
),
),
Container(
margin: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black38,
borderRadius: BorderRadius.circular(18)
),
width: _width,
padding: EdgeInsets.fromLTRB(14, 0, 14, 0),
child: DropdownButton(
style: GoogleFonts.montserrat(
color: Colors.white,
fontSize: 16,
textStyle: TextStyle(fontWeight:
FontWeight.w300)),
dropdownColor: Colors.cyan[700],
underline: SizedBox.shrink(),
items: tur,
onChanged: (value){
turdata = value;
setState(() {
});
},
hint: new Text("İlaç Türü", textAlign:
TextAlign.center
,
style: GoogleFonts.montserrat(
color: Colors.white,
fontSize: 16,
textStyle: TextStyle(fontWeight:
FontWeight.w300)),
),
value: turdata,
),
),
SizedBox(height: 20,),
FloatingActionButton.extended(
backgroundColor: Color(0xFF52b788),
onPressed: onSaveAlarm,
icon: Icon(Icons.alarm),
label: Text('Save'),
),
],
),
);
},
),
);
},
);

i made a custom class of dropdown you can customize this in your own way
import 'package:flutter/material.dart';
import 'package:rio/Utils/utils.dart';
import 'package:rio/widgets/widgets.dart';
import 'package:rio/generated/l10n.dart';
import 'package:responsive_flutter/responsive_flutter.dart';
import 'color.dart';
// ignore: must_be_immutable
class DropDownClass extends StatelessWidget {
var _hint;
var _val;
double _fontSize;
List _list = new List();
bool _icon;
bool _border;
Color _underLineColor, _dropDownColor, _textColor;
List get list => _list;
dynamic Function(dynamic) _listener;
DropDownClass({List list,
var hint,
Color underLineColor,
Color dropDownColor,
Color textColor,
double fontSize,
bool icon,
var val,
int type,
bool border = true,
dynamic Function(dynamic) listener,})
: _list = list,
_hint = hint,
_underLineColor = underLineColor,
_dropDownColor = dropDownColor,
_textColor = textColor,
_icon = icon,
_val = val,
_fontSize = fontSize,
_border = border,
_listener = listener;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Utils.selectedValue = null;
},
child: DropdownButtonHideUnderline(
child: DropdownButtonFormField<String>(
value: _val,
dropdownColor: _dropDownColor ?? CustomColors.white,
decoration:_border == true? InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: _underLineColor ?? Theme
.of(context)
.hintColor,
width: 1.0,
),
)
):InputDecoration(
border: InputBorder.none,
fillColor: Theme.of(context).cardColor,
filled: true),
style: robotoStyle(context,
color: _textColor ??
Theme
.of(context)
.primaryTextTheme
.bodyText1
.color,
fontWeight: FontWeight.w400,
fontSize: _icon == null || _icon == false
? ResponsiveFlutter.of(context).fontSize(_fontSize??1.5)
: ResponsiveFlutter.of(context).fontSize(_fontSize??1.5)),
isExpanded: true,
icon: _icon == null || _icon == false
? null
: Icon(
Icons.keyboard_arrow_down,
color: CustomColors.appBarTextColor,
),
hint: Text(_hint,
style: robotoStyle(context,
color: _icon == null || _icon == false
? Theme
.of(context)
.hintColor
: Theme
.of(context)
.textSelectionColor,
fontWeight: FontWeight.w400,
fontSize: _icon == null || _icon == false
? ResponsiveFlutter.of(context)
.fontSize(_fontSize??1.75)
: ResponsiveFlutter.of(context).fontSize(_fontSize??1.5))),
items: list.map((item) {
return DropdownMenuItem<String>(
value: item,
child: new Text(item,
style: robotoStyle(context,
color: _textColor ?? CustomColors.dropDownTextColor,
fontWeight: FontWeight.w400,
fontSize: _icon == null || _icon == false
? ResponsiveFlutter.of(context)
.fontSize(_fontSize??1.75)
: ResponsiveFlutter.of(context).fontSize(_fontSize??1.5))),
);
}).toList(),
onChanged: (value) {
_val = value;
if (_listener != null) _listener.call(value);
if (_icon == true) {
Utils.selectedValue = value;
S.load(Locale(value));
Utils.preferences.setString("lang", value);
}
// return val;
},
),
),
);
}
}

Related

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

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.

How to remove build side effects in this context, using Staggered Grid View Builder Flutter?

I am trying to create the build method without side effects (without blinking). I solved this problem using StatefulBuilder, but I read that I should rebuild 1000x times without any change or effect.
The Staggered Grid View Builder Widget are rebuilt when the keyboard is opening, or whenever I open again the page, or add/remove an item from it. That's good, normally, but with side effects like you see below. Maybe there is any solution to animate the remove/add functionality, or the infinite reloading and keep the rest of the items in cache. So I need to limit the builder recreate inside Grid View Builder?
On other applications I don't see this ugly "blinking". Where is the problem and how can I solve it? I used Animation Limiter but it's not working for me, neither PrecacheImage, somehow I need to rebuild without blink (first items).
My code:
class VisionBoard extends StatefulWidget {
const VisionBoard({Key? key}) : super(key: key);
#override
_VisionBoardState createState() => _VisionBoardState();
}
class _VisionBoardState extends State<VisionBoard> with SingleTickerProviderStateMixin {
ScreenshotController screenshotController = ScreenshotController();
String saveGoalsButtonText = "SAVE GOALS";
String wallpaperButtonText = "CREATE WALLPAPER";
String saveWallpaperButtonText = "SAVE";
bool createWallpaper = false;
bool isWallpaperCreated = false;
late File imageFile;
late String newImage;
late Uint8List imageRaw;
int noOfImages = 0;
late Uint8List wallpaperBytes;
String title = "My Vision Board";
String goals = "";
late List<String> visions = <String>[];
final TextEditingController _textFieldController = TextEditingController();
final TextEditingController _goalsController = TextEditingController();
static final _formKey = GlobalKey<FormState>();
#override
void initState() {
super.initState();
loadVisionBoardTitleAndImages();
}
void loadVisionBoardTitleAndImages() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//await prefs.clear();
setState(() {
_textFieldController.text = prefs.getString('titlu') ?? title;
visions = prefs.getStringList('visions') ?? <String>[];
_goalsController.text = prefs.getString('goals') ?? goals;
});
}
void _removeVisions(int index) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
isButtonDisabled=true;
visions.removeAt(index);
prefs.setStringList('visions', visions);
createWallpaper = false;
wallpaperButtonText = "CREATE WALLPAPER";
isWallpaperCreated = false;
});
await CreateWallpaperLayouts().createWallpaper(visions).then((value) {
setState(() {
wallpaperBytes = value;
wallpaper = Image.memory(wallpaperBytes);
precacheImage(wallpaper.image, context);
isButtonDisabled=false;
});
});
}
#override
Widget build(BuildContext context) {
return Sizer(
builder: (context, orientation, deviceType) {
return GestureDetector(
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Scaffold(
body: AnimationLimiter(
child: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/background-marmura.jpeg"), fit: BoxFit.cover)),
child: SafeArea(
child: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Column(
children: [
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Row(
children: [
Flexible(
child: Text(_textFieldController.text,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
IconButton(
icon: const Icon(
Icons.edit,
size: 21,
color: Color(0xff393432),
),
onPressed: () {
showAlertDialog(context, setState);
},
)
]);
}
),
const SizedBox(height: 5),
GridView.builder(
clipBehavior: Clip.none,
physics: const ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: SizeConfig.screenWidth!/3,),
itemCount: (visions.length == 12) ? visions.length : visions.length + 1,
itemBuilder: (BuildContext ctx, index) {
if (index < visions.length) {
return AnimationConfiguration.staggeredGrid(
position: index,
duration: const Duration(milliseconds: 1000),
columnCount: 3,
child: ScaleAnimation(
child: FadeInAnimation(
child: OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: index < visions.length, //bind with the boolean
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 25.w,
height: 25.w,
decoration: BoxDecoration(
image: DecorationImage(
image: CleverCloset.imageFromBase64String(visions[index]).image,
fit: BoxFit.fill),
borderRadius: const BorderRadius.all(
Radius.circular(
5.0) // <--- border radius here
),
),
),
Positioned(
top:0,
right:0,
child: ClipOval(
child: InkWell(
onTap: () {
_removeVisions(index);
},
child: Container(
padding: const EdgeInsets.all(5),
color: Colors.white,
child:
const Icon(
Icons.delete,
size: 16,
color: Color(0xff393432)),
),
),
),
),
],
clipBehavior: Clip.none,
),
),
),
),
);
}
else if(index<12){
return InkWell(
onTap: () {
_openGallery(context);
},
child:
Stack(
alignment: Alignment.center,
children:[
Container(
width: 25.w,
height: 25.w,
decoration:
BoxDecoration(
border: Border.all(color: const Color(0xff393432)),
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
),
child:
const Icon(
Icons.add,
size: 25,
color: Color(0xff393432),
)
),
],
),
);
}
else {
return Container(color: Colors.red);
}
}
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text("You can add up to 12 pictures.",
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
fontStyle: FontStyle.italic,
),),
],
),
const SizedBox(height: 10),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column (
children: [
if(visions.isNotEmpty && visions.length>1 && !isWallpaperCreated) Row(
children: [
SizedBox(
width: 50.w,
child: OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value)
enabled: !isWallpaperCreated &&
visions.isNotEmpty && visions.length > 1,
child: OutlinedButton(
onPressed: visions.isNotEmpty &&
visions.length > 1 ? () async{
setState(() {
wallpaperButtonText = "CREATING...";
//_createWallpaper();
});
wallpaperBytes = await CreateWallpaperLayouts().createWallpaper(visions);
setState(() {
noOfImages = visions.length;
isWallpaperCreated = true;
createWallpaper = true;
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
} : null,
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
18.0),
),
side: const BorderSide(
width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
wallpaperButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
),
],
),
const SizedBox(height:40),
if(createWallpaper==true) OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper, //bind with the boolean
child: Row(
children: const [
Flexible(
child: Text("Wallpaper",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
],
),
),
if(createWallpaper==true) const SizedBox(height:15),
if(createWallpaper==true)
OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children:[ Container(
width: 50.w,//double.infinity,
height: 50.h,//SizeConfig.screenHeight,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.memory(wallpaperBytes).image,
fit: BoxFit.fill,
)
),
//child: CreateWallpaperLayouts().createWallpaper(visions),
),],
),
),
if(createWallpaper==true) const SizedBox(height:10),
if(createWallpaper==true) StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return OpacityAnimatedWidget.tween(
opacityEnabled: 1, //define start value
opacityDisabled: 0, //and end value
enabled: createWallpaper,
child: Row(
children: [
SizedBox(
width: 50.w,
child: OutlinedButton(
onPressed: () {
_saveWallpaper(wallpaperBytes);
setState(() {
saveWallpaperButtonText = "SAVED!";
});
Future.delayed(const Duration(milliseconds: 1300), () {
setState(() {
saveWallpaperButtonText = "SAVE";
});
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
},
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
side: const BorderSide(width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
saveWallpaperButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
const SizedBox(width: 10),
],
),
);
}
),
if(createWallpaper==true) const SizedBox(height:50),
Row(
children: const [
Flexible(
child: Text("Goals & Affirmations",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Color(0xff393432),
),
),
),
],
),
],
);
}
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Text("Add goals and affirmations.",
style: TextStyle(
fontSize: 15,
),),
],
),
const SizedBox(height: 10),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
children: [
Card(
elevation: 0,
color: const Color(0xffEFEFEF),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: TextFormField(
key: _formKey,
controller: _goalsController,
maxLines: 8,
decoration: const InputDecoration(border: InputBorder.none),
),
)
),
const SizedBox(height: 10),
Row(
children: [
Container(
width: 50.w,
margin: const EdgeInsets.fromLTRB(0, 0, 0, 40),
child: OutlinedButton(
onPressed: () async{
setState(() {
saveGoalsButtonText = "SAVED!";
});
Future.delayed(const Duration(seconds: 1), () {
setState(() {
saveGoalsButtonText = "SAVE GOALS";
});
});
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
goals = _goalsController.text;
prefs.setString('goals', goals);
});
//Navigator.pushReplacementNamed(context, '/masterclasses');
},
style: OutlinedButton.styleFrom(
primary: const Color(0xffE4BCB4),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
side: const BorderSide(width: 3, color: Color(0xffE4BCB4)),
),
child: Text(
saveGoalsButtonText,
style: const TextStyle(
color: Color(0xff393432),
fontSize: 14,
fontWeight: FontWeight.w700,
)
),
),
),
],
),
],
);
}
),
],
),
),
),
),
),
),
),
);
}
);
}
}

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,
),
);
}
}

How to handle dropdownbox for listview flutter

I have a listView with dropdownbox attached each item. They are being populated with items. I have two issues now. First when I select from each item, the selected value does not appear on the checkbox. Also, I need to handle them independently such that they could be selected individually. What happens now is that on launch of the app, there is no value at default till I select. Also, when I select it sets all others on the list.
Here's my code guys.
ListView.builder(
physics: NeverScrollableScrollPhysics(),
itemCount: mainBloc.orders.length,
itemBuilder: (BuildContext context, int index) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: HexColor("#240C44"),
width: 0.5),
color: HexColor("#180332"),
borderRadius:
BorderRadius.all(Radius.circular(4))),
margin: EdgeInsets.only(
top: 10, bottom: 10, left: 0, right: 0),
child: ListTile(
title: Row(
children: <Widget>[
new Image.asset(
"assets/images/order_list_image.png",
width: 40,
height: 40,
),
Spacer(),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: mainBloc.orders[index].status=="enroute"?
HexColor("#FF9F1C"):
mainBloc.orders[index].status=="delivered"?
HexColor("#71F79F"):
Colors.white.withOpacity(0.6),
border: Border.all(
color: HexColor("#FF9F1C"),
style: BorderStyle.solid,
width: 0.80),
),
child: new DropdownButton<String>(
items: <String>['Delivered', 'Enroute', 'Processed'
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(
value,
style: TextStyle(
color: primaryColor,
fontSize: 14,
fontFamily: 'CircularStd',
fontWeight: FontWeight.w500,
),
),
);
}).toList(),
onChanged: (_) {
},
),
),
SizedBox(
width: 20,
),
IconButton(
icon: Image.asset(
"assets/images/edit.png",
width: 15,
height: 15,
color: Colors.white,
), onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
EditOrderPage(index:index)),
);
},
),
],
),
),
);
}),
To manage the state of each of your dropdownbutton you need to wrap your list item inside a StatefulWidget.
Here is my implementation of your example :
Code Sample
class ListItem extends StatefulWidget {
final String label;
final String defaultStatus;
ListItem({this.label, this.defaultStatus = 'Enroute'});
#override
createState() => _ListItemState();
}
class _ListItemState extends State<ListItem> {
String _status;
#override
void initState() {
super.initState();
_status = widget.defaultStatus;
}
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
color: _status == null || _status.toLowerCase() == 'processed'
? Colors.white.withOpacity(0.6)
: _status.toLowerCase() == "enroute"
? Color(0xFFFF9F1C)
: Color(0xFF71F79F),
border: Border.all(
color: Color(0xFFFF9F1C), style: BorderStyle.solid, width: 0.80),
),
child: Row(children: <Widget>[
if (widget.label != null && widget.label.isNotEmpty)
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text(widget.label),
),
DropdownButton<String>(
value: _status,
items:
<String>['Delivered', 'Enroute', 'Processed'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(
color: Theme.of(context).primaryColor,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
);
}).toList(),
onChanged: (val) => setState(() => _status = val),
),
]),
);
}
}
Try it on DartPad
Screenshot

Flutter Provider.of and search

I am a beginner in flutter and app development. I have a problem. I am using Provider.of in order to get my data. I am getting data and showing it in ListView.builder with no problem. But I want to make a search on my list.
Please refer to code below
class RecipeList extends StatefulWidget {
#override
_RecipeListState createState() => _RecipeListState();
}
class _RecipeListState extends State<RecipeList> {
List<Recipe>showList =List();//creating my list of searched data
#override
Widget build(BuildContext context) {
//getting my recipe list in order to show them
final recipes = Provider.of<List<Recipe>>(context);
showList=recipes;
final user = Provider.of<User>(context);
String _image;
Widget myImage(int index,)
{
if(recipes[index].image == ''){
return Image.asset('images/no_image.jpg');
}
else{
return
FadeInImage.assetNetwork(
width: 300,
height: 250,
placeholder: 'images/loading.webp',
image: recipes[index].image,
);
}
}
return StreamBuilder<UserData>(
stream:DatabaseService(uid: user.uid).userData,
builder: (context,snapshot){
if(snapshot.hasData) {
UserData userdata = snapshot.data;
if (userdata.is_admin == true) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.blue[200], Colors.orange[100]])),
child: Scaffold(
appBar: AppBar(
title: Text('Recipes'),
backgroundColor: Colors.transparent,
elevation: 0,
),
backgroundColor: Colors.transparent,
body: Column(
children: <Widget>[
Material(
elevation: 0,
color: Colors.transparent,
child: TextField(
onChanged: (val) {
val = val.toLowerCase();
setState(() {
showList = recipes.where((recipe){
var title = recipe.name.toLowerCase();
return title.contains(val);
}).toList();
});
},
decoration: InputDecoration(
labelText: "Search",
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(25.0)))),
),),
SizedBox(height: 15,),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: showList.length,
itemBuilder: (context, index) {
if (recipes[index].image == null) {
String _image = 'images/new.png';
}
else {
_image = recipes[index].image;
}
// print(recipes[index].image);
return Column(
children: <Widget>[
SlimyCard(
color: Colors.teal[200],
width: 300,
topCardHeight: 350,
bottomCardHeight: 300,
borderRadius: 15,
topCardWidget: Column(
children: <Widget>[
Text(recipes[index].name[0]
.toUpperCase() +
recipes[index].name.substring(1),
style: TextStyle(
fontSize: 35,
color: Colors.white,
fontWeight: FontWeight.bold,
),),
ClipRRect(borderRadius: BorderRadius
.circular(25.0),
child: myImage(index)
),
// Image.network('https://www.bbcgoodfood.com/sites/default/files/recipe-collections/collection-image/2013/05/chorizo-mozarella-gnocchi-bake-cropped.jpg')),
],
),
bottomCardWidget: SingleChildScrollView(
child: Column(
children: <Widget>[
Text('Ingredients',
style: TextStyle(
fontSize: 25,
color: Colors.white
),),
SizedBox(height: 5,),
Text(recipes[index].ingredients,
style: TextStyle(
fontSize: 16
),),
SizedBox(height: 20,),
Text('Recipe',
style: TextStyle(
fontSize: 25
,
color: Colors.white
),),
SizedBox(height: 5,),
Text(recipes[index].recipe,
style: TextStyle(
fontSize: 16
),),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius
.circular(7.0),
//side: BorderSide(color: Colors.orange)
),
color: Color.fromRGBO(
233, 217, 108, 1),
onPressed: () async {
final CollectionReference recipecollection = Firestore
.instance.collection(
'recipe');
await recipecollection.document(
recipes[index].id).delete();
StorageReference firestoreStorageref = await FirebaseStorage
.instance
.getReferenceFromUrl(
recipes[index].image);
firestoreStorageref.delete();
},
child: Text(
'Delete'
),
)
],
),
),
slimeEnabled: false,
),
SizedBox(height: 25,)
],
);
},
)),
],
)
),
);
}
I want to show this list on the search and modify it. first I fill it with data from the provider.
I have created a TextField for Search the onChanged method filters the typed value and returns a list. When I print in onChanged function it is working.
I am showing my list with ListView, when I print the size of showList in onChanged function, it filters and gives the right value but when I use it for itemCount it never changes
You can use searchable_dropdown instead of the TextField. You can assign the list to it and it will search the list based on the to string method so you have to override it.
Refer the link to the dependency: https://pub.dev/packages/searchable_dropdown.