The function can't be unconditionally invoked - flutter

I am trying to build a dropdown that is filled with values from the database.
The error happens at
items: machineList[index].serienummer((String items)
The error I get is:
The function can't be unconditionally invoked because it can be null.
I tried to add items: machineList[index].serienummer! or items: machineList[index].serienummer? and items: machineList[index].serienummer?? parameters but doesn't seem to solve it. Any help would be great.
This is my dart file
class _MachinepageState extends State<Machinepage> {
_MachinepageState();
final ApiService api = ApiService();
late List<Machine> machineList;
String dropdownvalue = "";
#override initState(){
super.initState();
_getMachine();
machineList = [];
}
void _getMachine() async{
machineList = (await ApiService().getMoreMachine(widget.klant.klantId.toString()));
Future.delayed(const Duration(seconds: 1)).then((value) => setState(() {}));
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(title: const Text('Menu'),
backgroundColor:Colors.deepOrange
),
body: machineList == null || machineList.isEmpty
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget> [
ListView.builder(
shrinkWrap: true,
itemCount: machineList == null? 0 : machineList.length,
itemBuilder: (BuildContext context, int index) => Row(
children: [
Container(
margin: const EdgeInsets.only(top: 20.0, bottom: 25.0),
child: DropdownButton(
// Initial Value
value: machineList[1].serienummer,
// Down Arrow Icon
icon: const Icon(Icons.keyboard_arrow_down),
// Array list of items
items: machineList[index].serienummer((String items) {
return DropdownMenuItem(
value: items,
child: Text(items),
);
}).toList(),
// After selecting the desired option, it will
// change button value to selected value
onChanged: (String? newValue) {
setState(() {
dropdownvalue = newValue!;
});
},
),
)
],
)
),
My Machinemodel.dart
import 'dart:convert';
import 'package:flutter/src/material/dropdown.dart';
List<Machine> welcomeFromJson(String str) => List<Machine>.from(json.decode(str).map((x) => Machine.fromJson(x)));
String welcomeToJson(List<Machine> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Machine {
Machine({
this.serienummerId,
this.serienummer,
this.bouwjaar,
this.urenstand,
this.locatie,
this.klantId,
});
int? serienummerId;
String? serienummer;
String? bouwjaar;
String? urenstand;
String? locatie;
String? klantId;
factory Machine.fromJson(Map<String, dynamic> json) => Machine(
serienummerId: json["SerienummerId"],
serienummer: json["Serienummer"],
bouwjaar: json["Bouwjaar"],
urenstand: json["Urenstand"],
locatie: json["Locatie"],
klantId: json["KlantId"],
);
Map<String, dynamic> toJson() => {
"SerienummerId": serienummerId,
"Serienummer": serienummer,
"Bouwjaar": bouwjaar,
"Urenstand": urenstand,
"Locatie": locatie,
"KlantId": klantId,
};
}

Since you passed the machineList[1].serienummer in DropdownButton value, I'm assuming that serienummer is not a list but some kind of value, i.e., an integer or string.
And you're trying to do this items: machineList[index].serienummer((String items) which will not work as items will only take lists as value.
So try this this:
items: machineList!.map((item) {
return DropdownMenuItem(
value: item.serienummer, //or whatever value you want to pass to the item
child: Text(item.serienummer.toString()),
);
})!.toList(),
Don't forget to put the null and empty check for machineList before passing it in DropDownButton items

Related

passing arguments in flutter from one screen to another

hi guys im a newbie in flutter and i have this worker app with the basic crud operations, and i want to use the same screen for adding or updating a contact but i run into an error
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return GestureDetector(
onTap: () {
Navigator.of(context)
.pushNamed('/Add-contact-screen', arguments: data);
},
child: ListTile(
title: Text(data['name']),
),
);
}).toList(),
);
this is my contacts list screen where on tap on a contact i navigate to the add contact screen with the arguments the contact object containing all the contact data like name, email, number etc. so that the form can be populated with the data, and this is how i receive those arguments in the add contact screen
import 'package:flutter/material.dart';
import '../../services/firestore_service.dart';
import '../../models/contacts/contact.dart';
import '../../utils/helper_widgets.dart';
class AddContactScreen extends StatefulWidget {
AddContactScreen({Key? key}) : super(key: key);
static const routeName = '/Add-contact-screen';
#override
State<AddContactScreen> createState() => _AddContactScreenState();
}
class _AddContactScreenState extends State<AddContactScreen> {
String category = 'Intern';
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final db = FirebaseFirestore.instance;
final firestoreService = FirestoreService();
var editMode = true;
#override
Widget build(BuildContext context) {
final routeArgs = ModalRoute.of(context)?.settings?.arguments as Map<dynamic, dynamic>;
return Scaffold(
appBar: AppBar(
title: editMode ? const Text('New Contact') : const Text('Edit'),
),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: [
addVerticalSpace(10),
DropdownButton(
value: category,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems,
onChanged: (String? newValue) {
setState(() {
category = newValue!;
});
},
),
TextFormField(
initialValue: routeArgs['name'],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {
},
child: const Text('Save'),
),
TextButton(
onPressed: () {
},
child: const Text('Edit'))
],
)
],
),
),
),
);
}
}
now this works fine the input field is populated with the value im getting from
ModalRoute.of(context)?.settings?.arguments as Map<dynamic, dynamic>;
and the contact is ready to be updated,
however when i click the add button to the contacts screen to open the add contact screen to add a brand new contact i get this error message
type 'Null' is not a subtype of type 'Map<dynamic, dynamic>' in type cast
clicking in the floating button or tapping the contact name navigates to the same page but doing with the button crashes the app i believe it has to be something about the arguments
as Map<dynamic, dynamic> making it non-nullable. You can do
final routeArgs =
ModalRoute.of(context)?.settings.arguments as Map<dynamic, dynamic>?;
Now you can do a null check before using routeArgs and those who accept null, you can do
initialValue: routeArgs?['name'],

CheckboxListTile state not changing when clicked inspite of calling setState

I am creating a List of checkboxes using ListView and CheckboxListTile as below. However, when I click or unclick on an item, the CheckboxListTile does not update and reflect the check or uncheck.
The code creating the ListView is below:
void _showDialog(context) {
showDialog(
context: context,
builder: (context) {
return Material(
child: ListView(
children: this.selectionList.map((e) {
return CheckboxListTile(
value: e.isSelected,
title: Text(e.name),
onChanged: (value) {
setState(() {
e.isSelected = value ?? false;
});
});
}).toList()),
);
});
}
Also including the full code:
import 'package:flutter/material.dart';
import 'package:recipevault/models/user_profile.dart';
class MultiSelectComponent extends StatefulWidget {
List<Interest> displayList; //initial values are marked as selected=true
MultiSelectComponent({Key? key, this.displayList = const <Interest>[]})
: super(key: key);
#override
_MultiSelectComponentState createState() => _MultiSelectComponentState();
}
class _MultiSelectComponentState extends State<MultiSelectComponent> {
List<Interest> selectionList = <Interest>[];
#override
void initState() {
// TODO: implement initState
super.initState();
setState(() {
this.selectionList = List.from(widget.displayList);
});
}
void _showDialog(context) {
showDialog(
context: context,
builder: (context) {
return Material(
child: ListView(
children: this.selectionList.map((e) {
return CheckboxListTile(
value: e.isSelected,
title: Text(e.name),
onChanged: (value) {
setState(() {
e.isSelected = value ?? false;
});
});
}).toList()),
);
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
GestureDetector(
onTap: () {
_showDialog(context);
},
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 1.2),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Select"),
Icon(Icons.arrow_downward),
],
),
),
),
Wrap(children: [
...selectionList
.where((element) => element.isSelected)
.map((e) => Chip(
label: Text(e.name),
onDeleted: () {
setState(() {
e.isSelected = false;
});
},
))
.toList(),
]),
]);
}
}
class Interest {
final String type, name;
final int id;
bool isSelected;
Interest(
{required this.id,
required this.name,
required this.type,
this.isSelected = false});
Interest.fromJson(Map<String, Object?> json)
: this(
id: json['id']! as int,
type: json['type']! as String,
name: json['name']! as String,
);
Map<String, Object?> toJson() {
return {
'id': this.id,
'type': this.type,
'name': this.name,
};
}
You have to extract the content of showDialog's builder to a separate StatefulWidget.

How to implement a process that uses flutter_secure_storage package to read and write the data [with Provider package]

I'm new to Flutter and Dart. I made a simple Todos app with Provider package and Consumer. I'm trying to implement a process that uses flutter_secure_storage package to read and write the list data on the device.
But I don't know how to implement it.
Checkbox widget requires a bool, while secure_storage requires the type of String. Therefore, it is necessary to convert both types. This also confuses me a little.
The code is below.
I would be happy if you could give me some advice.
main.dart
// Todos app example
import 'package:consumer_samp/list_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: ChangeNotifierProvider<ListModel>(
create: (context) => ListModel(),
child: MyHomePage('Todos app example'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage(this.title);
final String title;
final TextEditingController eCtrl = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter your ToDo item',
),
controller: eCtrl,
),
),
Consumer<ListModel>(
builder: (_, listModel, __) => FlatButton(
child: Text('Add'),
onPressed: () {
if (eCtrl.text != '') {
Map<String, dynamic> item = {
'value': false,
'text': eCtrl.text
};
listModel.addItem(item);
eCtrl.clear();
}
},
),
),
],
),
),
Center(
child: Consumer<ListModel>(builder: (_, listModel, __) {
var items = listModel.getItems;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 300,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int idx) =>
Container(
padding: const EdgeInsets.all(5),
color: Colors.green,
child: Row(
children: <Widget>[
Checkbox(
value: items[idx]['value'],
onChanged: (val) {
listModel.toggleValue(idx, val);
}),
Text(
items[idx]['text'],
style: TextStyle(
fontSize: 21, color: Colors.white),
),
],
),
),
),
),
],
);
}),
),
],
),
),
);
}
}
list_model.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class ListModel with ChangeNotifier {
List<Map<String, dynamic>> _items = [];
List<Map<String, dynamic>> get getItems => _items;
void addItem(Map<String, dynamic> item) {
_items.add(item);
notifyListeners();
}
void toggleValue(int idx, bool val) {
_items[idx]['value'] = val;
notifyListeners();
}
}
You can copy paste run full code below
You can use class TodoItem and save/load with JSON String
Provider logic for secure storage is in full code, too long to describe detail
code snippet
List<TodoItem> todoItemFromJson(String str) =>
List<TodoItem>.from(json.decode(str).map((x) => TodoItem.fromJson(x)));
String todoItemToJson(List<TodoItem> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class TodoItem {
TodoItem({
this.item,
this.checked,
});
String item;
bool checked;
factory TodoItem.fromJson(Map<String, dynamic> json) => TodoItem(
item: json["item"],
checked: json["checked"],
);
Map<String, dynamic> toJson() => {
"item": item,
"checked": checked,
};
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'dart:convert';
List<TodoItem> todoItemFromJson(String str) =>
List<TodoItem>.from(json.decode(str).map((x) => TodoItem.fromJson(x)));
String todoItemToJson(List<TodoItem> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class TodoItem {
TodoItem({
this.item,
this.checked,
});
String item;
bool checked;
factory TodoItem.fromJson(Map<String, dynamic> json) => TodoItem(
item: json["item"],
checked: json["checked"],
);
Map<String, dynamic> toJson() => {
"item": item,
"checked": checked,
};
}
class ListModel with ChangeNotifier {
FlutterSecureStorage _storage;
List<TodoItem> _items = [];
List<TodoItem> get getItems => _items;
initilaize() async {
print("initialize");
String jsonString = await _storage.read(key: "todo");
if (jsonString != null) {
_items = todoItemFromJson(jsonString);
notifyListeners();
}
}
ListModel.init(FlutterSecureStorage storage) {
print("init");
_storage = storage;
initilaize();
}
void update(FlutterSecureStorage storage) {
print("update");
_storage = storage;
}
void addItem(TodoItem item) {
_items.add(item);
notifyListeners();
}
void toggleValue(int idx, bool val) {
_items[idx].checked = val;
notifyListeners();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MultiProvider(
providers: [
Provider<FlutterSecureStorage>(create: (_) => FlutterSecureStorage()),
ChangeNotifierProxyProvider<FlutterSecureStorage, ListModel>(
create: (_) {
return ListModel.init(
Provider.of<FlutterSecureStorage>(_, listen: false));
},
update: (_, storage, listModel) => listModel..update(storage),
),
],
child: MyHomePage('Todos app example'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage(this.title);
final String title;
final TextEditingController eCtrl = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
actions: <Widget>[
Consumer2<ListModel, FlutterSecureStorage>(
builder: (_, listModel, storage, __) => IconButton(
icon: Icon(Icons.save),
onPressed: () async {
await storage.write(
key: "todo", value: todoItemToJson(listModel._items));
print("save done");
},
))
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Enter your ToDo item',
),
controller: eCtrl,
),
),
Consumer<ListModel>(
builder: (_, listModel, __) => FlatButton(
child: Text('Add'),
onPressed: () {
if (eCtrl.text != '') {
Map<String, dynamic> item = {
'value': false,
'text': eCtrl.text
};
listModel.addItem(
TodoItem(item: eCtrl.text, checked: false));
eCtrl.clear();
}
},
),
),
],
),
),
Center(
child: Consumer<ListModel>(builder: (_, listModel, __) {
var items = listModel.getItems;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
height: 300,
child: ListView.builder(
itemCount: items.length,
itemBuilder: (BuildContext context, int idx) =>
Container(
padding: const EdgeInsets.all(5),
color: Colors.green,
child: Row(
children: <Widget>[
Checkbox(
value: items[idx].checked,
onChanged: (val) {
listModel.toggleValue(idx, val);
}),
Text(
items[idx].item,
style: TextStyle(
fontSize: 21, color: Colors.white),
),
],
),
),
),
),
],
);
}),
),
],
),
),
);
}
}
As mentioned in the readme files by the following link
https://pub.dev/packages/flutter_secure_storage
The following code instantiate the storage, and the following code's must be in a async method as this returns a future.
final storage = new FlutterSecureStorage();
And you can pass the _items list as a value and you can set some key name
await storage.write(key: key, value: _items);
And then you could get that value by using the key name (which is set while storing)
List<Map<String, dynamic>> _value = await storage.read(key: key);
And then you could map the _value you get from storage and you can store it in _items. And then you could do various operations and queries inside your app now with all data you have.
Make a note! I haven't tried this approach in my code base. Just I said what I thought. Please try this in your code and comment me please.
The following code executes correctly use this in model, for storing data:
Future<void> _storingData() async {
final storage = new FlutterSecureStorage();
for (int i = 0; i < _items.length; i++) {
await storage
.write(key: _items[i]['text'], value: "${_items[i]['value']}")
.then((value) => print("success"));
}
}
For retrieving data:
Future<void> retrivingData() async {
final storage = new FlutterSecureStorage();
Map<String, String> allValues = await storage.readAll();
print(allValues);
allValues.forEach((key, value) {
bool val = bool.fromEnvironment(value, defaultValue: false);
Map<String, dynamic> item = {'value': val, 'text': key};
addItem(item);
});
}
and finally I stored all values again to a list.
You should do some changes in your code in main.dart according to the above methods based on your usage.

Save DropdownButtons and TextField selected Data into a List

I'm trying to create an AlertDialog that will receive a Future-List or a List of the data from the Dropdownbuttons and the TextField that are inside of these Alert. In my App when I call these function for the AlertDialog, there will be 3 DropdownButtons and 1 TextField so the User can select the info that he wants, and then when he presses "OK" from the AlerDialog, the data he selected will be inside of the list, so I can use it with Firestore.
This is my AlertDialog:
Future<List> createAlertDialog(BuildContext context){
return showDialog(context: context, builder: (ctx){
return AlertDialog(
title: Text('Text'),
content: Column(
children: [
DropdownButton(
value: iconNameSelected,
items: iconlistdrop,
hint: Text('Select Icon'),
onChanged: (value) {
iconNameSelected = value;
setState(() {});
},
),
DropdownButton(
value: activelabelSelected,
items: activelistdrop,
hint: Text('Select Active label'),
onChanged: (value1) {
activelabelSelected = value1;
setState(() {});
},
),
DropdownButton(
value: inactivelabelSelected,
items: inactivelistdrop,
hint: Text('Select InActive label'),
onChanged: (value2) {
inactivelabelSelected = value2;
setState(() {});
},
),
TextField(
),
],
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: (){
final values = [];
Navigator.of(ctx).pop(values);
},
)
],
);
});
}
Here's how I attempted to call it inside my InkWell widget:
createAlertDialog(context).then((value){
printf(value[0]);
printf(value[1]);
printf(value[2]);
printf(value[3]);
}
Here's some extra stuff from the Data I have inserted inside the DropdownButtons:
List<DropdownMenuItem<String>> iconlistdrop = [];
List<DropdownMenuItem<String>> activelistdrop = [];
List<DropdownMenuItem<String>> inactivelistdrop = [];
String iconNameSelected = null;
String activelabelSelected = null;
String inactivelabelSelected = null;
void loadIcon () {
iconlistdrop = [];
iconlistdrop.add(DropdownMenuItem(
child: Text('LightBulb'),
value: 'lightbulbOutline',
));
iconlistdrop.add(DropdownMenuItem(
child: Text('Lock'),
value: 'lock',
));
iconlistdrop.add(DropdownMenuItem(
child: Text('Check'),
value: 'check',
));
}
void activelbl () {
activelistdrop = [];
activelistdrop.add(DropdownMenuItem(
child: Text('On'),
value: 'On',
));
activelistdrop.add(DropdownMenuItem(
child: Text('Locked'),
value: 'Locked',
));
}
void inactivelbl () {
inactivelistdrop = [];
inactivelistdrop.add(DropdownMenuItem(
child: Text('Off'),
value: 'Off',
));
inactivelistdrop.add(DropdownMenuItem(
child: Text('Locked'),
value: 'Unlocked',
));
}
loadIcon();
activelbl();
inactivelbl();
My Class:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
var temperature;
var humidity;
Future getWeather() async {
http.Response response = await
http.get('http://api.openweathermap.org/data/2.5/weather?
q=Curitiba&units=metric&appid=8c1ce29a0b974e97562564d892cd5a97');
var results = jsonDecode(response.body);
setState(() {
this.temperature = results['main']['temp'];
this.humidity = results['main']['humidity'];
});
}
#override
void initState () {
this.getWeather();
super.initState();
}
#override
Widget build(BuildContext context) {
final AuthService _auth = AuthService();
final user = Provider.of<User>(context);
Future getSubCollection(){
return Firestore.instance.collection('dadosusuarios').document(user.uid).collection('buttons').getDocuments();
}
I would use a map for the values and separate the dialog to another widget and give it a constructor in case you might want it to have initial values.
import 'package:flutter/material.dart';
class MyAlertDialog extends StatefulWidget {
final Map<String, dynamic> initialValues;
const MyAlertDialog({
Key key,
this.initialValues,
}) : super(key: key);
#override
_MyAlertDialogState createState() => _MyAlertDialogState();
}
class _MyAlertDialogState extends State<MyAlertDialog> {
Map<String, dynamic> _values;
TextEditingController _controller;
#override
initState() {
super.initState();
_values = widget.initialValues ??
{'input1': 'One', 'input2': 'Two', 'input3': 'Free', 'input4': 'Four'};
_controller = TextEditingController(text: _values['input4']);
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Text'),
content: Column(
children: [
DropdownButton(
value: _values['input1'],
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text('Select Icon'),
onChanged: (value1) {
setState(() {
_values['input1'] = value1;
});
},
),
DropdownButton(
value: _values['input2'],
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text('Select Active label'),
onChanged: (value2) {
setState(() {
_values['input2'] = value2;
});
},
),
DropdownButton(
value: _values['input3'],
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text('Select InActive label'),
onChanged: (value3) {
setState(() {
_values['input3'] = value3;
});
},
),
TextField(
controller: _controller,
),
],
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: () {
_values['input4'] = _controller.text.trim();
Navigator.of(context).pop(_values);
},
)
],
);
}
}
Here i look if there is a value passed from the constructor. If not put some defaults. Update the map with each user input change and finally once the dialog popped return the map. Using map here is better in my opinion and would make pushing the values to Firestore easier.
var result = await showDialog(
context: context,
builder: (ctx) {
return MyAlertDialog(initialValues: /* Your default values if exist*/ );
});
print(result);

Dropdown in flutter from LIST

Displaying the data from my API based on the Dropdown selected value. I want to display on the same page. The data from the server(response) is displaying on the console. But still, this data is not displaying.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
//import 'package:json_parsing_example/model2.dart';
//import 'package:json_parsing_example/models.dart'
List<YouModel> youModelFromJson(String str) => List<YouModel>.from(json.decode(str).map((x) => YouModel.fromJson(x)));
String youModelToJson(List<YouModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class YouModel {
String columnName;
YouModel({
this.columnName,
});
factory YouModel.fromJson(Map<String, dynamic> json) => YouModel(
columnName: json["column_name"],
);
Map<String, dynamic> toJson() => {
"column_name": columnName,
};
}
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
String username;
String name;
UserModel({
this.username,
this.name,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
username: json["username"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"username": username,
"Name": name,
};
}
class Addoffers2 extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Addoffers2State();
}
class _Addoffers2State extends State<Addoffers2> {
List<String> _companies = [];
bool _isLoading = false;
String _selectedCompany;
#override
void initState() {
super.initState();
_selectedCompany=null;
_getcompanylist();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
_getcompanylist() async {
setState(() {
_isLoading = true;
});
print("getting..");
final responseStr =
await http.get('http://10.0.2.2/Flutter/GetCompanieslist.php');
//String responseStr = await loadFromAssets();
final listData = youModelFromJson(responseStr.body);
for(int i=0;i<listData.length;i++)
{
print('this is the list :'+listData[i].columnName);
// _companies.add(listData[i].columnName);
}
// above method is the standard method to get creating a model class and then get the list of strings
// I have just shown you but example is according to you code .
// this above loadFromAssets is that you hit the api and you get the json string response
// i have created a dummy json file where i can the String.
// Else everything is the same as below you just have to pass the response.body to the json.decode method.
var jsonData = json.decode(responseStr.body);
for (var u in jsonData) {
_companies.add(u.toString().substring(14, u.toString().length - 1));
}
for (int i = 0; i < _companies.length; i++) {
print(_companies[i].toString());
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
//double width = MediaQuery.of(context).size.width;
//double height = MediaQuery.of(context).size.height;
return MaterialApp(
//color: Colors.red,
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
backgroundColor: Theme.of(context).backgroundColor,
title: Text("Add.."),
),
body: Container(
color: Colors.blue,
// just put your height i have modified it replace it by height / 8
child: _isLoading
? CircularProgressIndicator()
: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//MainAxisAlignment: MainAxisAlignment.spaceBetween,
Text('Choose..'),
DropdownButtonHideUnderline(
child: DropdownButton(
// hint: Text('Choose Company'), // Not necessary for Option 1
value: _selectedCompany,
onChanged: (newValue) {
setState(() {
_selectedCompany = newValue;
// here i have taken the boolen variable to show and hide the list if you have not seleted the value from the dropdown the it will show the text and if selected the it will show you the list.
});
print(_selectedCompany);
},
items: _companies.map((company) {
return DropdownMenuItem(
child: new Text(company.toString()),
value: company,
);
}).toList(),
),
),
],
),
),
),
// this is to to check for the initial if string is null then show the text widget.
// else if the value is selected then it will show the listview
_selectedCompany == null
? Text('Select the dropdown value for list to appear.')// sample text you can modify
: Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
height: 100,
color: Theme.of(context).backgroundColor,
child: new FutureBuilder(
future: _getUsers(
_selectedCompany), // a Future<String> or null
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child: Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.white,
),
));
}
if (snapshot.hasError) {
return Center(
child: new Text(
'Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(
5.0, 8.0, 5.0, 8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context,
int index) {
List<UserModel> user =
snapshot.data;
var username =
user[index].username;
var stuname =
user[index].name;
print(
'This is the user name :$username');
print(
'This is the name : $stuname');
//var title=snapshot.data[index]["Title"];
// new Text(parsedDate.toString());
return StudentList2(
regdNo: username,
name: stuname);
}),
),
);
}
}),
),
),
],
)),
)),
);
}
}
Future<String> loadFromAssets2() async {
return await rootBundle.loadString('json/parse2.json');
}
// the above method is just for the sample purpose where you get you json String after hitting the api call for _getUsers method
Future<List<UserModel>> _getUsers(String selectedcompany) async {
// here you call you api and you get the response
var url = 'https://10.0.2.2/Flutter/getstudentdata.php;
var data = { 'company': selectedcompany};
// Starting Web Call with data.
var response = await http.post(url, body: json.encode(data));
print(response.body);
//String responseStr = await loadFromAssets2();
final userModel = userModelFromJson(response.body);
// I have just made the model class for where fromt he below you get the complete object and then added to the list and returned.
List<UserModel> users = [];
users.add(userModel);
print('This is the name : ${users[0].name}'); // Even this also not getting printed
return users;
}
class StudentList2 extends StatefulWidget {
final regdNo;
final name;
const StudentList2({
Key key,
this.regdNo,
this.name,
}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<StudentList2> {
bool visible = false;
#override
Widget build(BuildContext context) {
print(widget.regdNo.toString());
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: new Card(
color: Theme.of(context).primaryColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 2.0),
child: Container(
child: new Text(
widget.regdNo.toUpperCase(),
style: TextStyle(
color: Colors.yellowAccent,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
ListTile(
title: new Text(
widget.regdNo,
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
subtitle: new Text(
(widget.name),
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
),
),
//
],
)),
);
}
}
I am able to retrieve the data from the server and print it on the console. Still, the data is not displaying. I do not know where I did the mistake.
So I have completely updated the answer and there are many things that you don't follow according to the global standard.
So I have listed some of the key things that you should follow :
Following is you company list json :
[
{
"column_name": "ABC"
},
{
"column_name": "XYZ"
}
]
Following is the get user json that you will get :
{"username":"1111","Name":"ABC" }
And Later the model class I have create accordingly to the json that you provided and then you can create your own based in the added json.
There are Two model classes that I have created :
First model class is for the company :
// To parse this JSON data, do
//
// final youModel = youModelFromJson(jsonString);
import 'dart:convert';
List<YouModel> youModelFromJson(String str) => List<YouModel>.from(json.decode(str).map((x) => YouModel.fromJson(x)));
String youModelToJson(List<YouModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class YouModel {
String columnName;
YouModel({
this.columnName,
});
factory YouModel.fromJson(Map<String, dynamic> json) => YouModel(
columnName: json["column_name"],
);
Map<String, dynamic> toJson() => {
"column_name": columnName,
};
}
second mode class is for the user :
// To parse this JSON data, do
//
// final userModel = userModelFromJson(jsonString);
import 'dart:convert';
UserModel userModelFromJson(String str) => UserModel.fromJson(json.decode(str));
String userModelToJson(UserModel data) => json.encode(data.toJson());
class UserModel {
String username;
String name;
UserModel({
this.username,
this.name,
});
factory UserModel.fromJson(Map<String, dynamic> json) => UserModel(
username: json["username"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"username": username,
"Name": name,
};
}
Below is the main ui file just Check the comments that I have made so that it will be helpful for you .
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:json_parsing_example/model2.dart';
import 'package:json_parsing_example/models.dart';
void main() => runApp(Addoffers());
class Addoffers extends StatefulWidget {
#override
State<StatefulWidget> createState() => _AddoffersState();
}
class _AddoffersState extends State<Addoffers> {
List<String> _companies = [];
bool _isLoading = false;
String _selectedCompany;
#override
void initState() {
super.initState();
_selectedCompany=null;
_getcompanylist();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
_getcompanylist() async {
setState(() {
_isLoading = true;
});
print("getting..");
/* final response =
await http.get('http://10.0.2.2/Flutter/GetCompanieslist.php'); */
String responseStr = await loadFromAssets();
final listData = youModelFromJson(responseStr);
for(int i=0;i<listData.length;i++)
{
print('this is the list :'+listData[i].columnName);
// _companies.add(listData[i].columnName);
}
// above method is the standard method to get creating a model class and then get the list of strings
// I have just shown you but example is according to you code .
// this above loadFromAssets is that you hit the api and you get the json string response
// i have created a dummy json file where i can the String.
// Else everything is the same as below you just have to pass the response.body to the json.decode method.
var jsonData = json.decode(responseStr);
for (var u in jsonData) {
_companies.add(u.toString().substring(14, u.toString().length - 1));
}
for (int i = 0; i < _companies.length; i++) {
print(_companies[i].toString());
}
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
//double width = MediaQuery.of(context).size.width;
//double height = MediaQuery.of(context).size.height;
return MaterialApp(
//color: Colors.red,
home: Scaffold(
backgroundColor: Colors.red,
appBar: AppBar(
backgroundColor: Theme.of(context).backgroundColor,
title: Text("Add.."),
),
body: Container(
color: Colors.blue,
// just put your height i have modified it replace it by height / 8
child: _isLoading
? CircularProgressIndicator()
: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
//MainAxisAlignment: MainAxisAlignment.spaceBetween,
Text('Choose..'),
DropdownButtonHideUnderline(
child: DropdownButton(
// hint: Text('Choose Company'), // Not necessary for Option 1
value: _selectedCompany,
onChanged: (newValue) {
setState(() {
_selectedCompany = newValue;
// here i have taken the boolen variable to show and hide the list if you have not seleted the value from the dropdown the it will show the text and if selected the it will show you the list.
});
print(_selectedCompany);
},
items: _companies.map((company) {
return DropdownMenuItem(
child: new Text(company.toString()),
value: company,
);
}).toList(),
),
),
],
),
),
),
// this is to to check for the initial if string is null then show the text widget.
// else if the value is selected then it will show the listview
_selectedCompany == null
? Text('Select the dropdown value for list to appear.')// sample text you can modify
: Padding(
padding: const EdgeInsets.all(0.0),
child: Container(
height: 100,
color: Theme.of(context).backgroundColor,
child: new FutureBuilder(
future: _getUsers(
_selectedCompany), // a Future<String> or null
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Container(
child: Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.white,
),
));
}
if (snapshot.hasError) {
return Center(
child: new Text(
'Error ${snapshot.error}'),
);
} else {
return Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(
5.0, 8.0, 5.0, 8.0),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder:
(BuildContext context,
int index) {
List<UserModel> user =
snapshot.data;
var username =
user[index].username;
var stuname =
user[index].name;
print(
'This is the user name :$username');
print(
'This is the name : $stuname');
//var title=snapshot.data[index]["Title"];
// new Text(parsedDate.toString());
return StudentList2(
regdNo: username,
name: stuname);
}),
),
);
}
}),
),
),
],
)),
)),
);
}
}
Future<String> loadFromAssets2() async {
return await rootBundle.loadString('json/parse2.json');
}
// the above method is just for the sample purpose where you get you json String after hitting the api call for _getUsers method
Future<List<UserModel>> _getUsers(String selectedcompany) async {
/* var data = await http.post("http://10.0.2.2/Flutter/getstdata.php", body: {
"company": selectedcompany,
//print(data.body);
}); */
// here you call you api and you get the response
String responseStr = await loadFromAssets2();
final userModel = userModelFromJson(responseStr);
// I have just made the model class for where fromt he below you get the complete object and then added to the list and returned.
List<UserModel> users = [];
users.add(userModel);
print('This is the name : ${users[0].name}');
//final x=users.length.toString();
//debugPrint("records:" + users.length.toString());
//debugPrint("kkk:" + absentees.length.toString());
return users;
}
class StudentList2 extends StatefulWidget {
//MyHomePage(String branch);
final regdNo;
final name;
const StudentList2({
Key key,
this.regdNo,
this.name,
}) : super(key: key);
//final String branch;
//const StudentList({Key key, this.branch}) : super(key: key);
//MyHomePage(String branch);
// final String title;
// final String branch="";
// MyHomePage(String branch, {Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<StudentList2> {
bool visible = false;
//bool _btnEnabled = false;
//bool _validate = false;
// var _firstPress = true ;
//Color _iconColor = Colors.yellow;
//Color _iconColor2 = Colors.white;
//var poll;
//DateTime parsedDate;
#override
Widget build(BuildContext context) {
print(widget.regdNo.toString());
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: new Card(
color: Theme.of(context).primaryColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 2.0),
child: Container(
child: new Text(
widget.regdNo.toUpperCase(),
style: TextStyle(
color: Colors.yellowAccent,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
),
),
),
ListTile(
title: new Text(
widget.regdNo,
style: TextStyle(
color: Colors.black,
fontSize: 14.0,
),
),
subtitle: new Text(
(widget.name),
style: TextStyle(
color: Colors.black,
fontSize: 15.0,
),
),
),
//
],
)),
);
}
}
// This is not the good approach to create a model class just check the sample model class that i have created.
class User {
//final int index;
final String username;
final String name;
//final Float cgpa;
User(
this.username,
this.name,
);
}
And below is the sample Gif file for you :
As stated by #pskink the method _getcompanylist() is async. An async function runs asynchronously, which means that the rest of the program doesn't wait for it to complete. You can use a future builder to deal whit that or you can simply wait for it by using the await function. I believe that for your code snippet future builder is the better choice.