Flutter Edit and new Item on same component - flutter

At the moment I have a component that edits the info of a Record, it comes from localsorage so I have a FutureBuilder. I want to reuse this component for new records as well but the examples that I find online always have separate components for edit/add.
What is the best practice for this?
Widget build(BuildContext context) {
txtDuration.text = '';
txtDescription.text = '';
return Scaffold(
appBar: AppBar(title: const Text('Detail')),
body: FutureBuilder<Record?>(
future: recordController.getRecord(widget.recordId),
builder: (context, response) {
if (!response.hasData) {
return const Center(child: Text('Loading..'));
} else {
var result = response.data ?? [];
return SingleChildScrollView(
child: Column(children: [
Form(
key: _formKey,
child: Column(
children: [
TextFormField(
//2. textform field with validator logic
validator: (value) {
if (value == null || value.isEmpty) {
return "field cannot be empty";
}
return null;
},
controller: txtDescription,
decoration: const InputDecoration(
hintText: 'Enter the first number',
labelText: 'First Number',
),
),
const SizedBox(height: 20),
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return "field cannot be empty";
}
return null;
},
controller: txtDuration,
minLines: 1,
maxLines: 10,
decoration: const InputDecoration(
hintText: 'Enter the second number',
labelText: 'Second Number',
),
),
const SizedBox(height: 20),
new DropdownButtonHideUnderline(
child: new FutureBuilder<List<Category>>(
future: categoryController.getAll(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return new Container();
} else if (snapshot.hasData) {
return DropdownButton<String>(
isExpanded: true,
value: dropdownValue,
hint: Text('Select a category'),
icon: const Icon(Icons.arrow_drop_down),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: snapshot.data!
.map<DropdownMenuItem<String>>(
(Category value) {
return DropdownMenuItem<String>(
value: value.title,
child: Text(value.title),
);
}).toList(),
);
} else {
return CircularProgressIndicator();
}
},
),
),
],
)),
]));
}
}),
bottomNavigationBar: const MenuBottom(),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.save), onPressed: saveSession),
);
}

Related

Keyboard automatically closes in flutter while using form

In my code my keyboard closes automatically when using text formfield and I realized that it closes when I use Form and its key for validation, I am using list view with form but when I remove form it works fine but I need a form to validate, what should I do ?
Form(
key: _formKeyPlant,
child: ListView(children: [
SizedBox(
width: size.width,
child: Column(
children: [
CustomTextField(
controller: name,
title: 'Name',
validator: (text) {
if (text == null || text.isEmpty) {
return 'Required';
} else if (text.contains(RegExp(r'[0-9]')) ||
text.contains(RegExp(r'[#?!#$%^&*-]'))) {
return 'Invalid Input';
}
return null;
},
),
15.verticalSpace,
CustomTextField(
controller: location,
title: 'Location',
validator: (text) {
if (text == null || text.isEmpty) {
return 'Required';
} else if (text.contains(RegExp(r'[0-9]')) ||
text.contains(RegExp(r'[#?!#$%^&*-]'))) {
return 'Invalid Input';
}
return null;
},
),
15.verticalSpace,
CustomTextField(
enabled: false,
onTap: () async {
final response = await showAppBottomSheet(
context,
const TreesInfoBottomSheet(),
);
if (response != null) {
treeType.text = response['name'];
treeTypeId = response['id'];
}
},
controller: treeType,
title: 'Tree Type',
),
15.verticalSpace,
CustomTextField(
controller: ageOfTree,
inputType: TextInputType.number,
validator: (text) {
if (text == null || text.isEmpty) {
return 'Required';
} else if (text.contains(RegExp(r'[a-z]')) ||
text.contains(RegExp(r'[A-Z]')) ||
text.contains(RegExp(r'[#?!#$%^&*-]'))) {
return 'Invalid Input';
}
return null;
},
title: 'Age of Tree',
),
15.verticalSpace,
CustomTextField(
controller: registration,
title: 'Registration ID (Optional)',
),
15.verticalSpace,
CustomTextField(
maxLines: 6,
controller: comments,
labelText: 'comments',
),
},
),
40.verticalSpace,
],
).px(18),
),
]),
),
Correct formate of using textform filed with form and scroll widget is
Please try the below code your keyboard with never hide on textformfiled
final Key _key = GlobalKey();
FocusNode f1 = FocusNode();
FocusNode f2 = FocusNode();
#override
Widget build(BuildContext context) {
return Form(
key: _key,
child: SingleChildScrollView(
child: Column(
children: [
TextFormField(
focusNode: f1,
onFieldSubmitted: (c) {
f1.unfocus();
f2.requestFocus();
},
),
TextFormField(
focusNode: f2,
),
],
),
),
);
}

Flutter: Radio and Switch buttons isn't updating inside a listview.builder, but it works outside

I'm using a form to create multiples vehicle entries.
every time I click on the floating button it adds a vehicleform to the page one on top of the other. "A List type that receives widgets"
If I try to select the "leased radio option", the form doesn't update, and if hit the switch button right below, nothing happens again. BUT! Curiously if I hit any of the Dropdown State....it works for them (only the dropdown gets updated). and BUT! number 2: If I hit the floating button to add a new vehicle form, the changes made on the previous form gets carried to the new form. My theory is that the buttons are working under the hood, but the setStates are no running correctly
On the main_page.dart there is a stateful widget that calls vehicles_page() which holds all the scaffold and widgets for that form including a dropdown list which is called from a 3rd file(dropdown_forms.dart).
To guide towards the right direction, just lay your eyes at the end of the code on the build() function.
FYI - Flutter Doctor -v returned no errors
Yes -I'm using stateful widgets
Yes - I'm using setstates to update
No - I'm not a professional programmer, I'm a super beginner on flutter and this is my 3rd week playing with flutter
After running some tests.... if I remove them from the listview widget, it works fine.
main.dart
This is the main file
import 'package:flutter/material.dart';
import 'package:learningflutter/screens/parties_page.dart';
import 'package:learningflutter/screens/vehicles_page.dart';
import 'package:learningflutter/screens/incident_page.dart';
// import 'package:learningflutter/parties.dart';
enum VehicleType { pov, leased, pgti }
void main(List<String> args) {
runApp(const VortexEHS());
}
class VortexEHS extends StatelessWidget {
const VortexEHS({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'VortexEHS',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: _MainPage(),
);
}
}
class _MainPage extends StatefulWidget {
_MainPage({Key? key}) : super(key: key);
#override
State<_MainPage> createState() => __MainPageState();
}
class __MainPageState extends State<_MainPage> {
// int currentPageIndex = 1;
final screens = [
PartiesPage(),
VehiclesPage(),
FormIncident(),
FormIncident()
];
#override
Widget build(BuildContext context) {
return Container(child: VehiclesPage());
}
}
vehicles_page.dart (Code Below)
// ignore_for_file: unused_element, unused_field
import 'package:flutter/material.dart';
import '../dropdown_forms.dart';
enum VehicleType { pov, leased, pgti }
class VehiclesPage extends StatefulWidget {
const VehiclesPage({Key? key}) : super(key: key);
#override
State<VehiclesPage> createState() => _VehiclesPageState();
}
class _VehiclesPageState extends State<VehiclesPage> {
//Variables
VehicleType vehicleType = VehicleType.pov;
bool isCommercial = false;
String? _make;
String? _model;
String? _year;
String? _color;
String? _vimNumber;
String? _plate;
String? _ownerName;
String? _ownerAddress;
String? _ownerCity;
String? _ownerState;
String? _ownerZip;
String? _ownerPhone;
String? _insuranceCoName;
String? _insuranceCoPhone;
String? _policyHolderName;
String? _policyNumber;
List<Widget> vehicles = [];
Widget buildTypeVeihicle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('POV'),
leading: Radio(
value: VehicleType.pov,
groupValue: vehicleType,
onChanged: (VehicleType? value) {
setState(() {
vehicleType = value!;
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('Leased'),
leading: Radio(
value: VehicleType.leased,
groupValue: vehicleType,
onChanged: (VehicleType? value) {
setState(() {
vehicleType = value!;
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text(
'PGTI',
softWrap: false,
),
leading: Radio(
value: VehicleType.pgti,
groupValue: vehicleType,
onChanged: (VehicleType? value) {
setState(() {
vehicleType = value!;
});
}),
),
),
],
);
}
Widget _buildIsCommercial() {
return SwitchListTile(
title: const Text('This is commercial vehicle?'),
value: isCommercial,
onChanged: (bool value) {
setState(() {
isCommercial = value;
});
},
// secondary: const Icon(Icons.car_repair),
);
}
Widget _buildVehicleMake() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Make'),
validator: (String? value) {
if (value == null) {
return 'Make is required';
}
return null;
},
onSaved: (String? value) {
_make = value;
},
);
}
Widget _buildVehicleModel() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Model'),
validator: (String? value) {
if (value == null) {
return 'Model is required';
}
return null;
},
onSaved: (String? value) {
_model = value;
},
);
}
Widget _buildVehicleYear() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Year'),
validator: (String? value) {
if (value == null) {
return 'Year is required';
}
return null;
},
onSaved: (String? value) {
_year = value;
},
);
}
Widget _buildVehicleColor() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Color'),
validator: (String? value) {
if (value == null) {
return 'Color is required';
}
return null;
},
onSaved: (String? value) {
_color = value;
},
);
}
Widget _buildVehicleVinNumber() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Vin Number'),
validator: (String? value) {
if (value == null) {
return 'Vin Number is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildVehiclePlate() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Plate Number'),
validator: (String? value) {
if (value == null) {
return 'Plate Number is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerName() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Name'),
validator: (String? value) {
if (value == null) {
return 'Owner Name is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerAddress() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Address'),
validator: (String? value) {
if (value == null) {
return 'Owner Address is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerCity() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner City'),
validator: (String? value) {
if (value == null) {
return 'Owner City is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerZip() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Zip'),
validator: (String? value) {
if (value == null) {
return 'Owner Zip is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildOwnerPhone() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Phone'),
validator: (String? value) {
if (value == null) {
return 'Owner Phone is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildInsuranceCo() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Owner Insurance Company'),
validator: (String? value) {
if (value == null) {
return 'Owner Insurance Company is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
// ignore: unused_element
Widget _buildInsurancePhone() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Insurance Phone'),
validator: (String? value) {
if (value == null) {
return 'Insurance Phone is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildPolicyHolderName() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Policy Holder'),
validator: (String? value) {
if (value == null) {
return 'Policy Holder is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildPolicyNumber() {
return TextFormField(
decoration: const InputDecoration(labelText: 'Policy Number'),
validator: (String? value) {
if (value == null) {
return 'Policy Number is required';
}
return null;
},
onSaved: (String? value) {
_vimNumber = value;
},
);
}
Widget _buildVehiclesCard() {
return Container(
margin: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(color: Colors.blueGrey, blurRadius: 10, spreadRadius: -10)
],
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Container(
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
padding: const EdgeInsets.all(10),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Icon(Icons.directions_car, color: Colors.teal),
IconButton(
onPressed: () {
setState(() {
print('Trash presses');
});
},
icon: const Icon(Icons.delete),
color: Colors.red,
),
],
),
buildTypeVeihicle(),
_buildIsCommercial(),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildVehicleMake()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildVehicleModel()))
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildVehicleYear()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildVehicleColor()))
]),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Expanded(child: _buildVehicleVinNumber()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0, 0, 0),
child: _buildVehiclePlate(),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 20, 0, 0),
child: DropdownStatesUs())),
]),
const SizedBox(
height: 8,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: const [
Icon(
Icons.person,
color: Colors.teal,
),
],
),
Row(
children: [
Expanded(child: _buildOwnerName()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildOwnerPhone(),
)),
],
),
_buildOwnerAddress(),
Row(
children: [
Expanded(child: _buildOwnerCity()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 20, 0, 0),
child: DropdownStatesUs(),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildOwnerZip(),
)),
],
),
Row(
children: [
Expanded(child: _buildPolicyHolderName()),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 0, 0),
child: _buildPolicyNumber(),
)),
],
)
],
),
),
),
);
}
//
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.teal,
child: const Icon(Icons.add),
onPressed: () {
vehicles.add(_buildVehiclesCard());
// print(vehicles.length);
setState(() {});
},
),
body: Form(
child: ListView.builder(
primary: false,
itemCount: vehicles.length,
itemBuilder: (BuildContext context, int i) {
return vehicles[i];
},
),
),
);
}
}
Thanks for any help beforehand, any other advices or tips to make the code better, let me know
The current state changes behavior reflected on newly item because last item created before the radio-buttom or switch tile was changed. Once you switch the button and change the radioGroup value it will only get to the new created widget that will be trigger by floating action button.
Notice that List<Widget> vehicles = []; holds the created widgets. And while creating new widget you are using state variables like vehicleType and isCommercial. Once you click on 1st generated widget, these variables get new data based on your tap event. while the state is holding these variables, then again you click on fab to add item on vehicles to generate item with current state of vehicleType and isCommercial.
While every list-item will have its own state, it is better to create a new StatefulWidget for each item. Also, you can go for state-management like riverpod, bloc for future purpose.
A simplify version but not complete, you need to handle callback to get changes data or better start using riverpod or bloc for state management
enum VehicleType { pov, leased, pgti }
class VehiclesPage extends StatefulWidget {
const VehiclesPage({Key? key}) : super(key: key);
#override
State<VehiclesPage> createState() => _VehiclesPageState();
}
class _VehiclesPageState extends State<VehiclesPage> {
List<MyDataModel> vehicles = [];
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.teal,
child: const Icon(Icons.add),
onPressed: () {
vehicles.add(MyDataModel());
setState(() {});
},
),
body: Form(
child: ListView.builder(
primary: false,
itemCount: vehicles.length,
itemBuilder: (BuildContext context, int i) {
return ListItem(model: vehicles[i]);
},
),
),
);
}
}
class MyDataModel {
final VehicleType vehicleType;
final bool isCommercial;
MyDataModel({
this.vehicleType = VehicleType.pov,
this.isCommercial = false,
});
MyDataModel copyWith({
VehicleType? vehicleType,
bool? isCommercial,
}) {
return MyDataModel(
vehicleType: vehicleType ?? this.vehicleType,
isCommercial: isCommercial ?? this.isCommercial,
);
}
}
class ListItem extends StatefulWidget {
final MyDataModel model;
const ListItem({
Key? key,
required this.model,
}) : super(key: key);
#override
State<ListItem> createState() => _ListItemState();
}
class _ListItemState extends State<ListItem> {
late MyDataModel model = widget.model;
Widget buildTypeVeihicle() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('POV'),
leading: Radio(
value: VehicleType.pov,
groupValue: model.vehicleType,
onChanged: (VehicleType? value) {
setState(() {
model = model.copyWith(vehicleType: value);
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text('Leased'),
leading: Radio(
value: VehicleType.leased,
groupValue: model.vehicleType,
onChanged: (VehicleType? value) {
setState(() {
model = model.copyWith(vehicleType: value);
});
}),
),
),
Expanded(
child: ListTile(
horizontalTitleGap: 0,
dense: true,
title: const Text(
'PGTI',
softWrap: false,
),
leading: Radio(
value: VehicleType.pgti,
groupValue: model.vehicleType,
onChanged: (VehicleType? value) {
setState(() {
model = model.copyWith(vehicleType: value);
});
}),
),
),
],
);
}
Widget _buildIsCommercial() {
return SwitchListTile(
title: const Text('This is commercial vehicle?'),
value: model.isCommercial,
onChanged: (bool value) {
setState(() {
model = model.copyWith(isCommercial: value);
});
},
// secondary: const Icon(Icons.car_repair),
);
}
Widget _buildVehiclesCard() {
return Container(
margin: const EdgeInsets.all(8.0),
decoration: const BoxDecoration(
boxShadow: [
BoxShadow(color: Colors.blueGrey, blurRadius: 10, spreadRadius: -10)
],
),
child: Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Container(
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
padding: const EdgeInsets.all(10),
child: Column(
children: [
buildTypeVeihicle(),
_buildIsCommercial(),
],
),
),
),
);
}
#override
Widget build(BuildContext context) {
return _buildVehiclesCard();
}
}

Flutter - convert dropdownformfield to autocompleteFormField

I have a dropdownFormField which takes data from snapshot and working fine.
now the data has grown bigger, as such want to change it to autocompleteFormField.
The code for dropdownFormField I am using is like this:
Container(
height: 50.0,
padding: EdgeInsets.only(
left: 15, right: 15, top: 5),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
child: FutureBuilder(
future: _testkit,
builder: (context,
AsyncSnapshot<TestkitList> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(
child:
CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'error',
// '${snapshot.error}',
style: TextStyle(
color: Colors.red),
);
} else {
return DropdownButtonFormField<
String>(
hint:
Text("Select Testkit Name"),
value: _selectedTestkit,
onChanged: (newValue) async {
_selectedTestkit = newValue;
_selectedTestType =
await getTestType();
setState(() {});
print(
"the below line is printed in dropdownfield");
print(_selectedTestType);
},
validator: (value) => value ==
null
? 'Please select the Testkit'
: null,
items: (snapshot.data.data)
.map((item) =>
DropdownMenuItem<
String>(
child: Text(
item.attributes.name
.length >
30
? item
.attributes
.name
.substring(
0, 30)
: item
.attributes
.name,
),
value: item.id,
))
.toList(),
);
}
}
}),
)),
),
Now plugin example for autocompleteFormField is like below:
SimpleAutocompleteFormField<Person>(
decoration: InputDecoration(labelText: 'Person', border: OutlineInputBorder()),
suggestionsHeight: 80.0,
itemBuilder: (context, person) => Padding(
padding: EdgeInsets.all(8.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(person!.name, style: TextStyle(fontWeight: FontWeight.bold)),
Text(person.address)
]),
),
onSearch: (search) async => people
.where((person) =>
person.name.toLowerCase().contains(search.toLowerCase()) ||
person.address.toLowerCase().contains(search.toLowerCase()))
.toList(),
itemFromString: (string) {
final matches = people.where((person) => person.name.toLowerCase() == string.toLowerCase());
return matches.isEmpty ? null : matches.first;
},
onChanged: (value) => setState(() => selectedPerson = value),
onSaved: (value) => setState(() => selectedPerson = value),
validator: (person) => person == null ? 'Invalid person.' : null,
)
somehow I am not able to make it pickup and work as whether I am using classname TestkitList' or snapshot.data.data' replacing the person which is in my case is a future.
You could flutter-typeahead package
https://pub.dev/packages/flutter_typeahead/install
TypeAheadField(
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontStyle: FontStyle.italic
),
decoration: InputDecoration(
border: OutlineInputBorder()
)
),
suggestionsCallback: (pattern) async {
return await BackendService.getSuggestions(pattern);
},
itemBuilder: (context, suggestion) {
return ListTile(
leading: Icon(Icons.shopping_cart),
title: Text(suggestion['name']),
subtitle: Text('\$${suggestion['price']}'),
);
},
onSuggestionSelected: (suggestion) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ProductPage(product: suggestion)
));
},
)

Flutter error "A build function returned null"

I have added two dropdown buttons in my app. Selection values of 2nd button depend on 1st button and appear Form related to value in 2nd button. I have used them inside StreamBuilder.
I added multi-imagepicker into above form.
The page can scroll for the first time.but can not scroll after.it throws above error.
it is skipping stream: Firestore.instance.collection("category_names").snapshots(),
line when debugging
I have attached code and screenshots below
import 'package:carousel_pro/carousel_pro.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:multi_image_picker/multi_image_picker.dart';
import 'categories/vehicles/car.dart';
import 'categories/vehicles/van.dart';
import 'services/utils.dart';
class AdAdvertisement extends StatefulWidget {
#override
_AdAdvertisementState createState() => _AdAdvertisementState();
}
class _AdAdvertisementState extends State<AdAdvertisement> {
var selectedCurrency,selectedSub;
var value;
final databaseReference = Firestore.instance;
String now = new DateTime.now().toString();
List<Asset> images = List<Asset>();
//List<NetworkImage> _listOfImages = <NetworkImage>[];
List<String> imageUrls = <String>[];
//List<String> imageLocalLink = <String>[];
String _error = 'No Error Dectected';
bool isUploading = false;
bool carosal = false;
final _formKeyCar = GlobalKey<FormState>();
final _formKeyVan = GlobalKey<FormState>();
void ValueChanged(currencyValue){
setState(() {
selectedCurrency =currencyValue;
});
}
void ValueSubchange(subcategory){
setState(() {
selectedSub=subcategory;
});
}
void createRecord() async {
await databaseReference.collection("Advertisements")
.document(now)
.setData({
'title': 'Mastering Flutter',
'description': 'Programming Guide for Dart'
});
}
Widget _widgetForm() {
switch (selectedSub) {
case "car":
return _carForm();
break;
case "van":
build(context){
return vanForm();
};
//return _vanForm();
break;
}
}
//
//
Future<void> loadAssets() async {
List<Asset> resultList = List<Asset>();
String error = 'No Error Dectected';
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 10,
enableCamera: true,
selectedAssets: images,
cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
materialOptions: MaterialOptions(
actionBarColor: "#abcdef",
actionBarTitle: "Upload Image",
allViewTitle: "All Photos",
useDetailsView: false,
selectCircleStrokeColor: "#000000",
),
);
print(resultList.length);
print((await resultList[0].getThumbByteData(122, 100)));
print((await resultList[0].getByteData()));
print((await resultList[0].metadata));
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
carosal = true;
print('<<<<<<<<<<<<<<<<<<<');
print(images);
//_listOfImages = imageUrls.cast<NetworkImage>();
_error = error;
});
}
Widget _imageShow(){
if(carosal==true){
return CarouselSlider(
items: images
.map((e) => AssetThumb(asset:e, width: 300, height: 300,))
.toList(),
options: CarouselOptions(
height: 400,
aspectRatio: 16 /9 ,
viewportFraction: 0.8,
initialPage: 0,
enableInfiniteScroll: true,
reverse: false,
autoPlay: true,
autoPlayInterval: Duration(seconds: 3),
autoPlayAnimationDuration: Duration(milliseconds: 800),
autoPlayCurve: Curves.fastOutSlowIn,
enlargeCenterPage: true,
scrollDirection: Axis.horizontal,
),
);
}
else{
return Text('not yet selected');
}
}
void uploadImages(){
for ( var imageFile in images) {
postImage(imageFile).then((downloadUrl) {
imageUrls.add(downloadUrl.toString());
if(imageUrls.length==images.length){
String documnetID = DateTime.now().millisecondsSinceEpoch.toString();
Firestore.instance.collection('images').document(documnetID).setData({
'urls':imageUrls
}).then((_){
SnackBar snackbar = SnackBar(content: Text('Uploaded Successfully'));
//widget.globalKey.currentState.showSnackBar(snackbar);
setState(() {
images = [];
imageUrls = [];
carosal =false;
});
});
}
}).catchError((err) {
print(err);
});
}
}
Future<dynamic> postImage(Asset imageFile) async {
String fileName = DateTime.now().millisecondsSinceEpoch.toString();
StorageReference reference = FirebaseStorage.instance.ref().child(fileName);
StorageUploadTask uploadTask = reference.putData((await imageFile.getByteData()).buffer.asUint8List());
StorageTaskSnapshot storageTaskSnapshot = await uploadTask.onComplete;
print(storageTaskSnapshot.ref.getDownloadURL());
return storageTaskSnapshot.ref.getDownloadURL();
}
Widget _carForm() {
return Card(
child: Form(
key: _formKeyVan,
child: Column(children: <Widget>[
// Add TextFormFields and RaisedButton here.
//buildGridView(),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Brand';
// }
// //return null;
//},
decoration: const InputDecoration(
hintText: 'Enter your Car Brand',
labelText: 'Brand',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Model';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter your Car Model',
labelText: 'Car Model',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Model Year';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Car Model year',
labelText: 'Model Year',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Mileage';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Mileage',
labelText: 'Mileage ',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Enter Transmission type';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Transmission type',
labelText: 'Transmission ',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Fueltype';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Fuel type',
labelText: 'Fueltype ',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Engine capaciy';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Engine capacity',
labelText: 'Engine capacity(cc) ',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please enter Description';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Description here',
labelText: 'Description ',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
TextFormField(
//validator: (value) {
// if (value.isEmpty) {
// return 'Please Price';
// }
// return null;
//},
decoration: const InputDecoration(
hintText: 'Enter Price',
labelText: 'Price',
prefixIcon: Icon(Icons.add_circle)
),
),
SizedBox(height: 20.0),
RaisedButton(
child: new Text("add Image"),
onPressed: loadAssets,
),
SizedBox(height: 10.0,),
_imageShow(),
RaisedButton(
child: new Text("upload"),
onPressed: (){
if(images.length==0){
showDialog(context: context,builder: (_){
return AlertDialog(
backgroundColor: Theme.of(context).backgroundColor,
content: Text("No image selected",style: TextStyle(color: Colors.white)),
actions: <Widget>[
RaisedButton(
onPressed: (){
Navigator.pop(context);
},
child: Center(child: Text("Ok",style: TextStyle(color: Colors.white),)),
)
],
);
});
}
else{
SnackBar snackbar = SnackBar(content: Text('Please wait, we are uploading'));
//widget.globalKey.currentState.showSnackBar(snackbar);
uploadImages();
}
},
),
SizedBox(height: 20.0),
RaisedButton(
color: Color(0xff11b719),
textColor: Colors.white,
child: Padding(
padding: EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text("Submit", style: TextStyle(fontSize: 24.0)),
],
)),
onPressed: () {
createRecord();
},
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)
)
),
]
)
)
);
}
//Widget _vanForm() {
// return Form(
// key: _formKeyCar,
// child: Column(children: <Widget>[
// // Add TextFormFields and RaisedButton here.
// TextFormField(
// validator: (value) {
// if (value.isEmpty) {
// return 'Please enter some text';
// }
// return null;
// },
// decoration: const InputDecoration(
// hintText: 'Enter your Van Model',
// labelText: 'Model',
// ),
// ),
//
// ]));
//}
QuerySnapshot qs;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
alignment: Alignment.center,
child: Text('Advertisement'),
),
),
body: ListView(
children: <Widget>[
Text('Select category here'),
Text('Select category here'),
SizedBox(height: 40.0),
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("category_names").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
const Text("Loading.....");
else {
List<DropdownMenuItem> currencyItems = [];
List<DropdownMenuItem> currencySub = [];
for(int i=0;i<snapshot.data.documents.length;i++){
DocumentSnapshot snap = snapshot.data.documents[i];
currencyItems.add(
DropdownMenuItem(
child: Text(
//snap.data.values.toString(),
snap.documentID
),
value: "${snap.documentID}",
),
);
}
for (int i = 0; i < snapshot.data.documents.length; i++) {
DocumentSnapshot snap = snapshot.data.documents[i];
if(snap.documentID==selectedCurrency){
for (int j = 0; j < snap.data.length; j++) {
currencySub.add(
DropdownMenuItem(
child: Text(
snap.data['${j + 1}'].toString(),
style: TextStyle(color: Color(0xff11b719)),
),
value: snap.data['${j + 1}'].toString(),
),
);
}
}
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
items: currencyItems,
onChanged: (currencyValue) => ValueChanged(currencyValue),
value: selectedCurrency,
isExpanded: false,
hint: new Text(
"Choose category Type",
style: TextStyle(color: Color(0xff11b719)),
),
),
DropdownButton(
items: currencySub,
onChanged: (subcategory) => ValueSubchange(subcategory),
value: selectedSub,
isExpanded: false,
hint: new Text(
"Choose sub",
style: TextStyle(color: Color(0xff11b719)),
),
),
],
);
}
}),
_widgetForm(),
],
),
);
}
}
The StreamBuilder has a builder property and this property is of type AsyncWidgetBuilder<T> which is a typedef with the following implementation:
typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);
Therefore you need to return a widget:
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("category_names").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Text("Loading.....");
else if(snapshot.hasData)
return Text("data...");
else
return CircularProgressIndicator();
https://api.flutter.dev/flutter/widgets/AsyncWidgetBuilder.html

Flutter redirects on textFormField open

whenever i click on the textFormField the keyboard opens and closes almost immediately i think it kinda refreshes the page. i have another page with a form where i dont face this problem.
here is the code for that page, the other form i have in the app is almost identical to this one
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
import '../scoped_models/products.dart';
class ProductAdd extends StatelessWidget {
final _formData = {
'title': null,
'description': null,
'price': null,
'image': 'assets/food.jpg'
};
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Widget _titleField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Title'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid title';
}
},
onSaved: (value) {
print(value);
_formData['title'] = value;
},
);
}
Widget _descriptionField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Description'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid description';
}
},
onSaved: (value) {
print(value);
_formData['description'] = value;
},
);
}
Widget _priceField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Price'),
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid price';
}
},
onSaved: (value) {
print(value);
_formData['price'] = value;
},
);
}
Widget _submitButton(context) {
return RaisedButton(
textColor: Colors.white,
child: Text('LOGIN'),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
Navigator.pushReplacementNamed(context, '/products');
},
);
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<ProductsModel>(
builder: (context, child, ProductsModel model) {
return Container(
child: Center(
child: Container(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
_titleField(),
_descriptionField(),
_priceField(),
SizedBox(
height: 10.0,
),
_submitButton(context)
],
),
),
),
),
),
),
);
},
);
}
}
i'm using flutter version: 1.0.0
I was not able to reproduce the issue. I re-created your case and the keyboard worked just fine. I although skipped the scoped-model part of your code because I don't know how your setup is. But with minimal code, I couldn't replicate it. See below:
import 'package:flutter/material.dart';
import 'package:scoped_model/scoped_model.dart';
//import '../scoped_models/products.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ProductAdd(),
);
}
}
class ProductAdd extends StatelessWidget {
final _formData = {
'title': null,
'description': null,
'price': null,
'image': 'assets/food.jpg'
};
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Widget _titleField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Title'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid title';
}
},
onSaved: (value) {
print(value);
_formData['title'] = value;
},
);
}
Widget _descriptionField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Description'),
keyboardType: TextInputType.text,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid description';
}
},
onSaved: (value) {
print(value);
_formData['description'] = value;
},
);
}
Widget _priceField() {
return TextFormField(
decoration: InputDecoration(labelText: 'Enter Price'),
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid price';
}
},
onSaved: (value) {
print(value);
_formData['price'] = value;
},
);
}
Widget _submitButton(context) {
return RaisedButton(
textColor: Colors.white,
child: Text('LOGIN'),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
Navigator.pushReplacementNamed(context, '/products');
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(20.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
_titleField(),
_descriptionField(),
_priceField(),
SizedBox(
height: 10.0,
),
_submitButton(context)
],
),
),
),
),
),
),
);
}
}
My Flutter version: 0.11.10
this is my correction, hope it works; u need to add TextEditingController titlecontroller froeach TextFormField,dont use onsaved() ; and on the submitbutton funtion use this :
TextEditingController _pricecontroller;
Widget _priceField() {
return TextFormField(
//addcontroller;
controller : __pricecontroller
decoration: InputDecoration(labelText: 'Enter Price'),
keyboardType: TextInputType.number,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter a valid price';
}
},
onSaved: (value) {
print(value);
_formData['price'] = value;
},
);
}
Widget _submitButton(context) {
return RaisedButton(
textColor: Colors.white,
child: Text('LOGIN'),
onPressed: () {
if (!_formKey.currentState.validate()) {
_formKey.currentState.save();
_formData['title'] = __titlecontroller.text;
_formData['description'] = __desccontroller.text;
_formData['price'] = __pricecontroller.text;
}
Navigator.pushReplacementNamed(context, '/products');
},
);
}